Arduino wont start unless COM Port is open, works as intended afterwards

Hi all,

At the moment I am using the HC-05 bluetooth shield to wirelessly transmit IMU data from the arduino to my computer.

The idea is to get the current serial string that is being sent from matlab (ex. 255,255,255,255) and split it into 4 integers that are sent to 4 different motors.

Basically, the communications are as follows

  1. IMU → Ard → BT Serial Transmit
  2. MATLAB → BT Serial → Ard

Currently, the issue that I am facing is that arduino will not transmit any data on its own WITHOUT opening the com port first. Once the COM port is open, I can see that data is being transmitted and the sketch works as intended. After the COM is closed the data transfer and motor control continues to work as intended.

The problem is that I have to have a direct wire serial connection between the Ard and my laptop in order to open the COM port. This isn’t very desirable since I need to be using bluetooth connection.

I have been working with arduino for a while and have never seen anything like this. My code is below, please help :frowning:

// Define libraries
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <OneWire.h>
#include "MPU9255.h"
#include <SoftwareSerial.h>   //Software Serial Port
#define RxD 6
#define TxD 7

// SDA = A4, SCL = A5
// IMU Acceleration: X(+front/-back) & Z(+left/-right)
// IMU Gyroscope: Y(+CW/-CCW Yaw)

SoftwareSerial blueToothSerial(RxD, TxD);
// Define 16bit integers
int16_t accelCount[3];
int16_t gyroCount[3];
int16_t magCount[3];

// Define floats;
float f_accelCount[3]; // Array of size 3 for X Y Z
float f_gyroCount[3];
float f_magCount[3];

// Define scales and accuracy
float AccelScale, GyroScale, MagScale;
int gAccuracy = 250; // degrees per second
int aAccuracy = 2; // +- 2g
int mAccuracy = 6; // 0.6mGauss

// Define Calibration Offsets
const float aOffSet_x = -0.02;
const float aOffSet_y = 1.0;
const float aOffSet_z = -0.1;

const int gOffSet;

// Define packet count
int packet = 0;

// Define time;
unsigned long timer = millis();

// Define Motor Pins
int enA = 5;
int enB = 9;
int enC = 10;
int enD = 11;

// Initialize Incoming Serial Commands
int FL, FR, RL, RR;
String path; 

MPU9255 mpu(12, gAccuracy, aAccuracy, mAccuracy); // Create MPU9255 Sensor object

