Network wireless devices on the Arduino

Micro-controller the Arduino is an excellent platform for hobby projects of various degrees of complexity and usefulness. I will not say that the Arduino platform is the best choice for professional solutions (most would agree with the reverse), but for my Amateur "DIY" in the field of home automation is the best option. I.e. the controller is good by itself, but if, moreover, he will cease to be "in itself", and will be able to communicate with their own kind, while not acquiring additional wires, its usefulness and applicability can repeatedly grow. Let's start to build our home of SkyNet...

Budget radio modules

Based on our network we take the budget of radio modules operating at the frequency 433.90 MHz. The cost of one such module is about $2.5, so that a small cost in order to communicate with the outside world. Of course, when you can use ready ethernet modules, and even make a symbiosis with the besprovodnye routers on the basis of firmware, but in many cases it is easier and cheaper to do on here such radio modules.

Transmitter:



Receiver:



The quality of work and range of communication these modules leaves much to be desired, and I would not believe the positive comments from sellers about the range ">500m". At best 100 meters in an open area, well, and much less in the presence of concrete walls. However, for apartments or small suburban area they will last. You can use higher quality (therefore more expensive) radio modules, so the article may be seen as an ideological concept, applicable to many possible implementation choices.

Important point: in this guide I will not consider the option of creating a network with quality-controlled data transmission. If compared to ethernet protocols can be considered relevant, we are not going to build network TCP packages, but rather UDP.

Each module connects to the elementary controller — power supply via Vcc/Gnd, and output Data is connected to a free digital input on the micro controller. To improve the quality of reception/transmission, it is recommended to connect the antenna in the form of a wire with a size of 10-15 cm by the Way, the communication range depends on the supplied power supply module — if they are powered from the 12V, the range and reliability of communication increases significantly.

Receiver and transmitter connected to the micro-controller Arduino UNO R3



Thus, we made two devices: a first transmitter that will be "broadcast" to broadcast any information; a second receiver which, respectively, will be broadcast to "listen". Further, the fact that it would send and receive was meaningful and useful for us.

VirtualWire Library

What's good about the Arduino platform, so is the presence of a huge number of ready libraries for operation with various devices. You can, of course, with radio modules to work without any libraries, but then you need to develop a communications Protocol with checksums and other things. Fortunately, there is a library VirtualWire, support these (and similar) radio modules. Using this library is very easy to organize the sending and receiving small packets of information.

The principle of use: the transmitter generated a data set for forwarding (in the form of character strings or byte codes), and the receiver, when receiving a "valid" packet of data, their display. The easiest way to see the examples that come with the library.

Code transmitter using VirtualWire (example of library):
// // Simple example of how to use VirtualWire to transmit messages // Implements a simplex (one-way) transmitter with an TX-C1 module #include <VirtualWire.h> void setup() { Serial.begin(9600); // Debugging only Serial.println("setup"); // Initialise the IO and ISR vw_set_ptt_inverted(true); // Required for DR3100 vw_setup(2000); // Bits per sec } void loop() { const char *msg = "hello"; digitalWrite(13, true); // Flash a light to show transmitting vw_send((uint8_t *)msg, strlen(msg)); vw_wait_tx(); // Wait until the whole message is gone digitalWrite(13, false); delay(200); }


Receiver code:
View code
// receiver.pde
//
// Simple example of how to use VirtualWire to receive messages
// Implements a simplex (one-way) receiver with an Rx-B1 module
#include <VirtualWire.h>

void setup()
{
Serial.begin(9600); // Debugging only
Serial.println("setup");

// Initialise the IO and ISR
vw_set_ptt_inverted(true); // Required for DR3100
vw_setup(2000); // Bits per sec

vw_rx_start(); // Start the receiver PLL running
}

void loop()
{
uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;

if (vw_get_message(buf, &buflen)) // Non-blocking
{
int i;
digitalWrite(13, true); // Flash a light to show received good message
// Message with a good checksum received, dump it.
Serial.print("Got: "); 
for (i = 0; i < buflen; i++)
{
Serial.print(buf[i], HEX);
Serial.print(" ");
}
Serial.println("");
digitalWrite(13, false);
}
}


