ESP32 FreeRTOS запускает только 1 из 2 прерываний

#task #interrupt #esp32 #freertos

Вопрос:

Я создаю свет на лестнице с 2 датчиками движения вверху и кнопкой, которая включает последовательность светодиодных полос в зависимости от того, с какой стороны она обнаруживает движение. Я использую FreeRTOS и сделал 2 задачи и 2 прерывания, и они идентичны по сборке, но я могу заставить работать только ту, которая называется nside_interrupt. Это не проблема с пин-кодом, он обнаруживает cside, если я меняю пин-коды, поэтому я думаю, что проблема заключается в прерывании. Включен только код, вызывающий проблемы.

Любая помощь будет очень признательна =)

Основной код (tcp_server.c):

 #include <stdio.h>
#include <stdlib.h>

#include "led_strip.h"
#include "motion_detection.h"

#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "driver/gpio.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

#define PORT CONFIG_EXAMPLE_PORT
#define ESP_INR_FLAG_DEFAULT 0
#define MOTION_SENSOR_CSIDE_PIN 2
#define MOTION_SENSOR_NSIDE_PIN 4

int delay_sec = 5;
unsigned char mode_on_off_auto = 2; // 0
static const char *TAG = "Stairs project";

TaskHandle_t ISR = NULL;

void IRAM_ATTR mscside_isr_handler(void *arg)
{
    xTaskResumeFromISR(ISR);
}

void IRAM_ATTR msnside_isr_handler(void *arg)
{
    xTaskResumeFromISR(ISR);
}

void cside_interrupt(void *arg) {
    while(1) {
        vTaskSuspend(NULL);
        if (mode_on_off_auto == 2) {
            xTaskCreate(Active_Cside, "Active_Cside", 4096, (void*)delay_sec, 1, NULL);
        }
    }
}

void nside_interrupt(void *arg) {
    while(1) {
        vTaskSuspend(NULL);
        if (mode_on_off_auto == 2) {
            xTaskCreate(Active_Nside, "Active_Nside", 4096, (void*)delay_sec, 1, NULL);
        }
    }
}

void parse_message(char *message) {
    if (strstr(message, "Color") != NULL) {
        unsigned long color_RGB = strtol(message, NULL, 16);
        ESP_LOGI(TAG, "COLOR %ld", color_RGB);
        ls_set_color_RGB(color_RGB);
        if (mode_on_off_auto == 0) {
            ls_update_color(color_RGB);
        }
    }
    else if (strstr(message, "Steps") != NULL) {
        int steps = atoi(message);
        ESP_LOGI(TAG, "STEPS %d", steps);
        ls_set_steps(steps);
    }
    else if (strstr(message, "Leds") != NULL) {
        int leds = atoi(message);
        ESP_LOGI(TAG, "LEDS %d", leds);
        ls_set_leds(leds);
    } else if (strstr(message, "Cside") != NULL) {
        if (mode_on_off_auto == 2) {
            xTaskCreate(Active_Cside, "Active_Cside", 4096, (void*)delay_sec, 1, NULL);
        }
    } else if (strstr(message, "Nside") != NULL) {
        if (mode_on_off_auto == 2) {
            xTaskCreate(Active_Nside, "Active_Nside", 4096, (void*)delay_sec, 1, NULL);
        }
    } else if (strstr(message, "On") != NULL) {
        ls_update_color(ls_get_color_RGB());
        mode_on_off_auto = 0;
    } else if (strstr(message, "Off") != NULL) {
        ls_update_color(0);
        mode_on_off_auto = 1;
    } else if (strstr(message, "Auto") != NULL) {
        ls_update_color(0);
        mode_on_off_auto = 2;
    } else if (strstr(message, "Brightness") != NULL) {
        int brightness = atoi(message);
        ls_brightness(brightness);
    } else if (strstr(message, "Dim") != NULL) {
        int procent = atoi(message);
        ls_dim(procent);
    } else if (strstr(message, "Brighten") != NULL) {
        int procent = atoi(message);
        ls_brighten(procent);
    }
    else {
        ls_get_color_RGB();
        ESP_LOGI(TAG, "Unknown command: %s", message);
    }
}

void send_back_received(const int sock, int len, char *message) {
    // send() can return less bytes than supplied length.
    // Walk-around for robust implementation. 
    int loop_len = len;
    while (loop_len > 0) {
        int written = send(sock, message   (len - loop_len), loop_len, 0);
        if (written < 0) {
            ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
        }
        loop_len -= written;
    }
}

static void app_connection(const int sock)
{
    int len;
    char rx_buffer[128];

    do {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
        } else if (len == 0) {
            ESP_LOGW(TAG, "Connection closed");
        } else {
            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

            // Copy data to variable based on content
            parse_message(rx_buffer);

            send_back_received(sock, len, rx_buffer);
        }
    } while (len > 0);
}

