Interrupts - to use or not?

Hi,
I have a joystick controlled by an ESP32 using Arduino C sending on/off data to three tiny brushed DC motors (pager motors) controlled by an ESP8266 using Arduino C.

The code works nicely, but there is at least a full second delay in the data being received and the motors actuating. I use the serial monitor for troubleshooting, but have commented this out to help the data delay problem.

I wonder what a good approach to lowering the latency would be. Should I put all of these motors on interrupts? Are there any other approaches?

Thanks!

Hi Sophi and welcome to the forum.

Sophi:
If interested

No, we are not interested in following those links. Post your code here according to the forum guide in the sticky post, along with your schematics and links to data sheets or information pages for any unusual components. Its all there in the guide.

My imagination tells me that the two esp are communicating via WiFi and this is the cause of your latency problems. What protocol are you using to send the data/instructions over WiFi? I don't think using interrupts, if there is a way to use interrupts here, will make any difference at all to the latency.

Hi Paul,
Yes it is communicating over WiFi using TCP.

Here's the receiving (motors) and below this is the sending (client):

#include <ESP8266WiFi.h>
char ssid[] = "Linksys59517";             // SSID of home WiFi

WiFiServer server(80);
WiFiClient client = server.available();

IPAddress ip(192,168,1,213);              // name the IP address of the ESP
IPAddress gateway(192,168,1,1);           // gateway of your network netstat -nr | grep default
IPAddress subnet(255,255,255,0);          // subnet mask of your network

float batteryV;
int highBandLimit = 2500;
int lowBandLimit = 1400;
int slowStartLimit = 3600;
int slowStopLimit = 500;
int upDownInt;
int upDownInt2;
int val3Int;
int val4Int;
int motorValue;
int pwmVal0;
int pwmVal2;
String start;
String upDown;
String upDowna;
String upDownb;
String upDownc;
String upDownd;
String upDown2;
String upDown2a;
String upDown2b;
String upDown2c;
String upDown2d;
String val3;
String val3a;
String val3b;
String val3c;
String val3d;
String val4;
String val4a;
String val4b;
String val4c;
String val4d;

void setup() {
  //Serial.begin(115200);                   // only for debug
  WiFi.config(ip, gateway, subnet);       // forces fixed IP
  WiFi.begin(ssid, pass);                 // connects to the WiFi router
  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.println(".");
    delay(50);
  }  
  
  Serial.println("Connected to wifi");
 
  server.begin(); 
  pinMode(0, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(4, OUTPUT);    
  pinMode(5, OUTPUT);    
  pinMode(12, OUTPUT);   
  pinMode(13, OUTPUT);   
  pinMode(14, OUTPUT);   
  pinMode(16, OUTPUT);   
  digitalWrite(4,  LOW); 
  digitalWrite(5,  LOW); 
  digitalWrite(12, LOW); 
  digitalWrite(13, LOW); 
  digitalWrite(14, LOW); 
  digitalWrite(16, LOW); 
}

void loop () {
  WiFiClient client = server.available();
  Serial.println("Not connected");  
  //batteryV = analogRead(A0);
  if (client) {
    server.begin();
    while (client.connected()){
       if (!client.connected()) {
        digitalWrite(2, HIGH);
        delay(100);
        digitalWrite(2, LOW);
        delay(100);
        Serial.println();
        Serial.println("disconnecting.");
        client.stop();
        server.available();
        }
        
     if(client.available()){
        String request = client.readStringUntil('y');
        upDowna = request.charAt(0); //upDown
        upDownb = request.charAt(1);
        upDownc = request.charAt(2);
        upDownd = request.charAt(3);
        upDown = upDowna + upDownb + upDownc + upDownd;

        upDown2a = request.charAt(4); //upDown2
        upDown2b = request.charAt(5);
        upDown2c = request.charAt(6);
        upDown2d = request.charAt(7);
        upDown2 = upDown2a + upDown2b + upDown2c + upDown2d;
        
        val3a = request.charAt(8); //val3
        val3b = request.charAt(9);
        val3c = request.charAt(10);
        val3d = request.charAt(11);
        val3 = val3a + val3b + val3c + val3d;
        
        val4a = request.charAt(12); //val4
        val4b = request.charAt(13);
        val4c = request.charAt(14); 
        val4d = request.charAt(15);
        val4 = val4a + val4b + val4c + val4d;

        upDownInt = upDown.toInt();
        upDownInt2 = upDown2.toInt();
        val3Int = val3.toInt();
        val4Int = val4.toInt();
        Serial.println("upDown is: " + upDown);
        Serial.println("upDown2 is: " + upDown2);
        Serial.println("val3 is: " + val3);  
        Serial.println("val4 is: "+ val4);   
                    
        if (val3Int > highBandLimit && val4Int < highBandLimit 
         && val4Int > lowBandLimit){        // move forward, both motors CCW
            digitalWrite(5, LOW);
            digitalWrite(4, HIGH);
            digitalWrite(12, LOW);
            digitalWrite(13, HIGH);
            if(val3Int > highBandLimit && val3Int < slowStartLimit) { 
              for(int motorValue = highBandLimit ; motorValue <= slowStartLimit; motorValue += 10){
                //motorValue = min(motorValue, 3000);
                analogWrite(0, motorValue); 
                analogWrite(2, motorValue); 
                delay(1);
                }
             } 
          }              
          
         else if (val3Int < lowBandLimit && val4Int < highBandLimit 
          && val4Int > lowBandLimit){     // move backward, both motors CW
              digitalWrite(5, HIGH);
              digitalWrite(4, LOW);
              digitalWrite(13, LOW);
              digitalWrite(12, HIGH);
              if(val3Int > slowStopLimit && val3Int < lowBandLimit) {
                for(motorValue = lowBandLimit; motorValue >= 10; motorValue -= 10){
                  //min(motorValue, 3000);
                  analogWrite(0, (4095-motorValue)); 
                  analogWrite(2, (4095-motorValue));              
                  delay(1);
                }
              } 
           }
           
          else if (val4Int < lowBandLimit && val3Int < highBandLimit 
           && val3Int > lowBandLimit){  // move right, val4 only
              digitalWrite(12, HIGH);
              digitalWrite(13, LOW);
              digitalWrite(4, LOW);
              digitalWrite(5, LOW);
              Serial.println("right");
              analogWrite(0, (4095-motorValue)); 
              }
              
          else if (val4Int > highBandLimit && val3Int < highBandLimit 
           && val3Int > lowBandLimit){  // move LEFT, val3 only
              digitalWrite(12, LOW);
              digitalWrite(13, LOW);
              digitalWrite(4, HIGH);
              digitalWrite(5, LOW);
              Serial.println("left");
              analogWrite(0, (4095-motorValue)); 
              }
         
          else if (upDownInt < 2000){  // go down
              digitalWrite(16, HIGH);
              digitalWrite(14, LOW);
              Serial.println("down");
              }
              
          else if (upDownInt2 < 2000){  // go up
              digitalWrite(14, HIGH);
              digitalWrite(16, LOW);
              Serial.println("up");
              }
           
           
          else if (upDownInt >= 2000 && val3Int > lowBandLimit && val3Int < highBandLimit
           && val4Int > lowBandLimit && val4Int < highBandLimit){
              digitalWrite(4, LOW);
              digitalWrite(5, LOW);
              digitalWrite(12, LOW);
              digitalWrite(13, LOW);
              digitalWrite(14, LOW);
              digitalWrite(16, LOW);
              Serial.print("ALL OFF  ");
              }           
    }
  }
}
}

