Arduino delay is not precise - c++

I'm new to Arduino, I'm building a simple sensor circuit using a DHT22 and a SparkFun ESP8266 Thing Dev module which is programmable with Arduino.
In my loop code I want my device to wait 5min so I placed at the end delay(300000). The problem is that when I look into my database the timestamp when the data is collected by the sensor, sometimes it wait 2 minutes, sometimes 1 etc.
Furthermore, I have a logical check (if else) that should prevent my device to transmit data when the difference of temperature or humidity is less than 1. But my device seems not to care about it.
I'm sure there should be something wrong with my code but this is the first time I try to use C++ and Arduino so I wasn't able to figure out what just yet. Any help?
This is my code:
#include <DHT.h>
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN,DHTTYPE);
// here I declare the variable of humidity and temperature.
// every time it loops, it should get data from the sensor into
// temp1 and hum1, then check it against temp and hum if there
// are any changes in the values
float temp;
float hum;
float temp1;
float hum1;
int red_light_pin = 16;
int green_light_pin = 12;
int blue_light_pin = 13;
// Server, file, and port
const char hostname[] = "laundryireland.tk";
const String uri = "/write_data?";
const String arguments[3] = {"serial=","&temp=","&hum="};
const int port = 80;
String serialNumber;
WiFiClient client;
void RGB_color(int red_light_value, int green_light_value, int blue_light_value)
{
analogWrite(red_light_pin, red_light_value);
analogWrite(green_light_pin, green_light_value);
analogWrite(blue_light_pin, blue_light_value);
}
void setup() {
pinMode(red_light_pin,OUTPUT);
pinMode(green_light_pin,OUTPUT);
pinMode(blue_light_pin,OUTPUT);
RGB_color(0,0,255);
WiFi.persistent(false);
WiFiManager wifiManager;
//Initialize Serial
Serial.begin(9600);
dht.begin();
delay(100);
//Connect to WiFi
Serial.println("Connecting...");
wifiManager.autoConnect();
while (WiFi.status() != WL_CONNECTED ) {
delay(500);
Serial.print(".");
}
//Show that we are connected
Serial.println("Connected!");
Serial.println(WiFi.localIP());
serialNumber = WiFi.macAddress();
}
void loop() {
delay(2000);
temp1 = dht.readTemperature();
hum1 = dht.readHumidity();
while (temp1 == NULL || hum1 == NULL){
RGB_color(255,0,0);
delay(5000);
temp1 = dht.readTemperature();
hum1 = dht.readHumidity();
}
// THIS IS MY IF STATEMENT TO CHECK FOR
// CHANGES IN TEMP OR HUM
if (temp > temp1+1 || temp < temp1-1 || hum > hum1+1 || hum < hum1-1){
temp = temp1;
hum = hum1;
RGB_color(0,255,0);
Serial.print("Temperature: ");
Serial.println(temp);
Serial.print("Humidity: ");
Serial.println(hum);
Serial.println("Testing flask ");
if ( client.connect(hostname,port) == 0 ) {
Serial.println("Flask Test Failed!");
} else {
Serial.println("Flask Test Success!");
client.print("GET " + uri + arguments[0] + serialNumber +
arguments[1] + temp +
arguments[2] + hum +
" HTTP/1.1\r\n" +
"Host: " + hostname + "\r\n" +
"Connection: close\r\n" +
"\r\n");
delay(500);
while (client.available()){
String ln = client.readStringUntil('\r');
Serial.print(ln);
}
}
client.stop();
Serial.println();
Serial.println("Connection closed");
} else {
Serial.println("temp or hum not changed");
RGB_color(255,255,0);
}
// THIS SHOULD WAIT 5min BEFORE NEXT CHECK
delay(300000);
}
This is how my database looks like:
Solution
Has pointed out by the commenters and well described in the accepted answer, the use of mills() instead of delay() solve the issue with "fake" delay time.
Furthermore, the problem of the transmission of data even when the difference between the previous update and the current one was less than 1; was just a logic problem with my if-else statement. I solved with this code:
int deltaT = abs(temp-temp1);
int deltaH = abs(hum-hum1);
if (deltaT >= 1 || deltaH >= 1){
// code here

The loop function will be called again 300000msec AFTER the loop has finished.
So the time to the next recording is all the processing time of
* 2000 at the beginning
* the times for the functions dht.readTemperature() and dht.readHumidity();
* the time for sending bytes to the Serial output
* the time for client.connect and the network transfer time...
* and the 300000 at the end
A better approach is to get the current millis() from the system to check for 300000 msecs since the last reading.
Then calculate the next time for measurement and then do your processing
You can use a static variable for this to keep code together:
void loop() {
static unsigned long next = 0;
unsigned long now = millis();
if (now > next) {
… do processing
next = now + 300000;
} // if
} // loop()

Related

Lidar Sensors not working properly - How to work with two lidar Sensors over I2C on arduino

I'm currently working on a project with some friends about lidar measuraments based on ARDUINO and GARMIN Lidar v3HP and we are getting some reading that are questionable from the sensors. They seem to work but the measurements are not correct.
We have issues with the data and also with the address, we setup the sensors with two different addresses 0x42 and 0x43, but one of the sensors keeps on the default address.
#include <Arduino.h>
#include <Wire.h>
#include <stdint.h>
#include <LIDARLite_v3HP.h>
#include <I2CFunctions.h>
#define POWER_ENABLE_S1 12
#define POWER_ENABLE_S2 11
#define DEFAULT_ADDRESS 98
#define FAST_I2C
#define NUMERO_LIDARS 2
LIDARLite_v3HP Sensor1;
LIDARLite_v3HP Sensor2;
int detectedAddreses[NUMERO_LIDARS];
int currentAdd;
int deviceCount = 0;
int i = 0;
void scanI2C()
{
int nDevices = 0;
while (i < NUMERO_LIDARS)
{
for (byte addr = 1; addr < 127; ++addr)
{
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0)
{
Serial.print("Se encontro un dispositivo en ");
Serial.print(addr);
Serial.println(" !");
++nDevices;
detectedAddreses[i] = addr;
if (addr == DEFAULT_ADDRESS)
{
configSensors(i, 66 + deviceCount, addr);
detectedAddreses[i] = addr;
i++;
}else{
detectedAddreses[i] = addr;
i++;
}
}
else if (error == 4)
{
Serial.print("Error desconocido en ");
Serial.println(addr);
}
}
if (nDevices == 0)
{
Serial.println("No se encontraron dispositivos\n");
}
else
{
Serial.println("Terminado\n");
}
}
}
void configSensors(int sensor, uint8_t new_address, uint8_t old_address)
{
switch (sensor)
{
case 0:
Sensor1.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S1, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor1.configure(0,new_address);
break;
case 1:
Sensor2.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S2, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor2.configure(0,new_address);
i = 999;
break;
case 2:
break;
}
}
void setup()
{
Serial.begin(115200);
#ifdef FAST_I2C
#if ARDUINO >= 157
Wire.setClock(400000UL); // Set I2C frequency to 400kHz (for Arduino Due)
#else
TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
#endif
#endif
pinMode(POWER_ENABLE_S1, OUTPUT);
pinMode(POWER_ENABLE_S2, OUTPUT);
digitalWrite(POWER_ENABLE_S1, HIGH);
digitalWrite(POWER_ENABLE_S2, HIGH);
Wire.begin();
scanI2C();
digitalWrite(POWER_ENABLE_S1,HIGH);
digitalWrite(POWER_ENABLE_S2,HIGH);
Sensor1.configure(3,detectedAddreses[0]);
Sensor2.configure(3,detectedAddreses[1]);
}
void measure(){
float s1;
float s2;
Sensor1.waitForBusy();
Sensor1.takeRange();
Sensor1.waitForBusy();
s1 = Sensor1.readDistance(detectedAddreses[0]);
Sensor2.waitForBusy();
Sensor2.takeRange();
Sensor2.waitForBusy();
s2 = Sensor2.readDistance(detectedAddreses[1]);
Serial.println("Sensor 1: " + String(s1) + "; Sensor 2: " + String(s2));
}
void loop()
{
/*Serial.println(detectedAddreses[0]);
Serial.println(detectedAddreses[1]);*/
measure();
}
Based on your top comment, there may be an issue with configuring both lidars at the same time.
From factory default, they will both respond to the default I2C address 0x62. So, when you try to reconfigure one at a time, they will both respond [and there may be a race condition] and will both get programmed to the new I2C address.
If [and this is a big if] the lidar can save the configuration to non-volatile storage on the unit, you can connect one at a time [physically/manually] and give them different addresses. The unit saves the address. And, next time, will only respond to the "new" address.
Then, after both units have been reconfigured, you can then connect both simultaneously and they will respond individually [as desired].
I looked at the .pdf and the wiring diagram. You may be able to connect the lidar's power pin [or enable pin] to an Arduino GPIO port pin (instead of +5V). Then, you can control the power up of each unit individually. Then, you can reconfigure both as above. That is, assert power to one, reconfigure it, power it down [with the saved config]. Do this for the other unit. Then, you can power up both units [at this point, they are responding to different I2C addresses].
Don't know if Garmin starts up the lasers immediately or whether you have to give it a "start" command. Being able to control power individually may be a good thing if there is no separate start command.
I'm not familiar with Garmin's lidars, but I've written S/W to control Velodyne lidars and we had to apply power in a staggered manner because the power surge when they both started up would "brown out" the system. With Garmin, YMMV.
If all else fails, you may have to put each unit on a separate/different physical I2C bus [because you can't reconfigure them separately].
Here's the working code,
The sensors are hocked up in the same I2C bus, power enable pins to each sensor and ground conected to arduino. Power to the sensors is supplied by a 11.1V battery with a power regulator to 5V
#include <Arduino.h>
#include <Wire.h>
#include <stdint.h>
#include <LIDARLite_v3HP.h>
#include <I2CFunctions.h>
#define POWER_ENABLE_S1 12
#define POWER_ENABLE_S2 11
#define DEFAULT_ADDRESS 98
#define FAST_I2C
#define NUMERO_LIDARS 2
LIDARLite_v3HP Sensor1;
LIDARLite_v3HP Sensor2;
int detectedAddreses[NUMERO_LIDARS];
int currentAdd;
int deviceCount = 0;
int i = 0;
void scanI2C()
{
int nDevices = 0;
while (i < NUMERO_LIDARS)
{
for (byte addr = 1; addr < 127; ++addr)
{
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0)
{
Serial.print("Se encontro un dispositivo en ");
Serial.print(addr);
Serial.println(" !");
++nDevices;
detectedAddreses[i] = addr;
if (addr == DEFAULT_ADDRESS)
{
configSensors(i, 66 + deviceCount, addr);
detectedAddreses[i] = addr;
i++;
}else{
detectedAddreses[i] = addr;
i++;
}
}
else if (error == 4)
{
Serial.print("Error desconocido en ");
Serial.println(addr);
}
}
if (nDevices == 0)
{
Serial.println("No se encontraron dispositivos\n");
}
else
{
Serial.println("Terminado\n");
}
}
}
void configSensors(int sensor, uint8_t new_address, uint8_t old_address)
{
switch (sensor)
{
case 0:
Sensor1.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S1, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor1.configure(0,new_address);
break;
case 1:
Sensor2.setI2Caddr(new_address, 0, old_address);
digitalWrite(POWER_ENABLE_S2, LOW);
//detectedAddreses[sensor] = new_address;
deviceCount++;
Sensor2.configure(0,new_address);
i = 999;
break;
case 2:
break;
}
}
void setup()
{
Serial.begin(115200);
#ifdef FAST_I2C
#if ARDUINO >= 157
Wire.setClock(400000UL); // Set I2C frequency to 400kHz (for Arduino Due)
#else
TWBR = ((F_CPU / 400000UL) - 16) / 2; // Set I2C frequency to 400kHz
#endif
#endif
pinMode(POWER_ENABLE_S1, OUTPUT);
pinMode(POWER_ENABLE_S2, OUTPUT);
digitalWrite(POWER_ENABLE_S1, HIGH);
digitalWrite(POWER_ENABLE_S2, HIGH);
Wire.begin();
scanI2C();
digitalWrite(POWER_ENABLE_S1,HIGH);
digitalWrite(POWER_ENABLE_S2,HIGH);
Sensor1.configure(3,detectedAddreses[0]);
Sensor2.configure(3,detectedAddreses[1]);
}
void measure(){
float s1;
float s2;
digitalWrite(POWER_ENABLE_S1,HIGH);
digitalWrite(POWER_ENABLE_S2,LOW);
delay(25);
Sensor1.waitForBusy();
Sensor1.takeRange();
Sensor1.waitForBusy();
s1 = Sensor1.readDistance(detectedAddreses[0]);
digitalWrite(POWER_ENABLE_S1,LOW);
digitalWrite(POWER_ENABLE_S2,HIGH);
delay(25);
Sensor2.waitForBusy();
Sensor2.takeRange();
Sensor2.waitForBusy();
s2 = Sensor2.readDistance(detectedAddreses[1]);
Serial.println("Sensor 1: " + String(s1) + "; Sensor 2: " + String(s2));
}
void loop()
{
/*Serial.println(detectedAddreses[0]);
Serial.println(detectedAddreses[1]);*/
measure();
}

