espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.89k stars 7.33k forks source link

Is it possible to use can in the bootloader hook to update our application ? (IDFGH-7223) #8817

Closed yadon95 closed 2 years ago

yadon95 commented 2 years ago

Environment

Problem

We are trying to use CAN bus as a way to update our application using it in a bootloader hook (the one after the 2nd stage bootloader) with the OTA data partition. We have started our project using the example bootloader_hook project. The Bootloader's documentation (here) exeplains that we can't use the drivers and other functionalities unless we manually add them. We have tried to add the driver component with the CMake file (REQUIRES driver esp_system freertos) but we still have unresolved includes for freertos/FreeRtos.h and esp_system. The second option described by the documentation is to manually add the needed files to the bootloader_components folder. The problem is that the can driver uses a lot of different imports scattered everywhere. We've also seen someone trying to achieve UART in the bootloader's hoot but using the HAL level instead of the driver level.

Any help would be appreciated, Cheers !!

Source list :

Test project

Bootloader's hook CMakeList.txt

idf_component_register(SRCS "hooks.c" REQUIRES hal freertos driver)

Bootloader's hook.c

#include "esp_log.h"
 #include "driver/can.h"
 #include "driver/gpio.h"

void bootloader_hooks_include(void){
}

void bootloader_before_init(void) {
    ESP_LOGI("HOOK", "This hook is called BEFORE bootloader initialization");
}

void bootloader_after_init(void) {
    ESP_LOGI("HOOK", "Hook bootloader");
    can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, CAN_MODE_NORMAL);
    can_timing_config_t t_config = CAN_TIMING_CONFIG_500KBITS();
    can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
}
igrr commented 2 years ago

Hi @yadon95, Indeed including peripheral drivers into the 2nd stage bootloader is unlikely to work due to dependencies, as you have found. Another factor is that the 2nd stage bootloader is not (safely) updatable. So it is not recommended to put any kind of complex functionality into it, which you might later want to fix.

I can suggest two alternative approaches:

yadon95 commented 2 years ago

Hi @igrr, Thanks for your help, I went for your approach. Currently, I have overwritten the bootloader using the example template to boot on the factory app after the power on restart, and an OTA app if it is a software restart. I've managed to use CAN to get data in. But I have trouble understanding how to properly use the basic OTA functions. I have explored the OTA examples, but they essentially focus on HTTP OTA updates.

So i came up with a list system containing each line. Each line contains 8 bytes of data. To write them to the OTA partition, I've tried to write them using esp_ota_write_with_offset in a for loop iterating through the line list. I've also added the esp_ota_handle_t handle with esp_ota_begin(ESP_PARTITION_SUBTYPE_APP_OTA_0, OTA_SIZE_UNKNOWN , &handle); but i have an error message : E (91406) esp_ota_ops: OTA handle not found.

So, is there more documentation or examples that could be useful regarding the OTA process, the partitions and the bin format for ota_0?

*Here is the code i'm using to write data to OTA ` esp_err_t install_update(line_list update){

esp_err_t error;
esp_ota_handle_t handle;
nvs_flash_init();
printf("starting installation...\n");

esp_partition_t partition;

partition.address   = ESP_PARTITION_SUBTYPE_APP_OTA_0;
partition.size      = ESP_PARTITION_TABLE_OFFSET;
partition.type      = ESP_PARTITION_TYPE_APP;

error = esp_ota_begin(&partition, OTA_SIZE_UNKNOWN , &handle);

printf("Installing update ...\n======================\nUpdate file :\n\n");
for(int i = 1; i < line_list_lenght(update); i++){
    debug_print_line_list(update);
    error = esp_ota_write_with_offset(handle, *(update -> line -> content), sizeof(update -> line -> content), i);
    update = remove_first_line(update);
}
printf("======================\n");
error = esp_ota_end(handle);
return error;

} `

_PS : contentis defined as uint8_t content[8];_

Cheers!

mahavirj commented 2 years ago

@yadon95

yadon95 commented 2 years ago

Hi @mahavirj

I have the following partition table :

Label            Usage          Type ST Offset   Length
nvs              WiFi data        01 02 00009000 00004000
otadata          OTA data         01 00 0000d000 00002000
phy_init         RF data          01 01 0000f000 00001000
factory          factory app      00 00 00010000 00100000
ota_0            OTA app          00 10 00110000 00100000
ota_1            OTA app          00 11 00210000 00100000

I've been trying stuff using the code snippet you gave me, and it seems like my esp_ota_begin() sets an handle (=1), but a null esp_err_t error is returned.

Here is the code i'm using :

// Trying to get ota_0
esp_partition_t * partition;
partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL);
ESP_LOGI("OTA DEBUG", "first ota partition found has subtype %d at offset 0x%x", partition ->subtype, partition -> address);

esp_ota_handle_t handle = 0;
error = esp_ota_begin(partition, OTA_SIZE_UNKNOWN, &handle);
if(error == NULL){
        printf("error returned from esp_ota_begin is NULL");
}

char write_buff[10] = {1,2,3,4,5,6,7,8,9,10};
esp_ota_handle_t handle = NULL;
error = esp_ota_begin(partition, OTA_SIZE_UNKNOWN, &handle);

if(handle == NULL){
printf("The handle returned by esp_ota_begin is null\n");
} else {
printf("The handle returned by esp_ota_begin isn't null, it has value : %d\n", handle);
}

error = esp_ota_write(&handle, *write_buff, sizeof(write_buff));

It returns :

I (378) OTA DEBUG: first ota partition found has subtype 16 at offset 0x110000
error returned from esp_ota_begin is NULL
The handle returned by esp_ota_begin isn't null, it has value : 1
E (4378) esp_ota_ops: not found the handle

Is it possible to write 10 known bytes to OTA and then read OTA to check if they are correclly wirten before writting a whole application ?

mahavirj commented 2 years ago

@yadon95

Please share your entire application and console log that you get from device. I not quite clear from logs that you posted here. It appears code and logs do not match.

Is it possible to write 10 known bytes to OTA and then read OTA to check if they are correclly wirten before writting a whole application ?

Yes. You may use esp_partition_read to read back contents and verify it against written data

yadon95 commented 2 years ago

Hi @mahavirj I figured how to make it work, it seems like i had a memry space issue so i created a special 1M partition, and the second was that i needed to erase the parition before writing to it. Thanks for your help.

rnd-ash commented 2 years ago

Just an idea, but I think you don't need a custom bootloader hook to update over CANBUS. I've been able to implement KWP2000 software update on my own TCM utilizing the ESP32. If you are interested, check my code out here:

https://github.com/rnd-ash/ultimate-nag52-fw/tree/can-flashing/src/diag

This works by writing directly to an OTA partition the new code. This also has an advantage that unlike a real ECU, you cannot brick it!

yadon95 commented 2 years ago

Yes, I don't use a hook anymore, and I think I'm using a kinda similar approach overwriting the 2nd stage bootloader to launch an "update installer" application in certain conditions to download, verify and install the new update to an OTA partition following @igrr 's idea,