The example application shown below is for a debug station. The debug station receives a radio packet, echos its contents to the serial terminal, and replies with an acknowledgement packet. Note that the acknowledgement packet is different from an auto-ack packet: the radio sends an auto-ack behind the scenes. For more information on this and other details on using the radio, see the radio entry in my mechatronics lab guide.
Arduino and AVR versions of the radio code can be found at my Google Code repository. Miguel Moreto kindly provided Arduino sketches for these examples, which can also be found in the SVN repository.
packet.h
Programs that communicate with each other over the radio must use a common packet.h header. This header is how they share the structure of expected packets. Here’s an example defining two possible packet types, the MESSAGE packet and the ACK packet:
#ifndef PACKET_H_ #define PACKET_H_ #include /***** Add labels for the packet types to the enumeration *****/ typedef enum _pt { MESSAGE, ACK, } PACKET_TYPE; /***** Construct payload format structures *****/ // structures must be 29 bytes long or less. // structure for the MESSAGE packet type typedef struct _msg { uint8_t messageid; uint8_t address[5]; uint8_t messagecontent[23]; } pf_message_t; // structure for the ACK packet type typedef struct _ack { uint8_t messageid; } pf_ack_t; /***** Add format structures to the union *****/ /// The application-dependent packet format. Add structures to the union that correspond to the packet types defined /// in the PACKET_TYPE enumeration. The format structures may not be more than 29 bytes long. The _filler array is /// included to ensure that the union is exactly 29 bytes long. typedef union _pf { uint8_t _filler[29]; // makes sure the packet will be exactly 32 bytes long - this array should not be accessed directly. pf_message_t message; pf_ack_t ack; } payloadformat_t; /***** Leave the radiopacket_t structure alone. *****/ typedef struct _rp { PACKET_TYPE type; uint16_t timestamp; payloadformat_t payload; } radiopacket_t; #endif /* PACKET_H_ */ |
receiver.cpp
I’ll use C-style code for the example programs, with Arduino function calls. Assume that the project is linked against the Arduino libraries. I use Arduino pin 13 as an LED output; on the Arduino Uno (and similar boards), you can’t use the LED on pin 13 because the radio uses it for SPI communications (the SPI clock pin is hardwired to pin 13). If you’re using an Arduino Uno/Duemilanove/Pro/etc. then remove the writes to pin 13.
The following program will wait until it receives a MESSAGE packet, then respond to the sender with an ACK:
#include #include "arduino/Arduino.h" #include "radio/radio.h" uint8_t station_addr[5] = { 0xE4, 0xE4, 0xE4, 0xE4, 0xE4 }; // this must be volatile because it's used in an ISR (radio_rxhandler). volatile uint8_t rxflag = 0; radiopacket_t packet; int main() { // string buffer for printing to UART char output[128]; sei(); init(); // start the serial output module Serial.begin(100000); pinMode(13, OUTPUT); pinMode(10, OUTPUT); // radio's Vcc pin is connected to digital pin 10. // reset the radio digitalWrite(10, LOW); delay(100); digitalWrite(10, HIGH); delay(100); // initialize the radio, including the SPI module Radio_Init(); // configure the receive settings for radio pipe 0 Radio_Configure_Rx(RADIO_PIPE_0, station_addr, ENABLE); // configure radio transciever settings. Radio_Configure(RADIO_2MBPS, RADIO_HIGHEST_POWER); // print a message to UART to indicate that the program has started up snprintf(output, 128, "STATION START\n\r"); Serial.print(output); for (;;) { if (rxflag) { if (Radio_Receive(&packet) != RADIO_RX_MORE_PACKETS) { // if there are no more packets on the radio, clear the receive flag; // otherwise, we want to handle the next packet on the next loop iteration. rxflag = 0; } // This station is only expecting to receive MESSAGE packets. if (packet.type != MESSAGE) { snprintf(output, 128, "Error: wrong packet type (type %d).\n\r", packet.type); Serial.print(output); continue; } // Set the transmit address to the one specified in the message packet. Radio_Set_Tx_Addr(packet.payload.message.address); // Print out the message, along with the message ID and sender address. snprintf(output, 128, "Message ID %d from 0x%.2X%.2X%.2X%.2X%.2X: '%s'\n\r", packet.payload.message.messageid, packet.payload.message.address[0], packet.payload.message.address[1], packet.payload.message.address[2], packet.payload.message.address[3], packet.payload.message.address[4], packet.payload.message.messagecontent); Serial.print(output); // Reply to the sender by sending an ACK packet. packet.type = ACK; if (Radio_Transmit(&packet, RADIO_WAIT_FOR_TX) == RADIO_TX_MAX_RT) { snprintf(output, 128, "Could not reply to sender.\n\r"); Serial.print(output); } else { snprintf(output, 128, "Replied to sender.\n\r"); Serial.print(output); } // delay for a bit to give a visible flash on the LED. delay(50); digitalWrite(13, LOW); } } return 0; } /** * This function is a hook into the radio's ISR. It is called whenever the radio generates an RX_DR (received data ready) interrupt. */ void radio_rxhandler(uint8_t pipenumber) { // just set a flag and toggle an LED. The flag is polled in the main function. rxflag = 1; digitalWrite(13, HIGH); } |
transmitter.cpp
Finally, this program will send a single message packet to the debug station and read back the acknowledgement. Once it receives the ACK packet it will spin in an infinite loop until the microcontroller is reset (remember on AVR the main function should never return).
N.b. you should always read a packet out of the radio, even if you don’t use its contents. Otherwise the radio won’t assert any more interrupts when it receives new packets (it assumes you know there are data in the receive FIFO, so it doesn’t tell you that there are more).
To run this program, make sure there is a station running the receiver.cpp program. Run the transmit.cpp program. The receiver should print the message to its serial port, and send an ACK reply. The transmitter should get the ACK, and the receiver should print out a message indicating that the ACK was successfully sent back.
#include "arduino/Arduino.h" #include "radio/radio.h" #include volatile uint8_t rxflag = 0; // packets are transmitted to this address uint8_t station_addr[5] = { 0xE4, 0xE4, 0xE4, 0xE4, 0xE4 }; // this is this radio's address uint8_t my_addr[5] = { 0x98, 0x76, 0x54, 0x32, 0x10 }; radiopacket_t packet; int main() { init(); pinMode(13, OUTPUT); pinMode(10, OUTPUT); digitalWrite(10, LOW); delay(100); digitalWrite(10, HIGH); delay(100); Radio_Init(); // configure the receive settings for radio pipe 0 Radio_Configure_Rx(RADIO_PIPE_0, my_addr, ENABLE); // configure radio transceiver settings. Radio_Configure(RADIO_2MBPS, RADIO_HIGHEST_POWER); // put some data into the packet packet.type = MESSAGE; memcpy(packet.payload.message.address, my_addr, RADIO_ADDRESS_LENGTH); packet.payload.message.messageid = 55; snprintf((char*)packet.payload.message.messagecontent, sizeof(packet.payload.message.messagecontent), "Test message."); // The address to which the next transmission is to be sent Radio_Set_Tx_Addr(station_addr); // send the data Radio_Transmit(&packet, RADIO_WAIT_FOR_TX); // wait for the ACK reply to be transmitted back. for (;;) { if (rxflag) { // remember always to read the packet out of the radio, even // if you don't use the data. if (Radio_Receive(&packet) != RADIO_RX_MORE_PACKETS) { // if there are no more packets on the radio, clear the receive flag; // otherwise, we want to handle the next packet on the next loop iteration. rxflag = 0; } if (packet.type == ACK) { digitalWrite(13, HIGH); } } } for (;;); return 0; } void radio_rxhandler(uint8_t pipe_number) { rxflag = 1; } |