Need some help on without delay control... I think

Hello, so first of all i’m still pretty new to this so if my code could be changed to make it better please let me know. What I’m working on is for when i send a typed command of click and the I/O i’m using it will click it for a set amount of time and then release, without using the delay so i could have more than on click going on at the same time. i already have it with the delay in it and i would like to remove the delay. i hope that makes sense and that someone can help me. i have added the code below.

void setup() 
{
  Serial.begin(9600);


pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
}

String command;

void loop() 
{
  if(Serial.available())
    {
       char c = Serial.read();
             
       if(c == '\n')
       {
           parseCommand(command);
           command = "";
       }
       else
       {
         command += c;
       }
  }
 }


void parseCommand(String com)
{
  String part1;
  String part2;

  part1 = com.substring(0, com.indexOf(" "));
  part2 = com.substring(com.indexOf(" ") + 1);

  
  if(part1.equalsIgnoreCase("press"))
  {

    int pin = part2.toInt();
    
    digitalWrite(pin, HIGH);
  }
  else if(part1.equalsIgnoreCase("release"))
  {
    int pin = part2.toInt();
    
    digitalWrite(pin, LOW);
  }
  else if(part1.equalsIgnoreCase("click"))
   {

    int pin = part2.toInt();
    
    digitalWrite(pin, HIGH);
    delay(5000);
    digitalWrite(pin, LOW);
  }
  else {
    Serial.println(" COMMAND NOT VALID");
}
}

pars_test.ino (1.09 KB)

Look at the blink without delay example. When the command is click, you need to turn the pin on, and record when that pin was turned on. On every pass through loop(), you check to see if a pin is on (its on time is not zero is one way). If it is, you check to see if it has been on long enough (now minus its on time is greater than the interval). If it has been, you turn it off and set its on time to 0.

Hi-

IMHO... you should read up on several things.

1.) The blink without delay sketch/example in the IDE is a nice starting point of understanding 'without' delay.

2.) You might need to start looking into/reading up on Finite State Machines.. it expands on the above and outlines a more advanced way of programming where you might have several things going on at once.

its basically a coding approach that sets 'flags/states' that determine what the code will do..

The demo several things at a time is an extended example of BWoD

You may also find serial input basics useful.

...R

kwells2907:
i hope that makes sense and that someone can help me.

I’m sorry that I must tell you: Your sketch doesn’t make much sense for what you want to do.

Hopefully I can help you out.

A well structured program follows a programming logic and deals with data structures and algorithms.

The programming logic you can use for every Arduino program is the IPO programming model:

  • Input
  • Processing
  • Output

I use that all the time. Then your loop function will look like (always the same):

void loop() {
  input();
  processing();
  output();  
}

And then you just have to write your own functions that do the job.

input() takes all the input from the world outside the Arduino, such like reading time, sensors, buttons etc.
processing() deals with the programming logic in RAM only
output() sends the result to the world outside the Arduino, such like switching outputs on/off, sending text to Serial or displays, etc.

And to make things easy, I declare ‘data structures’ that provide the data for the program and then the algorithm uses for-loops to handle the data.

So let’s see my program:

struct iodevice_t{byte pin; const char* name; unsigned long cycleTime; boolean on; boolean clicked; long clickTime;};

const char* devices[]={"led", "motor", "pump", "relay"};
const char* actions[]={"=on", "=off", "=click"}; // not yet used in this program

iodevice_t iodevices[]={  // array for all our devices
  {13, devices[0], 5000}, // pin, name, cycleTime
  {3, devices[1], 5000},
  {4, devices[2], 5000},
  {5, devices[3], 5000},
};
#define NUMDEVICES (sizeof(iodevices)/sizeof(iodevices[0]))

char* serialInput()
{ // reads commands from Serial, return char pointer to command if detected
  static char buf[21];
  int count;
  if (!Serial.available()) return NULL; // no command, return NULL pointer
  delay(21); // small delay ==> wait for all chars of the command to arrive
  memset(buf,0,sizeof(buf)); // clear command buffer
  count=0;  // clear number of chars in the command
  while (Serial.available()) // while there is some Serial input ready
  {
    char c=Serial.read();  // read one char
    if (count<sizeof(buf)-1 && c>=32) // does the char fit in the command buffer?
    {
      buf[count]=c; // Yes, insert char in command buffer
      count++; // count the char
    }
  }  
  return buf; // return pointer to command buffer
}

long nowMillis; // variable to hold the current milliseconds of the millis() function
char* command;  // char pointer pointing to the command buffer

void input()
{
  nowMillis=millis();
  command= serialInput();
}

