Making a timed code that starts with a GUI

Hello,

I am starting a project where I will use an Arduino to control 2 electrical stimulation devices. I am making a GUI that will allow me to decide how many repetitions are sent, the on time, and the off time, and eventually more comprehensive parameters

I am using two LEDs right now to make the framework. I made a simple GUI with visual basic, that will send a string to the arduino via the Serial port

When I hit "Start", the following formatted string is sent

Aaabcxxyz
A = start signifier
aa = reps of led 1
b = on time led 1
c = off time led 1
xx, y z, same but for LED 2

I want to make it so when I hit "send" on the GUI, the program will run the blinks for the set amount of time. I used a flag to capture the start time in millis, and then have the program continue until the current time is > the start time + duration (based on aa,b,c). I can get this to work. So if I say LED 1, blink 10 times 1on/2off, and LED 2 blink 3 times, 3 on/3off, i get both doing their respective blinking and cessation simultaneously.

I then have a reset button on the GUI, that sends a string "s" that should tell the arduino to turn off the LEDS, and to reset the flag to 0, so i could run it again if I want. However, this is not working.


// ----CONSTANTS (won't change)

// the pin numbers for the LEDs
const int led_A_Pin = 6;
const int led_B_Pin = 9;

// ----- STRINGS
String data;  //String from VB
char d1;

//set sub strings for RED
String a_t;
String a_on;
String a_off;

//set sub strings for Blue
String b_t;
String b_on;
String b_off;



//------- VARIABLES (will change)

// used to record whether the LEDs are on or off
byte led_A_State = LOW;           //   LOW = off
byte led_B_State = LOW;


int a_duration;
int a_reps;
int a_ontime;
int a_offtime;

int b_duration;
int b_reps;
int b_ontime;
int b_offtime;


int a_LED_ON = 0;
int a_LED_OFF = 0;
int b_LED_ON = 0;
int b_LED_OFF = 0;


// Timing variables
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousLed_A_Millis = 0;
unsigned long previousLed_B_Millis = 0;

int pass = 0;
int Start_Time;

//========

void setup() {

  Serial.begin(9600);


  pinMode(led_A_Pin, OUTPUT);
  pinMode(led_B_Pin, OUTPUT);
  
  digitalWrite(led_A_Pin, LOW);
  digitalWrite(led_B_Pin, LOW);
  
}

  //=======

  void loop() {


  
  if (Serial.available()) {
    data = Serial.readString();
    d1 = data.charAt(0);

    //pull  data out of GUI
    a_t = data.substring(1, 3);   // repetitions
    a_on = data.substring(3, 4);  // on time
    a_off = data.substring(4, 5); // off time

    b_t = data.substring(5, 7);
    b_on = data.substring(7, 8);
    b_off = data.substring(8,9);

    

    //covert strings to integers
    a_ontime = a_on.toInt();      // on time
    a_offtime = a_off.toInt();    // off time
    a_reps = a_t.toInt();         // repetitions

    
    a_LED_ON = a_ontime * 1000;   //covert times to ms
    a_LED_OFF = a_offtime * 1000;

    b_ontime = b_on.toInt();
    b_offtime = b_off.toInt();
    b_reps = b_t.toInt();

    b_LED_ON = b_ontime * 1000;
    b_LED_OFF = b_offtime * 1000;

   
        
    //set times (total repetitions times total on/off cycle)
    a_duration = a_reps * ((a_LED_ON) + (a_LED_OFF));
    b_duration = b_reps * ((b_LED_ON) + (b_LED_OFF));

  }    
   
   switch (d1){
    
    // if first character of String is an A, run program
    case 'A':
        if (pass == 0) {         
          Start_Time = millis();
          pass++;
        }
      
        currentMillis = millis();

        // use currentMillis, PreviousMillis and intervals to change LedStates
        updateLed_A_State();
        updateLed_B_State();


        // Change the states of the LED if the passed time is less than duration
        // Turn LEDs off if time passed has surpassed duration

        
        if (currentMillis < (Start_Time + a_duration)) {
            digitalWrite(led_A_Pin, led_A_State);
            }
        else {
            digitalWrite(led_A_Pin, LOW);
            }
      
        if (currentMillis < (Start_Time + b_duration)) {
            digitalWrite(led_B_Pin, led_B_State);
            }
        else {
            digitalWrite(led_B_Pin, LOW) ;
              }
    break;

    // if first character is a s, turn LEDs off and reset flag pass
    case 's':
    digitalWrite(led_A_Pin, LOW);
    digitalWrite(led_B_Pin, LOW);
    pass = 0;
    break;
   }  
  } 
 
  //========


  void updateLed_A_State() {
   
    if (led_A_State == LOW) {
      
      if (currentMillis - previousLed_A_Millis >= a_LED_OFF) {
        led_A_State = HIGH;
        previousLed_A_Millis += a_LED_OFF;
       }
    }
    else {   
      if (currentMillis - previousLed_A_Millis >= a_LED_ON) {
        led_A_State = LOW;
        previousLed_A_Millis += a_LED_ON;
      } 
    }
  }

  //=======

  void updateLed_B_State() {
  
    if (led_B_State == LOW) {
      
      if (currentMillis - previousLed_B_Millis >= b_LED_OFF) {
        led_B_State = HIGH;
        previousLed_B_Millis += b_LED_OFF;
      }
    }
    else {
      if (currentMillis - previousLed_B_Millis >= b_LED_ON) {
        led_B_State = LOW;
        previousLed_B_Millis += b_LED_ON;
      }
    }
  }

  //=====END

Thank you
Tom

Does that code compile?
It looks like you've got misplaced closing braces

Edit: maybe not, just bad formatting.

Yeah, apologize for the messiness. I have been playing with it for a few hours so its changed a bit

I can get it to work when I first upload, and first set the GUI (i.e make LED 1 blink 5 times at 1 on 1 off, and LED 2 at something else). It even stops mid track whenever I hit my GUI "reset"

What isn't working is the reset on the timer. I want to make it so no matter how long the code has been in the arduino, i can get the timer going when I activate the GUI, and successive times without having to reupload to the arduino

Welcome

If d1 is s then it is not A, so your "program" won't run because it will only run when d1 is A. You have to move your "program" outside of the switch.

I only want it to run when d1 is A,

I have a start and reset button on the GUI.

I hit start and the A gets sent and it runs properly. I then hit reset to send the s. This all works.

What doesn't work is when I hit start again and resend the A. The blinks begin again but will not stop with the duration timer

Variables Start_Time, a_duration etc, should be unsigned long to avoid overflow

All hail hypnotoad

Follow up question for refinement

With the timer, sometimes at the beginning, or the very end, I will get a quicker blink. likely because the on off intervals aren't lining up completely with millis and the program processing speed. Is there a way to alleviate this.

i.e make the all the output blinks more identical to what was asked.

Here is an example on Wokwi : led_thing.ino - Wokwi Arduino Simulator

If you feel Serial response is slow, it's because readString have a default timeout of one second. A better idea would be to not use Strings at all :wink:

Thank you for that example.

And yes, I have seen many times that strings are not ideal. Now that I've got the base code working, now to improve it!

No, you may have seen that Strings are not ideal, but few would argue against strings.