Robot safety ; stop on signal loss

Hello world!
I’m new to programming in any language, but I have managed to figure out just enough to get started on a remote-controlled robot. So far I have an Arduino Fio with an Xbee acting as a controller, transmitting analogread values via Bill Porter’s EasyTransfer library to an Arduino Mega 2560 with another Xbee. The Mega takes in these values, maps them to servo angles, and puts out servo pulses to a Sabertooth 2x25 motor driver. My main concern right now, before I develop this project further and put in the other dozen functions is safety. I do not want my robot wandering off if it loses communication with the controller. Right now, the vehicle has so little code on it that if it loses signal, the servo outputs keep putting out the same signal to the motor driver until it receives instructions again. I started to make a safety routine based on comparing differences in millisecond count, but there the problems started getting worse. On the transmitter, I have a millis() count sent through to the receiver, and the receiver is supposed to store that value, compare it to the old value, and if they are not the same, execute the code that takes in the analogread values and puts out mapped servo values. If those two millis() values compared turn out to be the same, it is supposed to write 90 to all servo outputs and wait until the two compared milllis() are different again.
What actually happens is that the value is read in, stored, and the comparison and value shift are seemingly ignored, and the program returns to the top after the comparison.
controller side

#include <EasyTransfer.h> //Bill Porter's EasyTransfer library

EasyTransfer ET;
struct SEND_DATA_STRUCTURE{
  int drive; //the values to be sent once something is written to them
  int steer;
  int dome;
  int safenum;
};
int toggle;
SEND_DATA_STRUCTURE directions;
void setup(){
  Serial.begin(9600); //theres only one serial port on the transmitting device
  ET.begin(details(directions), &Serial); //setup of the library (variable group and serial port number)
  pinMode(A0, INPUT); //analog pins set to input, reading knobs
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  toggle = 1;
}
void loop(){
  directions.drive = analogRead(A0); //directions group, variable drive
  directions.steer = analogRead(A1); //directions group, variable steer
  directions.dome = analogRead(A2);  //directions group, variable dome
  directions.safenum = millis();
  ET.sendData(); //tells the library to assemble and fire off the data
  delay(40);
}

and receiver side

#include <EasyTransfer.h> //Bill Porter's EasyTransfer library
#include <Servo.h>  //the Servo library for making output pins
                    //act like airplane receiver channels
EasyTransfer ET;
struct RECEIVE_DATA_STRUCTURE{
  int drive; //library's internal 
  int steer; //values it is receiving 
  int dome;  //in the serial port
  int safenum;
};
int driveout; //values for shuffling stuff after reception
int steerout;
int domeout;
int midhi = 560;
int midlo = 464;
Servo spedo; //servo representing drive
Servo drivo; //servo representing steer
Servo domo;  //servo representing dome (head) rotation
int safeold = 1;
int safenew = 0;

RECEIVE_DATA_STRUCTURE directions;
void setup(){
  Serial.begin(9600); //for monitoring
  Serial1.begin(9600); //this is the port it receives instructions on
  ET.begin(details(directions), &Serial1); //telling the library
  //what set of values it is receiving and what serial port to watch
  spedo.attach(12); //attaching servo objects to output pins
  drivo.attach(11);
  domo.attach(10);
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Serial.println("center");
  spedo.write(90);
  drivo.write(90);
  domo.write(90);
  delay(2000);
  /*Serial.println("low");
  spedo.write(0);
  drivo.write(0);
  domo.write(0);
  delay(2000);
  Serial.println("high");
  spedo.write(179);
  drivo.write(179);
  domo.write(179);
  delay(2000);
  */
  digitalWrite(13, LOW);
}
void loop(){
  if(ET.receiveData()){
//mapping values from 0 to 1023 to servo angles of 0 to 179
//driveout = map(directions.drive,0,500,0,89)
//                and            ,524,1023,90,179)
Serial.println("run 1");
delay(100);
Serial.print("safenum 1 =");
Serial.println((directions.safenum));
Serial.print("safenew 1 =");
Serial.println((safenew));
Serial.print("safeold 1 =");
Serial.println((safeold));
(directions.safenum = safenew);
Serial.print("safenum 2 =");
Serial.println((directions.safenum));
Serial.print("safenew 2 =");
Serial.println((safenew));
Serial.print("safeold 2 =");
Serial.println((safeold));
delay(100);
Serial.println("run 2");
(safeold = safenew);
Serial.print("safenum 3 =");
Serial.println((directions.safenum));
Serial.print("safenew 3 =");
Serial.println((safenew));
Serial.print("safeold 3 =");
Serial.println((safeold));
delay(100);
if (safeold != safenew){
Serial.println("run 3");
Serial.print("safenum 4 =");
Serial.println((directions.safenum));
Serial.print("safenew 4 =");
Serial.println((safenew));
Serial.print("safeold 4 =");
Serial.println((safeold));
delay(100);
if (directions.drive <= midlo){
  driveout = map(directions.drive,0,midlo,0,89);
}
  else if (directions.drive >= midhi){
    driveout = map(directions.drive,midhi,1023,90,179);
  }
    else driveout = 90;
    //driveout = map(directions.drive,0,1023,0,179);
if (directions.steer <= midlo){
  steerout = map(directions.steer,1023,midlo,0,89);
}
  else if (directions.steer >= midhi){
    steerout = map(directions.steer,midhi,0,90,179);
  }
    else steerout = 90;
    //steerout = map(directions.steer,0,1023,0,179);
Serial.println("run 4");
if (directions.dome <= midlo){
  domeout = map(directions.dome,0,midlo,0,89); 
  }
  else if (directions.dome >= midhi){
    domeout = map(directions.dome,midhi,1023,90,179);
  }
    else domeout = 90;
    //domeout = map(directions.dome,0,1023,0,179);
Serial.println("run 5");
    Serial.print("speed = ");    //debugging statements
    Serial.print((driveout));
    Serial.print("direction = ");
    Serial.print((steerout));
    Serial.print("dome = ");
    Serial.println((domeout));
    spedo.write(driveout); //telling the servo objects to do their thing
    drivo.write(steerout);
    domo.write(domeout);
      }
  }
      else{
    return;  
  }
  }
  //oldval = val
  //val = safetything
  //if oldval != val
  //do stuff

