009 - Heartbeat sender

Welcome back to the RoboRemo tutorial.

In this tutorial I explain how to use the heartbeat sender and why it is useful. One problem with most DIY RC car project is the car continues moving when you loose connection. That is because you have a command for car stop, and when connection is lost, that command does not reach the car and the car does not "know" that you want to stop. One solution to this problem is to have some command that is periodically sent to the car. In this case, when the connection is lost - the car does not receive that periodic command anymore, and you can use that in the code as a trigger to stop the car. The heartbeat sender in RoboRemo is the interface item that periodically sends a command. You can configure the command string (item ID) and the period in milliseconds.



Arduino code before the heartbeat sender:

// RoboRemo Tutorial 9 - RC Car before Heartbeat Sender
// www.roboremo.com

// Copyright 2020 roboremo.com
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// The above copyright notice and disclaimer shall be included
// in all copies or substantial portions of the Software.

#define PIN_FORWARD  4
#define PIN_BACKWARD 5
#define PIN_LEFT     6
#define PIN_RIGHT    7

char cmd[100];
int cmdIndex;

void execute(char* cmd) {
  if(strcmp(cmd, "f")==0) { // forward
    digitalWrite(PIN_BACKWARD, LOW);  // disable backward
    digitalWrite(PIN_FORWARD, HIGH);  // enable forward
  } else if(strcmp(cmd, "b")==0) { // backward
    digitalWrite(PIN_FORWARD, LOW); 
    digitalWrite(PIN_BACKWARD, HIGH); 
  } else if(strcmp(cmd, "s")==0) { // stop
    digitalWrite(PIN_BACKWARD, LOW);  
    digitalWrite(PIN_FORWARD, LOW);
  } else if(strcmp(cmd, "l")==0) { // left
    digitalWrite(PIN_RIGHT, LOW);  
    digitalWrite(PIN_LEFT, HIGH);
  } else if(strcmp(cmd, "r")==0) { // right
    digitalWrite(PIN_LEFT, LOW);  
    digitalWrite(PIN_RIGHT, HIGH);
  } else if(strcmp(cmd, "c")==0) { // center
    digitalWrite(PIN_LEFT, LOW);  
    digitalWrite(PIN_RIGHT, LOW);
  }
}

void setup() {

  pinMode(PIN_FORWARD, OUTPUT);
  digitalWrite(PIN_FORWARD, LOW);
  
  pinMode(PIN_BACKWARD, OUTPUT);
  digitalWrite(PIN_BACKWARD, LOW);
  
  pinMode(PIN_LEFT, OUTPUT);
  digitalWrite(PIN_LEFT, LOW);
  
  pinMode(PIN_RIGHT, OUTPUT);
  digitalWrite(PIN_RIGHT, LOW);
  
  cmdIndex = 0;
  delay(500);
  Serial.begin(115200);
}

void loop() {
  if(Serial.available()) {
    char c = (char)Serial.read();
    if(c=='\r' || c=='\n') {
      cmd[cmdIndex] = 0;
      cmdIndex = 0;
      execute(cmd);
    } else {      
      cmd[cmdIndex++] = c;
    }
  }
}



Arduino code also considering the "alive" command from the heartbeat sender:

// RoboRemo Tutorial 9 - RC Car with Heartbeat Sender
// www.roboremo.com

// Copyright 2020 roboremo.com
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// The above copyright notice and disclaimer shall be included
// in all copies or substantial portions of the Software.

#define PIN_FORWARD  4
#define PIN_BACKWARD 5
#define PIN_LEFT     6
#define PIN_RIGHT    7

char cmd[100];
int cmdIndex;
unsigned long aliveReceivedMillis;

void execute(char* cmd) {
  if(strcmp(cmd, "f")==0) { // forward
    digitalWrite(PIN_BACKWARD, LOW);  // disable backward
    digitalWrite(PIN_FORWARD, HIGH);  // enable forward
  } else if(strcmp(cmd, "b")==0) { // backward
    digitalWrite(PIN_FORWARD, LOW); 
    digitalWrite(PIN_BACKWARD, HIGH); 
  } else if(strcmp(cmd, "s")==0) { // stop
    digitalWrite(PIN_BACKWARD, LOW);  
    digitalWrite(PIN_FORWARD, LOW);
  } else if(strcmp(cmd, "l")==0) { // left
    digitalWrite(PIN_RIGHT, LOW);  
    digitalWrite(PIN_LEFT, HIGH);
  } else if(strcmp(cmd, "r")==0) { // right
    digitalWrite(PIN_LEFT, LOW);  
    digitalWrite(PIN_RIGHT, HIGH);
  } else if(strcmp(cmd, "c")==0) { // center
    digitalWrite(PIN_LEFT, LOW);  
    digitalWrite(PIN_RIGHT, LOW);
  } else if(strcmp(cmd, "alive")==0) { // heartbeat sender's ID
    aliveReceivedMillis = millis();
  }
}

void setup() {

  pinMode(PIN_FORWARD, OUTPUT);
  digitalWrite(PIN_FORWARD, LOW);
  
  pinMode(PIN_BACKWARD, OUTPUT);
  digitalWrite(PIN_BACKWARD, LOW);
  
  pinMode(PIN_LEFT, OUTPUT);
  digitalWrite(PIN_LEFT, LOW);
  
  pinMode(PIN_RIGHT, OUTPUT);
  digitalWrite(PIN_RIGHT, LOW);
  
  cmdIndex = 0;
  delay(500);
  Serial.begin(115200);
}

void loop() {
  if(Serial.available()) {
    char c = (char)Serial.read();
    if(c=='\r' || c=='\n') {
      cmd[cmdIndex] = 0;
      cmdIndex = 0;
      execute(cmd);
    } else {      
      cmd[cmdIndex++] = c;
    }
  }

  if(millis() - aliveReceivedMillis > 600) {
    digitalWrite(PIN_FORWARD, LOW);
    digitalWrite(PIN_BACKWARD, LOW);
    digitalWrite(PIN_LEFT, LOW);
    digitalWrite(PIN_RIGHT, LOW);
  }
}