void setup() {

  // Minor environment initialization
  Wire.begin();//initialization of the I2C protocol
  TWBR = 24; // Two Wire Bit Rate Register
  Serial.begin(9600);
  pinMode(RxD, INPUT);
  pinMode(TxD, OUTPUT);

  pinMode(enA, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(enC, OUTPUT);
  pinMode(enD, OUTPUT);

  // Setup BT Connection
  setupBlueToothConnection();

  mpu.initMPU9250();// // Initalization of MPU Sensor
  float magCalibration[3];
  mpu.initAK8963(magCalibration);// Initialize magnometer

  GyroScale = 131.0;
  AccelScale = 16384.0;
  MagScale = 0.6;
}

void loop() {
  int blueToothLength;
  int incomingSerialDataIndex = 0; // Stores the next 'empty space' in the array
  char incomingSerialData[15];
  String blueToothData;
  while(blueToothSerial.available()) {
    incomingSerialData[incomingSerialDataIndex] = blueToothSerial.read();
    incomingSerialDataIndex++; // Ensure the next byte is added in the next position
    incomingSerialData[incomingSerialDataIndex] = '\0';
  }
  if (Serial.available()){ //check if there's any data sent from the local serial terminal, you can add the other applications here
  }
  
  blueToothData = String(incomingSerialData); // Cast Char to Str
  //Serial.println(blueToothData);
  blueToothLength = blueToothData.length(); // Remove Null
  if (blueToothLength == 1) {
    if (incomingSerialData[0] == 'S') {
      blueToothData = '0';
      //Serial.println("Stopped");
      stopMotors();
    } 
  } 
  getData(); 
  transmitData();
  FL = splitSerial(blueToothData, ',', 0).toInt();
  FR = splitSerial(blueToothData, ',', 1).toInt();
  RL = splitSerial(blueToothData, ',', 2).toInt();
  RR = splitSerial(blueToothData, ',', 3).toInt();
  // Enable the according pins 
  MotorControl(FL, FR, RL, RR);
     
  delay(100);
}

Which Arduino?

I’m using an Arduino Uno

The UNO has no way to detect if it's plugged into USB or if the COM port is open. There must be something else going on.

  delay(100);

That's going to cause problems. How many serial characters can arrive while you have your hands tied for 100 milliseconds?

    incomingSerialDataIndex++; // Ensure the next byte is added in the next position
    incomingSerialData[incomingSerialDataIndex] = '\0';

This is also going to cause problems. You never check if you ran off the end of your array. Use a named constant for the size of the array and always check that you are inside the array bounds before writing anything to the array.

Hi Morgan, thanks so much for your suggestions.

I need to send 10 packets per second since I’m using the measurements to determine relative position. 10 packets/s results in a more accurate system control.

As for the array bounds, I know that the max number of bytes (‘worst’ case) will be 15 (ex. in 255,255,255,255 number of bytes is 15).

This PWM value is subject to change based on the MATLAB calculations. It can be something like 100,125,80,80 where the number of bytes will then be 13, and so on.

  • Is it incorrect to preallocate the character array for the worst case scenario (15)?
  • Why does using a named constant change the interaction? Doesn’t the arduino know to stop reading the serial once the last byte is received in the incoming line as then nothing will be in the serial?

I observed the bluetoothserial in Tera Term. The code runs and works as intended but only AFTER I open the comport. It continues to work after the COM is closed as well. Before that, nothing is displayed in the bluetoothserial, not even the transmitData(); which leads me to believe that something is preventing the arduino from looping, at least initially.

Hopefully you can help me answer my questions, thanks again!

It sounds like the laptop is powering the Arduino, so use a separate supply -- batteries or wall wart.

jremington: It sounds like the laptop is powering the Arduino, so use a separate supply -- batteries or wall wart.

I get the same behaviour when using a 9v battery to power the arduino unfortunately :( (no serial connected). This is initially how I discovered it doesn’t work as intended, because I wasn’t receiving any IMU data in MATLAB when I wasn’t able to open the com port.

Edit: I will provide a video if required demonstrating the behaviour.

pinMode(RxD, INPUT); pinMode(TxD, OUTPUT);

Should these lines be there?

Where do you start softwareSerial.

blueToothSerial.begin(9600);

Leo..

Wawa: pinMode(RxD, INPUT); pinMode(TxD, OUTPUT);

Should these lines be there?

Where do you start softwareSerial.

blueToothSerial.begin(9600);

Leo..

That’s being called in another function called setupBluetoothConnection in the setup(), I haven’t shown the full program functions for simplicity.

I get the same behaviour when using a 9v battery to power the arduino unfortunately

9V block batteries are unsuitable for powering the Arduino. They will last only a few minutes if fresh.

With delay(100) I can assure you that you're not getting 10 packets per second. Maybe 9.99, maybe slower. The code below uses a better way to send exactly 10 packets per second (well, within the limitation of accuracy on the Arduino's resonator which will drift by a few parts per million.)

While working on that code, I also stopped it overrruning the 15-character buffer.

Remember that Serial data has no built-in 'start'. You never know if you've received the start or middle or end of a message unless you include that in the message itself. It looks like you are expecting messages which contain either a single 'S' or 4 numbers separated by commas. What if you only got 3 numbers? How does your code know to wait? What if you got the last 3 numbers from the previous message and the next character is an 'S'?

My modifications to your code don't cope with all of those what-if's. Without seeing the full format of all the message types you're sending, I can't make any meaningful guesses.

The use of big-S String is unnecessary. There are small-s string functions that do the same thing. I did not entirely remove the String processing, just to keep this example simple.

I'm not clear on what getData() does. I don't know if you want to run that only 10 times per second.

void loop() {
  const unsigned long TransmitInterval = 10000; //milliseconds - send out our data at this interval (10 times per second)
  const int BufferLength = 15;
  int blueToothLength;
  int incomingSerialDataIndex = 0; // Stores the next 'empty space' in the array
  char incomingSerialData[BufferLength];
  while(blueToothSerial.available()) {
    incomingSerialData[incomingSerialDataIndex] = blueToothSerial.read();
    incomingSerialDataIndex++; // Ensure the next byte is added in the next position
    if(incomingSerialDataIndex >= BufferLength) incomingSerialDataIndex = 0; //if we got too many characters, throw them all away and start again
    incomingSerialData[incomingSerialDataIndex] = '\0';
  }
  if (Serial.available()){ //check if there's any data sent from the local serial terminal, you can add the other applications here
  }

  if(bluetoothMessageIsComplete(incomingSerialData, incomingSerialDataIndex))) {
    //we got a complete message, according to some rules applied in the function that you have to write
    //maybe you just need a specific number of characters or you need to see a carriage-return at the end of the string?
    
    //Serial.println(blueToothData);
    blueToothLength = incomingSerialDataIndex;
    if (blueToothLength == 1) {
      if (incomingSerialData[0] == 'S') {
        blueToothData = '0';
        //Serial.println("Stopped");
        stopMotors();
      } 
    } else {
      //the only other 'complete' message we can get is the one with the 4 motor-control numbers
      String blueToothData = incomingSerialData; 
      FL = splitSerial(blueToothData, ',', 0).toInt();
      FR = splitSerial(blueToothData, ',', 1).toInt();
      RL = splitSerial(blueToothData, ',', 2).toInt();
      RR = splitSerial(blueToothData, ',', 3).toInt();
    }   
  } //else the bluetooth message isn't complete yet

  //always run the motor-control function as often as possible
  //This may need to check limit switches or other motor parameters and it
  //can't afford to wait hundreds of milliseconds before responding
  //to those inputs.
  MotorControl(FL, FR, RL, RR);      

  
  getData(); //MorganS: I have no idea what this does. Is this supposed to run on the 10 per second schedule?

  //Is it time to transmit the data on the 10 per second schedule?
  if(millis() - lastTransmitTime > TransmitInterval) {
    transmitData();
    while(millis()-lastTransmitTime > TransmitInterval) lastTransmitTime += TransmitInterval; //skip any missed transmissions
  }
}

