Run while loop for fixed time

Hello,

I'm trying to run a while loop for 200ms. I thought I approached the correct way but when the function is called it keep running so appearently I'm doing something wrong. My code looks as follows:

void Cmd_DriveLeft(CommandParameter& p)                                                               //Function for driving Left
{
  unsigned long now = millis();
  unsigned long end = now + 200;

  while (now < end)
  {
    int DutyCycle = 50;  
    digitalWrite(AI_1, HIGH);
    digitalWrite(BI_1, HIGH);
    ledcWrite(PWM_A_Chan, DutyCycle);
    ledcWrite(PWM_B_Chan, DutyCycle);
    pwm = DutyCycle; 
    now = millis();
  }
}

This function is called in a loop when I press a button. I want this loop only keeps running for 200ms and than stops and jumps out of this function. What am I doing wrong here?
I have the variable now assigned a time when the function starts. Than I add 200ms to it. And start a while loop where I say run this loop untill the end time has passed. Inside the while loop I keep updating the current time.

Could someone point out to me where I make the mistake?

void Cmd_DriveLeft(CommandParameter& p)                                                               //Function for driving Left
{
  unsigned long now = millis();
  unsigned long end = now + 200;

  while (now < end)
  {
    int DutyCycle = 50;  
    digitalWrite(AI_1, HIGH);
    digitalWrite(BI_1, HIGH);
    ledcWrite(PWM_A_Chan, DutyCycle);
    ledcWrite(PWM_B_Chan, DutyCycle);
    pwm = DutyCycle; 
    //now = millis();
    if (millis()- now >= end )
    {
      break;
    }
  }
}

or something like that. Error was not testing that millis had exceeded its time to live.

I don't think that's it. What you wrote should be equivalent to @Ray1981 's original.

What makes you think that? We can't see the rest of your code, but based on what we can see, nothing would apparently change when the while loop ends. So maybe it is ending but there is no visual sign that it had happened. Does something outside the function change any of those 4 pins?

Because when I press the button the motors keep running in Left direction. I can post my full code which is getting a bit long. But as far I know when a function is called by some event it should start and end. What I try to do now it make the function running just for 200ms.

//************************************************************************************************************************************//
//                                                                                                                                    //
//  Project: Rover                                                                                                                    //
//  Version: V0.1                                                                                                                     //
//  Date: Feb-2023                                                                                                                    //
//  Current version capabilities:                                                                                                     //
//        - Drive forward option                                                                                                      //
//        - Speed sensing of Left and Right motors                                                                                    //
//        - Interface with MegunoLink for speed monitoring and speed control                                                          //
//        V0.1:                                                                                                                       //
//        - Adjusted speed control hardware with Logic inverter port, so two pins less will be used                                   //
//        - Added WiFI communication with Megunolink                                                                                  //
//        - Clean up Loop() by adding new functions                                                                                   //
//        - Added controls for driving Forward, Backwards, Left, Right and Stop                                                       //
//************************************************************************************************************************************//

// Include SSID and password from a library file.
#if defined(ARDUINO_ARCH_ESP32)
#include "WiFi.h"
#include <ESPmDNS.h>
#elif defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#else
#error Not compatible with the selected board. 
#endif

#include <MegunoLink.h>
#include "CircularBuffer.h"
#include "ESPTCPCommandHandler.h"
#include "CommandProcessor.h"
#include "ArduinoTimer.h"


#define USEWIFICONFIGFILE
#ifdef USEWIFICONFIGFILE

// Include SSID and password from a library file. 
#include "WiFiConfig.h"
#else
// Option 2
const char *SSID = "Your SSID";
const char *WiFiPassword = "Your Password";
#endif

//Variables used for WiFi Server
const uint8_t ServerPort = 23;
WiFiServer Server(ServerPort);
ArduinoTimer SendTimer;
uint32_t PlottingPeriod = 200;
const int MaxConnections = 2;
TcpCommandHandler<MaxConnections> Cmds(Server);
CommandProcessor<> SerialCmds(Cmds);

String MakeMine(const char *NameTemplate);


