Arduino Mega with 3 Serial communications

Dear all,

Currently, I am working on an automatic rover that uses an ibus RC receiver to control it manually and GPS for automatic control. I can switch between manual and automatic with switch 9 of my rc transmitter. When I am in manual I can drive the rover around and it communicates perfectly with Serial1 ports (rc receiver). When I flick the switch it starts communicating with the GPS module over Serial2 which also kind of works I can get the position coordinates but when my script asks for the RC transmitter signals again with readRx() this doesn't work anymore and it stays in automatic mode because it doesn't receive the transmitter singles over Serial1 anymore. Two weeks ago we thought it was due to the use of Software serial due to previous use of Arduino nano and high baud rates. Because of this, I switched to a mega but still face the same problems. Now the use of software serial cannot be a problem anymore because now I use only hardware serial ports. I think it has something to do with buffers or timing maybe, who can help me out?

//Magnetomer
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>

//reciever
#define IBUS_BUFFSIZE 32    
#define IBUS_MAXCHANNELS 10 // I am using only 10 channels because my TX (FlySky i6) supports max 10 channels

//GPS
#include <TinyGPSPlus.h>
TinyGPSPlus gps; // dit betekend dat je dit later gps noemt bv bij gps.location.lat
float currentLat;
float currentLong;
float goalheading;
float numberSatellites;
float goalLat = 51.501526;
float goalLong = 5.580738;


//RECIEVER
static uint8_t ibusIndex = 0;
static uint8_t ibus[IBUS_BUFFSIZE] = {0};
int rcValue[IBUS_MAXCHANNELS];
int rcValue2[IBUS_MAXCHANNELS];
int rcValues3[IBUS_MAXCHANNELS] = {127,127,0,127,0,0,0,0,0,0};// om er voor te zorgen dat hij standaart de standaard waarden van de controller heeft. Anders gaat hij meteen sturen. 


//Steering
int leftspeed = 0;
int rightspeed = 0;


//Magnet
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
const float xoff PROGMEM = -73;    //from magnetometer calibration
const float yoff PROGMEM = -4;
const float declination PROGMEM = 2.5; //for Lieshout 2022
float currentheading;
const float Pi PROGMEM = 3.141559;

//Lamp
int relayPin = 7;


static boolean rxFrameDone;

void setup()
{
  Serial.begin(9600);
  Serial1.begin(115200); //Reciever Serial1 on pins D19 (RX) and D18 (TX)
  Serial2.begin(9600);//GPS
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);

}


void loop()
{
  //Serial.println(rcValues3[9]);
  led();
  readRx();
  manual();
  automatic();

}

void manual()
{
if (rcValues3[9] == 0){      //Bepalen welke switch dit moet worden en welke value
  rightspeed = (rcValues3[2]+(127-rcValues3[0]));
  leftspeed = (rcValues3[2]-(127-rcValues3[0]));
  leftspeed= constrain(leftspeed,0,255);
  rightspeed= constrain(rightspeed,0,255);
  analogWrite(6,rightspeed); 
  analogWrite(5,leftspeed); 
  }
}

void automatic()
{
  if (rcValues3[9] == 255) {     //Bepalen welke switch dit moet worden en welke value
    getGPS();
    getHeading();
  }
}

void getGPS()
{
    
  smartDelay(200); ////critical to make sure it reads GPS data even if shit is slowing down

  if(gps.location.isValid())  //is position known?
  {
    currentLat = gps.location.lat();
    currentLong = gps.location.lng();
    numberSatellites = gps.satellites.value();
    Serial.println(numberSatellites);
    
  }

  
  //Serial2.end();     //stop talking to the gps module  
  //rcValues3[9] = 0;

  goalheading = TinyGPSPlus::courseTo(currentLat,currentLong,goalLat,goalLong);

}

void getHeading()
{
  sensors_event_t event; 
  mag.getEvent(&event);
  currentheading = (((atan2(-(event.magnetic.y-yoff),(event.magnetic.x-xoff))*180)/Pi)+declination);
  Serial.println(currentheading);
}