Arduino simple timed loop without delay() - millis() doesn't work?

Have some arduino code for temp loggers that is VERY NEARLY working....!
I've built an OTA routine so I can update them remotely, however the delay() loop I had to ensure it only logged temperatures every 15 mins is now causing problems as it effectively freezes the arduino by design for 15mins, meaning OTA wouldn't work whilst it is in this state.
Some suggestions say just to flip to millis() instead, but I can't seem to get this working and it's logging ~20 records every second at the moment.
Ideally I just want delay_counter counting up to the value in DELAY_TIME, then running the rest of the code and resetting the counter.
Can anyone help me and point out what I'm doing daft in my code???
// v2 Temp sensor
// Connecting to Home NAS
#include <DHT.h>
#include <DHT_U.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <InfluxDbClient.h>
#define SSID "xxx" //your network name
#define PASS "xxx" //your network password
#define VersionID "v3"
#define SensorName "ServerUnit" //name of sensor used for InfluxDB and Home Assistant
// Temp Sensor 1 - GardenTropical
// Temp Sensor 2 - GardenRoom
// Temp Sensor 3 - Greenhouse
// Temp Sensor 4 - OutsideGreenhouse
// Temp Sensor 5 - ServerUnit
// Connection Parameters for Jupiter InfluxDB
#define INFLUXDB_URL "http://192.168.1.5:8086"
#define INFLUXDB_DB_NAME "home_assistant"
#define INFLUXDB_USER "xxx"
#define INFLUXDB_PASSWORD "xxx"
// Single InfluxDB instance
InfluxDBClient client(INFLUXDB_URL, INFLUXDB_DB_NAME);
// Define data point with measurement name 'DaveTest`
Point sensor("BrynyneuaddSensors");
#define PORT 80
#define DHTPIN 4 // what pin the DHT sensor is connected to
#define DHTTYPE DHT22 // Change to DHT22 if that's what you have
#define BAUD_RATE 115200 //Another common value is 9600
#define DELAY_TIME 900000 //time in ms between posting data to Home Server
unsigned long delay_counter = 0;
DHT dht(DHTPIN, DHTTYPE);
//this runs once
void setup()
{
Serial.begin(BAUD_RATE);
// Connect to WIFI
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print("*");
}
// Initialise OTA Routine
ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
//initalize DHT sensor
dht.begin();
// set InfluxDB database connection parameters
client.setConnectionParamsV1(INFLUXDB_URL, INFLUXDB_DB_NAME, INFLUXDB_USER, INFLUXDB_PASSWORD);
// Add constant tags - only once
sensor.addTag("device", SensorName);
// Check server connection
if (client.validateConnection()) {
Serial.print("Connected to InfluxDB: ");
Serial.println(client.getServerUrl());
} else {
Serial.print("InfluxDB connection failed: ");
Serial.println(client.getLastErrorMessage());
Serial.println(client.getServerUrl());
Serial.println("Exiting DB Connection");
}
}
//this runs over and over
void loop() {
ArduinoOTA.handle();
float h = dht.readHumidity();
Serial.print("Humidity: ");
Serial.println(h);
// Read temperature as Fahrenheit (isFahrenheit = true)
float c = dht.readTemperature();
Serial.print("Temperature: ");
Serial.println(c);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(c)) {
Serial.println("Reading DHT22 Failed, exiting");
return;
}
//update Influx DB channel with new values
updateTemp(c, h);
Serial.print("Writing to InfluxDB: ");
//INFLUXDB - clear temp data so it doesn't repeat
sensor.clearFields();
// Update Influx DB
sensor.addField("Temperature", c);
sensor.addField("Humidity", h);
Serial.println(sensor.toLineProtocol());
// Write data
client.writePoint(sensor);
//wait for delay time before attempting to post again
if(millis() >= DELAY_TIME){
delay_counter += 0;
}
//Increment Delay Counter
delay_counter++;
}
bool updateTemp(float tempC, float humid) {
WiFiClient client; // Create a WiFiClient to for TCP connection
Serial.println("Receiving HTTP response");
while (client.available()) {
char ch = static_cast<char>(client.read());
Serial.print(ch);
}
Serial.println();
Serial.println("Closing TCP connection");
client.stop();
return true;
}
Set a TimerObject. this seems to be what you want.
Download the Arduino TimerObject code from github and follow the installation instructions
#include "TimerObject.h"
Create the callback function
Create the TimerObject
Setup the TimerObject and periodically call update() in your loop():
// make sure to include the header
#include "TimerObject.h"
...
// setup your TimerObject
TimerObject* sensor_timer = new TimerObject(15 * 60 * 1000); // milliseconds
...
// define the stuff you want to do every 15 minutes and
// stick it in a function
// not sure what from your loop() needs to go in here
void doSensor()
{
float h = dht.readHumidity();
Serial.print("Humidity: ");
Serial.println(h);
// Read temperature as Fahrenheit (isFahrenheit = true)
float c = dht.readTemperature();
Serial.print("Temperature: ");
Serial.println(c);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(c)) {
Serial.println("Reading DHT22 Failed, exiting");
return;
}
//update Influx DB channel with new values
updateTemp(c, h);
Serial.print("Writing to InfluxDB: ");
//INFLUXDB - clear temp data so it doesn't repeat
sensor.clearFields();
// Update Influx DB
sensor.addField("Temperature", c);
sensor.addField("Humidity", h);
Serial.println(sensor.toLineProtocol());
// Write data
client.writePoint(sensor);
}
...
// add the timer setup to your setup()
// probably at the end is a good place
void setup()
{
...
// lots of stuff above here
sensor_timer->setOnTimer(&doSensor);
sensor_timer->Start();
}
// modify your loop() to check the timer on every pass
void loop()
{
ArduinoOTA.handle();
sensor_timer->Update();
}
If you don't want to wait 15 minutes for the first call of doSensor, you can explicitly call it at the end of your setup() function before you start the timer.
Here is an simple example how to use millis()
int last_report = -777;//dummy value
int REPORT_INTERVAL = 15 *60 ; // 15 minutes
void loop() {
ArduinoOTA.handle();
int interval = millis() / 1000 - last_report;
if (interval < REPORT_INTERVAL) {
return;
}
last_report = millis() / 1000;
//do some important stuff
}
Make it simole and use easy code:
const unsigned long timeIntervall = 15*60*1000; // 15 minutes
unsigned long timeStamp = 0;
void setup(){....}
void loop() {
ArduinoOTA.handle(); // is running all the time
// Code in this section only runs every timeIntervall - rollover safe
if(millis() - timeStamp > timeIntervall ){
float h = dht.readHumidity();
......
// Write data
client.writePoint(sensor);
timeStamp = millis(); // reset the timer
}
}