MorganS: With delay(100) I can assure you that you're not getting 10 packets per second. Maybe 9.99, maybe slower. The code below uses a better way to send exactly 10 packets per second (well, within the limitation of accuracy on the Arduino's resonator which will drift by a few parts per million.)

While working on that code, I also stopped it overrruning the 15-character buffer.

Remember that Serial data has no built-in 'start'. You never know if you've received the start or middle or end of a message unless you include that in the message itself. It looks like you are expecting messages which contain either a single 'S' or 4 numbers separated by commas. What if you only got 3 numbers? How does your code know to wait? What if you got the last 3 numbers from the previous message and the next character is an 'S'?

My modifications to your code don't cope with all of those what-if's. Without seeing the full format of all the message types you're sending, I can't make any meaningful guesses.

The use of big-S String is unnecessary. There are small-s string functions that do the same thing. I did not entirely remove the String processing, just to keep this example simple.

I'm not clear on what getData() does. I don't know if you want to run that only 10 times per second.

For future reference, I managed to completely rewrite how I handle the serial parsing through the 'Serial Input Basics' before seeing this post. It looks like arduino doesnt like the string class at all. The way that parsing is done in 'Serial Input Basics' post on this forum worked so much better for me.

getData just returns IMU sensor readings and puts them in the proper arrays for gyro/accel X Y and Z.

In matlab, I'm using a timer to loop a function that gets the IMU data transmitted from arduino and splitting those values. Based on the accel X Y & gyro Y readings I developed an MPC controller to use the values and find the required PWM values for each motor. These 4 integers will always be processed as int1,int2,int3,int4, unless 'stop button' is pressed in which matlab writes an 's' and the motors stop.

I will definitely try out the new 10 times per second transmission, thanks so much for that! I had no idea just setting a delay(100) wouldnt work.