Hello,
I am trying to code a sketch to use a moteino as a repeater node that will receive messages from far away nodes and repeat them towards the gateway.
I do not need bi-directional messages so only the nodes will send data to the extender and the extender to the gateway.
Node 20 ------------>
Extender Node 100 -------------> Gateway Node 1
Node 21 ------------>
I have come up with quite an advance code, but when I enable the radio.sendWithRetry line it will reboot from time to time (quite often). I think its due to reading/writing on memory array payloadOUT.
I think anyone can test the code in their existing setup as I have disabled the ACK so it will just read the default network set by Felix
This can solve my current problem of 2 nodes which are too far away, also opens a few options for future projects.
Thanks for any help.
edit: notice the symbol after the SMS print. There it normally writes the content of payloadOUT.
Sometimes it works, and message gets passed to the gateway node
// **********************************************************************************************************
// GarageMote garage door controller base receiver sketch that works with Moteinos equipped with HopeRF RFM69W/RFM69HW
// Can be adapted to use Moteinos using RFM12B
// This is the sketch for the base, not the controller itself, and meant as another example on how to use a
// Moteino as a gateway/base/receiver
// 2014-07-14 (C) [email protected], http://www.LowPowerLab.com
// **********************************************************************************************************
// Creative Commons Attrib Share-Alike License
// You are free to use/extend this code/library but please abide with the CCSA license:
// http://creativecommons.org/licenses/by-sa/4.0/
// **********************************************************************************
#include <RFM69.h> //get it here: https://github.com/lowpowerlab/rfm69
#include <RFM69_ATC.h> //get it here: https://github.com/lowpowerlab/RFM69
#include <RFM69_OTA.h> //get it here: https://github.com/lowpowerlab/RFM69
#include <SPIFlash.h> //get it here: https://github.com/lowpowerlab/spiflash
#include <SPI.h> //included with Arduino IDE (www.arduino.cc)
//*****************************************************************************************************************************
// ADJUST THE SETTINGS BELOW DEPENDING ON YOUR HARDWARE/SITUATION!
//*****************************************************************************************************************************
int NODEID = 1;
#define NETWORKID 200
//#define FREQUENCY RF69_433MHZ //Match this with the version of your Moteino! (others: RF69_433MHZ, RF69_868MHZ)
//#define FREQUENCY RF69_868MHZ
#define FREQUENCY RF69_915MHZ
#define ENCRYPTKEY "sampleEncryptKey" //has to be same 16 characters/bytes on all nodes, not more not less!
#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
#define LED 9
#define FLASH_CS 8
#define SERIAL_BAUD 115200
#define SERIAL_EN //comment out if you don't want any serial verbose output
#define ACK_TIME 30 // # of ms to wait for an ack
//*****************************************************************************************************************************
#ifdef SERIAL_EN
#define DEBUG(input) {Serial.print(input); delay(1);}
#define DEBUGln(input) {Serial.println(input); delay(1);}
#else
#define DEBUG(input);
#define DEBUGln(input);
#endif
RFM69 radio;
SPIFlash flash(FLASH_CS, 0xEF30); //EF40 for 16mbit windbond chip
void setup() {
Serial.begin(SERIAL_BAUD);
delay(10);
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW
radio.setHighPower(); //uncomment only for RFM69HW!
#endif
radio.encrypt(ENCRYPTKEY);
char buff[50];
sprintf(buff, "\nListening at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
DEBUGln(buff);
if (flash.initialize())
{
DEBUGln("SPI Flash Init OK!");
}
else
DEBUGln("SPI Flash Init FAIL! (is chip present?)");
Serial.print(F("Node_ID: ")); Serial.println(NODEID);
Serial.print(F("NETWORKID: ")); Serial.println(NETWORKID);
for (int i = 0; i < 100; i++) { Serial.print(F("-"));}Serial.println();
for (int i = 0; i < 100; i++) { Serial.print(F("-"));}Serial.println();
for (int i = 0; i < 100; i++) { Serial.print(F("-"));}Serial.println();
for (int i = 0; i < 100; i++) { Serial.print(F("-"));}Serial.println();
delay(5000);
}
char payloadOUT[161]; // Radio - Buffer to store the message to be send
void loop() {
if (radio.receiveDone())
{
int rssi = radio.RSSI;
DEBUG('[');DEBUG(radio.SENDERID);DEBUG("] ");
if (radio.DATALEN > 0)
{
for (byte i = 0; i < radio.DATALEN; i++)
DEBUG((char)radio.DATA[i]);
DEBUG(" [RSSI:");DEBUG(rssi);DEBUG("]");
}
Serial.print(F("\n\tData received from node: "));Serial.println(radio.SENDERID);
/*
* // Do not send request for now
if (radio.ACKRequested())
{
byte theNodeID = radio.SENDERID;
radio.sendACK();
DEBUG("[ACK-sent]");
}
*/
//Serial.print(TimeToString(millis()));
Serial.print(F("\nRAM before: "));Serial.println(checkFreeRAM());
// Save all the array to a global array Variable
// SaveArray(radio.DATA, sizeof(radio.DATA));
// Clear the array
memset(payloadOUT, 0, sizeof(payloadOUT));
Serial.print(F("\tSaving Array size: "));Serial.println(radio.DATALEN);
for(int i = 0; i < radio.DATALEN; i++)
{
//Serial.print(F("RAM for: "));Serial.println(checkFreeRAM());
Serial.print(i);Serial.print(F(")"));Serial.println((char)radio.DATA[i]);
payloadOUT[i] = (char)radio.DATA[i];
//if (payloadOUT[i] = '0') { Serial.println(F("EMPTY FOUND"));}
if (payloadOUT[i] == '\0') { Serial.println(F("EMPTY/null FOUND")); }
if (payloadOUT[i] == ' ') { Serial.println(F("SPACE FOUND")); }
}
Serial.print(TimeToString(millis()));
Serial.print(F("\nRAM after: "));Serial.println(checkFreeRAM());
#define GATEWAYID 100
// *********************** SEND MSG VIA RFM69 ***********************************************
byte buffLen=strlen(payloadOUT);
if (buffLen > 61) { Serial.println(F("*\n*\n*\nATENCION MENSAJE MUY LARGO\n*\n*\n*")); }
Serial.print(F("\tSending...\n"));
Serial.print(F("\tFrom Node: "));Serial.println(radio.SENDERID);
Serial.print(F("\tSize: "));Serial.println(buffLen);
Serial.print(F("\tSMS : "));Serial.println(payloadOUT);
if (radio.sendWithRetry(GATEWAYID, payloadOUT, buffLen, 1, 100)) { // uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries, uint8_t retryWaitTime
Serial.print(" ok!");
// success++;
// sendNewData = false;
}
else { Serial.print(" nothing..."); }
Serial.println();
// ************************ SEND MSG VIA RFM69 **********************************************
Serial.println(F("-------------------------------------"));
DEBUGln();
Blink(LED,3);
}
}
/*
void SaveArray(char * val, int val_array_size){
// Clear the array
memset(payloadOUT, 0, sizeof(payloadOUT));
//payloadOUT[0] = (char)0;
Serial.print(F("\tSaving Array size: "));Serial.println(val_array_size);
for(int i = 0; i < val_array_size; i++)
{
Serial.print(i);Serial.print(F(")"));Serial.println((char)val[i]);
payloadOUT[i] = (char)val[i];
if (payloadOUT[i] == '\0') { Serial.println(F("EMPTY FOUND"));}
//if (payloadOUT[i] = ' ') { Serial.println(F("SPACE FOUND"));}
}
}
*/
void Blink(byte PIN, int DELAY_MS)
{
pinMode(PIN, OUTPUT);
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
}
int checkFreeRAM()
{
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
char TimeToString(unsigned long t){
// A way to verbalize this class method is: "TimeToString() is a method of the FunctionClass class and uses an unsigned long as its argument.
// The method returns a char pointer."
// t is time in seconds = millis()/1000;
static char str[12];
t /= 1000;
long h = t / 3600;
t = t % 3600;
int m = t / 60;
int s = t % 60;
sprintf(str, "%04ld:%02d:%02d", h, m, s);
return str;
} Maybe this will help, this is the part I am interested to share and see what is causing the problem. (better than making you read the whole sketch)
Gathering the incoming message...
(http://i66.tinypic.com/1ju7br.png)
Sending it again...
(http://i68.tinypic.com/2m4t3q9.png)
Moteino Rebooting...
Moteino Resending the message correctly...
Briefly looking at your code I see a few things:
- you spend a ton of time "printing" the packets while you process the packets, you should remove or move that logic AFTER you process the packet, or at least make it very quick - ex instead of printing every character just call print(radio.PAYLOAD) etc
- remove the 100ms retry delay for the sendWithRetry, its huge
- not sure what's going on with the reboot, but unless you specifically reboot yourself, then you got some memory leaks with all those strings
Lots of code to optimize :)
Thanks Felix for your time.
- I added the extra printing trying to find of any of the characters were causing the reset. Apparently this is not the problem as I have removed the printing and now I only save the radio.DATA to the new variable. Like this...
payloadOUT[i] = (char)radio.DATA[i];
- I have a 100ms delay just during testing as I print the response ms time, just to see how things are going. I have reduced it after your comment.
I know there is lots of optimizing to do, but the real code which is doing some saving and send is not much. I cant seem to see why it is still rebooting.
Could you check this code? I am just adding 2 extra lines to your examples code, cant see how it affects.
Global variable
char payloadOUT[61]; // Radio - Buffer to store the message to be send
/* Clear the array */
memset(payloadOUT, 0, sizeof(payloadOUT));
for(int i = 0; i < radio.DATALEN; i++)
{
payloadOUT[i] = (char)radio.DATA[i];
}
and then...
byte buffLen=strlen(payloadOUT);
if (radio.sendWithRetry(GATEWAYID, payloadOUT, buffLen, 1, 50)) { // uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries, uint8_t retryWaitTime
Serial.print(" ok!");
}
else { Serial.print(" nothing..."); }
Big thanks!
hold on before spending any time helping me, I think I have found that the resets are happening when a message is received from 1 of the nodes. I am going to leave it running overnight and I will report back to see what is going on.
Thank you.
ok, no luck. Still the same problem.
Any idea?
Thanks a lot!
I think the problem comes when sendWithRetry gets the buffer message as I have set the rest of the parameters to defined values: sendWithRetry(100, payloadOUT, 10, 50)
const void* buffer
When the moteino reboots, I cant get to see the Serial.print I have added to the library. (this is the only modification I have made to the library)
bool RFM69::sendWithRetry(uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries, uint8_t retryWaitTime) {
uint32_t sentTime;
Serial.print(F("inside sendWithRetry"));
for (uint8_t i = 0; i <= retries; i++)
{
send(toAddress, buffer, bufferSize, true);
sentTime = millis();
while (millis() - sentTime < retryWaitTime)
{
if (ACKReceived(toAddress))
{
Serial.print(" ~ms:"); Serial.print(millis() - sentTime);
return true;
}
}
Serial.print(" RETRY#"); Serial.println(i + 1);
}
return false;
}
How can I verify at a deeper level, what is stored, or how it is stored payloadOUT?
maybe that way I can understand why it reboots when sending it to sendWithRetry.
Thank you very much
Do not use more than 3 retries. If you have to that indicates something is very wrong in your code/setup.
Your questions are more programming/c++ related, you have to use memory and the buffers very carefuly, there is only 2k of ram on the regular Moteino, 16k on the MoteinoMEGA.
Avoid delays and other overhead code or move it out of the packet intercepting code.
Based on the given examples you can add simple sendWithRetry or just send() immediately after receiving a package from your end node.
Hello Felix,
I understand the memory limitations. Your message gave me an idea, "forget about saving it to another array, just use what you have".
So I added the following just after your example code where you receive and print the message.
if (radio.receiveDone())
{
int rssi = radio.RSSI;
DEBUG('[');DEBUG(radio.SENDERID);DEBUG("] ");
if (radio.DATALEN > 0)
{
for (byte i = 0; i < radio.DATALEN; i++){
DEBUG((char)radio.DATA[i]);}
DEBUG(" [RSSI:");DEBUG(rssi);DEBUG("]");
}
if (radio.sendWithRetry(GATEWAYID, radio.DATA, radio.DATALEN, 1, 50)) { // uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries, uint8_t retryWaitTime
Serial.print(" ok!");
}
else { Serial.print(" nothing..."); }
}
As you can see, no new variables, no extra memory usage.
very strange.
Could someone do a quick test with something like my code above, to see what happens?
might it be an interrupt issue?
I seem to see that sometimes the resets happens often (every loop) others it happens every x number of messages.
Could it be that a new message is received and triggers an interrupt that cause it to reboot upon resume?
update:
This solves the problem of the reboots, although its not an acceptable solution...
but maybe helps finding out what is going on.
if (radio.SENDERID == 31) {
if (radio.receiveDone())
{
int rssi = radio.RSSI;
DEBUG('[');DEBUG(radio.SENDERID);DEBUG("] ");
if (radio.SENDERID == 31) {
if (radio.DATALEN > 0)
{
for (byte i = 0; i < radio.DATALEN; i++){
DEBUG((char)radio.DATA[i]);}
DEBUG(" [RSSI:");DEBUG(rssi);DEBUG("]");
}
if (radio.sendWithRetry(GATEWAYID, radio.DATA, radio.DATALEN, 1, 50)) { // uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries, uint8_t retryWaitTime
Serial.print(" ok!");
}
else { Serial.print(" nothing..."); }
Update 2:
Still happening :'(
Im out of ideas, the code is bare minimum now and still getting the reboots.
void loop(){
if (radio.receiveDone())
{
int rssi = radio.RSSI;
DEBUG('[');DEBUG(radio.SENDERID);DEBUG("] ");
if (radio.DATALEN > 0)
{
for (byte i = 0; i < radio.DATALEN; i++){
DEBUG((char)radio.DATA[i]);}
DEBUG(" [RSSI:");DEBUG(rssi);DEBUG("]");
}
if (radio.sendWithRetry(GATEWAYID, radio.DATA, radio.DATALEN, 1, 50)) { // uint8_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries, uint8_t retryWaitTime
Serial.print(" ok!");
}
else { Serial.print(" nothing..."); }
}
}