#define PWM_A 14                                                                                              //PWM Channel for left motor
#define PWM_A_Chan 0
#define AI_1 12                                                                                               //Enable Channel left motor, logic "0" is forward, inverter IC takes care of inversing signal for AI_2
#define PWM_B 25                                                                                              //PWM Channel for right motor
#define PWM_B_Chan 1
#define BI_1 26                                                                                               //Enable Channel right motor, logic "0" is forward, inverter IC takes care of inversing signal for BI_2
#define PWM_Res 8
#define PWM_Freq 15000
#define buffer_size 7                                                                                         //Buffer size for computing average of speed sensing signal
#define PI 3.14159265359

const byte slots = 20; 
// timing variables rightside motor speed sensor                                                              //Total slots on motor disk
volatile unsigned long usRight;
volatile unsigned long prevPulseUsRight; 
volatile unsigned long pulseUsRight;
volatile unsigned long prevPulseUsCopyRight; 
volatile unsigned long pulseUsCopyRight;
unsigned long pulsePeriodRight;
unsigned long AvgPulseTimeRight;

// timing variables leftside motor speed sensor
volatile unsigned long usLeft;
volatile unsigned long prevPulseUsLeft; 
volatile unsigned long pulseUsLeft;
volatile unsigned long prevPulseUsCopyLeft; 
volatile unsigned long pulseUsCopyLeft;
unsigned long pulsePeriodLeft;
unsigned long AvgPulseTimeLeft;

unsigned long prevMs;
unsigned long now;

// Variables used for calculation of Rotations Per Sec and AVG speed
float rpsRight = 0;
float rpsLeft = 0;
float AvgVelocityRight;
float AvgVelocityLeft;

float pwm;
int PWM_DutyCycle;

const unsigned long slotUs = 1000000 / slots;
const int RightMotorSpeedSens = 18;                                                       //Right motor Interrupt pin18 for speed sensing 
const int LeftMotorSpeedSens = 19;                                                        //Left motor Interrupt pin19 for speed sensing 
const float WheelDiameter = 0.0664;                                                       //Wheel diameter
unsigned int RightSpeedCount = 0; 
unsigned int LeftSpeedCount = 0;            
unsigned long tempbufferRight[buffer_size];
unsigned long tempbufferLeft[buffer_size];

InterfacePanel MyPanel; 
typedef CircularBuffer<unsigned long, buffer_size> PulsePeriodBuffer;                                
PulsePeriodBuffer PulseTimesRight;
PulsePeriodBuffer PulseTimesLeft;

//Interrupt for right speed sensor 
void IRAM_ATTR isrRight()
{
  usRight = micros();
  if ((usRight - pulseUsRight) > 7500)                                                               // debounce interval, also determines max rpm
  {  
    prevPulseUsRight = pulseUsRight;
    pulseUsRight = usRight;
  }
}

//Interrupt for left speed sensor
void IRAM_ATTR isrLeft()
{
  usLeft = micros();
  if ((usLeft - pulseUsLeft) > 7500)                                                               // debounce interval, also determines max rpm
  {  
    prevPulseUsLeft = pulseUsLeft;
    pulseUsLeft = usLeft;
  }
}

bool timeOut(unsigned long ms) 
{
  now = millis();
  if ((now - prevMs) >= ms) 
  {
    prevMs = now;
    return true;
  }
  return false;
}

//WiFi connect function, checks SSID and Password shows if a connection is made and displays the IP address on the serial port
void ConnectToWiFi()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(SSID, WiFiPassword);
  Serial.print("Connecting to "); Serial.println(SSID);

  uint8_t i = 0;
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print('.');
    delay(500);

    if ((++i % 16) == 0)
    {
      Serial.println(F(" still trying to connect"));
    }
  }

  Serial.print(F("Connected. My IP address is: "));
  Serial.println(WiFi.localIP());
}

//mDNS function to show device name, printed on the serial port
void AdvertiseServices()
{
  String MyName = MakeMine("MyDevice");
  if (MDNS.begin(MyName.c_str()))
  {
    Serial.println(F("mDNS responder started"));
    Serial.print(F("My name is: "));
    Serial.println(MyName.c_str());

    // Add service to MDNS-SD
    MDNS.addService("n8i-mlp", "tcp", ServerPort);
  }
  else
  {
    Serial.println(F("Error setting up MDNS responder"));
  }
}

/* Returns a semi-unique id for the device. The id is based
*  on part of a MAC address or chip ID so it won't be 
*  globally unique. */
uint16_t GetDeviceId()
{
#if defined(ARDUINO_ARCH_ESP32)
  return ESP.getEfuseMac();
#else
  return ESP.getChipId();
#endif
}

