Dash7Board Code Upgrade Protocol demonstration code.

Dependencies:   modem_ref_helper CRC

main.cpp

Committer:
Jeej
Date:
2019-08-20
Revision:
7:bfe920ee44f2
Parent:
6:1bdff7d6689c
Child:
8:6b7d38139b43

File content as of revision 7:bfe920ee44f2:

// @autor: jeremie@wizzilab.com
// @date: 2017-12-14

#include "modem_ref_helper.h"
#include "modem_callbacks.h"
#include "files.h"
#include "crc.h"

#define CHUNK_SIZE (128)

Semaphore modem_ready(0);
Queue<touch_t, 8> g_file_modified;

// CRC calculated on the incoming data stream
extern uint32_t stream_crc;
extern int32_t last_end;

const uint8_t default_root_key[] = DEFAULT_ROOT_KEY;

modem_callbacks_t callbacks = {
    .read       = my_read,
    .write      = my_write,
    .read_fprop = my_read_fprop,
    .flush      = my_flush,
    .remove     = my_delete,
    .udata      = my_udata,
    .lqual      = my_lqual,
    .ldown      = my_ldown,
    .reset      = my_reset,
    .boot       = my_boot,
    .busy       = my_busy
};

// Callback
void my_main_callback(uint8_t terminal, int8_t err, uint8_t id)
{
    (void)id;
    
    if (ALP_ERR_NONE != err)
    {
        modem_print_error(ALP_ITF_TYPE_D7A, err);
    }

    if (terminal)
    {
        modem_ready.release();
    }
}

void thread_file_modified()
{
    touch_t* touch;
    osEvent evt;
    Timer tim;
    uint32_t total = 0;
    uint32_t next_chunk = 0;
    uint32_t chunks=0;
    
    uint8_t id = modem_get_id(my_main_callback);
    
    PRINT("Ready\n");
    
    while (true)
    {
        evt = g_file_modified.get();
        touch = (evt.status == osEventMessage)? (touch_t*)evt.value.p : NULL;
        ASSERT(touch != NULL, "NULL touch pointer!\n");
        
        switch (touch->fid)
        {
            case FID_APP_CUP_CFG_BCAST:
                cup_cfg_bcast_header_t cup_cfg;
                
                ram_fs_read(FID_APP_CUP_CFG_BCAST, 0, sizeof(cup_cfg_bcast_header_t), (uint8_t*)&cup_cfg);
                        
                if (CUP_CMD_UPGRADE_FILE_START == cup_cfg.cmd)
                {
                    PRINT("Enter upload mode. Watchdog %ds\n", cup_cfg.to);
                    
                    tim.stop();
                    tim.reset();
                    total       = 0;
                    next_chunk  = 0;
                    stream_crc  = 0;
                    chunks      = 0;
                    
                    // Write to the modem CUP config file (as root)
                    // to enter upload mode
                    cup_cfg_t cup_start = {
                        .cmd = CUP_CMD_UPGRADE_UPLOAD,
                        .arch_nb = cup_cfg.to,
                        };
                    modem_write_file_root(FID_CUP_CFG, (uint8_t*)&cup_start, 0, 4, (uint8_t*)default_root_key, id);
                    modem_ready.acquire();
                }
                else if (CUP_CMD_UPGRADE_FILE_END == cup_cfg.cmd)
                {
                    float time_s = tim.read();
                    uint32_t expected_size = cup_cfg.len;
                    uint32_t expected_crc = cup_cfg.sig_new;
                
                    PRINT("\nDone. %d/%d bytes received in %d chunks in %.2f sec (%.2f kb/s)\r\n"
                          "Expected CRC 0x%08X. Stream CRC 0x%08X.\r\n",
                            total, expected_size, chunks, time_s, (double)(total/time_s)/1024.0,
                            expected_crc, stream_crc);
                    
                    tim.stop();
                    
                    // Compare expected crc with received data crc
                    if (stream_crc == expected_crc)
                    {
                        PRINT("Received complete binary.\n");
                    }
                    else
                    {
                        PRINT("Missed or corrupted data in this session.\n");
                    }
                    
                    
                    // Actual check is done here since we could have received some of the missed data in a previous session.
                    
                    /******************************/
                    /* Compute CRC on saved data. */
                    /******************************/

                    // If you have the full binary data,
                    // You can use this function to compute CRC
                    //binary_crc = crc32((char*)binary_data, expected_size);
                    
                    // Else just do a loop and a fast CRC on data chunks like in my_write
                }
                else
                {
                    PRINT("CUP command 0x%04X\n", cup_cfg.cmd);
                }
               
                break;
            case FID_APP_CUP_CODE:
                // Chunk data should be saved in my_write callback
                //PRINT("Got CUP code chunk length %d offset %d\n", touch->length, touch->offset);
                chunks++;
                
                if (!next_chunk)
                {
                    // First chunk, start timer
                    tim.start();
                    PRINT("Receiving CUP code chunks-> ");
                }
                
                if (next_chunk < touch->offset)
                {
                    PRINT("\nMissed %d chunk(s) at offset %d (got offset %d)\n", (touch->offset - next_chunk) / CHUNK_SIZE, next_chunk, touch->offset);
                    next_chunk = touch->offset + touch->length;
                    total += touch->length;
                }
                else if (next_chunk == touch->offset)
                {
                    next_chunk = touch->offset + touch->length;
                    total += touch->length;
                    PRINT(".");
                }
                else
                {
                     //PRINT("Repeat chunk(s) at offset %d\n", touch->offset);
                }
                
                // Notify modem code file to reset upload watchdog
                // else the device will exit upload mode after the watchdog timeout
                modem_notify_file(FID_CUP_CODE, 0, 1, id);
                modem_ready.acquire();
                
                break;
            default:
                PRINT("TOUCH FID %d OFF %d LEN %d\n", touch->fid, touch->offset, touch->length);
                break;
        }
        
        FREE(touch);
    }
}

