Hi guys, so I've read through the sticky thread about "getting started sending/receiving payloads" but I'm a little stumped on how to go about breaking up the incoming data and assigning to different variables.
Basically I have a two button remote. Up button and down button. When I press the up button, it sends a '1' command to the receiver to turn on a hydraulic pump. When I press the down button it sends a '0' command to open the drain valve.
In response, the receiver sends a position status back to the remote. '0' for the cylinder in the down position, '1' for the cylinder in the up position, and a '2' for some error state. This results in the LED on the remote flashing to indicate the status.
I had these basic functions working fine because it was easy to send 1 character back and forth and read one character.
However, the remote runs on batteries and I've since tried some difference approaches to consume less power. I've successfully put the remote to sleep between different button presses, but I wanted to try to go one step farther...
One strategy is to have the receiver send a 'sleeptime' number back to the remote in addition to the position status. This sleep number is an integer that represents the number of seconds the pump or drain is going to be on. This allows the Moteino to go to sleep for those seconds rather than remain on and consume battery. When the sleep time is up, Moteino can wake back up and poll the radio to see if there was an error message sent back and then go back to sleep again.
Since this requires sending back several numbers and then somehow separating them into two different variables (posstatus and sleeptime) at the remote, I am having some trouble working with the data coming back from the radio. I'm not sure the best way to approach this. The first single digit number is always the posstatus, and then after that the sleeptime can be set from 1 second to 30 seconds. So the data coming back can be 2 digits or 3 digits.
Is it best to just read these numbers into a string and then try and work with that? Or is there some way to get them directly into int or byte variables?
I'm sure this is easy for you guys but I'm still trying to wrap my head around the best way to convert numbers etc into the strings that are sent over the radio and then back into something usable on the other end.
Here is my remote code... you can see where I've commented that I'm not sure the best way to read in the data after calling radio.ReceiveDone()
#define EI_NOTEXTERNAL
#include <EnableInterrupt.h>
#include <LowPower.h>
#include <OneButton.h>
#include <RFM69.h>
#include <SPI.h>
// Setup a new OneButton on pin D5 (UP BUTTON) --> true parameter enables the pullup resistor on this pin
OneButton button1(5, true);
// Setup a new OneButton on pin D3 (DOWN BUTTON) --> true parameter enables the pullup resistor on this pin
OneButton button2(3, true);
#define UPPIN 5
#define DOWNPIN 3
// Addresses for this node. CHANGE THESE FOR EACH NODE!
#define NETWORKID 0 // Must be the same for all nodes
#define MYNODEID 2 // My node ID
#define TONODEID 1 // Destination node ID
// RFM69 frequency
#define FREQUENCY RF69_915MHZ
// AES encryption (or not):
#define ENCRYPT true // Set to "true" to use encryption
#define ENCRYPTKEY "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
// Use ACKnowledge when sending messages (or not):
#define USEACK true // Request ACKs or not
// Packet sent/received indicator LED (optional):
#define LED 4 // LED positive pin
//#define GND 8 // LED ground pin
#define posDOWN 0
#define posUP 1
#define posERROR 2
// Create a library object for our RFM69HCW module:
RFM69 radio;
int sendlength = 1;
char message[] = "1";
int posstatus = 0; //1 = pos up, 0 = pos down, 2 = pos error
int sleeptime = 0;
volatile bool newbutton = false;
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void setup() {
//PULL ALL UNUSED PINS TO PULLUP OR OUTPUT ... FLOATING PINS CAN WASTE BATTERY
pinMode(LED,OUTPUT);
digitalWrite(LED,LOW); //make sure LED is off
// link the button 1 functions.
button1.attachClick(click1); //enable single click detection
button1.attachNull(nullclick1); //enable debounce error function
// link the button 2 functions.
button2.attachClick(click2); //enable single click detection
button2.attachNull(nullclick2); //enable debounce error function
//Initialize the RFM69HCW:
radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
// Turn on encryption if set to true above
if (ENCRYPT)
radio.encrypt(ENCRYPTKEY);
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void upinterrupt(){
newbutton = true;
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void downinterrupt(){
newbutton = true;
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
// main code here, to run repeatedly:
void loop() {
strconvert = ""; //clear String
sleeptime = 0;
digitalWrite(LED,LOW); //make sure the LED is off before going to sleep indefinitely.
//These interrupts will wake the module when it is powered down to save battery
enableInterrupt(UPPIN, upinterrupt, FALLING); //enables interrupt on UPPIN and calls upinterrupt
attachInterrupt(digitalPinToInterrupt(DOWNPIN), downinterrupt, FALLING); //enables interrupt on DOWNPIN and calls downinterrupt
radio.sleep(); //Put the RFM69 to sleep
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); //Put Moteino to power down sleep. Will wake on interrupt and run the next line of code after ISR
//First line to run after the ISR that wakes the module:
disableInterrupt(UPPIN);
detachInterrupt(DOWNPIN); //Prevents interrupts being called again until everything is complete
while(newbutton){ //ISR that wakes up module will set this to true
button1.tick(); //check button1 (UP) for click, double click, hold
button2.tick(); //check button1 (UP) for click, double click, hold //checking to see what button was pressed and if click/doubleclick/hold
}
if (radio.receiveDone())
{
/*--------------------------------
* This is where I need to read the incoming data and separate it. The first number is the 'posstatus'. The next single or two digit number is the 'sleeptime'
*/
if (radio.ACKRequested())
{
radio.sendACK(); //immediately respond to acknowledge message was received (there is a time limit to respond)
}
switch (posstatus)
{
case posUP:
{
digitalWrite(LED,HIGH); //LED stays on to indicate position is up
for (byte y = 1; y < sleeptime; y++){ //save some battery by sleeping the Moteino while the pump is running but the LED stays on for this duration
LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
}
break;
}
case posDOWN:
{
for (int x = 0; x > (2*sleeptime)+1; x++){ //sleep two times longer than the pump downtime, plus 2 seconds thereby giving time to detect and send the error back
LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
}
if (radio.receiveDone()) //check for a new message
{
posstatus = (char)radio.DATA[0] - '0'; //convert the recieved data at entry [0] to an int. This is the position status.
if (radio.ACKRequested())
{
radio.sendACK(); //immediately respond to acknowledge message was received (there is a time limit to respond)
}
if (posstatus == 2){ //if error was detected, flash the error code
for (int i = 0; i < 7; i++){ //LED 7 long flashes for error state
digitalWrite(LED,HIGH);
LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,LOW);
LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF);
}
}
}
break;
}
}
}
} // loop
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
// -------> MOVE UP
// This function will be called when the button1 was pressed 1 time (and no 2. button press followed).
void click1() {
message[0] = '1';
if (radio.sendWithRetry(TONODEID, message, sendlength)){
digitalWrite(LED,LOW);
LowPower.powerDown(SLEEP_60MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,HIGH); //ack received, quick confirm flash
LowPower.powerDown(SLEEP_120MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,LOW);
LowPower.powerDown(SLEEP_250MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,HIGH);
LowPower.powerDown(SLEEP_120MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,LOW);
}
else{ //no ack received, assume receiver did not get message. Slow flash.
digitalWrite(LED,HIGH);
LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
digitalWrite(LED,LOW);
}
newbutton = false;
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
// -------> MOVE DOWN
void click2() {
message[0] = '2';
if (radio.sendWithRetry(TONODEID, message, sendlength)){
digitalWrite(LED,LOW);
LowPower.powerDown(SLEEP_60MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,HIGH); //ack received, quick confirm flash
LowPower.powerDown(SLEEP_120MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,LOW);
LowPower.powerDown(SLEEP_250MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,HIGH);
LowPower.powerDown(SLEEP_120MS, ADC_OFF, BOD_OFF);
digitalWrite(LED,LOW);
}
else{ //no ack received, assume receiver did not get message. Slow flash.
digitalWrite(LED,HIGH);
LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
digitalWrite(LED,LOW);
}
newbutton = false;
}
// click2
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//Debounce Error on first button
void nullclick1() {
newbutton = false; //in the event of a debounce error we still need to set this variable to false, otherwise while loop will keep moteino awake until the next button press essentially wasting battery
}
/*--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
//Debounce Error on second button
void nullclick2() {
newbutton = false; //in the event of a debounce error we still need to set this variable to false, otherwise while loop will keep moteino awake until the next button press essentially wasting battery
}
And this is my receiver code. I'm using the sprintf approach I saw in the sticky thread to put together something to send:
#include <RFM69.h>
#include <SPI.h>
// Addresses for this node. CHANGE THESE FOR EACH NODE!
#define NETWORKID 0 // Must be the same for all nodes
#define MYNODEID 1 // My node ID
#define TONODEID 2 // Destination node ID
// RFM69 frequency
#define FREQUENCY RF69_915MHZ
// AES encryption (or not):
#define ENCRYPT true // Set to "true" to use encryption
#define ENCRYPTKEY "TOPSECRETPASSWRD" // Use the same 16-byte key on all nodes
// Use ACKnowledge when sending messages (or not):
#define USEACK true // Request ACKs or not
// Packet sent/received indicator LED (optional):
#define LED 15 // LED positive pin
#define rmtUP 1
#define rmtDOWN 2
//Pins
int coilupmosfet = 30;
int coildownmosfet = 29;
int limitswitch = 28;
//Program Variables
bool posstatus = 0; //keeps track of state of coilovers ex: up/down where 0 = down
int uptime = 5;
int downtime = 12;
char message[4];
int remote = 0;
// Create a library object for the RFM69HCW module:
RFM69 radio;
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void setup()
{
pinMode(limitswitch, INPUT); //Input pin for limit switch
pinMode(coilupmosfet, OUTPUT);
pinMode(coildownmosfet, OUTPUT);
digitalWrite(coilupmosfet, LOW);
digitalWrite(coildownmosfet, LOW);
//Initialize the radio
radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
// Turn on encryption if set to true above
if (ENCRYPT)
radio.encrypt(ENCRYPTKEY);
}
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void loop() //main program loop
{
if (radio.receiveDone()) // Received a command from the remote!
{
remote = (char)radio.DATA[0] - '0'; //convert the recieved data back to the int data type. Consume this data before the next radio.whatever its called or it can be lost
if (radio.ACKRequested())
{
radio.sendACK(); //immediately respond to acknowledge message was received (there is a time limit to respond)
}
switch (remote)
{
case rmtUP:
{
coilup(); //extend the cylinder
remote = 0;
break;
}
case rmtDOWN:
{
coildown(); //retract the cylinder
remote = 0;
break;
}
}
}
}
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void coildown()
{
byte msglength = sprintf(message, "%d%d", 0, downtime); //sets msglength to the length of the string created in message. first %d is replaced with 0, second %d is replaced with downtime int
radio.sendWithRetry(TONODEID, message, msglength); //send the message back to the remote and retry if no ack was received.
digitalWrite(coilupmosfet, LOW); //turn off up solenoid
digitalWrite(coildownmosfet, HIGH); //turn on down solenoid
for (int x = downtime; x > 0; x--){ //initiate countdown using the settings for time
delay(1000);
}
digitalWrite(coilupmosfet, LOW); //make sure all solenoids are off
digitalWrite(coildownmosfet, LOW);
if (checkpos()){ //check limit switches to see if it worked, if still raised, try again.
digitalWrite(coilupmosfet, LOW); //turn off up solenoid
digitalWrite(coildownmosfet, HIGH); //turn on down solenoid
for (int x = downtime; x > 0; x--){ //initiate countdown using the settings for time
delay(1000);
}
digitalWrite(coilupmosfet, LOW); //make sure all solenoids are off
digitalWrite(coildownmosfet, LOW);
}
if (checkpos()){ //check the switch again... if the cylinder is still up...
sprintf(message, "%d%d", 2, downtime); //change the status to '2' to indicate there is some kind of error with the system
radio.sendWithRetry(TONODEID, message, msglength); //send error message back to remote
}
}
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
void coilup()
{
byte msglength = sprintf(message, "%d%d", 1, uptime); //sets msglength to the length of the string created in message. first %d is replaced with 1, second %d is replaced with uptime int
radio.sendWithRetry(TONODEID, message, msglength); //send the message back to the remote and retry if no ack was received.
digitalWrite(coilupmosfet, HIGH); //Activate up solenoid
digitalWrite(coildownmosfet, LOW); //make sure down solenoid is off
for (int x = uptime; x > 0; x--){ //initiate the countdown using the settings for the time
delay(1000); //delay 1 second
}
digitalWrite(coilupmosfet, LOW); //make sure all solenoids are off
digitalWrite(coildownmosfet, LOW);
}
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
bool checkpos(){
posstatus = digitalRead(limitswitch);
return posstatus;
}
/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
Any guidance would be great! This forum has taught me a lot already!