/* Append a semi-unique id to the name template */
String MakeMine(const char *NameTemplate)
{
  uint16_t uChipId = GetDeviceId();
  String Result = String(NameTemplate) + String(uChipId, HEX);
  return Result;
}

void Cmd_ListAll(CommandParameter &Parameters)
{
  Parameters.GetSource().print(F("PlottingPeriod [ms]="));
  Parameters.GetSource().println(PlottingPeriod);
}

 

void Cmd_SetPlottingPeriod(CommandParameter &Parameters)
{
  PlottingPeriod = Parameters.NextParameterAsInteger(PlottingPeriod);
}

 

void Cmd_Unknown()
{
  Serial.println(F("I don't understand"));
}


void setup() {
  //Setup Serial connection
  Serial.begin(115200);
  Serial.println(F("......Program starts....."));

  //Setup WiFI connection
  ConnectToWiFi();
  AdvertiseServices();
  // Start the TCP server
  Server.begin();
  Server.setNoDelay(true);
  

  // Setup the serial commands we can repond to
  Cmds.AddCommand(F("PlottingPeriod"), Cmd_SetPlottingPeriod);
  Cmds.AddCommand(F("ListAll"), Cmd_ListAll);
  Cmds.SetDefaultHandler(Cmd_Unknown);
  Cmds.AddCommand(F("MotorSpeed"), Cmd_DriveForwards);                //Command to communicate with Megunolink
  Cmds.AddCommand(F("btnDriveForward"), Cmd_DriveForwards);
  Cmds.AddCommand(F("btnDriveBackwards"), Cmd_DriveBackwards);
  Cmds.AddCommand(F("btnDriveLeft"), Cmd_DriveLeft);
  Cmds.AddCommand(F("btnDriveRight"), Cmd_DriveRight);
  Cmds.AddCommand(F("btnEmergStop"), Cmd_Stop);    

  pinMode(AI_1, OUTPUT);                                              //A motor setup input channels
  pinMode(BI_1, OUTPUT);                                              //B motor setup input channels
  pinMode(RightMotorSpeedSens, INPUT_PULLUP);                         //Setup input channels for speed sensing
  pinMode(LeftMotorSpeedSens, INPUT_PULLUP);

  ledcAttachPin(PWM_A, PWM_A_Chan);                                   //Setup A motor PWM channel
  ledcAttachPin(PWM_B, PWM_B_Chan);                                   //Setup B motor PWM channel
  ledcSetup(PWM_A_Chan, PWM_Freq, PWM_Res);
  ledcSetup(PWM_B_Chan, PWM_Freq, PWM_Res);

  attachInterrupt(digitalPinToInterrupt(RightMotorSpeedSens), isrRight, RISING);
  attachInterrupt(digitalPinToInterrupt(LeftMotorSpeedSens), isrLeft, RISING);
  Serial.println("Setup Ready...");

}

void loop() {
    #if defined(ARDUINO_ARCH_ESP8266)
      MDNS.update();
    #endif
  
  SerialCmds.Process();                                                                                     //Monitor serial commands
  Cmds.Process();                                                                                           //Monitor WiFi commands
    
  SpeedSensing();

  InterfacePanel MyPanel("", Cmds); 
                                                     
  MyPanel.SetNumber(F("RightSpeedGauge"), AvgVelocityRight);
  MyPanel.SetNumber(F("LeftSpeedGauge"), AvgVelocityLeft);                                                  // Set control value


  if (SendTimer.TimePassed_Milliseconds(PlottingPeriod))
  {
      Serial.println("~");
      TimePlot MyPlot("", Cmds);                                                                          //Needs to use Cmds to access the connections
      //Send Data To MegunoLink Pro
      MyPlot.SendData(F("Left Speed"), AvgVelocityLeft); 
      MyPlot.SendData(F("Right Speed"), AvgVelocityRight); 
      MyPlot.SendData("Setpoint", pwm/100);                                                               //PWM signal divided by 100 to scale equal with speed                                       
  }

  delay(50);
}


void Cmd_DriveForwards(CommandParameter& p)                                                           //Function for driving forward
{
  int DutyCycle = p.NextParameterAsInteger();  
  digitalWrite(AI_1, LOW);
  digitalWrite(BI_1, HIGH);
  ledcWrite(PWM_A_Chan, DutyCycle);
  ledcWrite(PWM_B_Chan, DutyCycle);
  pwm = DutyCycle;   
}

