Can you describe in detail the behavior of this nonBlockingDelay()
? Are you sure you aren't trying to have your cake and eat it too?
And this is the reason why I say:
All the example-codes delivered with the arduino-IDE should be fundamentally re-written to have a short and compact way that uses non-blocking timing in a way that can be used and RE-used for a good amount of often needed functionalities.
I'm well aware that there will no "one_covers_them_all-solution". That's why I write a "good amount of often needed"
I estimate that around 90% of all cases can be coded with 3 or 4 variants of this pattern.
If the basic examples show the non-blocking approach each newcomer would have a slightly higher hurdle at the beginning but is already on the right track for more complex coding
instead of learning "the wrong way" of linear code-execution and to learn new the
most outer circular repeated code-execution.
After having read this term "most outer circular repeated code-execution." you will have had the effect of a specific term that you do (not yet) know.
You will have had a "Huh ?! What's that?! - at least for a few seconds and then
- only having a glimpse of what this might mean (as code)
- still asking yourself "don't know this ! what is this? a "most outer circular repeated code-execution." can I have an example ?
I know what "most" means, I know what "outer" means, I have a glimps of what circular might mean in this case, I know what "repeated" means I know what "code-execution" means but what the heck is "most outer circular repeated code-execution??? "
Putting you back in the situation of a real beginner.
best regards Stefan
OK I take this example
For reading encoders with an interrupt that would work even with a lot of delay()s as the nature of an interrupt is to "the name is program" interrupt whatever the code is doing to run down the interrupt-service-code and then continue at exact that place of the normal code where interrupt occured.
No. This problem is solved in a different way. You use something that is called a hysteresis.
You define a value-range Rather small but bigger than how the ADC-value will jump and down. This range is the hysteresis.
Then you add a logic that checks has a value changed for more than hysteris.
If yes set value "UP" as long as the value jumps around below the hysteresis stay as you are.
If the direction changes the value has to go down for more than the hysteresis to set the value "DOWN"
as long as the value jumps around below the hysteresis stay as you are.
myPotValue = analogRead(myPot_Pin);
if (myPotValue > 0 && myPotValue < 240) {
// position 1
}
// if value changes from 240 to 241, 242, .... 254 nothing happends
// only if value reaches 255
if (myPotValue > 255 && myPotValue < 496) {
// position 2
}
// if value changes from 496 to 497, 498, .... 511 nothing happends
// only if value reaches 512
if (myPotValue > 512 && myPotValue < 752) {
// position 3
}
With 3 or 5, 10 or 20 keypresses the computer can handle this at the highest possible speed.
So in these cases you don't need a delay().
This becomes only a problem if you want to send thousands of keypresses at MBaud speeds
If you copy a 100 MByte file from an USB-stick to your harddisk you can see the copying proceeds with 10 to 20 MBytes per second, while your youtube video goes on playing at 1080 pixels without a hickup.
best regards Stefan
because the non-blocking timer must be run over and over to work while delay() keeps everything but interrupts from running. Not blocking means frequent time checks... the approaches and structures of delay (synchronous) code and asynchronous real world code are exclusive.
But what you should ask is how can you code multiple things happening at once and still use delay() and other blocking code?
You -could- use an interrupt-driven mini-OS like RTOS.
If you want faster, ditch the blocking approach and use timers that have far less Overhead than a task switcher. Either way you need state machines and the rest of the learning curve.
I have posted example sketches of technique-level cut&dry "undelay" since 2015 after converting a sketch made of sketches that all worked alone with delays then went 3 Stooges together. It had GSM, I2C and Serial and good news, the code that made them work alone worked!
When I made those parts non-blocking, ALL of the sensor and message code, and delay times was kept the same, I did not have to learn GSM or the I2C sensors, just change delays to timers and wrap each piece in a function in loop().
An example that I do need to update a bit. It includes how to loop inside of a state machine. The user pause code is a blocking kludge...
// CombineSketchesDemo 2022 by GoForSmoke @ Arduino.cc Forum
// Free for use, May 23, 2022 by GFS. Compiled on Arduino 2.1.0.5
// Free to post anywhere complete and as is.
// This sketch shows a general method to get rid of delays in code.
// You could upgrade code that delays to combine into this sketch.
// .. adding delays in loop cases
// revisions with forum help:
// dlloyd -- added state enums May 11. <--- changed as needed.
////////////////////////////////////////////////////////////////////////////
// This example/demo takes 3 sketches that use delay() and COMBINE THEM
// by using simple techniques the sketch demonstrates.
// Plus there's a user stop/go that violates how the 3 together work.
// Code that does not sit or loop in one spot to wait,
// does not block other code from executing during a wait.
// In 1 ms, code can use or lose 16000 cpu cycles. So don't block!
// The 3 delay-sketches void loop() code goes into 3 functions that run as tasks.
// Arduino void loop() runs the same functions over and over, these functions don't wait
// but instead check time and if time's not up then run the next function.
// if ( Serial.available() ) is just such a thing, not waiting around for data.
// Every delay() has a built-in timer. I replace it with a timer that only runs when set.
// The timer is first in the function so that the function can return until timeout.
// The timer only runs when the time to wait is set > 0, else the function continues.
// Where a delay was removed, the time is set and the timer runs for the next many loops.
// When the time is done, the time to wait set = 0. What was a delay() sets the timer.
// This demo also addresses loops inside of void loop(), which can hog cycles terribly.
// It also contains a state machine, a code tool of value beyond what the demo does.
// A State Machine is code written in steps of what to do according to what's been done.
// A state variable holds what to do next time the machine runs.
// perhaps the 1st state waits for input and when it gets it the code changes state..
// to run what to do with the input. State Machines can run inside of state machines.
// task StopGo. -- Block - unBlock on a keystroke to stop serial monitor
// task LoopCounter -- lets you know how often void loop() ran last second.
// LoopCounter value
#define microsInOneSecond 1000000UL
// task PrintNumbers -- prints from printNum to setNum, pnWait ms apart
// PrintNumbers variables
unsigned long pnStart;
unsigned long pnWait = 1000UL;
int setNum = 60;
int printNum = 0;
// task BlinkPattern -- blinks led13
// BlinkPattern variables
const byte ledPin = 13;
unsigned long blinkStart;
unsigned long blinkWait;
byte indexMax = 12;
byte index;
enum blinkStates {BLINK_1_ON, BLINK_1_OFF, BLINK_2_ON, BLINK_2_OFF, BLINK_3_ON, BLINK_3_OFF};
blinkStates blinkStep; // state tracking for BlinkPattern() below
void setup()
{
Serial.begin( 115200 );
Serial.println( F( "\n\n\n Combine Sketches Demo, free by GoForSmoke" ));
Serial.println( F( "This sketch shows how to combine sketches.\n" ));
Serial.println( F( "Press Enter to toggle monitor scrolling." ));
pinMode( ledPin, OUTPUT );
digitalWrite( ledPin, HIGH ); // 2 secs before the scroll starts
delay( 500 );
digitalWrite( ledPin, LOW );
delay( 500 );
digitalWrite( ledPin, HIGH );
delay( 500 );
digitalWrite( ledPin, LOW );
delay( 500 );
blinkStep = BLINK_1_ON; // actual value is 0
};
/* LoopCounter -- void loop() code to count loops per second
*
* delay( 1000 );
* Serial.println( "1" );
*/
void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
static unsigned long count, countStartMicros; // only this function sees these
count++; // adds 1 to count after any use in an expression, here it just adds 1.
if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
{
countStartMicros += microsInOneSecond; // for a regular second
Serial.println( count ); // 32-bit binary into decimal text = many micros
count = 0; // don't forget to reset the counter
}
}
/* PrintNumbers -- void loop() code to count from one value to another with wait between.
*
* for ( printNum = 0; printNum <= setNum; printNum++ )
* {
* Serial.print( F( "Number " ));
* Serial.print( printNum );
* Serial.print( F( " Time " ));
* Serial.println( millis() );
* delay( pnWait );
* }
*/
// this task runs once. suppose that StopGo made it start again?
void PrintNumbers() // a loop with a delay in it becomes...
{
if ( setNum < 1 ) return; // how to turn this task off, setNum = 0;
// This repeat timer replaces delay()
// start of repeat timer
if ( millis() - pnStart < pnWait ) // wait is not over
{
return; // instead of blocking, the undelayed function returns
}
Serial.print( F( "Number " ));
Serial.print( printNum );
Serial.print( F( " Time " ));
Serial.println( millis() );
pnStart += pnWait; // starting 1 sec after last start, not the same as = millis()
if ( ++printNum >= setNum )
{
setNum = printNum = 0;
}
}
/* BlinkPattern -- void loop() code to blink led13 using delay()
*
* digitalWrite( ledPin, HIGH ); -- BLINK_1_ON
* Serial.print( F( "BLINK_1_ON, time " ));
* Serial.println( millis());
* delay( 500 );
* digitalWrite( ledPin, LOW ); -- BLINK_1_OFF
* Serial.print( F( "BLINK_1_OFF, time " ));
* Serial.println( millis());
* delay( 500 );
* for ( i = 0; i < 12; i++ )
* (
* digitalWrite( ledPin, HIGH ); -- BLINK_2_ON
* Serial.print( F( "BLINK_2_ON, time " ));
* Serial.println( millis());
* delay( 250 );
* digitalWrite( ledPin, LOW ); -- BLINK_2_OFF
* Serial.print( F( "BLINK_2_OFF, time " ));
* Serial.println( millis());
* delay( 250 );
* }
* digitalWrite( ledPin, HIGH ); -- BLINK_3_ON
* Serial.print( F( "BLINK_3_ON, time " ));
* Serial.println( millis());
* delay( 1000 );
* digitalWrite( ledPin, LOW ); -- BLINK_3_OFF
* Serial.print( F( "BLINK_3_ON, time " ));
* Serial.println( millis());
* delay( 1000 );
*/
void BlinkPattern() // does the same as above without delay()
{
// This one-shot timer replaces every delay() removed in one spot.
// start of one-shot timer
if ( blinkWait > 0 ) // one-shot timer only runs when set
{
if ( millis() - blinkStart < blinkWait )
{
return; // instead of blocking, the undelayed function returns
}
else
{
blinkWait = 0; // time's up! turn off the timer and run the blinkStep case
}
}
// end of one-shot timer
// here each case has a timed wait but cases could change Step on pin or serial events.
switch( blinkStep ) // runs the case numbered in blinkStep
{
case BLINK_1_ON :
digitalWrite( ledPin, HIGH );
Serial.print( F( "BLINK_1_ON, time " ));
Serial.println( blinkStart = millis()); // able to set a var to a value I pass to function
blinkWait = 500; // for the next half second, this function will return on entry.
blinkStep = BLINK_1_OFF; // when the switch-case runs again it will be case 1 that runs
break; // exit switch-case
case BLINK_1_OFF :
digitalWrite( ledPin, LOW );
Serial.print( F( "BLINK_1_OFF, time " ));
Serial.println( blinkStart = millis());
blinkWait = 500;
blinkStep = BLINK_2_ON;
break;
case BLINK_2_ON :
digitalWrite( ledPin, HIGH );
Serial.print( F( "BLINK_2_ON, time " ));
Serial.println( blinkStart = millis());
blinkWait = 250;
blinkStep = BLINK_2_OFF;
break;
case BLINK_2_OFF :
digitalWrite( ledPin, LOW );
Serial.print( F( "BLINK_2_OFF, time " ));
Serial.println( blinkStart = millis());
blinkWait = 250;
// ****** this replaces the for-loop in non-blocking code. ******
if ( index++ < indexMax ) // index gets incremented after the compare
{
blinkStep = BLINK_2_ON;
}
else
{
index = 0;
blinkStep = BLINK_3_ON;
} // end of how to for-loop in a state machine without blocking execution.
break;
case BLINK_3_ON :
digitalWrite( ledPin, HIGH );
Serial.print( F( "BLINK_3_ON, time " ));
Serial.println( blinkStart = millis());
blinkWait = 1000;
blinkStep = BLINK_3_OFF;
break;
case BLINK_3_OFF :
digitalWrite( ledPin, LOW );
Serial.print( F( "BLINK_3_OFF, time " ));
Serial.println( blinkStart = millis());
blinkWait = 1000;
blinkStep = BLINK_1_ON; // start again
break;
}
}
void StopGo() // user keyboard block / unblock
{
if ( Serial.available() )
{
while ( Serial.available() ) // clearing the buffer
{
Serial.read();
delay(1); // don't care
}
}
else
{
return;
}
while ( ! Serial.available() ); // waiting for Go, ! is logical NOT
while ( Serial.available() ) // clearing the buffer
{
Serial.read();
delay(1); // don't care
}
}
void loop() // runs over and over, see how often
{
LoopCounter(); // the function runs as a task, the optimizer will inline the code.
PrintNumbers();
BlinkPattern();
StopGo();
}
I'm hard pressed to think of any useful multi-tasking application that doesn't require interaction / communication between the tasks.
It certainly would be easier for the user, but unfortunately is not that easy to implement, at least not without limitations. Fundamentally CPUs like Atmel AVR are designed to execute one thread of execution, and making them multitask takes extra code. RTOSes are designed for that purposes, but have an overhead in terms of RAM, Flash, CPU.
For small CPUs like AVR, an RTOS is probably overkill and can take up too much resources. Therefore some form of DIY approach is required when working at the bare metal level. Usually I implement a simple polled task design. I wouldn't then need any sort of delay() function, since the tasks are event driven.
Unless Arduino was redesigned from the ground up I don't think the holy grail of simple plug and play is possible when it comes to something like a nonBlockingDelay()
So you never want to have an LED blinking away? Events are fine, but you have to consider that time running out is an event.
a7
yeah, i think i am trying to have my cake and eat it,
I'm thinking from the mind of someone who struggles badly with coding.
wishing for simpler ways to do things that take sometimes quite a few lines of code for a non blocking timer in multiple places,
And instead being able to use one function name to add a delay that only blocks that particular part of the sketch.
maybe 'NonBlockingDealy' was the wrong term, as i now realise thanks to the replies on here that any delay needs to either block all the code, or only part of it (of switch to a multi threaded AVR board, which for a game controller with just 2 analogue inputs, and a few buttons may be overkill.
so maybe a function named 'DelayJustThisBit' ?
But as has been pointed out, we can do this, it just takes learning the code for it, and i guess picking one method and using it with all my code, as there seem to be many ways to do this timer thing.
:
Thinking about my keyboard thing, whilst the computer can handle maybe a few hundred keypress events at sub millisecond intervals, the game that is being played on the computer often can't, and if you send them too fast the in game control stops responding and misses steps (one is a train power wheel with 43 steps, but sending more than 5 'move right or left' keyboard events a second locks it up)
So i was thinking of a simple way to put a delay between each keypress sent... but then i guess i need to store them up and release them slowly?
How? I doubt Arduino would change their formula at this late date. The existing examples have worked pretty well for Arduino so far. Maybe the deficiencies in the code makes a selling point for newer and more expensive processors?
One way the forum could help is organizing rewrites of the examples, along the lines of the blink, blink without delay, and blink more things without delay. If we can do 50+ posts on a thread about the difference between delay() and nonblockingdelay(), we could surely do a thread on the ways to do a particular example differently/correctly.
From the noob user perspective, finding a set of alternative ways to code the same exact thing that they are having problems with applying to their situation might help them learn.
For example, How to Wire A Button or the confusions in Arduino Transistor Motor Control example & its AI-generated fritzing schematic isn't worth pointing people towards to clarify how to use a transistor--Its got a FET instead of a transistor, and the awkward layout hides things more than clarifies:
If the forum had specific tutorial threads targeted on specific official examples, it would give a place for the venerable hackers to hold forth on the how it isn't done the right way, whale also providing a thread for the less-experienced to see a diversity of opinions on how to do what they want to do.
void loop(void){
...
if( sendKeyPressPeriodically('x',500)){
...
}
...
}
bool sendKeyPressPeriodically(character ch, unsigned long interval){
static unsigned long last = 0; // remember the time with a static
unsigned long now = millis();
if (now - last < interval) return(false); // too early, abort!
last = now; // store the last keypress time
Serial.print(ch); // or whatever
return(true);
}
Starting small, how about something like this:
Yes exactly. Detecting and count the fast keypresses. Then buffering the number of keypresses and send them out at that speed the game does accept them.
This could even include if you press "up" 20 times very fast then press down 12 times very fast to immidiately count down 12 ticks from 20 to 8
If you describe in normal words but in detail what functionality you want
in combination with
your code that at least tries to build this functionality
You will receive support on this particular case.
You should describe it very detailed.
Example:
if you would like to represent this virtual train-power-wheel that has 43 steps and originally is controlled by two keys for step-up / step-down
with a rotary encoder-knob
The code would be as you described it
quickly counting up / down to the new target-value
and send out keypressed at that a slowed down speed the game does accept.
always comparing does the target-value from the encoder match the value of the virtual-game-wheel
and whenever there is a difference between detected target-value and the value that represents the send out keypresses from the key that does change the virtual-game-wheel towards the target-value.
best regards Stefan
This is absolutely true. I specialize in advancing early education in math and programming and it is absolutely possible to lose a student to confusion if certain fundamental steps are not taken. Take a step, then walk a path, then decide between paths, then circle back.
On the flip-side though, I think there should be both a simplest case and a more advanced non-blocking case for most of the examples. They could be categorically separated. Many newcomers, who have even the slightest talent, could be pointed directly to the non-blocking design patterns, and the one-offs or those in need of fundamental guidance could be pointed to the path as it already exists.
I guess thatβs what they shoot for with this
Here's a snippet of behavior from a real commercial product. The requirement is to dispense a heated fluid along one side of an object.
- Task 1: temperature controller for the fluid keeps fluid at a certain temperature
- Task 2: pressure controller for the pneumatic dispenser maintains pressurization of the heated fluid
- Task 3: motor controller to move the dispenser along the object after it's been given the "move" command
No interaction between any of these tasks is required. While the final product used a 32-bit processor, the initial unit was actually prototyped with an Arduino.
I might have opted not to make it so open-loop.
I am not quite sure if this is the same thing the OP is talking about.
But I needed not blocking code for my project also.
Because I wanted that Arduino managed to keep track on an input signal and calculate is for an output for the speedometer and also calculate it and send it on a CAN network.
The delay function gaved me headaches but I managed to work trough.
I used millis() to use "delay's" while other code still continues executing.
And yes I got from GPT, and modified it to my project. Which I dont define as the right way. But it helped me figuring out :).
I have this code that I sort-of like.
Theoretically, it should be C++ified, and given better names, and more options, and documented so it's suitable for beginners, but it's serviceable:
void loop() {
static unsigned long ledtime = 0;
static unsigned long atime, btime, ctime, nltime;
static int ledstate = false;
if (delay_without_delaying(ledtime, 500)) {
ledstate = !ledstate;
digitalWrite(13, ledstate);
}
if (delay_without_delaying(atime, 100)) {
Serial.print("A");
}
if (delay_without_delaying(btime, 200)) {
Serial.print("B");
}
if (delay_without_delaying(ctime, 30)) {
Serial.print("C");
}
if (delay_without_delaying(nltime, 1000)) {
Serial.print("\n");
}
}
I saw in your hub you used the same method for delay's
if (currentmillis - previousmillis >= time) {
previousmillis = currentmillis;
Works perfect on my project
Hello
This is a conceivable solution for the paradigm of procedural programming.
Have a nice day and enjoy coding in C++.