I have a RFM69HW network with 3 nodes. One is on node 1 and only listens. Two are sending messages every 5 seconds, nodes 3 and 6. The sending nodes are Moteino R4's and the receiving node is a Teensy 3.1 with an RFM69HW module.
After running for a few minutes, the receiving node stops receiving messages from one of the nodes, though all nodes continue to run.
When this happens, it continues in this state indefinitely, or until one of the nodes is turned off or reset.
If I turn off one of the nodes, the receiving node will start receiving messages from the node that is still running. This would seem to indicate that the nodes are always sending, but the other node is somehow interfering with the transmission.
If I restart a node which is not receiving messages, the receiving node in most cases will start receiving messages from that node. Sometimes the receiver then receives only from the node that was reset, and sometimes it will start receiving from both nodes. If it does start receiving from both nodes, it is only for a short period, and then it only receives from one node.
Sending node code (one of 10 files):
#include "Arduino.h"
#include <LowPower.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/io.h>
#include <RFM69.h>
#include "RFNode.h"
#include "PgmPrint.h"
#include "Config.h"
#include "DHT22.h"
#define ENCRYPTKEY "kd8w3a0mz8q01gbh" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW
void saveSettings(); // This function is called when the module configuration changes
RFNode rfNode; // Singleton class
// Call this function in setup() to initialize the RFM12B interface
void RFNode::setup( int cfg_node, int cfg_group, int cfg_freq, int cfg_spi_ss )
{
if( cfg_node == 0 )
cfg_node = config.node;
pinMode( 0, INPUT );
rfFrequency = cfg_freq;
networkGroup = cfg_group;
radio.initialize(cfg_freq,cfg_node,cfg_group);
#ifdef IS_RFM69HW
radio.setHighPower(); //uncomment only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
SerialPrint( "radio initialized: " );
Serial.print("Node: " ); Serial.print( cfg_node );
Serial.print( " Group: " ); Serial.println( cfg_group );
lastUpdate = millis();
waitingToTurnLedOff = false;
}
// Call this function in the main loop() to reive any pending messages and process them
void RFNode::loop()
{
checkForMessageReceived();
if( ((millis() - lastUpdate ) > config.updateFrequency))
{
lastUpdate = millis();
sendStatus();
}
}
void RFNode::checkForMessageReceived()
{
while(radio.receiveDone())
{
if( !configMode )
SerialPrint( "Receive Done()\n" );
ProcessMessage();
}
}
void RFNode::ProcessMessage()
{
int sender = radio.SENDERID; // radio.GetSender();
if( !configMode )
{
Serial.print( sender ); Serial.print(":");
for (byte i = 0; i < radio.DATALEN; i++)
Serial.print((char)radio.DATA[i]);
}
Serial.println();
radio.DATA[radio.DATALEN] = 0;
long m = atol( (char *) &radio.DATA[1] );
if( m != config.moduleID )
{
SerialPrint( "Addressed to different module.\n" );
return;
}
switch( radio.DATA[0] )
{
case 'S':
// Status Request
{
sendStatus();
break;
}
break;
case 'O':
processOutputCommand();
return;
case 'N':
processNodeChange();
break;
case 'C':
processConfigMessage();
break;
}
if (radio.ACK_REQUESTED)
{
radio.sendACK();
}
}
bool isdigital( char b )
{
return( isdigit( b ) || b == '-' );
}
const long powers10[] = { 1, 10, 100, 1000, 10000, 100000 };
char * RFNode::addLongToString( long value, char *b )
{
ltoa( value, b, 10 );
while( isdigit(*b))
b++;
return b;
}
char * RFNode::addIntToString( int Value, char *b, int digits )
{
// Display int with enough leading zeros to provide the specified number of digits
int tempValue;
int addZeros;
if( Value == 0 )
{
addZeros = digits - 1;
}
else
{
addZeros = digits; // addZeros = 3;
tempValue = Value;
while( tempValue > 0 )
{
tempValue /= 10;
addZeros--;
}
}
for( int i=0; i<addZeros; i++ )
{
*b++ = '0';
}
return addIntToString( Value, b );
}
char * RFNode::addIntToString( int value, char *b )
{
itoa( value, b, 10 );
while( isdigital(*b))
b++;
return b;
}
//const int batteryIn = 7;
const int lightIn = 0;
const int vernierIn = 1;
const int dht22Out = 5;
const int lightOut = 8;
const int vernierOut = 4;
const int humidityIndex = 13;
void RFNode::sendStatus( )
{
delay(1);
char *b;
memset( sendBuffer, 0, sizeof(sendBuffer) );
sendBuffer[0] = 's';
b = &sendBuffer[1];
ltoa( config.moduleID, b, 10 );
while( isdigit(*b) )
b++;
*b++ = ',';
int g;
byte t;
int aIndex;
for( g=0; g<RFNetwork::MAX_IO; g++ )
{
t = config.ioTypes[g];
if( t == ANALOG_IN )
{
aIndex = g - RFNetwork::ANALOG_START;
if( aIndex == lightIn )
{
pinMode( lightOut, OUTPUT );
digitalWrite( lightOut, LOW );
}
else if( aIndex == vernierIn )
{
pinMode( vernierOut, OUTPUT );
digitalWrite( vernierOut, HIGH );
}
}
else if( t == DHT22_IN )
{
pinMode( dht22Out, OUTPUT );
digitalWrite( dht22Out, LOW );
}
}
delay(2000);
for( g=0; g<RFNetwork::MAX_IO; g++ )
{
t = config.ioTypes[g];
if( t == DIGITAL_IN )
{
// io ID
*b++ = 'I';
// Index
b = addIntToString( g, b );
*b++ = ',';
// Value
b = addIntToString((int) digitalRead( g ), b );
*b++ = ',';
}
else if( t == ANALOG_IN )
{
// io ID
*b++ = 'A';
b = addIntToString( g, b );
*b++ = ',';
aIndex = g - RFNetwork::ANALOG_START;
int decPlaces = config.scalingDecPlaces[aIndex];
long a = analogRead( aIndex );
// Gain and offset are multiplied by 100000 and truncated to integer.
// after calulating the value, divide to get the number of decimal places needed.
long value = (
(
a * config.scalingGain[aIndex] + config.scalingOffset[aIndex] + (powers10[ 5 - decPlaces]/2)
// (1023 * 323 + 0 + 500) / 1000 =
)
/ powers10[ 5 - decPlaces ]
);
if( aIndex == vernierIn )
{
Serial.print( "Gain: " ); Serial.print( config.scalingGain[aIndex] ); Serial.print( ", Offset" ); Serial.print( config.scalingOffset[aIndex] );
Serial.print( "powDiv: " ); Serial.println( powers10[ 5 - decPlaces ] );
Serial.print( "Analog " ); Serial.print( aIndex ); Serial.print(" " ); Serial.print( a ); Serial.print( " " ); Serial.println( value );
}
b = addIntToString((int)( value/powers10[ decPlaces ]), b );
if( decPlaces > 0 )
{
*b++ = '.';
aIndex = (int) (abs(value % powers10[ decPlaces ]));
b = addIntToString( aIndex, b, decPlaces );
}
*b++ = ',';
}
else if( t == DIGITAL_OUT )
{
// io ID
*b++ = 'O';
// Index
b = addIntToString( g, b );
*b++ = ',';
// Value
b = addIntToString((int) digitalRead( g ), b );
*b++ = ',';
}
else if( t == DHT22_IN )
{
float temperature, humidity;
int itemp, ihumidity;
DHT22_Err_t rc = getDHT22(g, &temperature, &humidity);
if (rc != DHT22_ERR_NONE)
{
Serial.print( "DHT22 Error code " ); Serial.println( rc );
}
*b++ = 'A';
b = addIntToString( g, b );
*b++ = ',';
itemp = (int)(temperature * 10.0 + 0.5);
ihumidity = (int)(humidity * 10.0 + 0.5);
b = addIntToString(itemp/10, b );
*b++ = '.';
b = addIntToString( itemp % 10, b, 1 );
*b++ = ',';
*b++ = 'A';
b = addIntToString( humidityIndex, b );
*b++ = ',';
b = addIntToString( ihumidity/10, b );
*b++ = '.';
b = addIntToString( ihumidity % 10, b, 1 );
*b++ = ',';
}
}
for( g=0; g<RFNetwork::MAX_IO; g++ )
{
t = config.ioTypes[g];
if( t == ANALOG_IN )
{
int aIndex = g - RFNetwork::ANALOG_START;
if( aIndex == lightIn )
pinMode( lightOut, INPUT );
else if( aIndex == vernierIn )
pinMode( vernierOut, INPUT );
}
else if( t == DHT22_IN )
pinMode( dht22Out, INPUT );
}
b--;
*b = 0;
// Serial.println( "radio.Send" );
radio.send( GATEWAY_NODE, sendBuffer, strlen( sendBuffer ) );
if( !configMode )
Serial.println( sendBuffer );
// Serial.println( "sent" );
}
void RFNode::processOutputCommand()
{
// Output command
// If using output commands, lowPowerMode should not be used.
//O{Module},{index},{value}
// Parse ModuleID
char *b = (char *) &radio.DATA[1];
long id = atol( b );
if( id != config.moduleID )
{
SerialPrint( "Incorrect module id\n" );
return;
}
while( isdigit(*b) )
b++;
if( *b++ != ',' )
{
SerialPrint( "Missing parametres\n" );
return;
}
int index = atoi( b );
while( isdigit(*b) )
b++;
if( *b != ',' )
{
SerialPrint( "Missing parameters\n" );
return;
}
b++;
int value = atoi( b );
if( index < 0 || index >= RFNetwork::MAX_IO )
{
SerialPrint( "Index out of range\n" );
return;
}
int t = config.ioTypes[index];
if( t == 0 )
{
SerialPrint( "ioType[index] = 0\n" );
return;
}
if( t == DIGITAL_OUT )
{
SerialPrint( "Output:" );
Serial.print( index );
SerialPrint( ", " );
Serial.println( value );
if( value == 2 )
value = digitalRead( index ) ? 0 : 1;
digitalWrite( index, value );
// return;
}
else if( t == PWM_OUT )
{
SerialPrint( "PWM out\n" );
analogWrite( index, value );
}
// Send Confirmation
// radio.DATA[0] = 'o';
// radio.send( GATEWAY_NODE, (const char*)radio.DATA, strlen( (const char*)radio.DATA ) );
// char *b;
// Send a status message in response
if (radio.ACK_REQUESTED)
{
SerialPrint( "Sending ACK\n" );
radio.sendACK();
}
// Send Acknowldegement
memset( sendBuffer, 0, sizeof(sendBuffer) );
sendBuffer[0] = 'o';
b = &sendBuffer[1];
ltoa( config.moduleID, b, 10 );
while( isdigit(*b) )
b++;
*b++ = ',';
// io ID
*b++ = 'O';
// Index
b = addIntToString( index, b );
*b++ = ',';
// Value
b = addIntToString((int) digitalRead( index ), b );
*b = 0;
radio.send( GATEWAY_NODE, sendBuffer, strlen( sendBuffer ) );
// if( !configMode )
// Serial.println( "Sending status..." );
Serial.println( sendBuffer );
}
void RFNode::processNodeChange()
{
char *b = (char *) &radio.DATA[1];
long id = atol( b );
if( id != config.moduleID )
{
SerialPrint( "Incorrect module id\n" );
return;
}
while( isdigit(*b) )
b++;
if( *b++ != ',' )
{
SerialPrint( "Missing parametres\n" );
return;
}
int node = atoi( b );
config.node = node;
radio.initialize(rfFrequency,node,networkGroup);
saveSettings();
}
void RFNode::processConfigMessage()
{
//TODO:
saveSettings();
}
The main loop() checks for serial input, blinks the LED and calls RFNode::loop(), nothing else.
Any clues as to what is going on?
Thanks in advance,
Eric
This is the receiving node code (1 of 11 files):
#include "RFServer.h"
#include "Config.h"
#include "watchdog.h"
void saveSettings(); // This function is called when the module configuration changes
RFServer rfServer; // Singleton class
RFM69 radio(8,0,true);
#define VERBOSE_TRACE
#define ENCRYPTKEY "kd8w3a0mz8q01gbh" //exactly the same 16 characters/bytes on all nodes!
#define IS_RFM69HW
extern bool needToSendMessagesToServer;
void RFServer::setup( int cfg_node, int cfg_group, int cfg_freq, int cfg_spi_ss )
{
pinMode( 0, INPUT );
node = cfg_node;
group = cfg_group;
Serial.print( "radio.init(" ); Serial.print( cfg_freq ); Serial.print( ", " );
Serial.print( node ); Serial.print( ", " ); Serial.print( group ); Serial.println( " );" );
delay(100);
radio.initialize(cfg_freq, node, group );
#ifdef IS_RFM69HW
radio.setHighPower(); //uncomment only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
delay(100);
}
// Call this function in the main loop() to reive any pending messages and process them
void RFServer::loop()
{
int sender;
while(radio.receiveDone())
{
ProcessMessage();
}
updateConnectedStatus();
}
void RFServer::ProcessMessage()
{
int sender = radio.SENDERID;
#ifdef VERBOSE_TRACE
Serial.println(); Serial.print( sender ); Serial.print(":");
for (byte i = 0; i < radio.DATALEN; i++)
Serial.print((char)radio.DATA[i]);
Serial.println();
#endif
radio.DATA[radio.DATALEN]= 0;
switch( radio.DATA[0] )
{
case 's':
case 'o':
{
// Parse status message
// Format: s<module ID>,<sensor reading>,<sensor reading>,...
// <sensor reading>: <input type><io ID>,<index>,<value>
// Example: s1234,A2001,0,102.3,A2002,1,53.0,D2003,2,1
// Sensor status from module ID 1234
// Pin 0 - Analog value 102.3
// Pin 1 - Analog value 53.0
// Pin 2 - Digital value 1 (high)
// Parse ModuleID
char *b = (char *) &radio.DATA[1];
int moduleID = atoi( b );
while( isdigit(*b) )
b++;
if( *b != ',' )
break;
b++;
if( sender < FIRST_NODE_NUMBER || sender >= MAX_NODE || cfg.moduleConfig[sender].moduleID != moduleID )
{
Serial.print( "Assigning node. Sender: " ); Serial.print( sender ); Serial.print(", ModuleID: "); Serial.print( moduleID );
Serial.print( "cfg[n].moduleID: " ); Serial.println( cfg.moduleConfig[sender].moduleID );
assignNewNode( moduleID, sender );
break;
}
moduleStatus[sender].connected = true;
moduleStatus[sender].lastStatusTime = millis();
if( radio.DATA[0] == 'o' )
moduleStatus[sender].outputCommandConfirmed = true;
for( int p=0; p <= RFNetwork::MAX_IO; p++ )
moduleStatus[sender].ioID[p] = 0;
// Parse Analog and Digital input values
for( int i=0; i<MAX_STATUS_READINGS; i++ )
{
char type;
int value;
if( *b != 'A' && *b != 'I' && *b != 'O' )
break;
type = *b;
bool digital = (*b == 'I' || * b == 'O' );
b++;
int pin = atoi( b);
if( pin < 0 || pin > RFNetwork::MAX_IO )
break;
while( isdigit(*b) )
b++;
if( *b != ',' )
break;
b++;
int newValue = atoi(b);
if( digital && newValue != moduleStatus[sender].portValue[pin] )
needToSendMessagesToServer = true;
moduleStatus[sender].portValue[pin] = newValue; // TODO: Convert to int
moduleStatus[sender].ioID[pin] = pin;
moduleStatus[sender].ioType[pin] = type;
moduleStatus[sender].portValueTime[pin] = millis();
while( isdigit(*b) || *b == '.' )
b++;
if( *b != ',' )
break;
b++;
}
}
break;
case 'c':
// Configuration messsage confirmation
// TODO: Parse message and verify config
moduleStatus[sender].pendingConfiguration = false;
break;
}
if (radio.ACK_REQUESTED)
{
radio.sendACK();
}
if( moduleStatus[sender].pendingOutputCmd )
{
sendOutputCommand( sender );
}
if( moduleStatus[sender].pendingConfiguration)
{
sendConfiguration( sender );
}
}
void RFServer::updateConnectedStatus()
{
int i;
for( i=FIRST_NODE_NUMBER; i<=MAX_NODE; i++ )
{
long m = millis();
moduleStatus[i].connected = ( m - moduleStatus[i].lastStatusTime < NOT_CONNECTED_TIMEOUT );
}
}
void RFServer::outputCommand( int id, int index, int value )
{
int g;
Serial.println( "outputCommand");
for( int n=2; n<MAX_NODE; n++ )
{
if( cfg.moduleConfig[n].moduleID == id )
{
// Output command O<module id>,<index>,<output id>,<value>
sendBuffer[0] = 'O';
char *b = &sendBuffer[1];
b = addIntToString( cfg.moduleConfig[n].moduleID, b );
*b++ = ',';
b = addIntToString( index, b );
*b++ = ',';
b = addIntToString( value, b );
*b = 0;
moduleStatus[n].outputCommandConfirmed = false;
Serial.print( 'S' ); Serial.print( n );
Serial.print( ':'); Serial.print( sendBuffer ); Serial.print(" ");
for( int retries = 0; retries < 30; retries++ )
{
radio.send( n, sendBuffer, strlen( sendBuffer ) );
long lastSendOutputCommandTime = millis();
while( !moduleStatus[n].outputCommandConfirmed && ((millis() - lastSendOutputCommandTime) < 100 ))
{
if(radio.receiveDone())
{
ProcessMessage();
}
}
if( moduleStatus[n].outputCommandConfirmed )
{
return;
}
Serial.print(','); Serial.print(retries);
}
Serial.println( "FAILED TO SEND" );
return;
}
}
Serial.println( "Module not found" );
}
void RFServer::sendOutputCommand( int sender )
{
// Send an output command to the module.
// the pendingOutputCmd and PendingOutputs will get cleared when
// the module confirms the command.
char *b;
sendBuffer[0] = 'O';
b = &sendBuffer[1];
itoa( cfg.moduleConfig[sender].moduleID, b, 10 );
while( isdigit(*b) )
b++;
*b++ = ',';
int g;
for( g=0; g<RFNetwork::MAX_IO; g++ )
{
if( moduleStatus[sender].pendingOutputs[g] >= 0 && moduleStatus[sender].pendingOutputs[g] <=255 )
{
b = addIntToString( g, b );
*b++ = ',';
b = addIntToString( moduleStatus[sender].ioID[g], b );
*b++ = ',';
b = addIntToString( moduleStatus[sender].pendingOutputs[g], b );
*b++ = ',';
moduleStatus[sender].pendingOutputs[g] = -1;
}
}
b--;
*b = 0;
radio.send( sender, sendBuffer, strlen( sendBuffer ) );
}
void RFServer::assignNewNode( int moduleID, int node )
{
Serial.print( "Assigning new node to module " ); Serial.print( moduleID); Serial.print(", node was:"); Serial.println(node);
// Look if it's in the list already. If so assign it to the existing node number.
for( int g = FIRST_NODE_NUMBER; g < MAX_NODE; g++ )
{
if( cfg.moduleConfig[g].added && cfg.moduleConfig[g].moduleID == moduleID )
{
sendAssignNodeMessage( node, g );
saveSettings();
return;
}
}
// Look if it's in the list already. If so assign it to the existing node number.
for( int g = FIRST_NODE_NUMBER; g < MAX_NODE; g++ )
{
if( cfg.moduleConfig[g].moduleID == moduleID )
{
sendAssignNodeMessage( node, g );
saveSettings();
return;
}
}
// Check if the sender node number is available, if so assign it there
if( !cfg.moduleConfig[node].added && node >= FIRST_NODE_NUMBER && node < MAX_NODE )
{
// cfg.moduleConfig[node].added = true;
Serial.print( "Assigning module ID " ); Serial.println( moduleID );
Serial.print( "to node " ); Serial.println( node );
cfg.moduleConfig[node].moduleID = moduleID;
saveSettings();
return;
}
// Look for an unused node number, and add it there.
for( int g = FIRST_NODE_NUMBER; g < MAX_NODE; g++ )
{
if( !cfg.moduleConfig[g].added && !moduleStatus[g].connected )
{
//cfg.moduleConfig[g].added = true;
cfg.moduleConfig[g].moduleID = moduleID;
sendAssignNodeMessage( node, g );
saveSettings();
return;
}
}
Serial.println( "Error: No nodes available for adding module" );
}
void RFServer::sendConfiguration( int node )
{
// Send a configuration message to the module.
// the pendingConfiguration and pendingConfigurations[] will get cleared when
// the module confirms the command.
char *b;
sendBuffer[0] = 'C';
b = &sendBuffer[1];
itoa( cfg.moduleConfig[node].moduleID, b, 10 );
while( isdigit(*b) )
b++;
*b++ = ',';
int g;
for( g=0; g<RFNetwork::MAX_IO; g++ )
{
if( moduleStatus[node].pendingConfigurations[g] >= 0 && moduleStatus[node].pendingConfigurations[g] <=5 )
{
itoa( g, b, 10 );
while( isdigit(*b)) b++;
*b++ = ',';
itoa( moduleStatus[node].pendingConfigurations[g], b, 10 );
while( isdigit(*b) )
b++;
*b = ',';
}
}
*b = 0;
radio.send( node, sendBuffer, strlen( sendBuffer ) );
}
void RFServer::sendAssignNodeMessage( int node, int newNode )
{
// Send a node assignment command
char *b;
sendBuffer[0] = 'N';
b = &sendBuffer[1];
itoa( cfg.moduleConfig[newNode].moduleID, b, 10 );
while( isdigit(*b) )
b++;
*b++ = ',';
itoa( newNode, b, 10 );
while( isdigit(*b) )
b++;
*b = 0;
radio.send( node, sendBuffer, strlen( sendBuffer ));
moduleStatus[node].connected =true;
Serial.println( sendBuffer );
}
char * RFServer::addIntToString( int value, char *b )
{
itoa( value, b, 10 );
while( isdigit(*b))
b++;
return b;
}
The main loop() checks for serial input and calls RFServer::loop().
What is the Shake of their relative position? A V? A T? Can you reduce the power output of one of the nodes, or maybe all?
Quote from: luisr320 on June 12, 2014, 01:23:41 PM
What is the Shake of their relative position? A V? A T? Can you reduce the power output of one of the nodes, or maybe all?
The transmitters are about two feet away from the receiver, on either side. I tried reducing the power level to setPowerLevel(6). The symptoms remained the same.
I added a serial trace of the RSSI. Before transmitting, the radio.readRSSI() is in the range of -90 to -102. After receiving, the radio.RSSI is in the range of -69 to -42. This is with the power level set to 6.
The readRSSI() is about the same if I have 1, 2 or 3 transmitters operating.
Two feet? Those radios don't work very well at that distance from each other. Can you get them further apart? Like in different rooms?
You can have them 5cm from each other, it doesn't matter, there should not be interference.
Try the Node and Gateway examples. Once you get those to work add your own code around that. Your own code can starve the radio in reception mode, depending on interrupts and such so it's a good idea to familiarize yourself with how the library works if you make extensive use of interrupts.
I got it working!
I'm not sure why this works, but I replaced the delay(2000) in the sendStatus() function with,
unsigned long time2sec = millis();
while( millis() - time2sec < 2000 )
{
checkForMessageReceived();
}
I figured this out through trial and error - simplified my code, and then started adding stuff back in. I figured out the delay() was causing the problem. Why? I'm not sure. The checkForMessageReceived() calls radio.receiveDone() until there are no pending messages.
You want to call receiveDone() as often as possible ;)
Otherwise a packet might have been received and if you're not checking if it's ready you might miss it or it gets overwritten by another packet. There's only room for 1 packet in the radio buffer at 1 time. The lib could be extended to buffer for multiple packets. Maybe I will do that on the Moteino MEGA where you got TONS of RAM ;)