I implemented it on the Moteino side now. Seems to work for me.
My Gateway Moteino is also doing some IR Control of my AVR, just ignore this part...
Apart from this issue I do observed lost packets as I read somewhere else and the serial speed was reduced to 19200 in some of the last GW versions by you. My conclusion was that it must have had something to do with the usage of strings instead of char arrays in the demo sketch. I eliminated this also few month ago and set the speed up to 115200 again and did never observe any loss again since I was using this version of coding.
My code is linked and included below.
// ***************************************************************************************************************
// RFM69 receiver/gateway sketch, with ACK and optional encryption, and Automatic Transmission Control
// Passes through any wireless received messages to the serial port and vice versa + responds to ACKs
// ***************************************************************************************************************
// Scram @ 17.1.2018
//
// - first version with queuing
//
// ***************************************************************************************************************
//******************************* INCLUDES *********************************************
#include <RFM69.h> //get it here: https://www.github.com/lowpowerlab/rfm69
#include <RFM69_ATC.h> //get it here: https://www.github.com/lowpowerlab/rfm69
//****************************************************************************************************************
//************ IMPORTANT SETTINGS - YOU MUST CHANGE/CONFIGURE TO FIT YOUR HARDWARE *******************************
//****************************************************************************************************************
#define NODEID 6 //unique for each node on same network
#define NETWORKID 225 //the same on all nodes that talk to each other
#define FREQUENCY RF69_868MHZ //Match frequency to the hardware version of the radio on your Moteino (uncomment one):
#define ENCRYPTKEY "***********" //exactly the same 16 characters/bytes on all nodes!
#define RETRY_COUNT 5 //try so many times before giving up [2 is default]
#define RETRY_DELAY 50 //time in ms to retry [40ms is default]
#define IS_RFM69HW_HCW //uncomment only for RFM69HW/HCW! Leave out if you have RFM69W/CW!
//Auto Transmission Control - dials down transmit power to save battery
//Usually you do not need to always transmit at max output power
//By reducing TX power even a little you save a significant amount of battery power
//This setting enables this gateway to work with remote nodes that have ATC enabled to
//dial their power down to only the required level
#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL
#define MAX_BUFF_CHARS 12 // max command length in bytes
#define MAX_BUFF_LINES 32 // max commands in queue
#define RF_REPLY_TIMEOUT 600 // [600ms] timeout between received ACK of request till positive answer from target in ms
//#define SERIAL_EN // comment this out when deploying to an installed SM to save a few KB of sketch size
#ifdef SERIAL_EN
#define SERIAL_BAUD 115200
#define DEBUG(input) {Serial.print(input); delay(1);}
#define DEBUGln(input) {Serial.println(input); delay(1);}
#define SERIALFLUSH() {Serial.flush();}
#else
#define SERIAL_BAUD 115200
#define DEBUG(input);
#define DEBUGln(input);
#define SERIALFLUSH();
#endif
//****************************************************************************************************************
//************ HARDWARE PINs *************************************************************************************
//****************************************************************************************************************
#define LED_PIN 9 // D9
#define txPinIR 3 // D3
//****************************************************************************************************************
//******************* Constants IR *******************************************************************************
#define Carrier_Frequency 43000 //erzeugt auf meinem Oszi 37,7kHz
#define Duty_Cycle 61 //49:51 gemessen
#define NARROW_PW 400 //Pulse width in us
#define WIDE_PW 1270 //Pulse width in us
#define PREAMBLMARK 3285 //Pulse width in us
#define PREAMBLSPACE 1755 //Pulse width in us
#define PERIOD (1000000+Carrier_Frequency/2)/Carrier_Frequency
#define HIGHTIME PERIOD*Duty_Cycle/100
#define LOWTIME PERIOD - HIGHTIME
#define IR_REPEAT 8 //defines how often a IR-Command is sent repeatedly
unsigned CUSTOMCODE = 0xCDAB; //16-Bit Code is the same for all commands
unsigned PARITY = 0xF; //4-Bit Code is the same for all commands
unsigned SYSTEMCODE = 0xB; //4-Bit Code is the same for all commands
unsigned PRODUCT[] = {0xDE,0xCE,0xFE}; //8-Bit PRODUCT Code for "switch on", "stdby", "raspi input & on"
unsigned FUNCTION[] = {0xFF,0xFF,0xD2}; //8-Bit FUNCTION Code for "switch on", "stdby", "raspi input & on"
unsigned DATACHECK[] = {0x9E,0x8E,0x93}; //8-Bit DATACHECK Code for "switch on", "stdby", "raspi input & on"
//******************************* GLOBALS **************************************************************************
#ifdef ENABLE_ATC
RFM69_ATC radio;
#else
RFM69 radio;
#endif
char * cmd_queue[MAX_BUFF_CHARS][MAX_BUFF_LINES];
byte add_pointer = 0;
byte get_pointer = 0;
byte size_of_queue = 0;
long send_time = 0;
bool wait_for_reply = false;
byte target_to_reply = 0;
unsigned long packetCount = 0; // global packet counter
byte bitarray[50]; // holds the 48 Bits for sendin IR Kaseiyko Code
//******************************* SETUP **************************************************************************
void setup() {
Serial.begin(SERIAL_BAUD);
delay(10);
pinMode(txPinIR,OUTPUT); //IR amplifier
digitalWrite(txPinIR,LOW);
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW_HCW
radio.setHighPower(); //must include this only for RFM69HW/HCW!
#endif
radio.encrypt(ENCRYPTKEY);
radio.promiscuous(false);
char buff[50];
sprintf(buff, "\nListening at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
Serial.println(buff);
Serial.flush();
}
//******************************* FUNCTIONS *********************************************
// send IR MARK
void mark(unsigned long mLen) {
if (mLen==0) return;
unsigned long now = micros();
while ((micros() - now) < mLen) {
digitalWrite(txPinIR, HIGH);
delayMicroseconds(HIGHTIME-5);
digitalWrite(txPinIR, LOW);
delayMicroseconds(LOWTIME-4);
}
}
// send IR SPACE
void space(unsigned long sLen) {
if (sLen==0) return;
while (sLen>16383) {
delayMicroseconds(16383);
sLen -= 16383;
}
delayMicroseconds(sLen);
}
//fill IR array prior to sending
void fill_ir_array(byte item) {
int j=0;
for(int i=0 ;i<=15;i++) {
bitarray[j]=bitRead(CUSTOMCODE, i);
j++;
}
for(int i=0 ;i<=3;i++) {
bitarray[j]=bitRead(PARITY, i);
j++;
}
for(int i=0 ;i<=3;i++) {
bitarray[j]=bitRead(SYSTEMCODE, i);
j++;
}
for(int i=0 ;i<=7;i++) {
bitarray[j]=bitRead(PRODUCT[item], i);
j++;
}
for(int i=0 ;i<=7;i++) {
bitarray[j]=bitRead(FUNCTION[item], i);
j++;
}
for(int i=0 ;i<=7;i++) {
bitarray[j]=bitRead(DATACHECK[item], i);
j++;
}
}
void ir_send(byte command_id) {
DEBUG("Sending IR Code: "); DEBUGln(command_id);
for(int i=0;i<IR_REPEAT;i++) {
fill_ir_array(command_id); // load bitarray with codes "0", "1", "2" -> "switch on", "stdby", "raspi input & on"
mark(PREAMBLMARK); // send preamble mark
space(PREAMBLSPACE); // & space
for(int i=0 ;i<48;i++) { // send bits
if(bitarray[i]==0) {mark(NARROW_PW); space(WIDE_PW); }
if(bitarray[i]==1) {mark(NARROW_PW); space(NARROW_PW); }
}
mark(NARROW_PW); // seems to need it in the end as kindof trailer (like a half "1")
delay(70); // intercommand delay
}
}
// here to process incoming serial data after a terminator received
void process_cmd (char * data) {
char delimiter[] = ":";
char *ptr;
char part[MAX_BUFF_CHARS];
byte targetId;
byte sendLen = 0;
byte temperature = 0;
if (strncmp(data, "t", 1) == 0) { // if Gateway asked for temperature
byte temperature = radio.readTemperature(-2); // -2 = user cal factor, adjust for correct ambient
Serial.print( "Radio Temp is ");
Serial.print(temperature);
Serial.println(" Centigrade");
Serial.flush();
}
if (strncmp(data, "IR", 2) == 0) { // if Gateway asked for IR command
ptr = strtok(data,delimiter); // search for delimiter
if(ptr != NULL) { // if delimiter ist found somewhere we consider a proper command is here
sprintf(part, "%s", ptr); // part is now "IR"
ptr = strtok(NULL, ""); // rest of string
sprintf(part, "%s", ptr); // part is now everything right of delimiter -> hopefully only the ID
targetId = atoi(part); // convert to integer
if (targetId >= 0 && targetId<=2) // if in allowed range
{
ir_send(targetId); // send IR-Code
//Serial.println();
//Serial.print(F("send IR command : "));Serial.println(targetId);
}
}
} else {
ptr = strtok(data,delimiter); // If not IR command it's probably some message to any node -> search for delimiter ':'
if(ptr != NULL) { // if delimiter ist found somewhere we consider a proper command is here
sprintf(part, "%s", ptr);
targetId = atoi(part);
ptr = strtok(NULL, ""); // rest of string
sprintf(part, "%s", ptr);
if (targetId > 0 && targetId<=255)
{
while(part[sendLen]!='\n') {
sendLen++;
}
DEBUG("sending...");
//we're waiting only if we got ACK from target
if(radio.sendWithRetry(targetId, part, sendLen, RETRY_COUNT, RETRY_DELAY)) {
DEBUGln("ACK ok");
// we're expecting an answer from the target so we:
// 1. remember the sent time
// 2. set flag "wait_for_reply" which is blocking the queue processing
// 3. remember the target ID for which answer we're waiting for
send_time = millis();
wait_for_reply = true;
target_to_reply = targetId;
} else {
DEBUGln("no ACK");
}
}
}
}
} // end of process_data
// add char* to global queue
void add_to_queue (char * data) {
int i=0;
if((size_of_queue) == MAX_BUFF_LINES) {
//buffer overflow -> add nothing
DEBUGln(F("Buffer overflow!"));
} else {
// add line to cmd_queue[][add_pointer]
DEBUG(F("-> "));
while(data[i]!='\n') {
DEBUG((char)data[i]);
cmd_queue[i][add_pointer]=data[i];
i++;
}
cmd_queue[i][add_pointer]='\n';
DEBUGln();
size_of_queue++;
//round robin counter 0,1,2...(MAX_BUFF_LINES-1) results in MAX_BUFF_LINES elements
add_pointer = (add_pointer + 1) % MAX_BUFF_LINES;
}
}
// execute next cmd from global queue if there's one
void execute_cmd_from_queue() {
int i=0;
char tmp_cmd[MAX_BUFF_CHARS];
if(size_of_queue>0) {
DEBUG(F("<- "));
while(cmd_queue[i][get_pointer]!='\n') {
DEBUG((char)cmd_queue[i][get_pointer]);
tmp_cmd[i] = cmd_queue[i][get_pointer];
i++;
}
tmp_cmd[i] = cmd_queue[i][get_pointer];
DEBUGln();
size_of_queue--;
//round robin counter 0,1,2...(MAX_BUFF_LINES-1) results in MAX_BUFF_LINES elements
get_pointer = (get_pointer + 1) % MAX_BUFF_LINES;
process_cmd(tmp_cmd);
} else {
// basically do nothing when queue is empty
}
}
// here's the processing of single char/bytes as soon as they're coming from UART
void processIncomingByte (const byte inByte) {
static char input_line [MAX_BUFF_CHARS];
static unsigned int input_pos = 0;
switch (inByte)
{
case '\r': // end of text
input_pos = 0; // \r leads to buffer reset
break;
case '\n':
input_line [input_pos] = inByte; // terminating string with \n
add_to_queue(input_line); // fill up queue
// reset buffer for next time
input_pos = 0;
break;
default:
// keep adding if not full ... allow for terminating byte
if (input_pos < (MAX_BUFF_CHARS - 1)) {
input_line [input_pos++] = inByte;
} else {
// if theres no EOL coming before MAX_BUFF_CHARS is exceeded we'll just terminate and send it, last char is then lost
input_line [input_pos] = '\n'; // terminating string with \n
add_to_queue(input_line); // fill up queue
// reset buffer for next time
input_pos = 0;
}
break;
} // end of switch
} // end of processIncomingByte
void handle_radio_message() {
byte sending_target = 0;
sending_target = radio.SENDERID;
// if we're waiting for this specific node to reply reset flags -> enable sending of further commands from queue
if(wait_for_reply == true) {
if(target_to_reply == sending_target) {
wait_for_reply = false;
target_to_reply = 0;
DEBUG("time till answer: ");DEBUG((millis() - send_time));DEBUGln("ms");
}
}
//*******************************************************************************************
// all Serial.prints here are definitely needed
//*******************************************************************************************
// handover the RF data to the Raspberry Gateway Interface
Serial.print("#[");
Serial.print(++packetCount);
Serial.print(']');
Serial.print('[');Serial.print(sending_target, DEC);Serial.print("] ");
for (byte i = 0; i < radio.DATALEN; i++)
Serial.print((char)radio.DATA[i]);
Serial.print(" [RSSI:");Serial.print(radio.RSSI);Serial.print("]");
if (radio.ACKRequested())
{
byte theNodeID = radio.SENDERID;
radio.sendACK();
Serial.print("[ACK sent]");
}
Serial.println();
Serial.flush();
//*******************************************************************************************
// all Serial.prints here are definitely needed
//*******************************************************************************************
}
//******************************* MAIN **************************************************************************
void loop() {
// if somehting is received over UART process it
if(Serial.available () > 0) processIncomingByte (Serial.read ());
// if somehting is received over the air process it
if (radio.receiveDone()) handle_radio_message();
// if we're in wait state -> monitor for possible timout
if(wait_for_reply == true) {
if((millis() - send_time) >= RF_REPLY_TIMEOUT) {
// timeout -> we're no longer waiting for anything -> reset all flags so further queue processing is happening
wait_for_reply = false;
target_to_reply = 0;
DEBUGln(F("Node Timeout!"));
}
}
// if somehting is in the queue and we're not in wait state -> process it
if(wait_for_reply == false) {
// we're not waiting for any node to reply so keep on processing queue if something is within
if(size_of_queue>0) {
execute_cmd_from_queue();
}
}
}// end main