Trying to use DHT11 with a PxMatrix display on ESP32 board

I'm trying to display the readings from a DHT11 onto an LED Matrix. I can get the basic display to work, the issue is when I also put the time on the display. I started with the Morphing Clock as a base for the time then used the Adafruit Sensor code to read the DHT11. The issue seems to be with"
timerAlarmWrite(timer, 2000, true);
Which is setup to call:
void IRAM_ATTR display_updater(){
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
display.display(10);
portEXIT_CRITICAL_ISR(&timerMux);
}
If I slow the timer down I can get readings from the DHT11 but the morphing time display doesn't update enough to look fluid. I'm new to coding for these devices so I'm not sure where I should be looking to move these things out of each others way. Here is the full app if the timer is set to something above 25000 you will get temp results most of the time, but the less are dimmer and the colons flash (they shouldn't).
#define double_buffer
#include <PxMatrix.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include "Digit.h"
#include <Adafruit_Sensor.h>
#include <DHT.h>
const char* ssid = "Gallifrey";
const char* password = "ThisIsAGoodPlaceToPutAPassword!";
// ESP32 Pins for LED MATRIX
#define P_LAT 22
#define P_A 19
#define P_B 23
#define P_C 18
#define P_D 5
#define P_E 15 // NOT USED for 1/16 scan
#define P_OE 2
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D,P_E);
void IRAM_ATTR display_updater(){
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
display.display(10);
portEXIT_CRITICAL_ISR(&timerMux);
}
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;
unsigned long prevEpoch;
byte prevhh;
byte prevmm;
byte prevss;
//====== Digits =======
Digit digit0(&display, 0, 63 - 1 - 9*1, 17, display.color565(0, 250, 0));
Digit digit1(&display, 0, 63 - 1 - 9*2, 17, display.color565(0, 250, 0));
Digit digit2(&display, 0, 63 - 4 - 9*3, 17, display.color565(0, 250, 0));
Digit digit3(&display, 0, 63 - 4 - 9*4, 17, display.color565(0, 250, 0));
Digit digit4(&display, 0, 63 - 7 - 9*5, 17, display.color565(0, 250, 0));
Digit digit5(&display, 0, 63 - 7 - 9*6, 17, display.color565(0, 250, 0));
#define DHTPIN 27
#define DHTTYPE DHT11
//DHT_Unified dht(DHTPIN, DHTTYPE);
DHT dht(DHTPIN, DHTTYPE);
//DHT dht;
const uint32_t delayMS = 6000;
uint32_t lastRead;
void setup() {
display.begin(16); // 1/16 scan
display.setFastUpdate(true);
// Initialize Serial Monitor
Serial.begin(115200);
pinMode(DHTPIN, INPUT_PULLUP);
dht.begin();
// // Set delay between sensor readings based on sensor details.
lastRead = 0;
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &display_updater, true);
timerAlarmWrite(timer, 1500, true); /// The Problem is Here!!!???!!!!?
timerAlarmEnable(timer);
display.fillScreen(display.color565(0, 0, 0));
digit1.DrawColon(display.color565(100, 175, 0));
digit3.DrawColon(display.color565(100, 175, 0));
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// Initialize a NTPClient to get time
timeClient.begin();
timeClient.setTimeOffset(-28800);
}
void loop() {
while(!timeClient.update()) {
timeClient.forceUpdate();
}
formattedDate = timeClient.getFormattedDate();
// Extract date
int splitT = formattedDate.indexOf("T");
dayStamp = formattedDate.substring(0, splitT);
// Extract time
timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
displayLocalTemp();
updateTimeDisplay();
}
String readDHTTemperature() {
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
// Read temperature as Celsius (the default)
float t = dht.readTemperature(true);
// Read temperature as Fahrenheit (isFahrenheit = true)
//float t = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again).
if (isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return "--";
}
else {
Serial.println(t);
return String(t);
}
}
String readDHTHumidity() {
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
if (isnan(h)) {
Serial.println("Failed to read from DHT sensor!");
return "--";
}
else {
Serial.println(h);
return String(h);
}
}
void displayLocalTemp() {
uint32_t currentTime = millis();
uint32_t waited = currentTime - lastRead;
static String lastTemp;
static String lastHumid;
if (waited > delayMS) {
lastRead = currentTime;
String temp = readDHTTemperature();
String humidity = readDHTHumidity();
String preTemp = "T:";
String preHumidity = "H:";
String tempDisplay = preTemp + temp;
String humidDisplay = preHumidity + humidity;
Serial.print("temp: ");
Serial.print(temp);
Serial.print(" -- humidity: ");
Serial.println(humidity);
display.setTextColor(display.color565(0,0,0));
display.setCursor(20,16);
display.print(lastTemp);
display.setCursor(20,25);
display.print(lastHumid);
display.setTextColor(display.color565(0,255,0));
display.setCursor(20,16);
display.print(tempDisplay);
display.setCursor(20,25);
display.print(humidDisplay);
lastTemp = tempDisplay;
lastHumid = humidDisplay;
}
}
void updateTimeDisplay() {
unsigned long epoch = timeClient.getEpochTime();
if (epoch != prevEpoch) {
int hh = timeClient.getHours();
int mm = timeClient.getMinutes();
int ss = timeClient.getSeconds();
if (hh > 12) hh = hh % 12;
if (prevEpoch == 0) { // If we didn't have a previous time. Just draw it without morphing.
digit0.Draw(ss % 10);
digit1.Draw(ss / 10);
digit2.Draw(mm % 10);
digit3.Draw(mm / 10);
digit4.Draw(hh % 10);
digit5.Draw(hh / 10);
}
else
{
// epoch changes every miliseconds, we only want to draw when digits actually change.
if (ss!=prevss) {
int s0 = ss % 10;
int s1 = ss / 10;
if (s0!=digit0.Value()) digit0.Morph(s0);
if (s1!=digit1.Value()) digit1.Morph(s1);
//ntpClient.PrintTime();
prevss = ss;
}
if (mm!=prevmm) {
int m0 = mm % 10;
int m1 = mm / 10;
if (m0!=digit2.Value()) digit2.Morph(m0);
if (m1!=digit3.Value()) digit3.Morph(m1);
prevmm = mm;
}
if (hh!=prevhh) {
int h0 = hh % 10;
int h1 = hh / 10;
if (h0!=digit4.Value()) digit4.Morph(h0);
if (h1!=digit5.Value()) digit5.Morph(h1);
prevhh = hh;
}
}
prevEpoch = epoch;
}
}
You could try to assign tasks explicitly to a core.
When you start playing with ESP32 multi core code execution be aware of the following issues:
Both the setup and the main loop functions execute with a priority of 1.
Arduino main loop runs on core 1.
The execution is pinned, so it’s not expected that the core will change during execution of the program
On FreeRTOS (the underlying OS), tasks have an assigned priority which the scheduler uses to decide which task will run.
High priority tasks ready to run will have preference over lower priority tasks, which means that as long as a higher priority task can run, a lower priority task will not have the CPU.
CAUTION shared resources like Serial might be potential issues. Due to two core tasks accessing uncoordinated the same hardware may lead to deadlocks and crashes
For implementation purposes, you need to take in consideration that FreeRTOS priorities are assigned from 0 to N, where lower numbers correspond to lower priorities. So, the lowest priority is 0.
First of all, declare a global variable that will contain the number of the core where the FreeRTOS task to launch will be pinned
static int taskCore = 0; // The core the task should run on
now create the assignment of a task to the core in Setup()
xTaskCreatePinnedToCore(
myCoreTask, /* Function to implement the task */
"myCoreTask", /* Name of the task */
10000, /* Stack size in words */
NULL, /* Task input parameter */
0, /* Priority of the task */
NULL, /* Task handle. */
taskCore); /* Core where the task should run */
Here is a test function which you call in loop()
void myCoreTask( void * pvParameters ){
while(true){
Serial.println("Task running on core ");
Serial.print(xPortGetCoreID());
// This is here to show that other tasks run
// NEVER use in production
delay(1000);
}
}
Hope this gives you an idea how to tackle your problem, read more here RTOS and here ESP32-IDF

