Use variables with both cores and tasks ESP32 - c++

Im trying to read data from an i2c device, which is recieved by core 0, then that data is stored into some global values, and then those values are readed by the core 1, and then printed out. The problem is whenever the core 0 tries to access those variables, it outputs "guru meditation error core 0 panic'ed (loadprohibited). exception was unhandled". What is the way in which the 2 cores can communicate to one another?
TaskHandle_t Task1;
TaskHandle_t Task2;
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;
volatile int16_t adc0, adc1, adc2, adc3;
volatile float volts0, volts1, volts2, volts3;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Hello!");
Serial.println("Getting single-ended readings from AIN0..3");
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115");
//create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
//create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
2, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
delay(500);
if (!ads.begin()) {
Serial.println("Failed to initialize ADS.");
while (1);
}
}
void Task1code( void * pvParameters ){
for(;;){
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
adc0 = ads.readADC_SingleEnded(0);
adc1 = ads.readADC_SingleEnded(1);
adc2 = ads.readADC_SingleEnded(2);
adc3 = ads.readADC_SingleEnded(3);
volts0 = ads.computeVolts(adc0);
volts1 = ads.computeVolts(adc1);
volts2 = ads.computeVolts(adc2);
volts3 = ads.computeVolts(adc3);
delay(100);
}
}
void Task2code( void * pvParameters ){
delay(500);
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for(;;){}{
Serial.println("-----------------------------------------------------------");
Serial.print("AIN0: "); Serial.print(adc0); Serial.print(" "); Serial.print(volts0); Serial.println("V");
Serial.print("AIN1: "); Serial.print(adc1); Serial.print(" "); Serial.print(volts1); Serial.println("V");
Serial.print("AIN2: "); Serial.print(adc2); Serial.print(" "); Serial.print(volts2); Serial.println("V");
Serial.print("AIN3: "); Serial.print(adc3); Serial.print(" "); Serial.print(volts3); Serial.println("V");
delay(100);
}
}
void loop() {
}

You can create a semaphore and take it when try to access the variable. If you done accessing it, you can give it back. When you take a semaphore, other code blocks will wait for the other to give it back. You can configure the time it should wait for a given semaphore. Here is a (link) explaining in more details.
Here is an example:
SemaphoreHandle_t i2cSemaphore;
void createSemaphore(){
i2cSemaphore = xSemaphoreCreateMutex();
xSemaphoreGive( ( i2cSemaphore) );
}
// Lock the variable indefinietly. ( wait for it to be accessible )
void lockVariable(){
xSemaphoreTake(i2cSemaphore, portMAX_DELAY);
}
// give back the semaphore.
void unlockVariable(){
xSemaphoreGive(i2cSemaphore);
}
TaskHandle_t Task1;
TaskHandle_t Task2;
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;
volatile int16_t adc0, adc1, adc2, adc3;
volatile float volts0, volts1, volts2, volts3;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Hello!");
Serial.println("Getting single-ended readings from AIN0..3");
Serial.println("ADC Range: +/- 6.144V (1 bit = 3mV/ADS1015, 0.1875mV/ADS1115");
createSemaphore();
//create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
delay(500);
//create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
2, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
delay(500);
if (!ads.begin()) {
Serial.println("Failed to initialize ADS.");
while (1);
}
}
void Task1code( void * pvParameters ){
for(;;){
lockVariable();
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
adc0 = ads.readADC_SingleEnded(0);
adc1 = ads.readADC_SingleEnded(1);
adc2 = ads.readADC_SingleEnded(2);
adc3 = ads.readADC_SingleEnded(3);
volts0 = ads.computeVolts(adc0);
volts1 = ads.computeVolts(adc1);
volts2 = ads.computeVolts(adc2);
volts3 = ads.computeVolts(adc3);
unlockVariable();
vTaskDelay(100);
}
}
void Task2code( void * pvParameters ){
delay(500);
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for(;;){}{
lockVariable();
Serial.println("-----------------------------------------------------------");
Serial.print("AIN0: "); Serial.print(adc0); Serial.print(" "); Serial.print(volts0); Serial.println("V");
Serial.print("AIN1: "); Serial.print(adc1); Serial.print(" "); Serial.print(volts1); Serial.println("V");
Serial.print("AIN2: "); Serial.print(adc2); Serial.print(" "); Serial.print(volts2); Serial.println("V");
Serial.print("AIN3: "); Serial.print(adc3); Serial.print(" "); Serial.print(volts3); Serial.println("V");
unlockVariable();
vTaskDelay(100);
}
}
void loop() {
}