/*** Main function ------------------------------------------------------------- ***/
int main() {
    // Start & initialize
#ifdef DEBUG_LED
    DBG_OPEN(DEBUG_LED);
#else
    DBG_OPEN(NC);
#endif
    PRINT("\n"
          "-----------------------------------------\n"
          "---------------- Demo CUP ---------------\n"
          "-----------------------------------------\n");
              
    modem_helper_open(&callbacks);
    
    PRINT("--------------- APP infos ---------------\r\n");
    PRINT(" - Manufacturer ID:  %08X\r\n", f_rev.manufacturer_id);
    PRINT(" - Device ID:        %08X\r\n", f_rev.device_id);
    PRINT(" - Hardware version: %08X\r\n", f_rev.hw_version);
    PRINT(" - Firmware version: v%d.%d.%d [%02X]\r\n", f_rev.fw_version.major, f_rev.fw_version.minor, f_rev.fw_version.patch, f_rev.fw_version.id);
    PRINT(" - CUP max size:     %d\r\n", f_rev.cup_max_size);
    PRINT("-----------------------------------------\r\n");
    
    uint8_t id = modem_get_id(my_main_callback);

    PRINT("Register Files\n");
    modem_update_file(FID_HOST_REV, (alp_file_header_t*)&h_rev, (uint8_t*)&f_rev);
    modem_update_file(FID_APP_CUP_CFG_BCAST, (alp_file_header_t*)&h_cup_cfg_bcast, (uint8_t*)&f_cup_cfg_bcast);
    
    // Declare the cup code file
    // It needs a special handling of its data in my_write callback since we want to keep them.
    // (can't be done with the current RAM file system)
    modem_declare_file(FID_APP_CUP_CODE, (alp_file_header_t*)&h_cup_code, true, id);
    modem_ready.acquire();
    
    PRINT("Setup URCs\n");
    // Busy URC to know if modem enters CUP mode
    modem_enable_urc(ALP_URC_TYPE_BUSY, 0, 0, true, id);
    modem_ready.acquire();
    
    PRINT("Start D7A Stack\n");
    modem_activate_itf(ALP_ITF_TYPE_D7A, 24, 0,  ALP_D7A_ISTAT_RESP | ALP_D7A_ISTAT_UNS | ALP_D7A_ISTAT_EOP, true, id);
    modem_ready.acquire();
    
    PRINT("Notify Modem Version\n");
    modem_notify_file(D7A_FID_FIRMWARE_VERSION, 0, SIZE_HOST_REV, id);
    modem_ready.acquire();
    
    PRINT("Notify Host Version\n");
    modem_notify_host_rev(&f_rev, &h_rev, (uint8_t*)default_root_key);
    
    // id no longer needed
    modem_free_id(id);
        
    // Start file modified thread
    Thread th_file_modified(osPriorityNormal, 1024, NULL);
    osStatus status = th_file_modified.start(thread_file_modified);
    ASSERT(status == osOK, "Failed to start thread_file_modified (err: %d)\r\n", status);

#ifdef DEBUG_LED
    DigitalOut my_led(DEBUG_LED);
#endif
    
    // Set main task to lowest priority
    osThreadSetPriority(osThreadGetId(), osPriorityLow);
    while(true)
    {
        ThisThread::sleep_for(500);
#ifdef DEBUG_LED
        my_led = !my_led;
#endif
    }
}