Arduino Mega 2560 Serial Input Cycle Problem

Below is the code which can run in both UNO and Mega. You can find it is success in UNO but failed in Mega when 'C' is received every 100 ms. I would like to know why I get this result although I can solve the problem using serial port connection directly.

#include <Wire.h>
#include <NewPing.h>
#define SONAR_NUM 3                      // Number of Sonar
#define MAX_DISTANCE 400                  // Maximum distance (in cm) to ping
/*NewPing sonar[SONAR_NUM] = {              // Ultrasonic Sensor Object array
  NewPing(13, 12, MAX_DISTANCE),
  NewPing(11, 10, MAX_DISTANCE),
  NewPing(9, 8, MAX_DISTANCE)
};*/

uint8_t i;
char inbyte;           // command byte received from Rpi (Serial 1)
String sid;           // String of Ultrasonic sensor id
String dist;          // String of distance value
String outpackage;    // String sent to rpi via Serial 1
String irsensor;      // String of ir sensor id
String irstate;       // ir sensor state
// unsigned int iteration;
unsigned int duration;
unsigned int distance;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
//  Serial1.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:
  inbyte = ' ';

  if (Serial.available() > 0){
    inbyte = Serial.read(); 
  }
  if (inbyte == 'C'){
    for (i = 0; i < SONAR_NUM; i++){
//      delay(10);
/*      sid = String(i + 1);
      duration = sonar[i].ping();      
      distance = (duration/2)*0.0343;
      distance = sonar[i].ping_cm();
      dist = String(distance);
      outpackage = "u"+sid+" "+dist+"\n";
      Serial.print(outpackage);
      Serial1.print(outpackage);*/
      sid = String(i);
      Serial.print(sid);
    }    
    Serial.print("E\n");
  } else if (inbyte == ' '){
    // do nothing 
  } else {
    Serial.write(inbyte);
//    Serial1.write(inbyte);
  }
}

Did you actually try it? What serial output do you get from each of them?

I run your code on a Mega.

  • if stimulated via serial monitor, it works on a string of 'C's which are closer than 100 ms
  • if stimulated with processing there was no reception, regardless of the character spacing
  • it started to work after inserting a delay after the open, to give the processor time to reset

I guess you are triggering the bootloader by sending serial data in the reset phase,
like I did with the very first approach.

This is processing sketch I used

import processing.serial.*;
Serial port;

String inString = "nothing";

void setup() {
  size(400, 400);
  noStroke();
  background(200);
  printArray(Serial.list());
  port = new Serial(this, "COM28", 115200);
  port.bufferUntil('\n');
  delay(2000);
}
int lastSend;
byte msg[] = { 'C' };

void draw() {
  if (millis()-lastSend >= 100) {
    lastSend = millis();
    port.write(msg);
    print('C');
  }
}
void serialEvent(Serial p) {
  inString = trim(p.readString());
  println(inString);
}

I changed the test sketch a little, to show you a different way of producing the same output.
The sketch times the for loop, and changes the way to operate when a 'x' is received.

#include <Wire.h>
#include <NewPing.h>
#define SONAR_NUM 3   // Number of Sonar

uint8_t i;
char inbyte;          // command byte received from Rpi (Serial 1)
String sid;           // String of Ultrasonic sensor id
String dist;          // String of distance value
String outpackage;    // String sent to rpi via Serial 1
String irsensor;      // String of ir sensor id
String irstate;       // ir sensor state

unsigned int distance;

void setup() {
  Serial.begin(115200);
}

bool noString = true;

void loop() {
  if (Serial.available() > 0) {
    inbyte = Serial.read();
    if (inbyte == 'C') {
      uint32_t start = micros();
      for (i = 0; i < SONAR_NUM; i++) {
        distance = random(0, 400);
        if (noString) {
          Serial.write('u');
          Serial.print(i + 1);
          Serial.write(' ');
          Serial.println(distance);
        } else {
          sid = String(i + 1);
          dist = String(distance);
          outpackage = "u" + sid + " " + dist + "\n";
          Serial.print(outpackage);
        }
      }
      uint32_t dur = micros() - start;
      Serial.print("E ");
      Serial.print(dur);
      Serial.println(" us");
    } else if (inbyte == 'x') {
      noString = !noString;
    } else if (inbyte != ' ') {
      Serial.write(inbyte);
    }
  }
}

I changed the processing sketch to send 'Cx' so each 'scan' switches the method.

import processing.serial.*;
Serial port;

String inString = "nothing";

void setup() {
  size(400, 400);
  noStroke();
  background(200);
  printArray(Serial.list());
  port = new Serial(this, "COM28", 115200);
  port.bufferUntil('\n');
  delay(2000);
}
int lastSend;
byte msg[] = { 'C', 'x' };

void draw() {
  if (millis()-lastSend >= 100) {
    lastSend = millis();
    port.write(msg);
  }
}
void serialEvent(Serial p) {
  inString = trim(p.readString());
  println(inString);
}

And part its output

u1 244
u2 158
u3 96
E 1004 us
u1 371
u2 264
u3 360
E 1476 us
u1 198
u2 351
u3 340
E 1060 us
u1 103
u2 351
u3 1
E 1436 us
u1 5
u2 280
u3 18
E 916 us
u1 74
u2 49
u3 13
E 1376 us
u1 120
u2 225
u3 12
E 1004 us

Using Strings cost you about 400 us per scan, which makes it 40% slower.
I think the non String code is a lot cleaner and does not need any additional variables.

Whandall, thanks for your analysis and improvement suggestion. I will modify my code to use non string code.

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