Related

How to read motor encoder values with interrupts in FreeRTOS?

I am working on a project where I need to obtain precise angular velocity from four motor encoders. I am using ESP32 DEVKIT-V1 module, and would like to use four interrupts, which will fire when each motor encoder switches state. This produces a square signal of around 700 Hz (period of 1,42 ms). This needs to be done on one core due to timing restrictions, as the processor must not miss any ticks. This is why I decided to use FreeRTOS. As the tick rate of the ESP32 is 1 ms, it cannot read higher frequencies than 500 Hz (period of 2 ms).
I would like to call getEncoderTickNumber() function every time one of the four interrupts fires, however, I only get the ESP32 to continually reset. I also wish to pass the number of ticks (encoderValueA1 - A4) from function getEncoderTickNumber() to getEncoderRPM() by queues.
I am still a beginner in C/C++, so I would be very grateful if you could point out some beginner mistakes that I am making. Thank you for your time.
#include <Arduino.h>
// Motor encoder output pulse per rotation (AndyMark Neverest 60)
int ENC_COUNT_REV = 420;
// Pulse count from encoder
long encoderValueA1 = 0;
long encoderValueA2 = 0;
long encoderValueA3 = 0;
long encoderValueA4 = 0;
int currentStateMotorEncoderA1;
int currentStateMotorEncoderA2;
int currentStateMotorEncoderA3;
int currentStateMotorEncoderA4;
int previousStateMotorEncoderA1;
int previousStateMotorEncoderA2;
int previousStateMotorEncoderA3;
int previousStateMotorEncoderA4;
// Variable for RPM measuerment
int rpm1 = 0;
int rpm2 = 0;
int rpm3 = 0;
int rpm4 = 0;
#define INT_PIN1 17
#define INT_PIN2 18
#define INT_PIN3 19
#define INT_PIN4 16
#define PRIORITY_LOW 0
#define PRIORITY_HIGH 1
QueueHandle_t encoderQueueHandle;
#define QUEUE_LENGTH 4 //four rpm readings
long* pdata = &encoderValueA1;
void io_expander_interrupt()
{
xQueueSendToBackFromISR(&encoderQueueHandle, &pdata, NULL);
}
///////////
// TASKS //
///////////
void getEncoderTickNumber(void *parameter)
{
while (1)
{
if (xQueueReceiveFromISR(&encoderQueueHandle, &pdata, NULL) == pdTRUE)
{
currentStateMotorEncoderA1 = digitalRead(INT_PIN1);
currentStateMotorEncoderA2 = digitalRead(INT_PIN2);
currentStateMotorEncoderA3 = digitalRead(INT_PIN3);
currentStateMotorEncoderA4 = digitalRead(INT_PIN4);
if (currentStateMotorEncoderA1 != previousStateMotorEncoderA1)
{
encoderValueA1++;
}
if (currentStateMotorEncoderA2 != previousStateMotorEncoderA2)
{
encoderValueA2++;
}
if (currentStateMotorEncoderA3 != previousStateMotorEncoderA3)
{
encoderValueA3++;
}
if (currentStateMotorEncoderA4 != previousStateMotorEncoderA4)
{
encoderValueA4++;
}
previousStateMotorEncoderA1 = currentStateMotorEncoderA1;
previousStateMotorEncoderA2 = currentStateMotorEncoderA2;
previousStateMotorEncoderA3 = currentStateMotorEncoderA3;
previousStateMotorEncoderA4 = currentStateMotorEncoderA4;
}
}
}
void getEncoderRPM(void *parameter)
{
while (1)
{
rpm1 = (encoderValueA1 * 60) / ENC_COUNT_REV;
rpm2 = (encoderValueA2 * 60) / ENC_COUNT_REV;
rpm3 = (encoderValueA3 * 60) / ENC_COUNT_REV;
rpm4 = (encoderValueA4 * 60) / ENC_COUNT_REV;
encoderValueA1 = 0;
encoderValueA2 = 0;
encoderValueA3 = 0;
encoderValueA4 = 0;
vTaskDelay(1000 / portTICK_RATE_MS);
}
}
void printData(void *parameter)
{
while (1)
{
Serial.print("1:");
Serial.print(rpm1);
Serial.print(" 2:");
Serial.print(rpm2);
Serial.print(" 3:");
Serial.print(rpm3);
Serial.print(" 4:");
Serial.println(rpm4);
vTaskDelay(500 / portTICK_RATE_MS);
}
}
void setup()
{
Serial.begin(115200);
pinMode(INT_PIN1, INPUT);
attachInterrupt(INT_PIN1, getEncoderTickNumber, RISING);
pinMode(INT_PIN2, INPUT);
attachInterrupt(INT_PIN2, getEncoderTickNumber, RISING);
pinMode(INT_PIN3, INPUT);
attachInterrupt(INT_PIN3, getEncoderTickNumber, RISING);
pinMode(INT_PIN4, INPUT);
attachInterrupt(INT_PIN4, getEncoderTickNumber, RISING);
// Create the queue
encoderQueueHandle = xQueueCreate(QUEUE_LENGTH, sizeof(uint32_t));
xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS
getEncoderTickNumber, // Function to be called
"getEncoderTickNumber", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS) inside the heap
NULL, // Parameter to pass to function
PRIORITY_LOW, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
1); // Run on one core for demo purposes (ESP32 only)
xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS
printData, // Function to be called
"printData", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS) inside the heap
NULL, // Parameter to pass to function
PRIORITY_LOW, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
0); // Run on one core for demo purposes (ESP32 only)
xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS
getEncoderRPM, // Function to be called
"getEncoderRPM", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
PRIORITY_HIGH, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
0); // Run on one core for demo purposes (ESP32 only)
vTaskDelete(NULL); // Deletes the setup/loop task now that we are finished setting up (optional)
}
void loop()
{
}
There are quite a few problems in your code. Let's go over them one by one, see if it clears things up.
Firstly, don't delete the task in setup():
vTaskDelete(NULL); // Deletes the setup/loop task now that we are finished setting up (optional)
Arduino will manage the FreeRTOS tasks on its own, don't interfere with it. You may be causing your crash with that line alone.
Secondly, you're creating your tasks with a stack size of 1024 bytes which is too small. The task will likely corrupt the stack and crash. Start with a stack size of 4096 bytes for simple tasks, see if you can optimize later. Incidentally, you don't need any tasks at all for a simple implementation.
Thirdly, you don't seem to understand what an interrupt is and how to handle it. By calling this you're attaching the function getEncoderTickNumber() as an interrupt handler to all 4 GPIO inputs:
attachInterrupt(INT_PIN1, getEncoderTickNumber, RISING);
attachInterrupt(INT_PIN2, getEncoderTickNumber, RISING);
attachInterrupt(INT_PIN3, getEncoderTickNumber, RISING);
attachInterrupt(INT_PIN4, getEncoderTickNumber, RISING);
The function getEncoderTickNumber() cannot be the interrupt handler because it blocks with a while(1) loop - it will quickly trigger the watchdog and reboot. Additionally, you've already used this function as a task which runs in the background (and seems to expect input from the interrupt handlers).
Finally, you seem have a more suitable candidate for the position of an interrupt handler - the function io_expander_interrupt() - which currently doesn't do anything useful. Let's fix that.
You would need 4 interrupt handlers, one per each GPIO you're monitoring. Each handler is attached to its respective GPIO pin, triggers when the IO rises and each does its own encoder calculation. A simple implementation without extra tasks would look like this:
#include <Arduino.h>
// Motor encoder output pulse per rotation (AndyMark Neverest 60)
int ENC_COUNT_REV = 420;
// Pulse count from encoder. Must be volatile as it's shared between ISR and main task
volatile int encoderValueA1 = 0;
volatile int encoderValueA2 = 0;
volatile int encoderValueA3 = 0;
volatile int encoderValueA4 = 0;
#define INT_PIN1 17
#define INT_PIN2 18
#define INT_PIN3 19
#define INT_PIN4 16
void isr_rising_gpio1() {
encoderValueA1++
}
void isr_rising_gpio2() {
encoderValueA2++
}
void isr_rising_gpio3() {
encoderValueA3++
}
void isr_rising_gpio4() {
encoderValueA4++
}
void setup()
{
Serial.begin(115200);
pinMode(INT_PIN1, INPUT);
attachInterrupt(INT_PIN1, isr_rising_gpio1, RISING);
pinMode(INT_PIN2, INPUT);
attachInterrupt(INT_PIN2, isr_rising_gpio2, RISING);
pinMode(INT_PIN3, INPUT);
attachInterrupt(INT_PIN3, isr_rising_gpio3, RISING);
pinMode(INT_PIN4, INPUT);
attachInterrupt(INT_PIN4, isr_rising_gpio4, RISING);
}
void loop()
{
int rpm1 = (encoderValueA1 * 60) / ENC_COUNT_REV;
encoderValueA1 = 0;
int rpm2 = (encoderValueA2 * 60) / ENC_COUNT_REV;
encoderValueA2 = 0;
int rpm3 = (encoderValueA3 * 60) / ENC_COUNT_REV;
encoderValueA3 = 0;
int rpm4 = (encoderValueA4 * 60) / ENC_COUNT_REV;
encoderValueA4 = 0;
Serial.print("1:");
Serial.print(rpm1);
Serial.print(" 2:");
Serial.print(rpm2);
Serial.print(" 3:");
Serial.print(rpm3);
Serial.print(" 4:");
Serial.println(rpm4);
vTaskDelay(pdMS_TO_TICKS(1000));
}

