From serial to radio, and back again…
Tiny low power radio (LPR) modules operating at UHF ISM frequencies like 433MHz and 868MHz are available on the market that are ideal for remote measurement and control applications. They are usually controlled over an SPI interface. Poring over datasheets and writing a driver is, however, not everyone’s delight, and we have therefore designed a small board that carries an LPR module along with an ATmega328 microcontroller and a serial interface. Pre-programmed firmware allows the board to be used as a simple gateway for strings of characters between the serial interface and the ether.
FCC (USA) and ETSI (European) approved and certified LPR modules (Figure 1) operating at 3.3V are available from the Chinese manufacturer HopeRF. Also known as SRDs (short range devices) they are based on the Silicon Labs Si4421  and include the necessary support circuitry and a quartz crystal. The IC makes it relatively straightforward for advanced embedded systems developers to add wireless data transfer to their projects. The transceiver is controlled over an SPI interface: specified byte sequences act as commands that switch the device between transmit and receive modes, alter the data rate, and so on. The SPI interface is also used to transfer characters to be transmitted and characters that have been received.
Figure 1. The low-power radio module from Chinese manufacturer HopeRF (433MHz version shown here with header pins soldered on) is controlled over SPI. Versions are also available for the 868 MHz SRD band.
A designer with enough experience and enough patience could write the necessary firmware to drive the SPI interface, and, on top of that, a small Si4421 driver library to assemble the command byte sequences for the device and send them to it. In the simplest case such a library would provide a function to allow the sending of character strings generated by the application program over the wireless link; a further function could be provided to store received characters in a circular buffer for subsequent processing by the application program.
Shift those strings
Although example microcontroller-specific SPI drivers and Si4421 libraries can be found on the Internet, adapting these to your own firmware and project will take some time. In this article we take a different route, similar to that adopted with considerable success by FTDI for its USB interface drivers. FTDI’s USB-to-TTL converter ICs are popular chiefly because they are so easy to interface to the serial ports on microcontrollers. The designer is freed from having to do any special USB driver programming, and only has to worry about sending and receiving characters using the UART. Our board is designed so that you can simply plug in a HopeRF wireless module. As well as a power supply, the board includes an ATmega328 microcontroller and a 2-by-5 header that carries the UART RX and TX signals (Figure 2). The pin-out is based on the EEC specification presented in Elektor . Thanks to its firmware the 8-bit microcontroller on the board looks after all driving of the radio module. Elektor supply the board with preprogrammed example firmware, turning the board into a gateway between a UART and the “ether”. If a string (of up to 62 characters) is sent to the gateway, terminated by a <CR> character, it will be sent over the wireless link. In the reverse direction a string received over the wireless link is output over the serial interface: the data rate for serial communication in either direction is set at 9600baud.
The circuit diagram (Figure 3) is not particularly complicated. The central component is the ATmega328 microcontroller, which can be flashed with new firmware over ISP connector K2. The crystal and power supply circuits follow the standard datasheet arrangement. A LED and a button are connected to port pins PD4 and PD5. These provide a minimal user interface that can come in handy during firmware development on the board, as well as in test and in actual use.
Figure 3. The microcontroller is connected to the wireless module over a total of eight wires. Five of these are used by the pre-programmed firmware.
The RFM12B wireless module is connected to the microcontroller over a total of eight wires. Four of these comprise the SPI interface mentioned above, and these are connected directly to the hardware SPI port on the ATmega328. This is the same interface as the microcontroller uses when it is being programmed: pull-up resistor R3 takes the module’s select input /SEL high during programming, so that the wireless module does not try to interpret the bytes being transferred. When the microcontroller wants to send bytes to the radio module it must pull this signal low in software via port pin PB2. The pre-programmed software also makes use of the FFIT pin on the wireless module. The signals /FFS, /INT and /IRQ are connected to port pins on the microcontroller as well in order to allow you to make more flexible use of the wireless module in your own code.
K1 is the ECC connector mentioned above. The middle pair of pins provides access to the RX and TX signals of the microcontroller’s UART, while the other pins of the connector are taken to port pins PD2, PD3 and PD6 of the microcontroller. These could be used for flow control on the serial port or for dedicated functions. The lower left pin is connected to the 5V microcontroller supply. The gateway board can supply a small current over this pin, sufficient for an adapter to convert the TTL-level serial signals to RS-232 or RS-485. An RS-485 adapter for connection to the ECC was presented in the March 2014 edition .
The top right pin, conversely, allows the gateway board to be supplied with power over the connector, for example from a motherboard. Alternatively the board can be powered via K3 and voltage regulator IC2: the power source is selected using jumper JP1. Green LED D2 indicates when power is present. The gateway‘s 5-V operating voltage is used by a small small voltage converter to generate a 3.3V supply for a radio module; at the same time the 5V signals are converted down to 3.3V and vice versa. Thanks to the use of SMD components the gateway board is very compact and the artwork is always available for downloading from the project page set up for this article. There you can also your ready populated and tested Gateway board together with radio module type RFM12B-433. If you use a different radio module, do make sure you have a 3.3V version. Although there are surplus stocks around, the 5V version of the radio module is no longer manufactured.
On ANT1 connect a suitable piece of stiff wire around 17 cm long (quarter wavelength at 433MHz).
433MHz, 868MHz or ?? MHz?
That depends on the country you live in. One or more of the above frequency ranges may be allocated to ISM (industrial/scientific/medical) applications allowing the use of type-approved low-power radio modules by private individuals within the limits of national, state, or regional legislation.
The ECC leaves two uncommitted pins for use in special applications (shown in white in the figure in the text box). We have routed the UART RX signal to one of these pins so that RX, TX and ground are available on the connector as three adjacent pins in a straight line. As a result the BOB USB-to-serial converter  can be connected directly to the board for test purposes or even in a final project: see Figure 4. An FTDI 5V USB-to-serial cable can be fitted, but the pins in the socket need to be moved around so that the pinout matches .
Connect the USB adapter to a PC. Connect a second wireless module to a microcontroller board with RX, TX and ground pins available: connect TX on the microcontroller to RX on the ECC and vice versa. You can now send strings of up to 62 characters from the PC to the microcontroller board (Figure 5). At the PC end you can use a simple terminal program such as HTerm, which can be downloaded from the Internet free of charge . HTerm should be configured so that a complete line of characters is sent when the ‘Return’ key on the keyboard is pressed, with a <CR> (ASCII 13) character appended. To send data in the opposite direction you will need to arrange for the microcontroller to output a string using its UART, again terminated by a <CR> character.
The ready-programmed version of the wireless gateway has two modes: ‘send’ and ‘listen’. When power is applied the gateway enters send mode, with the yellow LED not illuminated. In this mode the board will accept strings over its serial connection and send them out over the air, but will not act as a receiver for data. Press the button, and the gateway will switch to listen mode, indicated by the LED lighting. The board will now listen for characters sent over the ether. When a <CR> character is received the entire string is output over the serial interface.
A gateway can also be switched between its two modes of operation by sending the sequence ‘@@c<CR>’ to its serial interface (the ‘c’ stands for `change’). The command ‘@@s<CR>’ (‘s’ for ‘swap’) causes both gateways to switch between one mode and the other. However, the second gateway only changes mode when it receives a string over the air that indicates that the first gateway has switched to listen mode. A gateway connected over its serial interface responds to the two special commands with the acknowledgement strings `MDL’ (‘mode listen’) or ‘MDS’ (‘mode send’). In this way we receive confirmation of which mode the gateway is now in and can proceed to test whether the connection is working via the serial interface (Figure 6).
Assuming that only a unidirectional connection is wanted, then initially the remote module will be set up in listen mode and strings can be sent to it. The direction of communication can be reversed at any time by sending the command ‘@@s<CR>’ to the gateway currently in send mode. The gateway forwards this command to the other end of the link, as a message which could be interpreted as meaning ‘I have finished transmitting and am
now switching to listen mode and will wait for your transmissions’. This behavior does not cover all possible application requirements, of course. For example, if a sensor board wants to send regular readings to a control system running on a PC, then the module connected to the PC will have to be left
in listen mode. However, this means that the PC is unable to send out commands, such as to tell the sensor board to change the interval between readings.
The gateway software therefore includes the possibility that both ends of the connection are in listen mode. Despite being in listen mode, characters can be sent to the serial interface on either board to be sent over the wireless link. For technical reasons, in this mode it is necessary to terminate the string to be sent with two <CR> characters (that is, press the Return key twice in the terminal program). In this bidirectional configuration either participant can be sending or receiving at any time and it is up to the application software (running on the PC or on the microcontroller board) to ensure that collisions do not occur.
The firmware that is programmed into the ready made boards is of course available as a sourcecode download from the Elektor website in the form of an Atmel Studio 6 project . The code is written in a modular fashion, based on the Embedded Firmware Library (EFL) . The microcontroller file for the ATmega328 sits at the bottom of the software stack, exposing to higher levels functions such as:
uint8 SPIMaster_TransceiveByte(int8 Handle, uint8 Databyte)
which sends a character over the SPI interface. This particular function is used by the low-level driver for the wireless module, which is located in the board file (BoardEFL.c). The low-level driver provides the following functions:
void WirelessModule _Command(uint8 WirelessBlockIndex, uint16 Command)
void WirelessModule_SendData(uint8 WirelessBlockIndex,
uint8* DataBuffer, uint8 DataLength)
void WirelessModule_ReceiveData(uint8 WirelessBlockIndex)
In this case WirelessBlockIndex is always zero, although in principle it would be possible to connect more than one wireless module to the microcontroller. The WirelessModule_Command() function can be used to send a two-byte command to the wireless module as specified in its datasheet . This function simply calls the lower-level function SPIMaster_TransceiveByte() twice while holding the /SEL pin low. The function WirelessModule_SendData() is responsible for sending characters. The function first sends the byte 0xB8 to the wireless module; subsequent bytes are then transmitted. The developers of the wireless module specify that a twobyte synchronization pattern (0x2DD4) should be sent before the payload data to improve the reliability of transmission. After all the payload bytes for transmission have been transferred over the SPI bus, the function appends a byte CHAR_WIRELESS_ENDOFTRANSMISSION to the end of the string: in BoardEFL.h this is defined to take the value 0x04.
The function WirelessModule_ReceiveData() is repeatedly called in listen mode. Its most important component is a loop which continuously receives characters. At the head of this loop is a small nested loop in which the FFIT pin is checked. When this pin is high it means that the wireless module has received a character and written it to its internal FIFO buffer. The function now takes /SEL low and sends the command 0xB000 to the module to fetch the received character. The code then returns to checking the FFIT pin. The wireless module starts to write characters into its FIFO as soon as it sees the synchronization pattern. The characters received are written to a circular buffer dedicated to the wireless module. Like all circular buffers in the EFL it has a default size of 64 bytes. When the character 0x04 is received the receive function exits its main loop. Note that the 0x04 character is not written to the circular buffer. By clearing a flag in a specified register the wireless module is instructed not to write further characters into its FIFO until the synchronization pattern is seen again. This will occur at the start of the next received string. The receive loop is also exited when the button is pressed or if a byte is received over the serial interface: this allows the bidirectional function described above to be implemented.
Configuring the Wireless Module
We have now covered all the functions that are used to control the wireless module. As usual for the EFL, the functions are written in a hardware-independent fashion and can be used on other boards with different wiring. The wiring configuration of the gateway board, including its button and LED, are defined in the function Board_Init() in the board file , which will need to be changed suitably to use the code with different hardware. Before we can send and receive characters we need to configure the wireless module. What command bytes do we have to send to the board using the function WirelessModule_Command() to achieve this? The only way to find out involves painstaking study of the module’s datasheet. For that reason we have written the library module WirelessInterfaceEFL. If the function WirelessInterface_LibrarySetup() is called when an application starts up it will carry out the basic configuration of the wireless module, for example setting its data rate to 9600 baud. The individual commands are commented in the code, including a cross-reference to the relevant section of the datasheet where you can find a more detailed explanation of what is going on.
The function WirelessInterface_Send() configures the module for transmission, activating the TX register and the transmitter. This function should only be called immediately before actually sending data in order to conserve power. The function WirelessInterface_Listen() switches the wireless module into FIFO mode, which, among other things, means that the FFIT pin mentioned above will signal the arrival of a new character. The receiver is also enabled.
The remaining piece of the puzzle is a mechanism which in listen mode will periodically read the character received over the air from the circular buffer where they are stored and send them out over the serial interface, and, in the opposite direction, take characters received over the serial interface from the other circular buffer where they are stored (by the UART interrupt code) and transmit them. These tasks are carried out by the library OneToOneGatewayEFL, which was described in a previous article . The library inspects the two circular buffers in turn to see if a <CR> character has been received. If so, the characters received up to that point are sent out over the appropriate channel in one go. The OneToOneGatewayEFL library has been extended for this project to handle the special commands, which always start with the sequence ‘@@’ and which are three bytes long.
A call to the function OneToOneGateway_SetSpecialCommandFunction() is used to tell the library what callback is to be called when a special command is received over the communications channel. In our case we specify the function SpecialCommandGateway(), which is implemented in the main part of the code. The parameter to this callback function is the third character of the command, here ‘c’ or ‘s’. Depending on which character is received the gateway will switch between its listen and send modes. The command is also sent over the wireless link to the other gateway module: this behavior can be configured using the second parameter to OneToOneGateway_SetSpecialCommandFunction(), which we have set to ‘TRUE’.
To test the gateways we can connect two units, each equipped with a BOB or FTDI cable, to two USB sockets on the same host computer. It is now possible to open two copies of the terminal program and send characters to and fro. If you enter the special command ‘@@c<CR>’ in both terminal windows both gateways will independently switch to listen mode. Each wireless module should reply with the string ‘MDL’ (‘mode listen’) over its serial interface, and hence in its terminal window. This means that serial communication is working correctly: if this test fails, it sometimes helps to try disconnecting and reconnecting the USB-to-serial adapter. You can now try sending short messages from one terminal to the other. Don’t forget that you need to press the Return key twice at the end of each message!