Sorry I can't read the code on my mobile phone, but an idea Perhaps send the instructions continuously and have it that if an instruction hasn't been received in so long, say 2 seconds, then stop

That's close to what I'm trying to do, but something in the code having to do with the interpretation of the millis() count isn't doing what I want it to. It might become clearer one you read the code.

You couldn't be bothered to format your code; why should anyone else be bothered to read it?

And that's another thing, what the hell does formatting even mean? I didn't come here to get flamed, admin, moderator, or not.

deancornell: And that's another thing, what the hell does formatting even mean?

There's an auto-format feature (command/control + T). It helps indent stuff.

A-HA! I figured that had to be somewhere. Thanks for being the first help I've ever received from a forum! Re-posting code NOW!

deancornell:
And that’s another thing, what the hell does formatting even mean?
I didn’t come here to get flamed, admin, moderator, or not.

No, you came here to ask people give the benefit of their time and experience to a complete stranger. You should do anything you can to make it easier for people to help you, for example laying your code out to make it easy for people to understand it. It only takes you a few seconds and saves time for everyone trying to help you.

Controller side

#include <EasyTransfer.h> //Bill Porter's EasyTransfer library

EasyTransfer ET;
struct SEND_DATA_STRUCTURE{
  int drive; //the values to be sent once something is written to them
  int steer;
  int dome;
  int safenum;
};
int toggle;
SEND_DATA_STRUCTURE directions;
void setup(){
  Serial.begin(9600); //theres only one serial port on the transmitting device
  ET.begin(details(directions), &Serial); //setup of the library (variable group and serial port number)
  pinMode(A0, INPUT); //analog pins set to input, reading knobs
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  toggle = 1;
}
void loop(){
  directions.drive = analogRead(A0); //directions group, variable drive
  directions.steer = analogRead(A1); //directions group, variable steer
  directions.dome = analogRead(A2);  //directions group, variable dome
  directions.safenum = millis();
  ET.sendData(); //tells the library to assemble and fire off the data
  delay(40);
}

Receiver (robot) side

#include <EasyTransfer.h> //Bill Porter's EasyTransfer library
#include <Servo.h>  //the Servo library for making output pins
//act like airplane receiver channels
EasyTransfer ET;
struct RECEIVE_DATA_STRUCTURE{
  int drive; //library's internal 
  int steer; //values it is receiving 
  int dome;  //in the serial port
  int safenum;
};
int driveout; //values for shuffling stuff after reception
int steerout;
int domeout;
int midhi = 560;
int midlo = 464;
Servo spedo; //servo representing drive
Servo drivo; //servo representing steer
Servo domo;  //servo representing dome (head) rotation
int safeold = 1;
int safenew = 0;