void readRx(){ 
  rxFrameDone = false;
  
  if ((Serial1.available()))
  {
    uint8_t val = Serial1.read();
    // Look for 0x2040 as start of packet
    if (ibusIndex == 0 && val != 0x20)
    {
      ibusIndex = 0;
      return 0;
    }
    if (ibusIndex == 1 && val != 0x40)
    {
      ibusIndex = 0;
      return 0;
    }

    if (ibusIndex == IBUS_BUFFSIZE)
    {
      ibusIndex = 0;
      boolean allInRange = true;
      int high=3;
      int low=2;
      for(int i=0;i<IBUS_MAXCHANNELS; i++)
      {
        rcValue[i] = (ibus[high] << 8) + ibus[low];
        high += 2;
        low += 2;
        
        rcValue2[i] = map(rcValue[i], 1000, 2000, 0, 255);
        
        if (rcValue2[i] < 0 || rcValue2[i] > 255)
        {
          allInRange = false;
        }
      }
      
      if (allInRange)
      {
        // Assign rcValue2 to a new array
        for (int i = 0; i < IBUS_MAXCHANNELS; i++)
        {
          rcValues3[i] = rcValue2[i];
        }

      }
      rxFrameDone = true;
      return 0;
    }
    else
    {
      ibus[ibusIndex] = val;
      ibusIndex++;
    }
  }
  else{
    for(byte i=0; i < IBUS_MAXCHANNELS; i++){
      //Serial.print(rcValues3[i]);
      //Serial.print("     ");
      if (i == IBUS_MAXCHANNELS-1){
        //Serial.println();//om er voor te zorgen dat hij na de loop toch aan een andere regel begint  
      }
    }
  }
  return;
}

void led(){
 if (rcValues3[4] == 255) {     //Bepalen welke switch dit moet worden en welke value
  digitalWrite(relayPin, HIGH);
  }
  else{
    digitalWrite(relayPin, LOW);
  } 
}


static void smartDelay(unsigned long ms){
  unsigned long start = millis();
  do
  {
    while (Serial2.available())
      gps.encode(Serial2.read());
  }  while (millis()-start < ms);
  
}
static void smartDelay(unsigned long ms){
  unsigned long start = millis();
  do  {
    while (Serial2.available())
      gps.encode(Serial2.read());
  }  while (millis()-start < ms);
}

what should this do?

smartDelay(200); ////critical to make sure it reads GPS data even if shit is slowing down

what?

so, while rover is in manual mode you can send command to switch in auto mode, and now rover hear not RC but GPS. why you awaiting that rover spontaneous switch back to manual?

little shorted

//Magnetomer
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>

//reciever
#define IBUS_BUFFSIZE 32
#define IBUS_MAXCHANNELS 10 // I am using only 10 channels because my TX (FlySky i6) supports max 10 channels

//GPS
#include <TinyGPSPlus.h>
TinyGPSPlus gps; // dit betekend dat je dit later gps noemt bv bij gps.location.lat
float currentLat;
float currentLong;
float goalheading;
float goalLat = 51.501526;
float goalLong = 5.580738;


//RECIEVER
static uint8_t ibusIndex = 0;
static uint8_t ibus[IBUS_BUFFSIZE] = {0};
int rcValue[IBUS_MAXCHANNELS];
int rcValue2[IBUS_MAXCHANNELS];
int rcValues3[IBUS_MAXCHANNELS] = {127, 127, 0, 127, 0, 0, 0, 0, 0, 0}; // om er voor te zorgen dat hij standaart de standaard waarden van de controller heeft. Anders gaat hij meteen sturen.

//Steering
int leftspeed = 0;
int rightspeed = 0;

//Magnet
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
const float xoff  = -73.0;    //from magnetometer calibration
const float yoff  = -4.0;
const float declination  = 2.5; //for Lieshout 2022
const float Pi  = 3.141559;

//Lamp
const int relayPin = 7;

static boolean rxFrameDone;

void setup() {
  Serial.begin(9600);
  Serial1.begin(115200); //Reciever Serial1 on pins D19 (RX) and D18 (TX)
  Serial2.begin(9600);//GPS
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, LOW);
}


void loop() {
  //Serial.println(rcValues3[9]);
  led();
  readRx();
  if (rcValues3[9] == 0) manual();
  if (rcValues3[9] == 255) automatic();
}

