Search code examples
winformsuser-interfaceserial-portarduino-uno

Why is Serial.read() not looped in Arduino


I've been trying to connect Gui(graphic user interface) to my Arduino to control my motor. There are 4 buttons: L(Low),M(Medium),H(High),S(Stop).When I click there, one of 4 characters will be sent to my Arduino. Combining with the signal from my Infrared Obstacle Avoidance(Output pin is connected to pin 2 on Arduino),my motor will run for a period of time accordingly: L(3s), M(5s), H(10s), S(not running). But when I click on any of these 3 buttons (L,M,H) with my hand covering the sensor, my motor will continue to run even after I release my hand. The motor only stop running when I releases my hand and click any of these 3 buttons or S button. Here is my Arduino code:

#include<AFMotor.h>
AF_DCMotor motor(4);
int sensor=2;
char sig;
int t;
void setup() {
  Serial.begin(9600);
  pinMode(sensor,INPUT);
}

void loop() {
      t = digitalRead(sensor);
      delay(100);
      sig = Serial.read();
        if(sig=='L')
         {  
              if(t==LOW)
                {
                 runMotor(3000);
                }
               else if(t==HIGH)
                {
                  stopMotor();
                }
         }

         else if(sig=='M')
         {
              if(t==LOW)
                {
                  runMotor(5000);
                }
               else if(t==HIGH)
                {
                  stopMotor();
                }
         } 

         else if(sig=='H')
         {
              if(t==LOW)
                {
                  runMotor(10000);
                }
               else if(t==HIGH)
                {
                 stopMotor();
                }
         } 

         else if(sig == 'S')
          {
                  stopMotor();
          }
}

void runMotor(int n)
{
   motor.setSpeed(200);
   motor.run(FORWARD);
   delay(n);
}

void stopMotor()
{
   motor.setSpeed(200);
   motor.run(RELEASE);
   delay(200);
}

And here is my Winform Code

private: System::Void run_Click(System::Object^ sender, System::EventArgs^ e) {
    
        serialPort1->Close();
        serialPort1->Open();
        if (listBox1->Text == "Low")
        {
            serialPort1->Write("L");
        }
        else if (listBox1->Text == "Medium")
        {
            serialPort1->Write("M");
        }
        else if (listBox1->Text == "High")
        {
            serialPort1->Write("H");
        }
        
    
}
private: System::Void stop_Click(System::Object^ sender, System::EventArgs^ e) {
    
    serialPort1->Close();
    serialPort1->Open();
    serialPort1->Write("S");
    
    
}

My design:

enter image description here

I've been trying to figure out what's the problem with my code and haven't been able to find it.


Solution

  • I cannot be certain of the problem without looking at Winforms code but I believe the problem might be related to having the code to stop the motor inside if statements that read serial input thus for it to work winforms app should be writing the value of the current button all the time in order be able to reach stopMotor function.

    Keep in mind that Serial.read() as per documentation will return -1 if there is no input to be read, so unless your winforms app sends the value of the button constantly, you won't be reaching the stop method (because of the if statements). Please see the following simplified sample.

    char sig;
    int t;
    void setup() {
      Serial.begin(9600);
    }
    void loop() {
        delay(1000);
        sig = Serial.read();
        Serial.println(sig);
        if(sig=='L')
        {  
            Serial.println("Case L");
        }
        else if(sig=='M')
        {
            Serial.println("Case M");
        } else
        {
          Serial.println("Read nothing");
        }
    }
    

    Output of the code from Serial Monitor

    As you can see here we only get inside if statements when input is received.

    One solution will be to only update sig variable when the value read is one of your settings, this way sig won't get overriden by empty serial reads and you won't need any extra code in UI.

    char readValue = Serial.read();
    if (readValue== 'L' || readValue == 'M' || readValue == 'H'){
       sig = readValue;
    }
    

    So basically after sending a signal, the computer will send nothing to Arduino ,therefor Serial.read() will wait till the next signal is sent. In that time, Arduino will do nothing and remain the previous stage of the motor