RECEIVE_DATA_STRUCTURE directions;
void setup(){
  Serial.begin(9600); //for monitoring
  Serial1.begin(9600); //this is the port it receives instructions on
  ET.begin(details(directions), &Serial1); //telling the library
  //what set of values it is receiving and what serial port to watch
  spedo.attach(12); //attaching servo objects to output pins
  drivo.attach(11);
  domo.attach(10);
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Serial.println("center");
  spedo.write(90);
  drivo.write(90);
  domo.write(90);
  delay(2000);
  /*Serial.println("low");
   spedo.write(0);
   drivo.write(0);
   domo.write(0);
   delay(2000);
   Serial.println("high");
   spedo.write(179);
   drivo.write(179);
   domo.write(179);
   delay(2000);
   */
  digitalWrite(13, LOW);
}
void loop(){
  if(ET.receiveData()){
    //mapping values from 0 to 1023 to servo angles of 0 to 179
    //driveout = map(directions.drive,0,500,0,89)
    //                and            ,524,1023,90,179)
    Serial.println("run 1");
    delay(100);
    Serial.print("safenum 1 =");
    Serial.println((directions.safenum));
    Serial.print("safenew 1 =");
    Serial.println((safenew));
    Serial.print("safeold 1 =");
    Serial.println((safeold));
    (directions.safenum = safenew);
    Serial.print("safenum 2 =");
    Serial.println((directions.safenum));
    Serial.print("safenew 2 =");
    Serial.println((safenew));
    Serial.print("safeold 2 =");
    Serial.println((safeold));
    delay(100);
    Serial.println("run 2");
    (safeold = safenew);
    Serial.print("safenum 3 =");
    Serial.println((directions.safenum));
    Serial.print("safenew 3 =");
    Serial.println((safenew));
    Serial.print("safeold 3 =");
    Serial.println((safeold));
    delay(100);
    if (safeold != safenew){
      Serial.println("run 3");
      Serial.print("safenum 4 =");
      Serial.println((directions.safenum));
      Serial.print("safenew 4 =");
      Serial.println((safenew));
      Serial.print("safeold 4 =");
      Serial.println((safeold));
      delay(100);
      if (directions.drive <= midlo){
        driveout = map(directions.drive,0,midlo,0,89);
      }
      else if (directions.drive >= midhi){
        driveout = map(directions.drive,midhi,1023,90,179);
      }
      else driveout = 90;
      //driveout = map(directions.drive,0,1023,0,179);
      if (directions.steer <= midlo){
        steerout = map(directions.steer,1023,midlo,0,89);
      }
      else if (directions.steer >= midhi){
        steerout = map(directions.steer,midhi,0,90,179);
      }
      else steerout = 90;
      //steerout = map(directions.steer,0,1023,0,179);
      Serial.println("run 4");
      if (directions.dome <= midlo){
        domeout = map(directions.dome,0,midlo,0,89); 
      }
      else if (directions.dome >= midhi){
        domeout = map(directions.dome,midhi,1023,90,179);
      }
      else domeout = 90;
      //domeout = map(directions.dome,0,1023,0,179);
      Serial.println("run 5");
      Serial.print("speed = ");    //debugging statements
      Serial.print((driveout));
      Serial.print("direction = ");
      Serial.print((steerout));
      Serial.print("dome = ");
      Serial.println((domeout));
      spedo.write(driveout); //telling the servo objects to do their thing
      drivo.write(steerout);
      domo.write(domeout);
    }
  }
  else{
    return;  
  }
}
//oldval = val
//val = safetything
//if oldval != val
//do stuff

No, you came here to ask people give the benefit of their time and experience to a complete stranger. You should do anything you can to make it easier for people to help you, for example laying your code out to make it easy for people to understand it. It only takes you a few seconds and saves time for everyone trying to help you.

And yet AWOL took his precious time to tell me I fucked up, but didn't even offer a hint of how to fix my fuckup. In my book, that's called flaming. Now can we drop the subject now that I know where the autoformatter is?

Can't you do a simple test for the elapsed time since the last command?

void loop(){
  if(ET.receiveData()){
     // all current stuff
     last_good_command = millis();
  }
  if ((millis() - last_good_command) > 1000)
      shit_the_bed();
}

BTW

 else{
    return;  
  }

is not necessary.


Rob

Another option might be to have the board on the robot send an “acknowledge” packet to the controller (every now and then), which the controller should immediately respond to; if the response is not received (or something other than the required response is received), shut down.

Thanks for formatting the code, its now possible to look at it.

On the receiver side you said "and the receiver is supposed to store that value, compare it to the old value, and if they are not the same.."

I cannot see that in your code at all. Where is it supposed to happen?

I can see a (directions.safenum = safenew) but that seems backwards, why are you assigning the safenum in the receiving structure a new value? Is the library supposed to work like that? I have not used that library, but would guess that its supposed to receive the safenew value and you are not supposed to overwrite it. How does the library get told there is a new value there?

Don't you need something like: if (directions.safenum == old_safenum) { // oops, no reception has taken place // do the safety stuff

} else { // ok, we got a reception old_safenum = directions.safenum; }

Shelleycat, your confusion is mostly caused by my nature as a syntax-challenged newb. :sweat_smile: I'm still learning. I'm gonna go see if correcting the (directions.safenum = safenum); to (safenum = directions.safenum); makes it work. Thanks for the tip!

deancornell:
And yet AWOL took his precious time to tell me I fucked up, but didn’t even offer a hint of how to fix my fuckup. In my book, that’s called flaming.
Now can we drop the subject now that I know where the autoformatter is?

It’s not flaming, it’s telling you what you did wrong. It’s convenient that there are tools available in Arduino to make it easy to address the problem, but you don’t need tools to format your code correctly. You just need to realise that formatting matters to your readers, which AWOL’s message told you. Without his help, you’d have spent the rest of your life pissing off the very people you were asking for help. Now that you know that I’m sure you’ll find it much easier to get help with your code.

Okay, after a couple more hours of staring and syntax correction, I got the robot to stop on loss of control signal. Thanks to this learning experience, I can finish this project with confidence now that I know it won’t try to run away from me.

1 Like

Thanks to this learning experience, I can finish this project with confidence now that I know it won't try to run away from me.

Wait till the next learning experience. You assume it will not run away because you assume you are able to code without mistakes. Wait till it runs away to prove you wrong ;)