void processing()
{
  // first check for pending serial commands
  for (int i=0;i<NUMDEVICES;i++)
  {
    // check if it is a valid command name
    if (command!=NULL && strstr(command,iodevices[i].name)==command)
    {
      Serial.print("Command: ");Serial.println(command);
      iodevices[i].clicked=true;
      iodevices[i].clickTime=nowMillis;
    }
  }
  // then check for timeout after a click action
  for (int i=0;i<NUMDEVICES;i++)
  {
    if (iodevices[i].clicked && nowMillis-iodevices[i].clickTime>=iodevices[i].cycleTime)
      iodevices[i].clicked=false; // cycle time has passed
  }
  // finally set to on or off state
  for (int i=0;i<NUMDEVICES;i++)
  { // if clicked state is already in progress, set to on state
    if (iodevices[i].clicked) 
      iodevices[i].on=true;
    else  
      iodevices[i].on=false;
  }
}

void output()
{
  // set all outputs accordingly to current on state
  for (int i=0;i<NUMDEVICES;i++)
  { 
    digitalWrite(iodevices[i].pin,iodevices[i].on);
  }
}


void setup() {
  Serial.begin(9600);
  Serial.println("*** Welcome to serial command processing ***");
  Serial.println();
  Serial.println("Valid commands are;");
  for (int i=0;i<NUMDEVICES;i++)
  {
    Serial.println(iodevices[i].name);
    pinMode(iodevices[i].pin,OUTPUT);
  }
  Serial.println();
  Serial.println("Please enter your commands:");
}

void loop() {
  input();
  processing();
  output();  
}

This is what it does: The program declares four devices {“led”, “motor”, “pump”, “relay”}; and those devices can be controlled by “commands from Serial”.

Currently only one command is implemented: If you enter the name of the device, its output pin will be set to HIGH (on) for the amount of “cycleTime”.

If you need to handle different command, the program can easily be adapted to different action, like setting a device “always on”, or “immediate off” while is is on during the “cycleTime”.

So extended commands may look like:
led=click
led=off
led=on

But currently this is not supported. The program only lools if the command starts with the name of the device, and then the device is set to ‘on’ for the duration of “cycleTime”.

Like that? Or what do you want?

jurs: I'm sorry that I must tell you: Your sketch doesn't make much sense for what you want to do.

Hopefully I can help you out.

Like that? Or what do you want?

Thank you this is quite helpful

What my overall goal is that i can type commands in like;

"Press X" and it would turn I/O 1 to High "Release X" and it would turn I/O 1 to LOW

That works my pars program great. The commands I'm stuck on is

"Click X" and it will it would turn I/O 1 to High for a set amount of time and it would turn I/O 1 to LOW or what i would prefer more is: "Click X 1000" and it will it would turn I/O 1 to High for a 1000 millisecond and then it would turn I/O 1 to LOW

I'm guessing that this would be more difficult

kwells2907: "Click X 1000" and it will it would turn I/O 1 to High for a 1000 millisecond and then it would turn I/O 1 to LOW

I'm guessing that this would be more difficult

The code in serial input basics shows how to receive data like that

By the way it would be a lot more efficient if you reduce "Click" to "C"

...R

kwells2907: What my overall goal is that i can type commands in like;

"Press X" and it would turn I/O 1 to High "Release X" and it would turn I/O 1 to LOW

That works my pars program great. The commands I'm stuck on is

"Click X" and it will it would turn I/O 1 to High for a set amount of time and it would turn I/O 1 to LOW or what i would prefer more is: "Click X 1000" and it will it would turn I/O 1 to High for a 1000 millisecond and then it would turn I/O 1 to LOW

I'm guessing that this would be more difficult

No, that's not difficult and exactly what my programming example does: After the command is received, switch HIGH for some time, then switch LOW after timeout. It would need just a few additional lines of code to add "always HIGH" and "always LOW" commands as well.

If you really want a weird logic with a mixed up command sequence like: "Click X 1000" which is: "command ==> device ==> parameter" you'd need some additional programming to parse the whole string BEFORE you can start evaluating the command.

But if you'd use the normal command sequence like: "x click 1000" which is "device ==> command ==> parameter" you can handle the command simple from left to right: - at first is device recognition, so the device is alerted - second is the action, so the device can initiate the action - last is the parameter(s) action as modifier(s) for the action

All very easy with structured programming, based on data structures and algorithms.

Do you need some more lines of code to extend the command interpreter that I posted in reply #4?

jurs: Do you need some more lines of code to extend the command interpreter that I posted in reply #4?

Some more lines of code would be great, and i will change to be x click 1000.

Thank you everyone for the help it has been most valuable