communication Protocol

The next step we will be reaching a new level of abstraction, namely the development of a standard package structure, which will exchange all of our devices. This will allow us to connect to our network with new equipment that can use signals from existing devices.

I will give the data structure that I found to be optimal with the available hardware capabilities. So, below is a list of main parameters, which are sent in broadcast with each package:

device_id — the ID of the device that sent the packet. Data type: unsigned int (length 2 bytes, value range from 0 to 65535) — I think that is enough for a home network.

destination_id — the ID of the device who designed the package. The data type is the same as that device_id. It is important to note that the packets will still get all the receivers, but the software on the receiver can discard packets that the device is not intended. The same can be taken as the rule that the value "0" in this field indicates a broadcast packet.

packet_id — the ID of the package. Type is the same unsigned int. According to the plan, when sending the packet is "marked" by a random number that can be used to re-send the same package several times with some interval, due to the unreliability of the Protocol it makes sense, but the receiving device should filter repeated commands in order to perform the same action in response to the data packet.

command — type commands. Byte data type (length 1 byte, value range from 0 to 255). This so-called "class team" and in fact the information about what data we send. For example, we can generate your own table of commands, relegating to the control command to the opening/closing member 10, and to command the transmission of data on temperature of room 15. Importantly, this table we have constant. And you can do it even smarter — to spy the possible commands in the same ZWave Protocol to use their table to all "as at adults", and didn't have to worry about the safety of these valuable information.

data — the actual data. The int data type (length 2 bytes the range of values from -32,768 to 32,767. In this field, we pass directly the data as a single number. A little? Well, it seemed to me sufficient. The temperature can be transmitted (for example uslovijah that it multiplied by 100), the status of a motion sensor is easy, the command for receiver with relay — easy. Text data to an external display, of course, will not send, but this goal has not been set, but for my current and future devices will suffice for the eyes and a couple of dozen numbers to describe all possible commands.

In the end we have packet length is 9 bytes. Short package, in fact, a very well — first, the less the probability that he is on the road "will break"; second, less time in a shipment, which reduces the likelihood of sharing the airwaves multiple devices. Incidentally, the latter circumstances would require "economically", i.e., not often to send information. It is desirable to periodically send readings the interval between the sessions varied a little. But all this must already be considered when integrating a particular device. Anyway, I would not suggest too lean on the versatility of the structure to the detriment of the minimum packet size of the transmitted data.
So, we decided on the package structure, you now need to implement the exchange. Here we comes to the aid of another useful library called EasyTransfer. Actually, it works "on top" VirtualWire, allowing to describe the data structure on the receiver/transmitter and to exchange is not a set of byte-codes, and the specified structure as a whole.

In our case, the data structure will have the following form:
the
struct SEND_DATA_STRUCTURE{
unsigned int device_id;
unsigned int destination_id; 
unsigned int packet_id;
byte command;
int data;
};


It is essential that the structure on the receiver and preactice was one-to-one, otherwise we will get incorrect information. Actually, therefore, important to determine in advance the package structure.

A few words about the field device_id. It can be set manually for each device, but I went on more simple way — when you first start generating it's values randomly and is recorded in energy-independent memory EEPROM. The probability that multiple devices receive the same identifier in the range of field values unsigned int is extremely small and, again, in my case the risk is justified.

Let's use the knowledge for writing example of implementation on our exchange Protocol. The transmitter we will have to send the value of the internal counter, and the receiver can display it.

Transmitter code:
View code
#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h> // this library is needed to work with energy-independent memory

const int led_pin = 13;
const int transmit_pin = 2;
unsigned int unique_device_id = 0;
unsigned int count = 1;

//create object
EasyTransferVirtualWire ET; 

SEND_DATA_STRUCTURE struct{
//our data structure. it must be identically defined on the receiver and the transmitter
//furthermore, the size of the structure shall not exceed 26 bytes (the limit VirtualWire)
unsigned int device_id;
unsigned int destination_id; 
unsigned int packet_id;
byte command;
int data;
};

//variable data-our structures
SEND_DATA_STRUCTURE mydata;

//below are a few functions to write data of type unsigned int in EEPROM
void EEPROMWriteInt(int p_address, unsigned int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);

EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}

unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);

return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}

void setup()
{
// initialization block
pinMode(led_pin, OUTPUT);

ET.begin(details(mydata));
vw_set_tx_pin(transmit_pin); //setting the pin connected to data-input transmitter
vw_setup(2000); //transmission speed
Serial.begin(9600);
randomSeed(analogRead(0));


// Read/writable Device ID
Serial.print("Getting Device ID... "); 
unique_device_id=EEPROMReadInt(0);
if (unique_device_id<10000 || unique_device_id>60000) {
Serial.print("N/A, updating... "); 
unique_device_id=random(10000, 60000);
EEPROMWriteInt(0, unique_device_id);
}
Serial.println(unique_device_id);

}

void loop()
{
mydata.device_id = unique_device_id;
mydata.destination_id = 0;
mydata.packet_id = random(65535);
mydata.command = 0;
mydata.data = count;

digitalWrite(led_pin, HIGH); // turn on the led to display the transfer process
Serial.print("Transmitting packet "); 
Serial.print(mydata.packet_id); 
Serial.print(" device id "); 
Serial.print(mydata.device_id); 
Serial.print(" data: "); 
Serial.print(mydata.data);
Serial.print(" ... "); 
ET.sendData(); // send data

digitalWrite(led_pin, LOW);
Serial.println("DONE");
delay(1000);
count = count + 1;
}


The receiver in our case will simply listen to the broadcast and show all the commands sent by the transmitters. Under each device, the host team, the code should be modified, by adding, if necessary, the filter for the target device and the command class.

Receiver code:
View code
#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h>

const int led_pin = 13;
const int receive_pin = 2;
unsigned int unique_device_id = 0;

//create object
EasyTransferVirtualWire ET; 
char buf[120];

SEND_DATA_STRUCTURE struct{
//our data structure. it must be identically defined on the receiver and the transmitter
//furthermore, the size of the structure shall not exceed 26 bytes (the limit VirtualWire)
unsigned int device_id;
unsigned int destination_id; 
unsigned int packet_id;
byte command;
int data;
};

//variable data-our structures
SEND_DATA_STRUCTURE mydata;

//below are a few functions to write data of type unsigned int in EEPROM

{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);

EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}

unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);

return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}

void setup()
{
pinMode(led_pin, OUTPUT);
Serial.begin(9600); // Debugging only

ET.begin(details(mydata));
// Initialise the IO and ISR
vw_set_rx_pin(receive_pin);
vw_setup(2000); // Speed
vw_rx_start(); // Start receive mode

// Device ID
Serial.print("Getting Device ID... "); 
unique_device_id=EEPROMReadInt(0);
if (unique_device_id<10000 || unique_device_id>60000) {
Serial.print("N/A, updating... "); 
unique_device_id=random(10000, 60000);
EEPROMWriteInt(0, unique_device_id);
}
Serial.println(unique_device_id);

}

void loop()
{
if(ET.receiveData()) // received package data processed
{
digitalWrite(led_pin, HIGH);
Serial.print("Got: ");
Serial.print("Device ID: ");
Serial.print(mydata.device_id);
Serial.print(" Destination ID: ");
Serial.print(mydata.destination_id); 
Serial.print(" Packet ID: ");
Serial.print(mydata.packet_id);
Serial.print(" Command: ");
Serial.print(mydata.command);
Serial.print(" Data: ");
Serial.print(mydata.data);
Serial.println();
digitalWrite(led_pin, LOW);
}
}


Yay! Our Skynet in the air! It is already possible to do much useful, but there is no limit to perfection... Moving on.

Integration in MajorDoMo

The next stage is embedding all of our "economy" in a more complex environment to control the House. In this case, using MajorDoMo, but can likewise be integrated with any other system.