void Cmd_DriveBackwards(CommandParameter& p)                                                           //Function for driving Backwards
{
  int DutyCycle = p.NextParameterAsInteger();  
  digitalWrite(AI_1, HIGH);
  digitalWrite(BI_1, LOW);
  ledcWrite(PWM_A_Chan, DutyCycle);
  ledcWrite(PWM_B_Chan, DutyCycle);
  pwm = DutyCycle;   
}

void Cmd_DriveLeft(CommandParameter& p)                                                               //Function for driving Left
{
  unsigned long now = millis();
  unsigned long end = now + 200;

  while (now < end)
  {
    int DutyCycle = 65;  
    digitalWrite(AI_1, HIGH);
    digitalWrite(BI_1, HIGH);
    ledcWrite(PWM_A_Chan, DutyCycle);
    ledcWrite(PWM_B_Chan, DutyCycle);
    pwm = DutyCycle; 
    //now = millis();
    if (millis()- now >= end )
    {
      break;
    }
  }
  
}

void Cmd_DriveRight(CommandParameter& p)                                                              //Function for driving Right
{
  int DutyCycle = 65;  
  digitalWrite(AI_1, LOW);
  digitalWrite(BI_1, LOW);
  ledcWrite(PWM_A_Chan, DutyCycle);
  ledcWrite(PWM_B_Chan, DutyCycle);
  pwm = DutyCycle;   
}

void Cmd_Stop(CommandParameter& p)                                                           //Function for stop driving
{
  int DutyCycle = 0;  
  digitalWrite(AI_1, LOW);
  digitalWrite(BI_1, HIGH);
  ledcWrite(PWM_A_Chan, DutyCycle);
  ledcWrite(PWM_B_Chan, DutyCycle);
  pwm = DutyCycle;   
}

void averagebuffer()
{
  AvgPulseTimeRight = 0;
  AvgPulseTimeLeft = 0;
  for(int a=0;a<buffer_size;a++)
  {
    AvgPulseTimeRight += tempbufferRight[a];                                              //Sum up buffer values
    AvgPulseTimeLeft += tempbufferLeft[a];    
  }

  AvgPulseTimeRight = AvgPulseTimeRight/buffer_size;                                      //Compute average of buffer values by dividing by buffer length
  AvgPulseTimeLeft = AvgPulseTimeLeft/buffer_size;
}

void MoveAverageSpeed()
//This function computes the avverage for Left and Right speed values 
{
    for(PulsePeriodBuffer::ForwardIterator Iterator(PulseTimesRight); Iterator.AtEnd() == false; Iterator.Next())
    {
    tempbufferRight[Iterator.ItemNumber()]=Iterator.CurrentValue();
    }
    
    for(PulsePeriodBuffer::ForwardIterator Iterator(PulseTimesLeft); Iterator.AtEnd() == false; Iterator.Next())
    {
    tempbufferLeft[Iterator.ItemNumber()]=Iterator.CurrentValue();
    }
    averagebuffer();
}

void SpeedSensing()
//This functions determines the current speed of Left and Right wheels
{
  if (timeOut(250)) 
  {                                                                                       // 4 Hz update frequency
    cli();                                                                                //stop interrupts
    prevPulseUsCopyRight = prevPulseUsRight;
    pulseUsCopyRight = pulseUsRight;
    prevPulseUsCopyLeft = prevPulseUsLeft;
    pulseUsCopyLeft = pulseUsLeft;
    sei();                                                                                //allow interrupts
    pulsePeriodRight = pulseUsCopyRight - prevPulseUsCopyRight;                           //Length of one pulse period
    pulsePeriodLeft = pulseUsCopyLeft - prevPulseUsCopyLeft;

    PulseTimesRight.Add(pulsePeriodRight);
    PulseTimesLeft.Add(pulsePeriodLeft);
    MoveAverageSpeed();
    if(pulsePeriodRight != 0) 
    {
    rpsRight = (float)slotUs / (float)AvgPulseTimeRight ;                                   //Compute RPS value
    AvgVelocityRight = (PI * WheelDiameter) * rpsRight;                                     //Compute measured speed in m/s right wheel
    rpsLeft = (float)slotUs / (float)AvgPulseTimeLeft ;                                     //Compute RPS value
    AvgVelocityLeft= (PI * WheelDiameter) * rpsLeft;                                        //Compute measured speed in m/s left wheel
    }
  }
  
  if(pwm == 0)
  {
    AvgVelocityLeft = 0;
    AvgVelocityRight = 0;
  }
}