kwells2907:
Some more lines of code would be great, and i will change to be x click 1000.

OK, here is the code for an extended example:

struct iodevice_t{byte pin; const char* name; unsigned long cycleTime; boolean on; boolean clicked; long clickTime;};

const char* devices[]={"led", "motor", "pump", "relay"};
#define NUMDEVICES (sizeof(devices)/sizeof(devices[0]))

const char* actions[]={"on", "off", "click"};
#define NUMACTIONS (sizeof(actions)/sizeof(actions[0]))

iodevice_t iodevices[]={  // array for all our devices
  {13, devices[0] }, // pin, name
  {3,  devices[1] },
  {4,  devices[2] },
  {5,  devices[3] },
};

char* serialInput()
{ // reads commands from Serial, return char pointer to command if detected
  static char buf[21];
  int count;
  if (!Serial.available()) return NULL; // no command, return NULL pointer
  delay(21); // small delay ==> wait for all chars of the command to arrive
  memset(buf,0,sizeof(buf)); // clear command buffer
  count=0;  // clear number of chars in the command
  while (Serial.available()) // while there is some Serial input ready
  {
    char c=Serial.read();  // read one char
    if (count<sizeof(buf)-1 && c>=32) // does the char fit in the command buffer?
    {
      buf[count]=c; // Yes, insert char in command buffer
      count++; // count the char
    }
  }  
  return buf; // return pointer to command buffer
}

long nowMillis; // variable to hold the current milliseconds of the millis() function
char* device;
char* action;
char* parameter;

void input()
{
  nowMillis=millis();
  char* command= serialInput();  // char pointer pointing to the command buffer
  if (command==NULL) // no command ready
  { // so we have nothing to handle
    device=NULL;
    action=NULL;
    parameter=NULL;
  }
  else
  { 
    device= strtok(command," ");
    action= strtok(NULL," ");
    parameter= strtok(NULL," ");
  }
}

boolean commandSuccess;
void processing()
{
  commandSuccess=false;
  // check for commands to be handled
  if (device!=NULL)
  {
    for (int i=0;i<NUMDEVICES;i++)
    { // check if it is a valid device name
      if (strcmp(iodevices[i].name, device)==0)
      { // 'i' contains now the device index for our command
        // check for 'on'
        if (strcmp(actions[0], action)==0) 
        {
          iodevices[i].on=true; 
          commandSuccess=true;
        }
        // check for 'off'
        if (strcmp(actions[1], action)==0)
        {
          iodevices[i].on=false; 
          commandSuccess=true;
        }          
        // check for 'click'
        if (strcmp(actions[2], action)==0)
        {
          long duration=atol(parameter);
          if (duration>0)
          {
            iodevices[i].cycleTime=duration;
            iodevices[i].clicked=true;
            iodevices[i].clickTime=nowMillis;
            iodevices[i].on=true;
            commandSuccess=true;
          }
        }
      }
    }
  }
  
  // then check for timeout after a click action
  for (int i=0;i<NUMDEVICES;i++)
  {
    if (iodevices[i].clicked && nowMillis-iodevices[i].clickTime>=iodevices[i].cycleTime)
    {
      iodevices[i].clicked=false; // cycle time has passed
      iodevices[i].on=false; 
    }
  }
}

void output()
{
  // set all outputs accordingly to current on state
  for (int i=0;i<NUMDEVICES;i++)
  { 
    digitalWrite(iodevices[i].pin,iodevices[i].on);
  }
  
  if (device)
  {
    if (commandSuccess) Serial.print("SUCCESS: ");
    else Serial.print("ERROR  : ");
    Serial.print(device);Serial.print(' ');
    Serial.print(action);Serial.print(' ');
    Serial.print(parameter);
    Serial.println();
  }
  
}


void setup() {
  Serial.begin(9600);
  Serial.println("*** Welcome to serial command processing ***");
  Serial.println();
  Serial.println("Valid commands are (time '1234' just as example):");
  for (int i=0;i<NUMDEVICES;i++)
  {
    pinMode(iodevices[i].pin,OUTPUT);
    for (int j=0;j<NUMACTIONS;j++)
    {
      Serial.print(iodevices[i].name);
      Serial.print(' ');
      Serial.print(actions[j]);
      Serial.print(' ');
      if (j==2) Serial.print(1234);
      Serial.println(' ');
    }
  }
  Serial.println();
  Serial.println("Please enter your commands:");
}

void loop() {
  input();
  processing();
  output();  
}

The command actions supported are now:

  • on
  • off
  • click 1234 (on for the milliseconds given in the parameter)

