In the previous post <link> I wrote about how I discovered broadcast mode can cause problems as you increase the number of nodes. That led to a method of monitoring each node to see what it was actually doing, and by watching each of them in turn, seeing how the traffic patterns work out.
Well, I left it kind of up-in-the-air on exactly how I did it. Basically, I used the examples that Akiba of Freaklabs released for various functions and created a sniffer of sorts. This is necessary because the XBee only sends out the serial port data intended for the particular radio module you're plugged into. That's what makes broadcast mode so useful, you can see all the traffic and chase problems. With a Freakduino Chibi board, you can monitor all the devices on your network at once, but that is way too much information to try to wade through. By setting the Chibi board to promiscuous mode and then writing code to filter by address, you can watch any node in the network to see what is going on with that particular device. This makes it possible to see its traffic even though you aren't using an XBee.
Here's the code I came up with:
A couple of things to keep in mind. The Chibi board is only running at 8 Mhz, so it can't keep up with unlimited traffic flying through the air at 250Kb forever. If you spend too much time in the code checking and decoding, you'll miss packets. The Chibi library sets a buffer size of 1024 to handle packets being received to help with this, but you'll have to reduce this size because the MCU only has 2K of ram to begin with. I point this out in the code. The maximum size of a packet is 127 bytes, so make your parsing buffer longer than that. I print the maximum packet length in the code above, and have never seen a packet larger, so it appears the library is working fine for separating the packets. I also put a routine in the code to monitor memory usage. Use this to keep track so you don't run out with the various buffers and such.
Here's a sample of the output using the serial monitor from the Arduino IDE:
Well, I left it kind of up-in-the-air on exactly how I did it. Basically, I used the examples that Akiba of Freaklabs released for various functions and created a sniffer of sorts. This is necessary because the XBee only sends out the serial port data intended for the particular radio module you're plugged into. That's what makes broadcast mode so useful, you can see all the traffic and chase problems. With a Freakduino Chibi board, you can monitor all the devices on your network at once, but that is way too much information to try to wade through. By setting the Chibi board to promiscuous mode and then writing code to filter by address, you can watch any node in the network to see what is going on with that particular device. This makes it possible to see its traffic even though you aren't using an XBee.
Here's the code I came up with:
The Arduino Sketch
/*
This sketch enables promiscuous mode on the Freakduino boards and then
filters by device address to watch a particular node.
I had to change the buffer size in the chibi library for promiscuous mode
because, being set to 1024 with another buffer for the packet to be manipulated
caused the board to run out of RAM. Just go into the chibi library and
find the receive buffer in chb_buf.h and set it to what you want. I used the
value 500, and it seems to work fine
Before using this sketch, also go into the chibiUsrCfg.h file and
enable promiscuous mode. To do this, change the definition:
#define CHIBI_PROMISCUOUS 0
to
#define CHIBI_PROMISCUOUS 1
When not using promiscuous mode, disable this setting by changing
it back to 0.
*/
#include <chibi.h>
#include <avr/pgmspace.h>
#include <MemoryFree.h>
/*
Addresses of devices in my network
Long Short
Garage Controller 407A 38AF A019
Power Monitor 4031 566B CD6B
Controller 4055 B0A6 97FA
Status Display 406F B6B5 9BE8
Pool Controller 4079 B2BD 9370
Temp1 406F 7FAC 1B1E
House Clock 4055 B094 0
Acid Pump 406F B7AF 7D58
*/
char *deviceName[] = {"Garage Controller",
"Power Monitor",
"Controller",
"Status Display",
"Pool Controller",
"Temp1",
"House Clock",
"Acid Pump"};
uint16_t deviceAddress[] = {0xA019,
0xCD6B,
0x97FA,
0x9BE8,
0x9370,
0x1B1E,
0x0,
0x7D58};
uint16_t listenTo;
byte Pbuf[500];
char Dbuf[50];
int longest = 0;
void showMem(){
strcpy_P(Dbuf,PSTR("Mem = "));
Serial.print(Dbuf);
Serial.println(freeMemory());
}
/**************************************************************************/
// Initialize
/**************************************************************************/
void setup()
{
// Init the chibi stack
Serial.begin(115200);
chibiInit();
strcpy_P(Dbuf,PSTR("\n\r***************I'm alive**************"));
Serial.println(Dbuf);
unsigned int choice = 0;
Serial.println("Pick a device to listen to");
for(int i = 0; i < sizeof(deviceName)/sizeof(deviceName[0]); i++){
Serial.print((char)(i + 'A'));
Serial.print(" - ");
Serial.println(deviceName[i]);
}
Serial.print("> ");
// this only accepts a single character for the device choice.
// that's because the chibi board runs at 8MHz on the internal clock and
// internal clocks aren't accurate. This means the higher baud rates sometimes
// have problems. When I worked on this I couldn't get the board to accept more
// than one character correctly at baud rates over 57K. Works fine at 9600,
// but I want to output the data far faster than that.
while(1){
if(Serial.available() != 0){
char c = Serial.read();
Serial.write(c);
Serial.println();
choice = toupper(c) - 'A';
if (choice < sizeof(deviceName)/sizeof(deviceName[0]))
break;
else {
Serial.println(" oops, try again");
Serial.print("> ");
}
}
}
listenTo = deviceAddress[choice];
strcpy_P(Dbuf,PSTR("Starting Radio, Result = "));
Serial.print(Dbuf);
Serial.println(chibiSetChannel(12),DEC);
strcpy_P(Dbuf,PSTR("Listening to "));
Serial.print(Dbuf);
Serial.print(deviceName[choice]);
strcpy_P(Dbuf,PSTR(" at address "));
Serial.print(Dbuf);
Serial.println(listenTo, HEX);
showMem();
}
/**************************************************************************/
// Loop
/**************************************************************************/
void loop()
{
// Check if any data was received from the radio. If so, then handle it.
if (chibiDataRcvd() == true)
{
int len;
uint16_t senderAddress;
// send the raw data out the serial port in binary format
len = chibiGetData(Pbuf);
if (len > longest)
longest = len;
senderAddress = chibiGetSrcAddr();
if (senderAddress == listenTo && len > 0){
strcpy_P(Dbuf,PSTR("From: "));
Serial.print(Dbuf);
Serial.print(senderAddress,HEX);
strcpy_P(Dbuf,PSTR(" Len: "));
Serial.print(Dbuf);
Serial.print(len);
strcpy_P(Dbuf,PSTR(" Longest: "));
Serial.print(Dbuf);
Serial.println(longest);
for (int i= 0; i < len; i++){
uint8_t nibble = (uint8_t) (Pbuf[i] >> 4);
if (nibble <= 9)
Serial.write(nibble + 0x30);
else
Serial.write(nibble + 0x37);
nibble = (uint8_t) (Pbuf[i] & 0x0F);
if (nibble <= 9)
Serial.write(nibble + 0x30);
else
Serial.write(nibble + 0x37);
Serial.print(' ');
}
Serial.println();
for (int i= 0; i < len; i++){
uint8_t nibble = (uint8_t) (Pbuf[i] >> 4);
Serial.write(' ');
if (iscntrl(Pbuf[i]))
Serial.write(' ');
else
Serial.write(Pbuf[i]);
Serial.write(' ');
}
Serial.println();
}
}
}
This sketch enables promiscuous mode on the Freakduino boards and then
filters by device address to watch a particular node.
I had to change the buffer size in the chibi library for promiscuous mode
because, being set to 1024 with another buffer for the packet to be manipulated
caused the board to run out of RAM. Just go into the chibi library and
find the receive buffer in chb_buf.h and set it to what you want. I used the
value 500, and it seems to work fine
Before using this sketch, also go into the chibiUsrCfg.h file and
enable promiscuous mode. To do this, change the definition:
#define CHIBI_PROMISCUOUS 0
to
#define CHIBI_PROMISCUOUS 1
When not using promiscuous mode, disable this setting by changing
it back to 0.
*/
#include <chibi.h>
#include <avr/pgmspace.h>
#include <MemoryFree.h>
/*
Addresses of devices in my network
Long Short
Garage Controller 407A 38AF A019
Power Monitor 4031 566B CD6B
Controller 4055 B0A6 97FA
Status Display 406F B6B5 9BE8
Pool Controller 4079 B2BD 9370
Temp1 406F 7FAC 1B1E
House Clock 4055 B094 0
Acid Pump 406F B7AF 7D58
*/
char *deviceName[] = {"Garage Controller",
"Power Monitor",
"Controller",
"Status Display",
"Pool Controller",
"Temp1",
"House Clock",
"Acid Pump"};
uint16_t deviceAddress[] = {0xA019,
0xCD6B,
0x97FA,
0x9BE8,
0x9370,
0x1B1E,
0x0,
0x7D58};
uint16_t listenTo;
byte Pbuf[500];
char Dbuf[50];
int longest = 0;
void showMem(){
strcpy_P(Dbuf,PSTR("Mem = "));
Serial.print(Dbuf);
Serial.println(freeMemory());
}
/**************************************************************************/
// Initialize
/**************************************************************************/
void setup()
{
// Init the chibi stack
Serial.begin(115200);
chibiInit();
strcpy_P(Dbuf,PSTR("\n\r***************I'm alive**************"));
Serial.println(Dbuf);
unsigned int choice = 0;
Serial.println("Pick a device to listen to");
for(int i = 0; i < sizeof(deviceName)/sizeof(deviceName[0]); i++){
Serial.print((char)(i + 'A'));
Serial.print(" - ");
Serial.println(deviceName[i]);
}
Serial.print("> ");
// this only accepts a single character for the device choice.
// that's because the chibi board runs at 8MHz on the internal clock and
// internal clocks aren't accurate. This means the higher baud rates sometimes
// have problems. When I worked on this I couldn't get the board to accept more
// than one character correctly at baud rates over 57K. Works fine at 9600,
// but I want to output the data far faster than that.
while(1){
if(Serial.available() != 0){
char c = Serial.read();
Serial.write(c);
Serial.println();
choice = toupper(c) - 'A';
if (choice < sizeof(deviceName)/sizeof(deviceName[0]))
break;
else {
Serial.println(" oops, try again");
Serial.print("> ");
}
}
}
listenTo = deviceAddress[choice];
strcpy_P(Dbuf,PSTR("Starting Radio, Result = "));
Serial.print(Dbuf);
Serial.println(chibiSetChannel(12),DEC);
strcpy_P(Dbuf,PSTR("Listening to "));
Serial.print(Dbuf);
Serial.print(deviceName[choice]);
strcpy_P(Dbuf,PSTR(" at address "));
Serial.print(Dbuf);
Serial.println(listenTo, HEX);
showMem();
}
/**************************************************************************/
// Loop
/**************************************************************************/
void loop()
{
// Check if any data was received from the radio. If so, then handle it.
if (chibiDataRcvd() == true)
{
int len;
uint16_t senderAddress;
// send the raw data out the serial port in binary format
len = chibiGetData(Pbuf);
if (len > longest)
longest = len;
senderAddress = chibiGetSrcAddr();
if (senderAddress == listenTo && len > 0){
strcpy_P(Dbuf,PSTR("From: "));
Serial.print(Dbuf);
Serial.print(senderAddress,HEX);
strcpy_P(Dbuf,PSTR(" Len: "));
Serial.print(Dbuf);
Serial.print(len);
strcpy_P(Dbuf,PSTR(" Longest: "));
Serial.print(Dbuf);
Serial.println(longest);
for (int i= 0; i < len; i++){
uint8_t nibble = (uint8_t) (Pbuf[i] >> 4);
if (nibble <= 9)
Serial.write(nibble + 0x30);
else
Serial.write(nibble + 0x37);
nibble = (uint8_t) (Pbuf[i] & 0x0F);
if (nibble <= 9)
Serial.write(nibble + 0x30);
else
Serial.write(nibble + 0x37);
Serial.print(' ');
}
Serial.println();
for (int i= 0; i < len; i++){
uint8_t nibble = (uint8_t) (Pbuf[i] >> 4);
Serial.write(' ');
if (iscntrl(Pbuf[i]))
Serial.write(' ');
else
Serial.write(Pbuf[i]);
Serial.write(' ');
}
Serial.println();
}
}
}
A couple of things to keep in mind. The Chibi board is only running at 8 Mhz, so it can't keep up with unlimited traffic flying through the air at 250Kb forever. If you spend too much time in the code checking and decoding, you'll miss packets. The Chibi library sets a buffer size of 1024 to handle packets being received to help with this, but you'll have to reduce this size because the MCU only has 2K of ram to begin with. I point this out in the code. The maximum size of a packet is 127 bytes, so make your parsing buffer longer than that. I print the maximum packet length in the code above, and have never seen a packet larger, so it appears the library is working fine for separating the packets. I also put a routine in the code to monitor memory usage. Use this to keep track so you don't run out with the various buffers and such.
Here's a sample of the output using the serial monitor from the Arduino IDE:
It doesn't wrap around, but that could be easily added. I just use the scroll bar on the bottom to move around. I output the hex values as well as the ascii so I can recognize the various packets as well as decode the binary portions of the data.
So, as your network grows, or you have problems right off the bat, consider this combination of things as a possible tool to help you get past it. Of course, you're welcome to grab the code and modify it to your particular network and need for monitoring.