VirtualWire
Sending Messages Using Low-Cost Wireless Modules
Problem
You want to transmit data between two Arduino boards using simple, low-cost wireless modules.
Solution
This recipe uses simple transmit and receive modules such as the SparkFun 315 MHz: WRL-10535 and WRL-10533, or the 434 MHz: WRL-10534 and WRL-10532.
Wire the transmitter as shown in Figure 14-1 and the receiver as in Figure 14-2. Some modules have the power line labeled VDD instead of Vcc.
Figure 14-1 Figure 14-2
The transmit sketch sends a simple text message to the receive sketch, which echoes the text to the Serial Monitor. The transmit and receive sketches use the VirtualWire library written by Mike McCauley to provide the interface to the wireless hardware. The library can be downloaded fromhttp://www.open.com.au/mikem/arduino/VirtualWire-1.5.zip:
/* SimpleSend This sketch transmits a short text message using the VirtualWire library connect the Transmitter data pin to Arduino pin 12 */ #include <VirtualWire.h> void setup() { // Initialize the IO and ISR vw_setup(2000); // Bits per sec } void loop() { send("hello"); delay(1000); } void send (char *message) { vw_send((uint8_t *)message, strlen(message)); vw_wait_tx(); // Wait until the whole message is gone }
The receive sketch also uses the VirtualWire library:
/* SimpleReceive This sketch displays text strings received using VirtualWire Connect the Receiver data pin to Arduino pin 11 */ #include <VirtualWire.h> byte message[VW_MAX_MESSAGE_LEN]; // a buffer to hold the incoming messages byte msgLength = VW_MAX_MESSAGE_LEN; // the size of the message void setup() { Serial.begin(9600); Serial.println("Ready"); // Initialize the IO and ISR vw_setup(2000); // Bits per sec vw_rx_start(); // Start the receiver } void loop() { if (vw_get_message(message, &msgLength)) // Non-blocking { Serial.print("Got: "); for (int i = 0; i < msgLength; i++) { Serial.write(message[i]); } Serial.println(); } }
Discussion
The VirtualWire library defaults to pin 12 for transmit and pin 11 for receive, but see the documentation link at the end of this recipe if you want to use different pins. Setup
initializes the library. The loop
code simply calls a send
function that calls the library vw_send
and waits for the message to be transmitted.
The receive side initializes the library receive logic and then waits in loop
for the message.vw_get_message
will return true
if a message is available, and if so, each character in the message is printed to the Serial Monitor.
The VirtualWire library handles the assembly of multiple bytes into packets, so sending binary data consists of passing the address of the data and the number of bytes to send.
The sending sketch that follows is similar to the transmit sketch in this recipe’s Solution, but it fills the message buffer with binary values from reading the analog input ports using analogRead
. The size of the buffer is the number of integers to be sent multiplied by the number of bytes in an integer (the six analog integer values take 12 bytes because each int
is two bytes):
/* SendBinary Sends digital and analog pin values as binary data using VirtualWire library See SendBinary in Chapter 4 */ #include <VirtualWire.h> const int numberOfAnalogPins = 6; // how many analog pins to read int data[numberOfAnalogPins]; // the data buffer const int dataBytes = numberOfAnalogPins * sizeof(int); // the number of bytes // in the data buffer void setup() { // Initialize the IO and ISR vw_setup(2000); // Bits per sec } void loop() { int values = 0; for(int i=0; i <= numberOfAnalogPins; i++) { // read the analog ports data[i] = analogRead(i); // store the values into the data buffer } send((byte*)data, dataBytes); delay(1000); //send every second } void send (byte *data, int nbrOfBytes) { vw_send(data, nbrOfBytes); vw_wait_tx(); // Wait until the whole message is gone }
Note
The sizeof
operator is used to determine the number of bytes in an int
.
The receive side waits for messages, checks that they are the expected length, and converts the buffer back into the six integer values for display on the Serial Monitor:
/* SendBinary Sends digital and analog pin values as binary data using VirtualWire library See SendBinary in Chapter 4 */ #include <VirtualWire.h> const int numberOfAnalogPins = 6; // how many analog integer values to receive int data[numberOfAnalogPins]; // the data buffer // the number of bytes in the data buffer const int dataBytes = numberOfAnalogPins * sizeof(int); byte msgLength = dataBytes; void setup() { Serial.begin(9600); Serial.println("Ready"); // Initialize the IO and ISR vw_set_ptt_inverted(true); // Required for DR3100 vw_setup(2000); // Bits per sec vw_rx_start(); // Start the receiver } void loop() { if (vw_get_message((byte*)data, &msgLength)) // Non-blocking { Serial.println("Got: "); if(msgLength == dataBytes) { for (int i = 0; i < numberOfAnalogPins; i++) { Serial.print("pin "); Serial.print(i); Serial.print("="); Serial.println(data[i]); } } else { Serial.print("unexpected msg length of "); Serial.println(msgLength); } Serial.println(); } }
The Serial Monitor will display the analog values on the sending Arduino:
Got: pin 0=1023 pin 1=100 pin 2=227 pin 3=303 pin 4=331 pin 5=358
Bear in mind that the maximum buffer size for VirtualWire is 30 bytes long (the constantVW_MAX_MESSAGE_LEN
is defined in the library header file).
Wireless range can be up to 100 meters or so depending on supply voltage and antenna and is reduced if there are obstacles between the transmitter and the receiver.
Also note that the messages are not guaranteed to be delivered, and if you get out of range or there is excessive radio interference some messages could get lost. If you need a guaranteed wireless delivery mechanism, the ZigBee API used in recipes that follow is a better choice, but these inexpensive modules work well for tasks such as displaying the status of Arduino sensors—each message contains the current sensor value to display and any lost messages get replaced by messages that follow.