Should I put all of these motors on interrupts?

No.

If you must use a wireless method of communicating with the motors, choose a direct one, like bidirectional radio modules. It will be much faster.

I think I can see over 300 millisecs of delay()s in the code in Reply #2. If you want a responsive program they all need to go.

Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

And see Using millis() for timing. A beginners guide if you need more explanation.

There will certainly be no noticeable wireless latency using a pair of nRF24L01+ transceivers. Have a look at this Simple nRF24L01+ Tutorial.

If you must use ESP WiFi devices UPD will probably be faster than TCP.

I don't know if ESP-NOW works between an ESP32 and an ESP8266, It may be worth investigating.

...R

Thank you @Robin2!

Also readStringUntil() has a default wait time of 1000mS if it doesn’t find the termination character. You can change this timeout if needed.

Sophi:
I wonder what a good approach to lowering the latency would be. Should I put all of these motors on interrupts? Are there any other approaches?

Ah yes, ever the trap for young [players - neophytes, "newbies"; not necessarily just a matter of age. :grinning: I see the word "interrupt" in a topic and odds on it is going to be entirely inappropriate. :roll_eyes:

There is the temptation - "I need my program to take notice fast" - "I need to interrupt what it is doing".

An easy way to control the direction of the program. Well, no, interrupts are not a lazy way of writing a program.

A hardware interrupt has three aspects.

  • Something must be attended to immediately.
  • It is possible to do it immediately.
  • It does not directly affect the flow of the main program which it is interrupting.
    Point one: "Immediately" means in the realm of machine operation; we are talking microseconds; events which happen and must be dealt with on this scale; evanescent and invisible to human perception. Examples of this are servicing communications adapters and disk operations. Responding to key-presses or contact closures which are many thousands of times slower is not an appropriate application for interrupts and causes difficulties with contact "bounce".

Second point: In particular, "delay" is not part of interrupts. If something involves a delay, it did not need an interrupt to effect it. An interrupt might initiate a delay by some means, but will then terminate and return to the main process. The mechanism of the delay might generate an interrupt when it later expires.

Finally, the whole mechanism of an interrupt is that it in no way interferes with the main process. This is self-evident as the interrupt may of course occur at any point in the main process, so the effect of any any interaction with that main process would be totally unpredictable and almost certainly catastrophic. The single exception here is where it is desirable that the interrupt abort the main process and effect a complete reset.

The interrupt may certainly set a flag or semaphore for which the main process may check as necessary, but this is the only way it can effect the direction taken.


All that said, I do wonder what "put all of these motors on interrupts" could possibly mean? :astonished:

Paul__B:
All that said, I do wonder what "put all of these motors on interrupts" could possibly mean? :astonished:

Me2 :wink:

Proper automatisation would receive commands in a non-blocking way, then handle each received command. Calls like client.readStringUntil() have a blocking smell. Also Serial.println("Not connected") at the begin of loop() binds the execution to the speed of Serial.

Interrupts are not required for the motor operation, and are already used in the network firmware. As already pointed out, replacing delay() by millis() timekeeping will make the code more responsive.