In fact, the principle of integration in the organization of a "bridge" between your computer and our radio network. Below I give an example of the creation of the "listening bridge", whose task is to "listen" to the airwaves and broadcast all received packets on Wednesday MajorDoMo. The latter, in turn, will be engaged in their treatment — to respond with some action, or simply display the resulting data in different interfaces.

In control panel, the script will create a script of reception of messages, called easyRF.
Script code:

the
$device_id=$params['did'];
$destination_id=$params['dest'];
$packet_id=$params['pid'];
$command_id=$params['c'];
$data=$params['d'];
say("device $device_id came package $packet_id team $command_id and data $data");


After adding this code immediately, you can call http link:
192.168.0.17/objects/?script=easyRF

(192.168.0.17 is the address of your server)

The next step is stream data received from the Arduino to the MajorDoMo system. Here are the options — you can add to your Arduino-ethernet module receiver and immediately send http requests over the network and can be connected to micro-controller via USB and use the program ArduinoGW, which "listens" to the COM port and in the presence of a key sequence corresponding to the http request itself forwards it to the network.

Let's use the second method because it requires no additional equipment. In this case, the receiver code will look like this:
View code
#include <VirtualWire.h>
#include <EasyTransferVirtualWire.h>
#include <EEPROM.h>

const int led_pin = 13;
const int receive_pin = 2;
unsigned int unique_device_id = 0;

//create object
EasyTransferVirtualWire ET; 
char buf[120];

SEND_DATA_STRUCTURE struct{
//our data structure. it must be identically defined on the receiver and the transmitter
//furthermore, the size of the structure shall not exceed 26 bytes (the limit VirtualWire)
unsigned int device_id;
unsigned int destination_id; 
unsigned int packet_id;
byte command;
int data;
};

//variable data-our structures
SEND_DATA_STRUCTURE mydata;

//below are a few functions to write data of type unsigned int in EEPROM
void EEPROMWriteInt(int p_address, unsigned int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);

EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}

unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);

return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}

void setup()
{
pinMode(led_pin, OUTPUT);
Serial.begin(9600); // Debugging only

ET.begin(details(mydata));
// Initialise the IO and ISR
vw_set_rx_pin(receive_pin);
vw_setup(2000); // Bits per sec
vw_rx_start(); // Start the receiver PLL running

// Device ID
Serial.print("Getting Device ID... "); 
unique_device_id=EEPROMReadInt(0);

Serial.print("N/A, updating... "); 
unique_device_id=random(10000, 60000);
EEPROMWriteInt(0, unique_device_id);
}
Serial.println(unique_device_id);

}

void loop()
{
if(ET.receiveData()) // received package data processed
{
digitalWrite(led_pin, HIGH);
Serial.print("Got: ");
Serial.print("Device ID: ");
Serial.print(mydata.device_id);
Serial.print(" Destination ID: ");
Serial.print(mydata.destination_id); 
Serial.print(" Packet ID: ");
Serial.print(mydata.packet_id);
Serial.print(" Command: ");
Serial.print(mydata.command);
Serial.print(" Data: ");
Serial.print(mydata.data);
Serial.println();
digitalWrite(led_pin, LOW);
sprintf(buf, "GET /objects/?script=easyRF&did=%u&dest=%u&pid=%u&c=%u&d=%i HTTP/1.0", (int)mydata.device_id, (int)mydata.destination_id, (int)mydata.packet_id, (int)mydata.command, (int)mydata.data);
Serial.println(buf); // print the string for the HTTP request (there may be added the ethernet shield and
Serial.println(); 
}
}


That's all! Creating the device you are adding radio modules and custom interactions.

Further development

As I wrote above, this article may be regarded as concept ideas, which can develop in many directions, and not even retaining the original structure of the package.

Will give a few thoughts that came to mind:

* Create nodes "robust" exchange (use on one device and the receiver and transmitter and organize the exchange of packets with the control of delivery, control the delivery of allocated individual class teams)
* Using more expensive and reliable radio modules
* Implement the procedure for linking one device to another without requiring changes to your code (translated into "binding" of two devices and record in the EEPROM of the pair of the device)

This is all. Thank you for your attention!
Article based on information from habrahabr.ru

Комментарии