void manual() {
  rightspeed = (rcValues3[2] + (127 - rcValues3[0]));
  leftspeed = (rcValues3[2] - (127 - rcValues3[0]));
  leftspeed = constrain(leftspeed, 0, 255);
  rightspeed = constrain(rightspeed, 0, 255);
  analogWrite(6, rightspeed);
  analogWrite(5, leftspeed);
}

void automatic() {
  getGPS();
  getHeading();
}

void getGPS() {
  while (Serial2.available() > 0)gps.encode(Serial2.read());

  if (gps.location.isValid()) { //is position known?
    currentLat = gps.location.lat();
    currentLong = gps.location.lng();
    Serial.println(gps.satellites.value());
  }
  goalheading = TinyGPSPlus::courseTo(currentLat, currentLong, goalLat, goalLong);
}

void getHeading() {
  sensors_event_t event;
  mag.getEvent(&event);
  Serial.println( (atan2(-(event.magnetic.y - yoff), (event.magnetic.x - xoff)) * 180.0) / Pi + declination, 1);
}

void readRx() {
  rxFrameDone = false;
  while (Serial1.available() > 0)  {
    uint8_t val = Serial1.read();
    // Look for 0x2040 as start of packet
    if (ibusIndex == 0 && val != 0x20)    {
      ibusIndex = 0;
      return ;
    }
    if (ibusIndex == 1 && val != 0x40)    {
      ibusIndex = 0;
      return ;
    }
    if (ibusIndex == IBUS_BUFFSIZE)    {
      ibusIndex = 0;
      boolean allInRange = true;
      int high = 3;
      int low = 2;
      for (int i = 0; i < IBUS_MAXCHANNELS; i++) {
        rcValue[i] = (ibus[high] << 8) + ibus[low];
        high += 2;
        low += 2;
        rcValue2[i] = map(rcValue[i], 1000, 2000, 0, 255);
        if (rcValue2[i] < 0 || rcValue2[i] > 255)allInRange = false;
      }
      if (allInRange)for (int i = 0; i < IBUS_MAXCHANNELS; i++)rcValues3[i] = rcValue2[i];   // Assign rcValue2 to a new array

      rxFrameDone = true;
      return;
    }
    else    {
      ibus[ibusIndex] = val;
      ibusIndex++;
    }
  }
  return;
}

void led() {
  if (rcValues3[4] == 255)digitalWrite(relayPin, HIGH);      //Bepalen welke switch dit moet worden en welke value
  else    digitalWrite(relayPin, LOW);
}

While it is in auto mode it shouldnt stay their it loops through the severals states in the loop(). So it also goes through readRX again. Then it should also get the position of switch 9 with value 0 or 255. When this is 0 it should switch back to manual. If it is 255 it should go again through auto mode.

ah, yes, readRx,.. then, if something block reading of Serial2 its stuck complete programm, that is what you trying?

The smartDelay() function is featured in some of the example sketchs for TinyGPS, so it gets copied over to other code out of context and without understanding of what it does. The example sketch uses it to implement a delay while still feeding the GPS data to TinyGPS, but in real code this is better done using millis() for timing.

When receiving GPS data over serial, it is essential that the receive buffer not overflow, so the serial input has to be checked at no more than 65mS intervals at 9600 baud. The same applies to the RC receiver in the OP's code, the input needs to be checked before the receive buffer can accumulate 64 bytes at 115200 baud, around 5.5mS. You have to be very careful of anything that takes a significant amount of time, such as driving an OLED display or addressable LED strip, printing larger amounts of text to a slow serial connection, etc. SoftwareSerial can make this a much bigger problem, because it uses interrupts extensively, which will slow down everything else.

2 Likes

Thank you all for your responses I dont really understand how the smartDelay is working indeed. Also not why it would block my code. But I tried the code of @kolaha and it looks like it is working really happy with it! Thank you a lot already, besides i will try to understand it better by my self for future code! Also thank you @david_2018 for your detailed explaination!

Thank you a lot!

Do you have picture of your Rover? :clinking_glasses:


Wiring is still a ratnest but will be better in further. Want to build better chassis and pcb in futher.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.