Arduino UNO + HC05 => Code runs with Delay / slow

Hey,

I build an Breadboard as Prototype for an LED Controler (controlls 2 different RGB Strips). The Arduino controls 6 Transistors, each leads to 1 GND Pin of one LED-Strip.

All worked fine, I created the PCB on PC an though before I make one, I test the circuit again. And now I sometimes have to wait almost 2 secconds for the Arduino to respond ( Change Lights). The Code runs without Delays and I tried to minimize the Time by reducing Debug Logs via USB to almost 0. Nothing makes it answer fast.

From my Phone I send CommandID's (and some data) to make it run.
These Commands work instand:

  • 0 (On/Off)
  • 4 (Fade)
  • 8 (Sides)

These Commands work not instantly:

  • 1 (Set LED's to Red) => 2s
  • 2 (Set Random Color) => 1,5s

The other Commands (5, 6, 7) are not working [need to Debug], Modes 9-12 aren't even programmed yet.

So what can it be that this Delay is there? Interesting herefore is, that even the use of a button follows the Delay (Button Press sets commandID).

Here is the Code:

#include <SoftwareSerial.h>

// Define Pins
#define HC_RX 7 // 12
#define HC_TX 4 // 11
#define redPin_A 11
#define greenPin_A 10
#define bluePin_A 9
#define redPin_B 6
#define greenPin_B 5
#define bluePin_B 3
#define button_0 8
#define button_1 12
#define button_2 15
#define button_3 14
#define statusLED 13


// Variables
bool writeDebugLines = true;

float HC_CommunicationBaudrate = 9600.0;

char CommandStartChar = '*';              // Char to start an Command or String
int commandID;                       // ID of the read Command
int lastCommandID;        // CommandID of the Command before the new command

bool lightsActivated = false;             // Check if Output is activated
bool useDefaultColors;
int r,g,b;          // First Side Colors
int r_2,g_2,b_2;    // Seccond Side Colors;

int last_r, last_g, last_b; // Color Backups to save Memory and reduce Time

bool setLED;

unsigned long int millisMemory; // Stores the Time in ms 

int fadePhaseIndex = 0;   // Stores the current Phase of the FadeMode
unsigned int generallFadeTime = 100;   // Stores the time in ms between two Fades

unsigned int generallCycleTime; // Stores the Time an Mode-Cycle needs
const unsigned int maxCycleTime = 65000;
const unsigned int minCycleTime = 1000;

float brightness = 1.0f;    // Max Brightness of the Lights
bool brightnessChanged = false;

enum SideSelectionenum {Both, Side_A, Side_B};
const String SideSelectionNames[] =  {"Both", "Side A", "Side B"};
SideSelectionenum selectedSide = Both;

int button_0_State = 0;
int button_1_State = 0;
int button_2_State = 0;
int button_3_State = 0;

// Define BT-Serial
SoftwareSerial btSerial(HC_TX, HC_RX); // TX | RX => von HC05 (From Arduino its other way round)


void setup() {
  // Start Debugging to Computer via USB-Serial
    if( writeDebugLines){
      Serial.begin(9600);
      Serial.println("Los geht's");
      Serial.println("Befehle:");
      Serial.println("*1 => An-/Ausschalten");
      Serial.println("*2 r g b (r_2, g_2, b_2) => Farbe darstellen => Klammer = optional");
      Serial.println("*3 a => zufällige Farbe darstellen => a=2 > 2 Random Farben");
      Serial.println("*4 => Fade durch die Farben");
      Serial.println("*5 => Aktuelle Daten per Serial senden");
      Serial.println("*6 time => Change FadeTime in ms");
      Serial.println("*7 5-100 => Change Brightness in %");
      Serial.println("*8 Cycle Sides");
      Serial.println("*9 Flash");
      Serial.println("*10 Custom Flash");
      Serial.println("*11 Random Mode");
      Serial.println("*12 Random LED");
    }

  // Start Bluetooth Connection
    btSerial.begin(9600);  

  // Pin Stuff
    pinMode(redPin_A, OUTPUT);
    pinMode(greenPin_A, OUTPUT);
    pinMode(bluePin_A, OUTPUT);
    pinMode(redPin_B, OUTPUT);
    pinMode(greenPin_B, OUTPUT);
    pinMode(bluePin_B, OUTPUT);

    pinMode(statusLED, OUTPUT);

    pinMode(button_0, INPUT);
    pinMode(button_1, INPUT);
    pinMode(button_2, INPUT);
    pinMode(button_3, INPUT);

  // Variable Stuff
  generallCycleTime = minCycleTime;
}

void loop() { // run over and over
  CollectInput();
  Process();
  Output();
}

void CollectInput(){
  HandleHC05Data();
  HandleSerialData();


  // Get ButtonInput
  int button_0_currState = digitalRead(button_0);
  int button_1_currState = digitalRead(button_1);
  int button_2_currState = digitalRead(button_2);
  int button_3_currState = digitalRead(button_3);

  if(button_0_currState == 1 && button_0_State != 1){
      // On / Off
        button_0_State = button_0_currState;

        if (writeDebugLines) Serial.println("Button 0 pressed");

        commandID = 1;
  }
  else if(button_0_currState == 0 && button_0_State != 0){
      button_0_State = button_0_currState;
  }

  
  if(!lightsActivated) return;  // Stop here when lights are out

  if(button_1_currState == 1 && button_1_State != 1){
      // Mode
        button_1_State = button_1_currState;
        if (writeDebugLines) Serial.println("Button 1 pressed");

        // Cycle trough Modes
          if(commandID == 4 || commandID == 400)  // Fade Mode
          {
              commandID = 9;
              lastCommandID = 9;
          }
          else if (commandID == 2) // RGB
          {
              commandID = 3;
              lastCommandID = 3;
          }
          else if (commandID == 3 || commandID == 0) // Random RGB
          {
              commandID = 4;
              lastCommandID = 4;
          }
          else if (commandID == 9 || commandID == 10) // Flash
          {
              commandID = 12;
              lastCommandID = 12;
          }
          else if(commandID == 12)  // Random LED
          {
            commandID = 3;
            lastCommandID = 3;
          }

          if(writeDebugLines)
          { 
            Serial.print("New CommandID: ");
            Serial.println(commandID);
          }
  }
  else if(button_1_currState == 0 && button_1_State != 0){
      button_1_State = button_1_currState;
  }

  if(button_2_currState == 1 && button_2_State != 1){
      // Time
        button_2_State = button_2_currState;
        if(writeDebugLines) Serial.println("Button 2 pressed");

        // Cycle trough Time
          generallCycleTime += 5000;
          if(generallCycleTime >= maxCycleTime){
              generallCycleTime = minCycleTime;
          }
      
        if(writeDebugLines)
        {
          Serial.print("New CycleTime: ");
          Serial.println((String)generallCycleTime);
        }
        RecalculateTimes();
  }
  else if(button_2_currState == 0 && button_2_State != 0){
      button_2_State = button_2_currState;
  }

  if(button_3_currState == 1 && button_3_State != 1){
      // LED's
        button_3_State = button_3_currState;
        if(writeDebugLines) Serial.println("Button 3 pressed");
      
        // Cycle trough Sides
        commandID = 8;
  }
  else if(button_3_currState == 0 && button_3_State != 0){
      button_3_State = button_3_currState;
  }
}


void Process(){
  if(commandID == 1){
      // Handle Lights Activasion
      lightsActivated = !lightsActivated;
      
      if(lightsActivated){
        commandID = 2;    // SetRGB Mode
        useDefaultColors = true;
        SendDataOverBoth("Activated Lights");
        fadePhaseIndex = 0;
        digitalWrite(statusLED, 1);
      }
      else{
        commandID = 0;
        r = 0;
        g = 0;
        b = 0;
        setLED = true;
        SendDataOverBoth("Deactivated Lights");
        digitalWrite(statusLED, 0);

      }
  }

  if(!lightsActivated) return; 
  
  // Handle Modes
  switch(commandID)
  {
    case 2:
    {
      // RGB Mode 
          if(useDefaultColors){
              r=255;
              g=255;
              b=255;
              useDefaultColors = false;
          }
          else{
            r = btSerial.parseInt();
            g = btSerial.parseInt();
            b = btSerial.parseInt();

            r_2 = btSerial.parseInt();
            g_2 = btSerial.parseInt();
            b_2 = btSerial.parseInt();

            if(writeDebugLines)
            {
              Serial.print("R2: "); Serial.println(r_2);
              Serial.print("G2: "); Serial.println(g_2);
              Serial.print("B2: "); Serial.println(b_2);
            }
          }
          setLED = true;
          commandID = 0;
        }
        break;
    case 3:
    {
       // Random RGB
          int a = btSerial.parseInt();

          r = random(0,257);
          g = random(0,257);
          b = random(0,257);

          if(a == 2 )
          {
            r_2 = random(0,257);
            g_2 = random(0,257);
            b_2 = random(0,257);
          }
          else{
            r_2 = r;
            g_2 = g;
            b_2 = b;
          }

          setLED = true;
          commandID = 0;

            if (writeDebugLines)
            {
              Serial.print("R1: "); Serial.println(r);
              Serial.print("G1: "); Serial.println(g);
              Serial.print("B1: "); Serial.println(b);
              Serial.print("R2: "); Serial.println(r_2);
              Serial.print("G2: "); Serial.println(g_2);
              Serial.print("B2: "); Serial.println(b_2);
            }
        }
        break;
    case 4:
    {
       // Fade Start
            fadePhaseIndex = 0;
              // Set first color and start Time
              SendDataOverBoth("Started Fade");
              millisMemory = millis();
              r = 0;
              g = 0;
              b = 255;
              setLED = true;
            fadePhaseIndex++;
            commandID = 400;
            lastCommandID = 400;
            
            r_2 = r;
            g_2 = g;
            b_2 = b;

            RecalculateTimes();
        }
        break;
    case 400:
    {
       // Fade Cycle
          if(millis() - millisMemory > generallFadeTime){
              // Next Step after 100ms
              if(fadePhaseIndex < 256){
                // increase red => blue to violett
                  r++;
              }
              else if(fadePhaseIndex < 511){
                // decrease blue => violet to red
                  b--;
              }
              else if(fadePhaseIndex < 766){
                // increase green => red to yellow
                  g++;
              }
              else if(fadePhaseIndex < 1021){
                // decrease red => yellow to green
                  r--;
              }
              else if(fadePhaseIndex < 1276){
                // increase blue => green to tal
                  b++;
              }
              else if(fadePhaseIndex < 1531){
                // decrease green => tal to blue
                  g--;
              }

              fadePhaseIndex++; // Increase Index for next Step
              setLED = true;    // Allow to set the LED Outpus
              millisMemory = millis();  // Change LastTime to now

              if(fadePhaseIndex >= 1531){
                // Reset PhaseIndex to restart Fading
                fadePhaseIndex = 0;
              }

              r_2 = r;
              g_2 = g;
              b_2 = b;
            }     
        }     
        break;
    case 5:{
      // Read current Data and return to lastCommand
        SendDataOverBoth("Red: ");
        SendDataOverBoth((String)r);
        SendDataOverBoth("Green: ");
        SendDataOverBoth((String)g);
        SendDataOverBoth("Blue: ");
        SendDataOverBoth((String)b);
        SendDataOverBoth("Seite: ");
        SendDataOverBoth((String)SideSelectionNames[selectedSide]);
        commandID = lastCommandID;
      }
      break;
    case 6:{
      // Change FadeTime and return to lastCommand
         int _cycleTime = btSerial.parseInt();

        if(_cycleTime > minCycleTime && _cycleTime < maxCycleTime){
            // FadeTime between 10ms and 1min
          generallCycleTime = _cycleTime;
          SendDataOverBoth("New CycleTime: ");
          SendDataOverBoth((String)generallCycleTime);
        }
        commandID = lastCommandID;

        RecalculateTimes();
      }
      break;
    case 7:{
      // Change Brightness and return to lastCommand
        int bright = btSerial.parseInt();

        if(bright >= 5 && bright <= 100){
          brightness = bright /100.0f;
          SendDataOverBoth("New Brightness: ");
          SendDataOverBoth((String)brightness);     
          setLED = true;         
          brightnessChanged = true;
        }
        commandID = lastCommandID;
      }
      break;
    case 8:{
      // Cycle trough Sides and return to lastCommand
      switch (selectedSide){
          case Both:
            selectedSide = Side_A;
            break;
          case Side_A:
            selectedSide = Side_B;
            break;
          case Side_B:
            selectedSide = Both;
            break;
      }
        commandID = lastCommandID;
      }
      break;
    case 9:{
      // Flash

      }
      break;
    case 10:{
       // Custom Flash

      }
      break;
    case 11:{
      // Random Mode

      }
      break;
    case 12:{
      // Random LED

      }
      break;
  }
}

void RecalculateTimes(){
  // Recalculate Times for every Cycle
        if(commandID == 400 || commandID == 4){
          // Fade Time
            generallFadeTime = (float)generallCycleTime / 1531;
            
          SendDataOverBoth("New FadeTime: ");
          SendDataOverBoth((String)generallFadeTime);
        }
        else{
            // ???
        }
}

void Output(){
  // In this Block the Output is set
    if(setLED) WriteLEDLights();
}

// Read Data from HC05 to Serial
void HandleHC05Data(){
  if (btSerial.available()) {

    char data = btSerial.read();
    //SendDataOverSerial(data);
  
    if(data == CommandStartChar){
      // Read Command
      lastCommandID = commandID;
      commandID = btSerial.parseInt();
      SendDataOverSerial(commandID);
    }
  }
}

// Write the LED Lights to the Outputs
void WriteLEDLights(){
  // Only write & Log the LED Pins that have changed. => Saves ressources and makes the Log more readable 
  
  // Write Red Pin
  if(r != last_r || brightnessChanged){
    if(selectedSide == Both || selectedSide == Side_A){
      // Red Pin of Side A is active
      analogWrite(redPin_A, r * brightness);
    }
    else{
      // Red Pin of Side A is inactive
      analogWrite(redPin_A, 0);
    }

    if(selectedSide == Both || selectedSide == Side_B){
      // Red Pin of Side B is active
      analogWrite(redPin_B, r * brightness);
    }
    else{
      // Red Pin of Side B is inactive
      analogWrite(redPin_B, 0);
    }

    last_r = r;
    if(writeDebugLines) Serial.print("Red: "); Serial.println(r);
  }
  
  // Write Green Pin
  if(g != last_g || brightnessChanged){
    if(selectedSide == Both || selectedSide == Side_A){
      // Green Pin of Side A is active
      analogWrite(greenPin_A, g * brightness);
    }
    else{
      // Green Pin of Side A is inactive
      analogWrite(greenPin_A, 0);
    }

    if(selectedSide == Both || selectedSide == Side_B){
      // Green Pin of Side B is active
      analogWrite(greenPin_B, g * brightness);
    }
    else{
      // Green Pin of Side B is inactive
      analogWrite(greenPin_B, 0);
    }

    last_g = g;
    if(writeDebugLines) Serial.print("Green: "); Serial.println(g);
  }
  
  // Write Blue Pin
  if(b != last_b || brightnessChanged){
    if(selectedSide == Both || selectedSide == Side_A){
      // Blue Pin of Side A is active
      analogWrite(bluePin_A, b* brightness);
    }
    else{
      // Blue Pin of Side A is inactive
      analogWrite(bluePin_A, 0);
    }

    if(selectedSide == Both || selectedSide == Side_B){
      // Blue Pin of Side B is active
      analogWrite(bluePin_B, b* brightness);
    }
    else{
      // Blue Pin of Side B is inactive
      analogWrite(bluePin_B, 0);
    }

    last_b = b;
    if(writeDebugLines) Serial.print("Blue: "); Serial.println(b);
  }

  brightnessChanged = false;
  setLED = false;  
}


// Send Data from Serial to HC05
void HandleSerialData(){
  if (Serial.available()) {
    char data = Serial.read();

    if(data == CommandStartChar){
      SendDataOverHC05(Serial.readString());
    }
    else{
      SendDataOverHC05(data);
    }
  }
}

void SendDataOverBoth(char data){
  SendDataOverSerial(data);
  SendDataOverHC05(data);
}
void SendDataOverBoth(String data){
  SendDataOverSerial(data);
  SendDataOverHC05(data);
}
void SendDataOverSerial(char data){
  if(writeDebugLines) Serial.println(data);
}
void SendDataOverSerial(String data){
  if(writeDebugLines) Serial.println(data);
}
void SendDataOverHC05(char data){
  btSerial.println(data);
}
void SendDataOverHC05(String data){
  btSerial.println(data);
}

Probably serial timeout. Could be while reading from USB serial or HC05 serial.

Try setting the timeouts to 0 and 2000 for USB serial and later HC05 serial to see which affects the delay you observe.

Sounds good, gonna test that. But what can happen if you set it too low?
Does it then fail to read longer data?

Then use readStringUntil() to use a terminator to decide the end of the data stream (mainly '\n').
When read the terminator, function determines that it has acquired all the data and continues without waiting for a timeout.

https://www.arduino.cc/reference/en/language/functions/communication/serial/readstringuntil/

Instead of adjusting the timeout, send a terminator. btSerial.parseInt() will wait for a non-digit till it times out. If you send e.g. "1\n" instead of "1", it will pick up the non-digit ('\n') and stop the parsing.

Similar applies to e.g. Serial.readString() but in that case you will have to use Serial.readStringUntil() after adding the terminator in your sender.

If you set it too low, it might timeout on multi-character input.

1 Like

I am not suggesting that @rpgfabi can fix the problem by adjusting the timeout, only confirm that the delays are being caused by timeout, and determine which serial input is causing them.

Hey,

tested that. Sending now for the RGB Mode (COmmandID 2) following Code: *\n2\n255\n0\n0\n => Now should every parseInt() (CommandID, Red, Green, Blue) end directly after the int. But it doesnt. Timeout is still there.

Tested the Timeout thing. It works faster if btSerial Timeout is low (resetted it). So btSerial is the Problem.

Problem found. I made it possible to read 2 RGB Values. Since I sometimes only send 1, it goes in Timeout with 3 values => 3s. Now I send CommandID 1/2 (depending on amount) and check the amount of RGB Values => almost instant answer. Changing that for the other Commands

1 Like