Hello I've been struggling to attach 2 HW interrupts to specific cores.
I mean, i want one interrupt in core 0 and one interrupt in core 1.
The doc from the esp32 (dual core) says that each core has an isr so it should be possible to attach an interrupt on each core.
This should be done configuring the config task on each core. I have an example that i thought should work but when i check wich core is the interrupt i have both on the same. Depending on wich task i pinned to a core first both are in core 0 or core 1. Here is my code test: <<
So, it's impossible to attach an interrupt to each core in arduino right?
The things with interrupts in different cores:
one is way faster than the other and sometimes it will block the second interrupt to be analized.
One interrupt ticks the cadence (the fast tick one) and the second ticks the speed weel (slow).
I can get errors on speed due to the cadence ticking. (i see it on the real code).
I found a work around, since one of the core is only used for bluetooth comms. I just do a fast loop that checks the state of the pin (not using the interrupt) and since it's the speed one, everything looks ok. But is an ugly workaround....I was looking for clean code.
#include "sdkconfig.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void setup()
{
gpio_config_t io_cfg = {}; // initialize the gpio configuration structure
io_cfg.mode = GPIO_MODE_INPUT; // set gpio mode. GPIO_NUM_0 input from water level sensor
io_cfg.pull_down_en = GPIO_PULLDOWN_ENABLE; // enable pull down
io_cfg.pin_bit_mask = ( (1ULL << GPIO_NUM_0) | (1ULL << GPIO_NUM_18) ); //bit mask of the pins to set, assign gpio number to be configured
gpio_config(&io_cfg); // configure the gpio based upon the parameters as set in the configuration structure
//
io_cfg = {}; //set configuration structure back to default values
io_cfg.mode = GPIO_MODE_OUTPUT;
io_cfg.pin_bit_mask = ( (1ULL << GPIO_NUM_4) | (1ULL << GPIO_NUM_5) | (1ULL << GPIO_NUM_19) ); //bit mask of the pins to set, assign gpio number to be configured
gpio_config(&io_cfg);
gpio_set_level( GPIO_NUM_4, LOW); // deenergize relay module
gpio_set_level( GPIO_NUM_5, LOW); // deenergize valve
gpio_set_level( GPIO_NUM_19, LOW); // deenergize valve
}
the gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) might be of interest.
The ESP32 has 3 memory spaces for the main processor. Memory space is broken down into memory for core0 memory for core1 and shared memory. It's that shared memory I want my variables going into whenever possible. In those cases using as much of the ESP32's API as possible becomes the programing method I chose to use. Which includes using freeRTOS.
ALL global variables created under the Arduino IDE are stored on core1. The only thing you can do to change this is to use the ESP-IDF.
All variables made inside a task are stored on the core the task is assigned to.
Using a task on core0 when ble or wifi is in use will cause erratic behavior's.
don't use millis()! Its slow.
void fDoMoistureDetector1( void * parameter )
{
//wait for a mqtt connection
while ( !MQTTclient.connected() )
{
vTaskDelay( 250 );
}
int TimeToPublish = 5000000; //5000000uS
int TimeForADreading = 100 * 1000; // 100mS
uint64_t TimePastPublish = esp_timer_get_time(); // used by publish
uint64_t TimeADreading = esp_timer_get_time();
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = 10; //delay for 10mS
float RemainingMoisture = 100.0f; //prevents pump turn on during start up
bool pumpOn = false;
uint64_t PumpOnTime = esp_timer_get_time();
int PumpRunTime = 11000000;
uint64_t PumpOffWait = esp_timer_get_time();
uint64_t PumpOffWaitFor = 60000000; //one minute
float lowMoisture = 23.0f;
float highMoisture = 40.0f;
for (;;)
{
xSemaphoreTake( sema_WaterCactus, portMAX_DELAY );
//read AD values every 100mS.
if ( (esp_timer_get_time() - TimeADreading) >= TimeForADreading )
{
xEventGroupSetBits( eg, evtADCreading0 );
TimeADreading = esp_timer_get_time();
}
xQueueReceive(xQ_RM, &RemainingMoisture, 0 ); //receive queue stuff no waiting
//read gpio 0 is water level good. Yes: OK to run pump : no pump off. remaining moisture good, denergize water pump otherwise energize water pump.
if ( RemainingMoisture >= highMoisture )
{
WaterPump0_off();
}
if ( !pumpOn )
{
log_i( "not pump on ");
if ( gpio_get_level( GPIO_NUM_0 ) )
{
if ( RemainingMoisture <= lowMoisture )
{
//has one minute passed since last pump energize, if so then allow motor to run
if ( (esp_timer_get_time() - PumpOffWait) >= PumpOffWaitFor )
{
gpio_set_level( GPIO_NUM_5, HIGH); //open valve
WaterPump0_on();
log_i( "pump on " );
pumpOn = !pumpOn;
PumpOnTime = esp_timer_get_time();
}
}
//xSemaphoreGive( sema_RemainingMoisture );
} else {
log_i( "water level bad " );
WaterPump0_off();
gpio_set_level( GPIO_NUM_5, LOW); //denergize/close valve
PumpOffWait = esp_timer_get_time();
}
} else {
/*
pump goes on runs for X seconds then turn off, then wait PumpOffWaitTime before being allowed to energize again
*/
if ( (esp_timer_get_time() - PumpOnTime) >= PumpRunTime )
{
log_i( "pump off " );
WaterPump0_off(); // after 5 seconds turn pump off
gpio_set_level( GPIO_NUM_5, LOW); //denergize/close valve
pumpOn = !pumpOn;
PumpOffWait = esp_timer_get_time();
}
}
// publish to MQTT every 5000000uS
if ( (esp_timer_get_time() - TimePastPublish) >= TimeToPublish )
{
xQueueOverwrite( xQ_RemainingMoistureMQTT, (void *) &RemainingMoisture );// data for mqtt publish
TimePastPublish = esp_timer_get_time(); // get next publish time
}
xSemaphoreGive( sema_WaterCactus );
xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil( &xLastWakeTime, xFrequency );
}
vTaskDelete( NULL );
}// end fDoMoistureDetector1()
use esp_timer_get_time();. In the above task I use esp_timer_get_time(); as one would use millis(). A ESP32 millis() call is made to the esp_timer_get_time(); macro that cuts the return value down from a 64bit number to a 32 bit number. That cutting takes time. Also, esp_timer_get_time(); overflows in about 204+ years.
code snippets are poo! when trying to solve an issue like you present.
I also use for timing the xTaskGetTickCount(); which comes from the freeRTOS software timer. I know it may not be as accurate as a hardware timer but I do not need the accuracy and the xTaskGetTickCount(); is synced to freeRTOS operations.
trigger a timed task this way insures; the task will run on time over using vTaskDelay(x) and not require a hardware trigger.
IMPORTANT.
End all tasks this way
} //for or while ending
vTaskDelete( NULL );
}// end fDoMoist
when testing your tasks to get an idea of stack size to set, start with 10K. Then use this //log_i( "DoTheBME280Thing high watermark % d", uxTaskGetStackHighWaterMark( NULL ) ); to get an idea of how much the task is actually using. Then comment out the print. Oh, add 2000 more to the stack size that uxTaskGetStackHighWaterMark( NULL ) reports and you'll be fine.