Create an array at different memory locations for each loop

Good morning everyone,
I am currently working on a data acquisition project, where I have to read sensors (at around 10 kHz) and transmit the data via Wi-Fi and the MQTT-protocol. I am using an ESP32 for both of these tasks.
One core is doing the sensor reading and the other core does the transmitting stuff. I also use the FreeRTOS for this.
Now, I want to pass the data as efficient as possible between the task. Currently I'm using the xQueue function built in the FreeRtos. I pass pointers in the Queue which point to an array, where one datapackage is stored.
Task one:
*sensor reading*
for(xx)
{
data_array[x] = sensor_data;
}
if {packageSize == 120}
{
xQueueSend(Queue1, &data_pointer, 0);
}
________________________
Task two:
if( uxQueueMessagesWaiting(Queue1) >= 1)
{
xQueueReceive(Queue1, &received_pointer, 0);
memcpy(data_send, received_pointer, packageSize);
* MQTT-Client sending data_send *
}
You see, my problem isn't the creation of the array with different pointers. The sensor reading task needs to create an array for every package, without overwritting the previous one.
My initial idea was to use the new and delete combination but it gave me strange results.
Is there any way I can change the location of the array on the memory at every loop of task one?
EDIT:
/* general variables*/
const int len = 150;
uint8_t data_received[len];
uint8_t data_send[len];
uint8_t *queue_pointer = 0;
uint8_t *received_pointer = 0;
uint8_t *to_delete_pointer = 0;
uint8_t dummy_data = 0;
int v = 0;
/* multithreading variables */
TaskHandle_t SPI_COM;
TaskHandle_t WIFI;
QueueHandle_t buffer_daten;
/* --------------------- Fake-SPI-Kommunikation auf Core 1 -------------------- */
void SPI_COM_code(void *pvParameters)
{
for (;;)
{
while (v <= 10000)
{
//queue_pointer = new int[len]; // creates a new array
queue_pointer = data_received;
queue_pointer[dummy_data] = dummy_data;
dummy_data++;
delayMicroseconds(100); // Dummy-Interrupt
if (dummy_data == len - 1)
{
dummy_data = 0;
xQueueSend(buffer_daten, &queue_pointer, 0);
v++;
}
}
}
}
/* --------------------- WiFi-Übertragung auf Core 0 --------------------- */
void WIFI_code(void *pvParameters)
{
for (;;)
{
//MQTT_connect();
if (uxQueueMessagesWaiting(buffer_daten) > 0)
{
xQueueReceive(buffer_daten, &received_pointer, 0);
to_delete_pointer = received_pointer;
memcpy(data_send, received_pointer, len);
// Data gets published by MQTT-Client
delayMicroseconds(12);
//delete[] to_delete_pointer; // deletes array, which was send
}
}
}
/* ----------------------------------- Setup ---------------------------------- */
void setup()
{
disableCore0WDT(); // <----- MÖGLICHE PROBLEMQUELLE
Serial.begin(115200);
buffer_daten = xQueueCreate(1000, sizeof(int));
xTaskCreatePinnedToCore(
SPI_COM_code, /* Task function. */
"SPI_COM", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&SPI_COM, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
delay(500);
xTaskCreatePinnedToCore(
WIFI_code, /* Task function. */
"WIFI", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
2, /* priority of the task */
&WIFI, /* Task handle to keep track of created task */
0); /* pin task to core 1 */
delay(500);
}
void loop()
{
}
I would suggest you use a RTOS Message Buffers for this task
With this functions you could copy your array into the buffer and the second task could get it, when the data is available.
In both cases the consumer task should use the timeout '0' to request the data.
If the MQTT task is faster than the data acquisition (and it should be or your buffers will overflow sooner or later) this will lead to invalid pointers:
xQueueReceive(buffer_daten, &received_pointer, 0);
If the is no data available the function will return immediately giving you an invalid received_pointer.
You should either check the return value of xQueueReceive or set the timeout to portMAX_DELAY.

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

Entering multiple SPI interfaces

I am having a problem with my code for arduino m0 (using microchip SAMD21). There are two SPI interfaces, first classic and second with int variable in front of the pin name, int MISO, for instance. Does someone know, how to control this classic SPI interface?
I have also attached my code.
PS: Code stucks in begin function of OZONE2CLICK sensor...
#include "Arduino.h"
#include <MQ131.h>
// include RFM69 library
#include <SPI.h>
// Local
#define PC_BAUDRATE 56700
#define MS_DELAY 0 // Number of milliseconds between data sending and LED signalization
#define LED_DELAY 100
#define Serial SerialUSB
// SD card
#define sd_cs_pin 35 // set SD's chip select pin (according to the circuit)
float PPMO2;
float PPBO2;
float MGM3O2;
float UGM3O2;
const byte pinSS = 2; //cs pin
const byte pinRDY = 12;
const byte pinSCK = 13;
const byte O2Pin = 10;
#define DcPin 8
// SD card file
File file; // SD library variable
// LEDS
#define D13_led_pin 42 // D13 LED
#define M_led_pin 36 // MLED
// Local variables
int idCounter = 1;
bool isBmeOk = true;
bool isSdOk = true;
bool isRadioOk = true;
bool isGpsConnected = true;
void OZONE2CLICKCalibrate ()
{
Serial.println("2");
//MQ131.begin(pinSS, pinRDY, O2Pin, LOW_CONCENTRATION, 10000); //(int _pinCS, int _pinRDY, int _pinPower, MQ131Model _model, int _RL)
Serial.println("99");
Serial.println("Calibration in progress...");
MQ131.calibrate();
Serial.println("Calibration done!");
Serial.print("R0 = ");
Serial.print(MQ131.getR0());
Serial.println(" Ohms");
Serial.print("Time to heat = ");
Serial.print(MQ131.getTimeToRead());
Serial.println(" s");
}
void OZONE2CLICKMeasure ()
{
Serial.println("Sampling...");
MQ131.sample();
Serial.print("Concentration O3 : ");
PPMO2 = MQ131.getO3(PPM);
Serial.print(PPMO2);
Serial.println(" ppm");
Serial.print("Concentration O3 : ");
PPBO2 = MQ131.getO3(PPB);
Serial.print(PPBO2);
Serial.println(" ppb");
Serial.print("Concentration O3 : ");
MGM3O2 = MQ131.getO3(MG_M3);
Serial.print(MGM3O2);
Serial.println(" mg/m3");
Serial.print("Concentration O3 : ");
UGM3O2 = MQ131.getO3(UG_M3);
Serial.print(UGM3O2);
Serial.println(" ug/m3");
}
void setup()
{
Serial.begin(PC_BAUDRATE);
// wait for the Arduino serial (on your PC) to connect
// please, open the Arduino serial console (right top corner)
// note that the port may change after uploading the sketch
// COMMENT OUT FOR USAGE WITHOUT A PC!
// while(!Serial);
Serial.println("openCanSat PRO");
Serial.print("Node ");
Serial.print(MYNODEID,DEC);
Serial.println(" ready");
// begin communication with the BME280 on the previously specified address
// print an error to the serial in case the sensor is not found
if (!bme.begin(BME280_ADDRESS_OPEN_CANSAT))
{
isBmeOk = false;
Serial.println("Could not find a valid BME280 sensor, check wiring!");
return;
}
// begin communication with the INA219
ina219.begin();
// check of Gps is connected
Wire.beginTransmission(0x42); // 42 is addres of GPS
int error = Wire.endTransmission();
if (error != 0)
{
isGpsConnected = false;
}
// begin communication with gps
gps.begin();
// Uncomment when you want to see debug prints from GPS library
// gps.debugPrintOn(57600);
if(!radio.initialize(FREQUENCY, MYNODEID, NETWORKID))
{
isRadioOk = false;
Serial.println("RFM69HW initialization failed!");
}
else
{
radio.setFrequency(FREQUENCYSPECIFIC);
radio.setHighPower(true); // Always use this for RFM69HW
}
pinMode(D13_led_pin, OUTPUT);
}
void loop()
{
pinMode(SS, OUTPUT);
digitalWrite(SS, HIGH);
pinMode(DcPin, OUTPUT);
pinMode(O2Pin, OUTPUT);
digitalWrite(DcPin, HIGH);
digitalWrite(O2Pin, HIGH);
delay(10000);
OZONE2CLICKCalibrate();
OZONE2CLICKMeasure();
}
It looks the code opening the SPI connection is commented out:
MQ131.begin(pinSS, pinRDY, O2Pin, LOW_CONCENTRATION, 10000);
You need to configure the SPI connection to get any data from your device.
Refer to reference code from the manufacturer or library you're using to make sure your programming against it correctly.
Please format your code with predictable spacing. This is pretty hard to read.
Since you're using C++, prefer to use:
constexpr <type> NAME = <value>;
rather than macros:
#define NAME (<value>)
Since this is a bare metal compilation, using return in the setup() or loop() functions does not stop them. You probably want something more like while (true) {}. This will loop the code indefinitely, rather than proceed in a bad state.
i.e.:
void stop_forever() {
Serial.println("fatal error detected, stoping forever.");
while (true) {}
}
// then, use it later:
// ...
if (error) {
stop_forever();
}
// ...