1

I would like to control a toy car from the server. I use RadioHead library for communication and it works fine.

The application running on the server side is as follows.

#include <RHReliableDatagram.h>
#include <RH_ASK.h>
#include <SPI.h>

#define SERVER_ADDRESS 1
#define ROBOT_ADDRESS 2
#define GO_RIGHT "Go right"
#define GO_LEFT "Go left"
#define GO_FORWARD "Go forward"
#define GO_BACK "Go back"
#define STOP "Stop"
#define SERVER_DELAY 500

unsigned int transmitterPin = 10;
unsigned int receiverPin = 12;

RH_ASK transmitterServer(2000, receiverPin, transmitterPin, 0);

RHReliableDatagram radioCommunication(transmitterServer, SERVER_ADDRESS);

void setup()
{
    Serial.begin(9600);
    if (!radioCommunication.init())
        Serial.println("Radio communication failed");
}

uint8_t buffer[RH_ASK_MAX_MESSAGE_LEN];

void sendData(uint8_t * data, uint8_t length)
{
    Serial.println("I'm sending commands to the robot");

    // Send a command to the robot
    if (radioCommunication.sendtoWait(data, length, ROBOT_ADDRESS))
    {
        // Now wait for a response from robot
        uint8_t from;
        if (radioCommunication.recvfromAckTimeout(buffer, &length, 2000, &from))
        {
            Serial.print("I've got - ");
            Serial.print((char*) buffer);
            Serial.print(" - message from the robot whose addres is : ");
            Serial.print("0x");
            Serial.println(from, HEX);
        }
        else
        {
            Serial.println("No response. Does the robot works?");
        }
    }
    else
        Serial.println("Attempt to resend data failed.");
    delay(SERVER_DELAY);
}

void loop()
{
    sendData(GO_RIGHT, sizeof(GO_RIGHT));
    sendData(GO_LEFT, sizeof(GO_LEFT));
    sendData(GO_FORWARD, sizeof(GO_FORWARD));
    sendData(STOP, sizeof(STOP));
    sendData(GO_BACK, sizeof(GO_BACK));
    sendData(STOP, sizeof(STOP));
}

The application running on the robot is as follows.

#include <RHReliableDatagram.h>
#include <RH_ASK.h>
#include <SPI.h>

#define SERVER_ADDRESS 1
#define ROBOT_ADDRESS 2
#define GO_RIGHT "Go right"
#define GO_LEFT "Go left"
#define GO_FORWARD "Go forward"
#define GO_BACK "Go back"
#define STOP "Stop"
#define RIGHT_LIGHT 4
#define LEFT_LIGHT 7
#define FORWARD_LIGHT 8

unsigned int receiverPin = 2;
unsigned int transmitterPin = 12;

RH_ASK robotTransmitter(2000, receiverPin, transmitterPin, 0);
RHReliableDatagram radioCommunication(robotTransmitter, ROBOT_ADDRESS);

void setup()
{
    Serial.begin(9600);
    pinMode(RIGHT_LIGHT, OUTPUT);
    pinMode(LEFT_LIGHT, OUTPUT);
    pinMode(FORWARD_LIGHT, OUTPUT);

    if (!radioCommunication.init())
        Serial.println("Radio communication failed");
}

uint8_t buffer[RH_ASK_MAX_MESSAGE_LEN];
uint8_t command[RH_ASK_MAX_MESSAGE_LEN];

void moveRight()
{
    Serial.println("------------");
    Serial.println("I'm going right");
    Serial.println("------------");

    /*
       // Time consuming task

    for (int i = 0; i < 3; ++i)
    {
        digitalWrite(RIGHT_LIGHT, HIGH);
        delay(1000);
        digitalWrite(RIGHT_LIGHT, LOW);
        delay(1000);
    }

    */
}

void moveLeft()
{
    Serial.println("------------");
    Serial.println("I'm going left");
    Serial.println("------------");

    /*
       // Time consuming task

    for (int i = 0; i < 3; ++i)
    {
        digitalWrite(LEFT_LIGHT, HIGH);
        delay(1000);
        digitalWrite(LEFT_LIGHT, LOW);
        delay(1000);
    }

    */
}

void runCommand(char* command)
{
    if (strcmp(command, GO_RIGHT) == 0)
    {
        moveRight();
    }
    else if (strcmp(command, GO_LEFT) == 0)
    {
        moveLeft();
    }
}

void loop()
{
    uint8_t response[] = "OK";
    if (radioCommunication.available())
    {
        uint8_t length = sizeof(buffer);
        uint8_t from;
        if (radioCommunication.recvfromAck(buffer, &length, &from))
        {
            Serial.print("I got - ");
            Serial.print((char*) buffer);
            Serial.print(" - command from the server whose addres is ");
            Serial.print("0x");
            Serial.println(from, HEX);
        }
        // Send the message to the server (with retries) and wait for an ack.
        if (!radioCommunication.sendtoWait(response, sizeof(response), from))
            Serial.println("Repeated attemps to send data to server failed");
        strncpy(command, buffer, sizeof(command));
        runCommand(command);
    }
}

As it stands, the serial output of robot is as follows: radiohead serial

Let's increase the SERVER_DELAY variable to 2000. This way the server will send a command every two seconds.

Besides this, let's run a command that will take some time on the robot. To do this we might uncomment time consuming task lines.

So that, when the robot receives Go right command, it will try to turn on and off lights three times.

After making these changes let's look at the serial output of robot.

Robot serial output

What happens is, after receiving the Go left command it tries to blink LEFT_LIGHT three times. But meanwhile it misses Go forward command. And again after receiving the Go right command it tries to blink RIGHT_LIGHT three times but misses Go left command.

What I would like to do is when robot receives a new command, it should stop whatever it was doing and execute the new command.

How should I do this? Any ideas. Thanks.

1

An easy-enough fix would be to replace your delay calls with something that regularly checks if data is available from the server.

Something like:

/* returns true if the sleep was interrupted by incoming data */
/* warning: does not wait at all if data is available */
bool interruptible_delay(unsigned long ms)
{
  unsigned long end = millis() + ms;
  while (millis() < end) {
    if (radioCommunication.available()) {
      return true;
    }
    delay(10); /* tune to taste */
  }
  return false;
}

This should work as long as the innner delay is much shorter than your communication timeouts.

You will most likely need to handle the case where the sleep was interrupted in your "action" functions if they become more complexe than a led blink. I.e. if your action is a series of complex motor moves, and a message arrives in the middle, you need to design the function so that it is in a safe or neutral state before returning to the main loop.

1
  • I was about to figure out that might be related to delay function. Your solution is brilliant. It works like a charm. Thanks.
    – Erdem
    Nov 21 '20 at 10:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.