static void tcp_server_task()
{
    char addr_str[128];
    int addr_family = AF_INET;
    int ip_protocol = 0;
    int opt = 1;
    struct sockaddr_in6 dest_addr;

    struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)amp;dest_addr;
    dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
    dest_addr_ip4->sin_family = AF_INET;
    dest_addr_ip4->sin_port = htons(PORT);
    ip_protocol = IPPROTO_IP;

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }

    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, amp;opt, sizeof(opt));

    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr *)amp;dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1) {

        ESP_LOGI(TAG, "Socket listening");

        struct sockaddr_in6 source_addr;
        uint addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)amp;source_addr, amp;addr_len);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        inet_ntoa_r(((struct sockaddr_in *)amp;source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
        
        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);

        app_connection(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    ESP_ERROR_CHECK(example_connect()); // Make wifi connection. examples/protocols/README.md

    gpio_setup();

    ls_setup(GPIO_NUM_0);
    xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
}

void gpio_setup(void) { // https://www.youtube.com/watch?v=VkCvKtRsunUamp;ab_channel=ControltheController
    gpio_pad_select_gpio(MOTION_SENSOR_CSIDE_PIN);
    gpio_pad_select_gpio(MOTION_SENSOR_NSIDE_PIN);
    // Set pin direction
    gpio_set_direction(MOTION_SENSOR_CSIDE_PIN, GPIO_MODE_INPUT);
    gpio_set_direction(MOTION_SENSOR_NSIDE_PIN, GPIO_MODE_INPUT);
    // Set interrupt on falling edge
    gpio_set_intr_type(MOTION_SENSOR_CSIDE_PIN, GPIO_INTR_NEGEDGE);
    gpio_set_intr_type(MOTION_SENSOR_NSIDE_PIN, GPIO_INTR_NEGEDGE);
    // Install default ISR-service 
    gpio_install_isr_service(ESP_INR_FLAG_DEFAULT);
    // Attach ISR-routines
    gpio_isr_handler_add(MOTION_SENSOR_CSIDE_PIN, mscside_isr_handler, NULL);
    gpio_isr_handler_add(MOTION_SENSOR_NSIDE_PIN, msnside_isr_handler, NULL);
    // Create and start task
    xTaskCreate( cside_interrupt, "cside_interrupt", 4096, NULL, 10, amp;ISR );
    xTaskCreate( nside_interrupt, "nside_interrupt", 4096, NULL, 10, amp;ISR );
}
 

Функции обнаружения движения (motion_detection.c)

 #include "motion_detection.h"

unsigned char Cflag = 0;
unsigned char Nflag = 0;
unsigned char rerun = 0;
unsigned int stepdelay = 200;

static const char *TAG = "Motion detection driver";

void Active_Cside(void * ptr) {
    int delay_sec = (int) ptr;
    unsigned long tempColor = ls_get_color_RGB();
    if (Nflag == 0 amp;amp; Cflag == 0) {
        ESP_LOGI(TAG, "Cside activated with rerun=%i", rerun);
        Cflag = 1;
        rerun = 0;
        // Call waterfall with 200 ms delay, primary and off color
        ls_walk_uc_side(stepdelay, tempColor, 0);
        for(int i = delay_sec*10; i > 0; i--) {
            // 100 ms delay
            vTaskDelay(100 / portTICK_PERIOD_MS);
            if (i % 10 == 0) {
            ESP_LOGI(TAG, "Time left: %i seconds", i/10);
            }
        }
        if (rerun == 0) {
        ls_walk_uc_side(200, 0, tempColor);
        Cflag = 0;      
        }
    }   
    // Check for detection at opposite side
    else if (Nflag == 1) {
        rerun = 0;
        ESP_LOGI(TAG, "Rerun value: %i", rerun);
        ESP_LOGI(TAG, "Nside deactivated");
        ls_walk_not_uc_side(200, 0, tempColor);
        Nflag = 0;
    }
    else { 
        rerun  ;
        ESP_LOGI(TAG, "Rerun value: %i", rerun);
        for(int i = delay_sec*10; i > 0; i--) {
            // 100 ms delay
            vTaskDelay(100 / portTICK_PERIOD_MS);
            if (i % 10 == 0) {
                ESP_LOGI(TAG, "Time left: %i seconds", i/10);
            }
        }
        rerun--;
        ESP_LOGI(TAG, "Rerun value: %i", rerun);
        if (rerun == 0) {
            ls_walk_uc_side(stepdelay, 0, tempColor);
            Cflag = 0;
        }
    }   
     
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    vTaskDelete(NULL); 
}


void Active_Nside (void * ptr) {
    int delay_sec = (int) ptr;
    unsigned long tempColor = ls_get_color_RGB();
    if (Nflag == 0 amp;amp; Cflag == 0) {
        ESP_LOGI(TAG, "Nside activated with rerun=%i", rerun);
        Nflag = 1;
        rerun = 0;
        // Call waterfall with 200 ms delay, primary and off color
        ls_walk_not_uc_side(200, tempColor, 0);
        for(int i = delay_sec*10; i > 0; i--) {
            // 100 ms delay
            vTaskDelay(100 / portTICK_PERIOD_MS);
            if (i % 10 == 0) {
                ESP_LOGI(TAG, "Time left: %i seconds", i/10);
            }
        }
        if (rerun == 0) {
        ls_walk_not_uc_side(200, 0, tempColor);
        Nflag = 0;
        }
    }
    // Check for detection at opposite side
    else if (Cflag == 1) {
        rerun = 0;
        ESP_LOGI(TAG, "Rerun value: %i", rerun);
        ESP_LOGI(TAG, "Cside deactivated");
        ls_walk_uc_side(200, 0, tempColor);
        Cflag = 0;

    }
    else {
        rerun  ;
        ESP_LOGI(TAG, "Rerun value: %i", rerun);
        for(int i = delay_sec*10; i > 0; i--) {
            // 100 ms delay
            vTaskDelay(100 / portTICK_PERIOD_MS);
            if (i % 10 == 0) {
                ESP_LOGI(TAG, "Time left: %i seconds", i/10);
            }
        }
        rerun--;
        ESP_LOGI(TAG, "Rerun value: %i", rerun);
        if (rerun == 0) {
            ls_walk_not_uc_side(200, 0, tempColor);
            Cflag = 0;
        }
    }
    
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    vTaskDelete(NULL);
}
 

Комментарии:

1. Использование xTaskSuspend/Resume(FromISR) в качестве механизма синхронизации не рекомендуется (я бы сказал, неправильно). Например, возобновление запущенной задачи ничего не делает, событие теряется. Также можно динамически создавать/удалять задачи, но это не лучший подход.