If it helps here is the full code

I think it is running for 200ms.

Are you pressing the stop button after 200ms? If not, why would the motors stop running in the left direction?

Maybe my english is terrible but when pressing the button for left direction the function it calls should run only for 200ms. I do not press the stop button. When I press the stop button the motors stop running as I want to. That was not the issue. The issue was when I press the left button the motors should run in left direction only for 200 ms.

I fixed the issue as follows:

void Cmd_DriveLeft(CommandParameter& p)                                                               //Function for driving Left
{
  unsigned long end = millis() + 200;
  int DutyCycle = 65; 

  while (millis() < end)
  {
     
    digitalWrite(AI_1, HIGH);
    digitalWrite(BI_1, HIGH);
    ledcWrite(PWM_A_Chan, DutyCycle);
    ledcWrite(PWM_B_Chan, DutyCycle);
    pwm = DutyCycle; 

  }
  DutyCycle = 0;
  ledcWrite(PWM_A_Chan, DutyCycle);
  ledcWrite(PWM_B_Chan, DutyCycle);
}

So my error was that I did not tell the function what to do with the DutyCycle after the time was passed and therefore the motors kept running

1 Like

Almost correct. You did not tell the motors to stop after 200ms. Updating the DutyCycle variable alone would not do that.

Here is an easier way to code it:

void Cmd_DriveLeft(CommandParameter& p)                                                               //Function for driving Left
{
  const int DutyCycle = 65; 

  digitalWrite(AI_1, HIGH);
  digitalWrite(BI_1, HIGH);
  ledcWrite(PWM_A_Chan, DutyCycle);
  ledcWrite(PWM_B_Chan, DutyCycle);
  pwm = DutyCycle; // what is this for?
  delay(200);
  ledcWrite(PWM_A_Chan, 0);
  ledcWrite(PWM_B_Chan, 0);
}
1 Like

Yes. This below is functionally identical:

  digitalWrite(AI_1, HIGH);
  digitalWrite(BI_1, HIGH);
  ledcWrite(PWM_A_Chan, DutyCycle);
  ledcWrite(PWM_B_Chan, DutyCycle);
  pwm = DutyCycle; 

  while (millis() < end)
  {
 
  }

Where is ledcWrite()?

Or just delay(), as @PaulRB points out.

a7

You mean what is ledcWrite()? It's an ESP32-only function. Not sure how it is different to analogWrite().

The function ledcwrite is a ESP32 function used for PWM. Talking about this, my both motors run at different speeds while I use the same output signal e.g. PWM 255. So I connected my scope to its output and mesured the following signal.

This is a nasty ugly PWM signal IMHO. This is masured without any load to it. Any opinions?

I agree. Very ugly!

A huge amount of ringing. So much that it almost dominates the desired PWM signal.

The forum cannot help you fix this problem without a detailed schematic.

Well I took another ESP and send the same code to it and measured on the output and got this:

I start to thing this esp is not functioning well.

1 Like

No, I did mean where, but now knowing what means I don't need to read it.

THX

a7

1 Like

Offcourse I understand that I do not have fast a schematic. I should have a nice program for it because I do not like Fritzing.

I use the output D14 for PWM and send the PWM signal to a TB6612FNG. I supply the ESP by a (V battery hooked up to a TL8705 and motor supply comes from a seperate source.

But even these comments wont help you much I guess. I'll see if I can make a proper schematic drawing of how I connected everything

That does not seem relevant now. When you replaced the ESP and the signal was completely different, and the signal was as expected from a PWM signal instead of crazy, it seems very probable that the first ESP was damaged or faulty.

No sorry, I took another ESP and placed it in a Breadboard than the signal looks perfect when I hook it up to my circuit it looks again ugly

There is some interference or mistake with grounding I do not know ...yet

I agree

Investigating further I noticed the following.

The PWM signal is ugly when I supply it by a external power supply made with a 9V battery and a TL7805 to supply the ESP with 5Vdc.

The PWM signal looks perfect when I plug in the USB cable and supply power via the pc or a USB hub.

This makes me think there is an grounding issue somewhere

Oooohhh, please don't say it is a PP3 size battery.

What's a PP3 size battery?