/* * hmi.c * * Created on: Feb 13, 2023 * Author: Sword */ #include #include "nvs_flash.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "freertos/semphr.h" #include "driver/gpio.h" #include "esp_log.h" #include "sdkconfig.h" #include "esp_timer.h" #include "uart_ifx.h" #include "esp_task_wdt.h" #include "modem.h" #include "comms.h" #include "port.h" #include "adc_ifx.h" #include "i2c_sensors.h" #include "data_processing.h" #include "hmi.h" static const char* TAG = "HMI"; #define LOG_LOCAL_LEVEL ESP_LOG_INFO #define HMI_BUF_SIZE 20 #define HMI_EVENTS_ITERATION_COUNTS 8 #define LED_SOLID_DURATION 300 #define OTA_UPDATE_DURATION 1000 // Duration for BLUE_FLASHING event when OTA session is happening #define BLUE_FLASHING_DURARION 500 // Duration for BLUE_FLASHING led_event #define RED_FLASHING_DURARION 1000 // Duration for RED_FLASHING led_event #define BLUE_RED_FLASHING_1SEC_DURARION 1000 // Duration for BLUE_RED_FLASHING led_event #define BLUE_RED_SOLID_CHARGED_DURARION 9700 // Duration for BLUE_RED_SOLID_CHARGED led_event #define BLUE_RED_FLASHING_CHARGED_DURARION 100 // Duration for BLUE_RED_FLASHING_CHARGING led_event #define BLUE_RED_FLASHING_200ms_DURARION 200 // Duration for BLUE_RED_FLASHING led_event #define NORMAL_HMI_DURATION 1000 // Duration for NORMAL_HMI cycle (cycle without triggered events) static QueueHandle_t hmi_queue; // FIFO queue that stores the leds event static bool usb_connection_state = false; // USB connection state flag (true--> connected, false--> disconnected) static bool continue_current_leds_state; // Led_event Continuing flag static bool pushbutton_state = false; // pushbutton state flag static bool blue_red_sync = false; // Synchronization flag when usb_charging is the current usb_led_event static bool device_failure_state = false; static bool device_not_onboarded_state = false; // ON-BOARDED flag when device data didn't get sent to server static bool ota_update_session = false; // OTA-Update flag to indicate that firmware update is happening right now static bool alternate_cycle_for_both = false; // A flag to make both LEDs blink in an alternating cycle static hmi_leds_state_t continued_leds_state; // Chosen led_event to be continued after 5 times of execution static hmi_leds_state_t leds_state = BLUE_RED_LEDS_OFF; // Led_event when usb is not connected static hmi_leds_state_t prev_leds_state = BLUE_RED_LEDS_OFF; // prev Led_event when usb is not connected static hmi_leds_state_t usb_leds_state = BLUE_RED_LEDS_OFF; // Led_event when usb is connected static uint8_t num_events_in_queue; // Number of Events in hmi_queue static uint16_t hmi_duration = NORMAL_HMI_DURATION; // Duration for each Led_event cycle static uint8_t fully_charged_counter; /************* STATIC FUNCTIONS ****************/ /* This function should be executed when the pushbutton is pressed (HIGHEST priority)*/ static void hmi_pushbutton_pressed_task(void) { /* 1- Execute the BLUE_LED_SOLID event*/ hmi_duration = LED_SOLID_DURATION; port_blue_led_on(); port_red_led_off(); /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ vTaskDelay(hmi_duration / portTICK_PERIOD_MS); /* 3- Check if the pushbutton is not pressed*/ if(port_is_pushbuttonNotPressed()) { /* If yes ---> reset pushbutton_state flag*/ pushbutton_state = false; } } /* This function should be executed when the device failure is occurred (second HIGHEST priority)*/ static void hmi_device_failure_task(void) { /* 1- Execute the RED_LED_SOLID event*/ hmi_duration = LED_SOLID_DURATION; port_blue_led_off(); port_red_led_on(); /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ vTaskDelay(hmi_duration / portTICK_PERIOD_MS); } /* This function should be executed when the device is not on-boarded yet (Third HIGHEST priority)*/ static void hmi_device_not_onboarded_task(void) { /* 1- Execute the RED_LED_FLASHING event*/ hmi_duration = RED_FLASHING_DURARION; port_blue_led_off(); port_red_led_toggle(); /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ vTaskDelay(hmi_duration / portTICK_PERIOD_MS); } /* This function should be executed when OTA Update is happening ( device_not_onboarded > priority > events_in_queue)*/ static void hmi_ota_firmware_update_task(void) { /* 1- Execute the RED_LED_FLASHING event*/ hmi_duration = OTA_UPDATE_DURATION; port_blue_led_toggle(); port_red_led_toggle(); alternate_cycle_for_both = true; /* 2- Delay duration is equal to BLUE_FLASHING_DURARION*/ vTaskDelay(hmi_duration / portTICK_PERIOD_MS); } /* This function should be executed when USB is not connected ( device_failure > priority > USB)*/ static void hmi_events_in_queue_task(void) { int counter = 0; /*Check if the current led_event shall be continued or not */ if((true == continue_current_leds_state) && (continued_leds_state == prev_leds_state)) { /*if true: the new led_state is the same as the old led state */ leds_state = prev_leds_state; } else { /*Extract the event from the queue (if exists)*/ xQueueReceive(hmi_queue, &leds_state, (TickType_t)100); /*Set the previous led_state*/ prev_leds_state = leds_state; /*Reduce number of led_events in queue by 1*/ num_events_in_queue--; /*Log*/ ESP_LOGI(TAG,"Extracting LED_EVENT = %d from HMI-QUEUE to execute it",leds_state); } /*Check if BLUE & RED LED should be synchronized*/ if((BLUE_RED_LEDS_FLASHING_200ms == leds_state) && (false == blue_red_sync)) { /*Turn off both leds for synchronization*/ port_red_led_off(); port_blue_led_off(); blue_red_sync = true; } /*Each event should occur 5 times to observe it clearly*/ for(counter = 0; counter < HMI_EVENTS_ITERATION_COUNTS; counter++) { /* 1- Evaluate the extracted event*/ switch(leds_state) { case BLUE_LED_FLASHING: //LTE_connecting hmi_duration = BLUE_FLASHING_DURARION; port_blue_led_toggle(); port_red_led_off(); break; case BLUE_LED_SOLID: //pushbutton_pressed break; case RED_LED_FLASHING: //Device has not been on-boarded break; case RED_LED_SOLID: //Device Failure. Need to reset break; case BLUE_RED_LEDS_FLASHING_200ms: //low battery warning hmi_duration = BLUE_RED_FLASHING_200ms_DURARION; port_red_led_toggle(); port_blue_led_toggle(); break; case BLUE_RED_LEDS_FLASHING_1SEC: //USB_PLUGGED_CHARGING case BLUE_RED_LEDS_SOLD: //USB_PLUGGED_CHARGED case BLUE_RED_LEDS_USB_OFF: case BLUE_LED_FLASHING_OTA: // OTA-SESSION is happening break; case BLUE_RED_LEDS_OFF: hmi_duration = NORMAL_HMI_DURATION; port_red_led_off(); port_blue_led_off(); break; } /* 2- Delay duration is dynamic according the extracted led_event*/ vTaskDelay(hmi_duration / portTICK_PERIOD_MS); /* 3- Check the pushbutton state and device failure state*/ if((true == pushbutton_state) || (true == device_failure_state)) { /*If yes then break the for-loop*/ break; } } /*Reset the Synchronization flag*/ blue_red_sync = false; } /* This function should be executed when USB is connected (LOWEST priority)*/ static void hmi_usb_connected_task(void) { /*Evaluate the extracted event*/ switch(usb_leds_state) { case BLUE_LED_FLASHING: case BLUE_LED_SOLID: case RED_LED_FLASHING: case RED_LED_SOLID: case BLUE_LED_FLASHING_OTA: case BLUE_RED_LEDS_FLASHING_200ms: break; /* Charging LED-event is violate toggling each one second (on, off, on, off and continue like this) */ case BLUE_RED_LEDS_FLASHING_1SEC: hmi_duration = BLUE_RED_FLASHING_1SEC_DURARION; port_red_led_toggle(); port_blue_led_toggle(); break; /* Charged LED-event is violate toggling 2 times each 100ms and then of for 9.7 second */ case BLUE_RED_LEDS_SOLD: if(fully_charged_counter < 3) { /* Increase the fully_charged_counter by 1 */ fully_charged_counter++; hmi_duration = BLUE_RED_FLASHING_CHARGED_DURARION; port_red_led_toggle(); port_blue_led_toggle(); } else { /* Reset the fully_charged_counter to 0 */ fully_charged_counter = 0; hmi_duration = BLUE_RED_SOLID_CHARGED_DURARION; port_red_led_off(); port_blue_led_off(); } break; case BLUE_RED_LEDS_USB_OFF: hmi_duration = BLUE_RED_FLASHING_1SEC_DURARION; port_red_led_off(); port_blue_led_off(); break; case BLUE_RED_LEDS_OFF: break; } vTaskDelay(hmi_duration / portTICK_PERIOD_MS); } /************ NON-STATIC FUNCTIONS *************/ /* This function is to initialize HMI service (Creating hmi_queu and turn off both leds)*/ void hmi_init(void) { /* 1- Create the HMI-QUEUE*/ hmi_queue = xQueueCreate(HMI_BUF_SIZE, sizeof(uint8_t)); /* 2- Set the hmi_duration to normal and turn off both leds*/ hmi_duration = NORMAL_HMI_DURATION; num_events_in_queue = 0; continue_current_leds_state = false; port_red_led_off(); port_blue_led_off(); ESP_LOGI(TAG,"HMI-Service has been initialized successfully"); } /* This function is used to set the state of USB-CONNECTION*/ void hmi_set_usb_connection_state(bool usb_state) { usb_connection_state = usb_state; } #if 0 void hmi_do_usb_event_inside_hmi(bool decision) { do_it_inside_hmi = decision; } void hmi_exception_usb_event(void) { /* Check if to do the USB event inside hmi-task or outside it (inside pausing-task) */ if(do_it_inside_hmi) { /* Check the USB-LED-event */ if(usb_leds_state == BLUE_RED_LEDS_FLASHING_1SEC) { /* Turn off both LEDs */ port_red_led_off(); port_blue_led_off(); /* Wait for about 300ms (to simulate the measurement time) */ vTaskDelay(300/portTICK_PERIOD_MS); /* Turn on both LEDs */ port_red_led_on(); port_blue_led_on(); /* Wait for about 333ms */ vTaskDelay(333/portTICK_PERIOD_MS); /* Turn off both LEDs */ port_red_led_off(); port_blue_led_off(); /* Wait for about 333ms */ vTaskDelay(333/portTICK_PERIOD_MS); /* Turn on both LEDs */ port_red_led_on(); port_blue_led_on(); /* Wait for about 333ms */ vTaskDelay(333/portTICK_PERIOD_MS); /* Turn off both LEDs */ port_red_led_off(); port_blue_led_off(); } else if(usb_leds_state == BLUE_RED_LEDS_SOLD) { /* Turn off both LEDs */ port_red_led_off(); port_blue_led_off(); /* Wait for about 300ms (to simulate the measurement time) */ vTaskDelay(300/portTICK_PERIOD_MS); /* Turn on both LEDs */ port_red_led_on(); port_blue_led_on(); /* Wait for about 1000ms */ vTaskDelay(1000/portTICK_PERIOD_MS); /* Turn off both LEDs */ port_red_led_off(); port_blue_led_off(); } } else { if(usb_leds_state == BLUE_RED_LEDS_FLASHING_1SEC) { port_red_led_on(); port_blue_led_on(); vTaskDelay(333/portTICK_PERIOD_MS); port_red_led_off(); port_blue_led_off(); vTaskDelay(333/portTICK_PERIOD_MS); port_red_led_on(); port_blue_led_on(); vTaskDelay(333/portTICK_PERIOD_MS); port_red_led_off(); port_blue_led_off(); } else if(usb_leds_state == BLUE_RED_LEDS_SOLD) { port_red_led_on(); port_blue_led_on(); vTaskDelay(1000/portTICK_PERIOD_MS); port_red_led_off(); port_blue_led_off(); } } } #endif /* This function is used to set which Led_event should be executed next time*/ void hmi_set_leds_state(hmi_leds_state_t status) { uint32_t batt_mv; float g_voltage ; /*Check if the led-event should be pushed to the hmi_queue*/ switch(status) { case BLUE_LED_FLASHING: //Should be pushed to hmi_queue /*Continue executing this led_event till it's stopped by hmi_stop_continued_led_state() function */ continue_current_leds_state = true; // RISE the led_event continuing_Flag continued_leds_state = BLUE_LED_FLASHING; // Set the flag to be continued to be BLUE_LED_FLASHING num_events_in_queue++; // increase the number of events in hmi_queue by 1 xQueueSend(hmi_queue, &status, 100); // Push the new led_state to the hmi_queue (if possible) blue_red_sync = false; // reset synchronization flag ESP_LOGI(TAG,"BLUE Flashing (continuing) has been added to hmi_queue"); break; case RED_LED_FLASHING: //Should not be pushed to hmi_queue(it has its own function --> hmi_device_not_onboarded_task()) device_not_onboarded_state = true; //Set the device_not_onboarded_state flag to be true blue_red_sync = false; //reset synchronization flag ESP_LOGI(TAG,"RED LED should Flashes now (if no higher-priority event is occurred)"); break; case BLUE_LED_SOLID: //Should not be pushed to hmi_queue(it has its own function --> hmi_pushbutton_pressed_task()) pushbutton_state = true; //Set the pushbutton_state flag to be true blue_red_sync = false; //reset synchronization flag break; case RED_LED_SOLID: //Should not be pushed to hmi_queue(it has its own function --> hmi_device_failure_task()) device_failure_state = true; //Set the device_failure_state flag to be true blue_red_sync = false; //reset synchronization flag break; case BLUE_RED_LEDS_OFF: //Should be pushed to hmi_queue xQueueSend(hmi_queue, &status, 100); // Push the new led_state to the hmi_queue (if possible) num_events_in_queue++; // increase the number of events in hmi_queue by 1 blue_red_sync = false; // reset synchronization flag break; case BLUE_RED_LEDS_FLASHING_200ms: num_events_in_queue++; // increase the number of events in hmi_queue by 1 xQueueSend(hmi_queue, &status, 100); // Push the new led_state to the hmi_queue (if possible) ESP_LOGI(TAG,"BLUE_RED_LED_FLASHING_200ms has been added to hmi_queue"); break; case BLUE_RED_LEDS_FLASHING_1SEC: //Should not be pushed to hmi_queue(it has its own function --> hmi_usb_connected_task()) /* Check if the USB is plugged and the battery is existed */ if((true == usb_connection_state)) { /*Update the current usb_leds_state*/ usb_leds_state = status; ESP_LOGI(TAG,"Voilate Flashing has been set"); } else { ESP_LOGI(TAG,"Voilate Flashing can't be set. Battery may not be connected"); } break; case BLUE_RED_LEDS_SOLD: //Should not be pushed to hmi_queue(it has its own function --> hmi_usb_connected_task()) /* Check if the USB is plugged and the battery is existed and its voltage level is fine */ if((true == usb_connection_state)) { /*Update the current usb_leds_state*/ usb_leds_state = status; /*reset synchronization flag*/ blue_red_sync = false; ESP_LOGI(TAG,"Voilate Solid has been set"); } else { ESP_LOGI(TAG,"Voilate Solid can't be set. Battery may not be connected or its volt. level is too low"); } break; case BLUE_LED_FLASHING_OTA: //Should not be pushed to hmi_queue(it has its own function --> hmi_ota_firmware_update_task()) /*reset synchronization flag*/ blue_red_sync = false; /* set ota_flag*/ ota_update_session = true; break; case BLUE_RED_LEDS_USB_OFF: /* Check if the USB is plugged and the battery is existed and its voltage level is fine */ if((true == usb_connection_state)) { /*Update the current usb_leds_state*/ usb_leds_state = status; /*reset synchronization flag*/ blue_red_sync = false; //ESP_LOGI(TAG,"Voilate Solid/Flashing has been stopped"); } } /*Exit the critical shared section between different tasks*/ //taskEXIT_CRITICAL(&hmi_mutex); } /* This function is used to stop the continuing led_event*/ void hmi_stop_continued_led_state(hmi_leds_state_t led_event) { /* Check if the led_event that should be stopped is the same as the current continuing led_event*/ if(continued_leds_state == led_event) { /*If yes then : */ /* 1- Disallow continuing the current running Led_event */ continue_current_leds_state = false; /* 2- Set the continued Led_event to be BLUE_RED_LEDS_OFF (and it won't executed because the continue flag is false)*/ continued_leds_state = BLUE_RED_LEDS_OFF; /*Log*/ ESP_LOGI(TAG,"Stopping the current continued LED_EVENT = %d",led_event); } } /* This function is used to stop red flashing*/ void hmi_stop_red_flashing(void) { device_not_onboarded_state = false; } /* This function is used to stop ota_LED-event */ void hmi_stop_ota_event(void) { ota_update_session = false; } bool leds_enabled = true; void set_leds_enable(bool status) { leds_enabled = status; if(status == false) { port_red_led_off(); port_blue_led_off(); } } /* This is the hmi_task that will be executed periodically*/ void hmi_task(void *pvParameters) { for(;;) { while(leds_enabled == false) { vTaskDelay(10 / portTICK_PERIOD_MS); port_red_led_off(); port_blue_led_off(); blue_red_sync = false; } if(true == pushbutton_state) // 1) Check if the pushbutton is pressed { /* reset the alternating flag */ alternate_cycle_for_both = false; /*If pushbutton is pressed then do the following task*/ hmi_pushbutton_pressed_task(); } else if(true == device_failure_state) // 2) Check if the device failure is occurred { /* reset the alternating flag */ alternate_cycle_for_both = false; /*If device failure is occurred then do the following task*/ hmi_device_failure_task(); } else if(true == device_not_onboarded_state) // 3) Check if the device failure is occurred { /* reset the alternating flag */ alternate_cycle_for_both = false; /*If device has not been onBoarded yet then do the following task*/ hmi_device_not_onboarded_task(); } else if(true == ota_update_session) // 4) Check if the OTA-Session is happening now { /*Check if the alternating flag is false*/ if(false == alternate_cycle_for_both) { /*Turn on the BLUE and turn off the RED*/ port_red_led_off(); port_blue_led_on(); alternate_cycle_for_both = true; } /*If device is doing OTA-Update session now then do the following task*/ hmi_ota_firmware_update_task(); } else { if((0 < num_events_in_queue) || (true == continue_current_leds_state)) // 5) Check if there are led_events in the hmi_queue { /* reset the alternating flag */ alternate_cycle_for_both = false; /*If there are led_events inside the hmi_queue then do the following task*/ hmi_events_in_queue_task(); } else { if(true == usb_connection_state) // 6) Check if the USB is plugged { /*Check if the synchronization flag is false*/ if(false == blue_red_sync) { /*Turn off both leds for synchronization*/ port_red_led_off(); port_blue_led_off(); blue_red_sync = true; } /*If USB is connected then do the following task*/ hmi_usb_connected_task(); } else { // If none of the previous conditions were true then turn off both leds hmi_duration = NORMAL_HMI_DURATION; port_red_led_off(); port_blue_led_off(); vTaskDelay(hmi_duration / portTICK_PERIOD_MS); } } } } vTaskDelete(NULL); }