If you want to rename the devices, just give them different names like:
const char* devices={“a”, “x”, “y”, “z”};

And if you would like to change the number of devices, don’t forget not only to extend the device names in the ‘devices’ array, but also the initialization data in the ‘iodevices’ array.

Thank you so much jurs this is exactly what i needed you are a huge help.

And thank you to everyone else for the help

kwells2907: Thank you so much jurs this is exactly what i needed you are a huge help.

And thank you to everyone else for the help

BTW, I just found what might be a problem. Or perhaps not.

If you set a device "on" while a "click" is active at the same time, the device will be set to "off" after the click time has passed.

It you want to set a device to "forever on" while a "click" is active and you send "on", you'd have to shut off the "clicked" variable to false when handling the "on" state. Perhaps change to:

        if (strcmp(actions[0], action)==0) 
        {
          iodevices[i].on=true; 
          iodevices[i].clicked=false; // add this line to cancel current 'click' action
          commandSuccess=true;
        }

Jurr

jurs: BTW, I just found what might be a problem. Or perhaps not.

Nope not an issues

but now im adding some servo control to it any advice or ideas

Working with commands like:

(Servo X +100) and the servo would move forward a set amount or (servo X -100) and the servo would move back a set amount

if i used 50 vs 100 it would go half the set distances. i have some ideas on how i should do this but any advice would be much appreciated

Thanks all

kwells2907:
but now im adding some servo control to it any advice or ideas

Working with commands like:

(Servo X +100) and the servo would move forward a set amount
or
(servo X -100) and the servo would move back a set amount

if i used 50 vs 100 it would go half the set distances.
i have some ideas on how i should do this but any advice would be much appreciated

Nice idea.
The command set can be extended easily.

But if you want to introduce a complete new class of devices, that cannot be set to on/off via HIGH/LOW on an output pin, but are controlled by servo logic, you will have to change the “struct iodevice_t” data structure as well as you would have to process different types of devices in the processing() function.

jurs: Nice idea. The command set can be extended easily.

But if you want to introduce a complete new class of devices, that cannot be set to on/off via HIGH/LOW on an output pin, but are controlled by servo logic, you will have to change the "struct iodevice_t" data structure as well as you would have to process different types of devices in the processing() function.

For introducing a new set could I just make a new set of "struct iodevice_t" named something else or should i try to change the current one?

Generally speaking, without wanting to offend anyone, this is not a very good structure:

void loop() {
  input();
  processing();
  output();  
}

All the coupling between functions is done through invisible global variables. Things are much easier to debug if data interactions are done through parameter passing and return values.

kwells2907: For introducing a new set could I just make a new set of "struct iodevice_t" named something else or should i try to change the current one?

For introducing a bunch of new devices that can simply be switched HIGH and LOW, you just have to invent some new names for them, like:

const char* devices[]={"led", "motor1", "motor2", "motor3", 
                        "x", "y", "z",
                        "pump1", "pump2", "pump3",
                        "relay1", "relay2", "relay3"
              };

Then define a pin that belongs to each device name:

iodevice_t iodevices[]={  // array for all our devices
  {13, devices[0] }, // pin, name
  {2,  devices[1] },
  {3,  devices[2] },
  {4,  devices[3] },
  {5,  devices[4] },
  {6,  devices[5] },
  {7,  devices[6] },
  {8,  devices[7] },
  {9,  devices[8] },
  {10,devices[9] },
  {11,devices[10] },
  {12, devices[11] },
  {A1, devices[12] },
};

Ready you are with extending the program for several more HIGH/LOW control devices.

But if you want a complete different class of devices, that are not controlled by HIGH/LOW, but by different control like PWM or servo control, you would have to rewrite quite a bit of the example sketch in each of the parts, perhaps exclusive the input() function: - data structures needs to be changed - maybe new data structures have to be worked out - processing logic needs to be changed - output logic needs to be changed

The programming example posted in reply #9 does what you have initially asked for: Setting a device on/HIGH or off/LOW or click/HIGH-for-some-time.

The example sketch does not what you have posted AFTER I posted the ready-to-use code. I'm not a mind-reader.

jurs: For introducing a bunch of new devices that can simply be switched HIGH and LOW, you just have to invent some new names for them, like:

The example sketch does not what you have posted AFTER I posted the ready-to-use code. I'm not a mind-reader.

Yes I got that part I have already added all the HIGH/LOW control on the sketch that i have created and modified. all that in for was extremely helpful allowing me to get a working program that i was unable to do myself. I am now trying the add and change that program to be able to control servos as well. so this is moving on from my first question but due to the fact you where all so helpful with the first question is was hoping that you would be able to help me with adding servo control