Incoming values to serial port are sometime corrupted or missing

I have written simple bluetooth transmitter and receiver on two arduino nano v3 boards. Bluetooth modules are HM-10 connected into hardware serial ports. It works, but on receiver side I often receive corrupted values and many values are missing. Where is a problem:
I am beginner in arduino. If it is possible a need to explain deeply. Thanks.
Transmitter code:
const long waitingInterval = 20000;
unsigned long lastSend = micros();
void setup()
{
Serial.begin(19200);
Serial.println("Started");
}
bool delay() {
if(micros() >= lastSend + waitingInterval) {
lastSend = micros();
return true;
}
return false;
}
void loop()
{
if(delay()) {
String mil = String(millis());
String sendingText = mil + ";" + mil + ";" + mil + ".";
Serial.println(sendingText);
}
}
Output of transmitter serial monitor interface:
10548;10548;10548.
10568;10568;10568.
10589;10589;10589.
10609;10609;10609.
10629;10629;10629.
10649;10649;10649.
10670;10670;10670.
10690;10690;10690.
10711;10711;10711.
10730;10730;10730.
10750;10750;10750.
10771;10771;10771.
10791;10791;10791.
10812;10812;10812.
10831;10831;10831.
10852;10852;10852.
10872;10872;10872.
10893;10893;10893.
10913;10913;10913.
10933;10933;10933.
10953;10953;10953.
10974;10974;10974.
10994;10994;10994.
11014;11014;11014.
11034;11034;11034.
11055;11055;11055.
11075;11075;11075.
11096;11096;11096.
11115;11115;11115.
Receiver code:
void setup() {
Serial.begin(19200);
Serial.println("Started");
}
void loop() {
if(Serial.available()) {
String incomingData = String();
char incomingChar = Serial.read();
if(incomingChar == '.') {
incomingData = bufferString;
Serial.print(bufferString);
bufferString = String();
} else {
bufferString += String(incomingChar);
return;
}
}
Output of receiver serial monitor interface:
10548;10548;10548
10568;10568;10568
10589;10589;10589
10609;10609;10609
10629;10629;10629
106410771
10791;10791;10791
10812;10812;10812
10831;10831;10831
10852;10852;10852
10872;10872;10872
10893;10893;11034;11034;11034
11055;11055;11055
11075;11075;11075
11096;11096;11096
11115;11115;11115
One problem is simply calling Serial.available() simply returns the number of bytes available to be read in the buffer; it could be exactly the number of bytes you need, it could be less, or more. Because of this, you might read extra data, too little, or too much data. More so, in higher level transmission protocols sometimes after a device receives data it will send an ACK(acknowledgement) back to the sender, saying it is ready for more data.
Edit** It should also be noted that the comment talking about mutex's isn't correct. Mutexes are typically used to synchronize code across multiple threads of execution on the same device. The key is that they are a shared resource across the thread's heap space. This is NOT the case when using two different arduino devices; thus, even if you could use them, it would be useless.
For your code, I would suggest the following edits to the transmitter:
#define MIN_TIMEOUT 3
void recieveAck(){
bool validAck = false;
uint8_t timeout_cnt = 0x00;
while(timeout_cnt < MIN_TIMEOUT){
//Wait for receiving device to respond with two bytes then send next
char incomingBytes[2];
Serial.readBytes(incomingBytes, 0x02);
if(incomingBytes[0] == 0xBB && incomingBytes[1] == 0xCC)
break;
timeout_cnt++;
}
}
void loop()
{
if(delay()) {
String mil = String(millis());
String sendingText = mil + ";" + mil + ";" + mil + ".";
Serial.println(sendingText);
recieveAck();
}
}
And to the receiver:
#define NEXT_INC_SIZE 2 //Expects one byte at a time
void sendAck(){
char outData[2];
outData[0] = 0xBB;
outData[1] = 0xCC;
Serial.write(outData, 2); //Write the ack data
}
void loop() {
if(Serial.available() >= NEXT_INC_SIZE){
char incomingBytes[2];
Serial.readByte(incomingBytes, NEXT_INC_SIZE); //Read exactly how many bytes you need
//Do stuff with the data here....
sendAck();
}
}

Overcome Arduino memory limitations with multiple sensors

I have an Arduino Nano piggybacked on a ENC28j60 ethernet module. I have eight (8) DHT22 sensors (named A, B, C ... H ) and i want to write their temperature and humidity data to Pushingbox.
The program is working great with ONE sensor. So that's good. However when i un-remark (i.e. take out the //'s) for anything more than one sensor, yes even for ONE other sensor, it won't write anything at all.
The arduino IDE complier says:
Sketch uses 23824 bytes (77%) of program storage space. Maximum is 30720 bytes.
Global variables use 1870 bytes (91%) of dynamic memory, leaving 178 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.
FYI, If i un-remark just one other sensor it goes up +2% to 93% of dynamic memory and stops working. So i'm assuming its a memory problem. i have already removed all floats (and made integers *10 to keep one decimal place of accuracy), i need 8 instances of DHT so considered reducing the library size but the .h seems tiny and pretty lean already (within the .h file it even boasts: Very low memory footprint - Very small code. so i havent modified this or the .cpp.
The overall code i have written is not large, maybe there are some in-efficiencies there, but i can't see that it is going to make anything near the amount of memory saving needed for the next 7 sensors.
The full arduino code (written for all 8 sensors, with 'ghosted' bits for 4 sensors and in use only working for one sensor) is below:
#include "DHTesp.h"
//#include <SPI.h>
#include <UIPEthernet.h>
DHTesp dhtA;
//DHTesp dhtB;
//DHTesp dhtC;
//DHTesp dhtD;
//DHTesp dhtE;
//DHTesp dhtF;
//DHTesp dhtG;
//DHTesp dhtH;
const int ledPin = LED_BUILTIN;
int ledState = LOW;
int interval = 10; // this is the number of seconds between reads (120=2mins)
int numReads = 5; // Number of reads between posting to google docs.
int multFact = 10; // multiplication factor 10 = 1 dec. place 100 = 2 dec places
byte mac[] = {0xBE, 0xEF, 0xDE, 0xAD, 0xDE, 0xAD }; //Ethernet shield MAC. Andy's working {0xBE, 0xEF, 0xDE, 0xAD, 0xDE, 0xAD}
byte ip[] = { 192,168,1,12 }; // Arduino device IP address
char devid [] = "vCE3D036xxxxxxxx"; // Gsheets device ID from Pushingbox ('x's for privacy:)
char server[] = "api.pushingbox.com";
EthernetClient client;
void setup()
{
Serial.begin(9600);
Serial.println ("RESTART");
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Trying to connect...");
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
while(true);
}
else{
Serial.print("OK, connected. Ethernet ready. ");
// print the Ethernet board/shield's IP address:
Serial.print("IP address: ");
Serial.println(Ethernet.localIP());
}
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println();
//Serial.println("A-Stat\t\tA-Temp (C)\tA-humdid%\tA-HeatI (C)\tB-Stat\t\tB-Temp (C)\tA-humdid%\tB-HeatI (C)");
Serial.println("\t\t\t\tt-A\th-A\tt-B\th-B\tt-C\th-C\tt-D\th-D\tt-E\th-E\tt-F\th-F\tt-G\th-G\tt-H\th-H");
//hey dB for some reason the Ethernet sheild uses pin D2 :( and pins 10,11,12,13
// https://arduinodiy.wordpress.com/2013/04/07/connect-an-enc28j60-ethernet-module-to-an-arduino/
// assign data pins
dhtA.setup(3);
//dhtB.setup(4);
//dhtC.setup(5);
//dhtD.setup(6);
//dhtE.setup(7);
//dhtF.setup(8);
//dhtG.setup(9);
//dhtH.setup(10); //watchout! i think Ethernet uses this pin too?
pinMode(ledPin, OUTPUT);
//end of void setup
}
void loop()
{
int Ahumid = 0; int Atemp = 0;
int Bhumid = 0; int Btemp = 0;
int Chumid = 0; int Ctemp = 0;
int Dhumid = 0; int Dtemp = 0;
//int Ehumid = 0; int Etemp = 0;
//int Fhumid = 0; int Ftemp = 0;
//int Ghumid = 0; int Gtemp = 0;
//int Hhumid = 0; int Htemp = 0;
int j=0;
for (j = 1; j <= numReads ; j++ ) {
int p = 0;
for (p=1; p <= interval ; p++) {
delay (1000);
// swap the led state
if (ledState == LOW) { ledState = HIGH; } else { ledState = LOW; }
Serial.print (p); //show the seconds passing
Serial.print (",");
digitalWrite(ledPin, ledState);
}
Serial.print (" Reading");
Atemp += dhtA.getTemperature()*multFact; Ahumid += dhtA.getHumidity()*multFact;
//Btemp += dhtB.getTemperature()*multFact; Bhumid += dhtB.getHumidity()*multFact;
//Ctemp += dhtC.getTemperature()*multFact; Chumid += dhtC.getHumidity()*multFact;
//Dtemp += dhtD.getTemperature()*multFact; Dhumid += dhtD.getHumidity()*multFact;
// print the readings
//Serial.print(dhtA.getStatusString());
Serial.print("\t"); Serial.print(Atemp);
Serial.print("\t"); Serial.print(Ahumid);
Serial.print("\t"); Serial.print(Btemp);
Serial.print("\t"); Serial.print(Bhumid);
Serial.print("\t"); Serial.print(Ctemp);
Serial.print("\t"); Serial.print(Chumid);
Serial.print("\t"); Serial.print(Dtemp);
Serial.print("\t"); Serial.print(Dhumid);
Serial.println();
// and so here endeth 'j', the number of reads
}
Serial.print ("Avg...");
Atemp = Atemp/numReads; Ahumid = Ahumid/numReads;
Btemp = Btemp/numReads; Bhumid = Bhumid/numReads;
Ctemp = Ctemp/numReads; Chumid = Chumid/numReads;
Dtemp = Dtemp/numReads; Dhumid = Dhumid/numReads;
// print the averages so we can see what it is going to send
Serial.print("\t\t\t");
Serial.print("\t"); Serial.print(Atemp); Serial.print("\t"); Serial.print(Ahumid);
Serial.print("\t"); Serial.print(Btemp); Serial.print("\t"); Serial.print(Bhumid);
Serial.print("\t"); Serial.print(Ctemp); Serial.print("\t"); Serial.print(Chumid);
Serial.print("\t"); Serial.print(Dtemp); Serial.print("\t"); Serial.print(Dhumid);
Serial.println();
Serial.print ("Prep for upload... ");
if (client.connect(server, 80))
{
Serial.print("Connected OK ... writing...");
client.print("GET /pushingbox?devid=");
client.print(devid);
client.print("&tempA="); client.print(Atemp);
client.print("&tempB="); client.print(Btemp);
client.print("&tempC="); client.print(Ctemp);
client.print("&tempD="); client.print(Dtemp);
client.print("&tempE=29&tempF=39&tempG=49&tempH=59");
//now humidity too
client.print("&humidA="); client.print(Ahumid);
client.print("&humidB="); client.print(Bhumid);
client.print("&humidC="); client.print(Chumid);
client.print("&humidD="); client.print(Dhumid);
client.print("&humidE=26&humidF=27&humidG=28&humidH=29");
client.print("&submit=Submit");
client.println(" HTTP/1.1");
client.println("Host: api.pushingbox.com");
client.println("Connection: close");
client.println();
Serial.println("written OK. & connection closed.");
Serial.println(); //Serial.println();
delay(1000); // maybe take this out to keep time stable?
client.stop();
}
else {
Serial.println("** NO CONNEX **"); Serial.println();
}
//here endeth void loop
}
Extra info (that i dont think is relevant, but maybe): IDE compiler also reports:
WARNING: library DHT_sensor_library_for_ESP32 claims to run on [esp32] architecture(s) and may be incompatible with your current board which runs on [avr] architecture(s).
Use the F macro for double-quoted string literal prints. Instead of this:
Serial.println ("RESTART");
or
client.print("GET /pushingbox?devid=");
... do this:
Serial.println ( F("RESTART") );
or
client.print( F("GET /pushingbox?devid=") );
This will easily save a bunch o' RAM.
I would also suggest using the single quoted literal for single characters, not the double quote:
Serial.print( '\t' );