I'm looking for an advice on how I should code an HC-SR04 that is used to measure the water level in a tank.
What I have so far:
duration = sonar.ping_median(5);
if( distance >=400 || distance <=2)
{
Serial.println("Out of range");
}
distance = ( duration / 2 ) * 0.0343;
percent = constrain((map(distance, 12 , 2 , 0 , 100)),0,100);
// displaying data using an OLED
delay(500); // also needed for OLED to display.
What's the best way to get rid of a delay in this case?
My project will have at least 3-4 sensors that need to output their data ( OLED for example + sending it to Blynk ) , relay , motor etc. Each of them have some kind of delay.
Im looking for an ideea to get rid of them so my code runs smoothly.
Maybe building some kind of timer..
There are thousands of ways how to realise timers.
A lot of different libraries that offer timer-functionality.
as an allday example with easy to follow numbers
delay() is blocking. As long as the delay is "delaying" nothing else of the code can be executed.
Now there is a technique of non-blocking timing.
The basic principle of non-blocking timing is fundamental different from using delay()
You have to understand the difference first and then look into the code.
otherwise you might try to "see" a "delay-analog-thing" in the millis()-code which it really isn't
Trying to see a "delay-analog-thing" in millis() makes it hard to understand millis()
Having understood the basic principle of non-blocking timing based on millis() makes it easy to understand.
imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in.
Baking time 10 minutes
You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02 (snapshot of time)
You start reading the newspaper and from time to time looking onto your watch
watch shows 13:02. 13:02 - 13:02 = 0 minutes passed by not yet time
watch shows 13:03. 13:03 - 13:02 = 1 minute passed by not yet time
watch shows 13:04. 13:04 - 13:02 = 2 minutes passed by not yet time
watch shows 13:05 when did I start 13:02? OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven
New basetime 13:05 (the snapshot of time)
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time (13:14 - 13:05 = 9 minutes is less than 10 minutes
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)
You did a repeated comparing how much time has passed by
This is what non-blocking timing does.
In the code looking at "How much time has passed by" is done
currentTime - startTime >= bakingTime
bakingTime is 10 minutes
13:06 - 13:05 = 1 minute >= bakingTime is false
13:07 - 13:05 = 2 minutes >= bakingTime is false
...
13:14 - 13:05 = 9 minutes >= bakingTime is false
13:15 - 13:05 = 10 minutes >= bakingTime is TRUE time for timed action!!
So your loop() is doing
void loop()
// doing all kinds of stuff like reading the newspaper
if (currentTime - previousTime >= period) {
previousTime = currentTime; // first thing to do is updating the snapshot of time
// time for timed action
}
it has to be coded exactly this way because in this way it manages the rollover from Max back to zero
of the function millis() automatically
baldengineer.com has a very good tutorial about timing with function millis() too .
There is one paragraph that nails down the difference between function delay() and millis() down to the point:
The millis() function is one of the most powerful functions of the Arduino library. This function returns the number of milliseconds the current sketch has been running since the last reset. At first, you might be thinking, well that’s not every useful! But consider how you tell time during the day. Effectively, you look at how many minutes have elapsed since midnight. That’s the idea behind millis()!
Instead of “waiting a certain amount of time” like you do with delay(), you can use millis() to ask “how much time has passed”?
@StefanL38 Thank you for taking your taking and explain , and also for that link.
@Koepel. I dont have a full sketch with all the sensors yet, as I'm still working on it. But lets take this for example, where I'm displaying some data from the HC-SR04 on my OLED.
This is for calculating how much water is left in my water tank, so I probably want to read this data only once every 2-3 seconds , I dont see the point of doing more often
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "NewPing.h"
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define echoPin 26 // attach pin D2 Arduino to pin Echo of HC-SR04
#define trigPin 25 //attach pin D3 Arduino to pin Trig of HC-SR04
#define max_distance 400
NewPing sonar (trigPin,echoPin,max_distance);
float distance,duration; // variable for the distance measurement
int percent;
void setup() {
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //initialize with the I2C addr 0x3C (128x64)
display.clearDisplay();
}
void loop() {
duration = sonar.ping_median(5);
if( distance >=400 || distance <=2)
{
Serial.println("Out of range");
}
distance = ( duration / 2 ) * 0.0343;
//distance = sonar.convert_cm(duration);
percent=constrain((map(distance, 12 , 2 , 0 , 100)),0,100);
if( percent <= 30 )
{
// alert through a notification on Blynk.
}
display.setCursor(30,0); //oled display
display.setTextSize(3);
display.setTextColor(WHITE);
display.println(percent);
display.setCursor(70,0);
display.setTextSize(3);
display.println(" %");
display.display();
display.setCursor(10,40); //oled display
display.setTextSize(3);
display.setTextColor(WHITE);
display.print(distance,1);
display.setCursor(70,40);
display.setTextSize(3);
display.println(" cm");
display.display();
delay(500); // want to get rid of this.
display.clearDisplay();
}
I have seen so many newcomers struggling with this short hint to the BwD-example and with the - what I call - "the standard millis() explanation" about the function millis().
Short of writing the code for them I don't know that I can make it any clearer. BWoD turns an LED on and off, it could equally call a function instead of changing the state of an LED, hence my suggestion
The tutorials that I linked to go into greater detail that would be overkill to post here
From experience one of two things usually happen after suggesting use of the BWoD principle
The OP takes the principle on board and solves their problem
The OP tries to use the principle, fails, posts their code and we move on from there
@UKHeliBob Thank you for the sources, I will definetly go and read them , and try on my own . If anything fails or I have further question , I will come back here. Thanks again
That is the best way forward. BWoD is fundamentally simple but is more difficult to apply to replace delay()s in existing code than if you start by using it as your timing mechanism.
In your case I think that if you do as I suggest you can simply delete the delay() you indicated and just call the function to read and display the value at the frequency you want. Personally I would split the code for reading and display the value into 2 suitably named functions so that they can be tested independently but I will leave that to you to decide
Once you have one "timer" working you can add as many as you like, all independent, as long as you use different variables
I'm reading all this articles and they're amazing. I will try to re-cast all my code using those amazing libraries and come back with a response. I'm actually using an ESP32, but with Arduino framework, so the Simple Multitasking on any board is amazing. Thanks !!
For a real newcomer using RTOS functions would be a too big challenge.
If it is easy to use prove me wrong by providing really easy to understand and well documented examples.
(I'm very sure latest at the condition well documented examples it will fail)
Having spent years working on multi-threaded coding and having coded the ESP32 using both simple multi-threading and RTOS, I prefer simple multi- threading because it is so simple and easy to use. The FreeRTOS manual is 400 pages long.
ESP32's FreeRTOS is a cooperative multi-tasking configured on Arduino as a pre-emptive multitasking system (thanks @Whandall ). Edit - However once you start running tasks at different priorities, you have to program delays/yields into your tasks to give other tasks a chance to run so it looks like co-operative multi-tasking. If you try running all your tasks at the same priority then some tasks can be skipped unless you code to avoid that.
You need to learn new methods for starting tasks and if you use the default method, your task can be run on either core, so you can find your task competing with the high priority Radio tasks for time. Also if you have multiple tasks distributed across the two cores, you have to worry about safely transferring data between the tasks in a thread safe manner, i.e. locks, semaphores, critical sections etc. Finally due to a quirk in the way the ESP32 implements the task switching, you can find your task is not called at all, or called less often then you would expect. You can code around this problem, but it takes extra effort.
Running a high speed stepper at 1mS per step is difficult on ESP32 RTOS where the minimum one tick delay is 15mS.
Simple Multi-tasking gives you very fine grain control over how often your 'task' runs.
I don't have to prove anything, it was my opinion.
Learning about the environment that already runs, does not hurt.
Learning a new, personal abstraction to replace already existing functionality is just nuts.