Demo fro training

Dependencies:   X_NUCLEO_IKS01A1 d7a_1x mbed-rtos mbed wizzi-utils

main.cpp

Committer:
mikl_andre
Date:
2016-11-21
Revision:
0:429446fe396d

File content as of revision 0:429446fe396d:

// This project is a demo of the DASH7 1.x stack
// @autor: jeremie@wizzilab.com
// @date: 2016-08-23
//
// ----- SETUP -----
// This programm has been tested with the following hardware:
// --> NUCLEO-L152RE + DASH7 enabled SHIELD_SP1ML_v1.0
// For this demo to work, you need the previously described hardware.
// A DASH7 1.x gateway have to be available in your area in order to retreive the data.
//
// ----- FILE SYSTEM -----
// DASH7 is a file system based protocol. You read and write in files that are present on your device or your modem.
// Some callbacks can be implemented by the user for allowing the stack to acceed the local file system. (See description of the callbacks for more details)
// The file system is user defined and can be changed to fit your needs.
// This demo uses a simple RAM file system.
// The files are defined in files.h
// The files values are initialized in files.cpp
// 
// ----- SENSORS -----
// The sensors can be simulated (random data).
//
// ----- DEMO -----
// This demo starts 7 threads (one for each sensor value) that will periodically read the sensor value
// and depending on the sensor configuration file, will send the data to the DASH7 network (notification)
// You can also report the alarm status by pushing the board's button (blue)
// 
// ----- DEBUG -----
// Several debugging options are available in wizzi-utils/dbg/dbg.h
// An ASSERT is a fatal error. By default, this error will reboot the device.

#include "mbed.h"
#include "rtos.h"
#include "rtos_idle.h"
#include "d7a.h"
#include "sensors.h"
#include "simul.h"
#include "dbg.h"
#include "DebouncedInterrupt.h"
#include "files.h"

// Select the sensors
#if 0
    #define _MAG_EN_        1
    #define _ACC_EN_        1
    #define _GYR_EN_        1
    #define _PRE_EN_        1
    #define _HUM_EN_        1
    #define _TEM1_EN_       1
    #define _TEM2_EN_       1
#else
    #define _MAG_EN_        0
    #define _ACC_EN_        0
    #define _GYR_EN_        0
    #define _PRE_EN_        0
    #define _HUM_EN_        0
    #define _TEM1_EN_       0
    #define _TEM2_EN_       0
#endif

// Semaphore for notifiying button presses
Semaphore button_user(0);

// Interrupt Service Routine on button press.
void button_push_isr( void )
{
    button_user.release();
}

// file modified queue
Queue<void, 8> fm_queue;


// -----------------------------------------------
// callbacks
// -----------------------------------------------
/**
    Write data into a file.

    @param const uint8_t            File ID
    @param const uint16_t           Offset from start of data
    @param const uint16_t           Size of data to be written
    @param const uint8_t* const     Pointer to the data to write
    @return uint32_t                Size of written data
*/
uint32_t write_file_callback(const uint8_t file_id,
                             const uint16_t offset,
                             const uint16_t size,
                             const uint8_t* const content)
{
    uint32_t ret = 0;
    
    IPRINT("Write file %d offset:%d size:%d\r\n", file_id, offset, size);
    
    ret = fs_write_file(file_id, offset, size, content);

    WARNING(ret, "File %d not found\r\n", file_id);
    
    // Indicate that the file has been modified
    fm_queue.put((void*)file_id);
    
    return ret;
}

/**
    Read data from a file.

    @param const uint8_t            File ID
    @param const uint16_t           Offset from start of data
    @param const uint16_t           Size of data to be read
    @param const uint8_t* const     Pointer to the reading buffer
    @return uint32_t                Size of read data
*/
uint32_t read_file_callback(const uint8_t file_id,
                            const uint16_t offset,
                            const uint16_t size,
                            uint8_t* buf)
{
    uint32_t ret = 0;

    IPRINT("Read file %d offset:%d size:%d\r\n", file_id, offset, size);
    
    ret = fs_read_file(file_id, offset, size, buf);

    WARNING(ret, "File %d not found\r\n", file_id);
    
    return ret;
}

/**
    Called when a notification is finished

    @param const uint8_t            File ID
    @param const uint8_t            Error code
    @return void
*/
void notif_done_callback(const uint8_t file_id, const uint8_t error)
{
    PRINT("Notif FID %d done. err %d\r\n", file_id, error);
}

void unsolicited_callback(d7a_msg_t** uns)
{
    PRINT("UNSOLICITED MESSAGE:\r\n");
    d7a_print_msg(uns);
    d7a_free_msg(uns);
}

// callbacks structure
const d7a_callbacks_t callbacks = {
    .write_file = write_file_callback,   // If NULL you cannot declare files
    .read_file = read_file_callback,     // If NULL you cannot declare files
    .notif_done = notif_done_callback,
    .unsolicited_msg = unsolicited_callback,
};

// Com configuration for the DASH7 shield
const d7a_com_config_t shield_config = {
    .tx  = D10,
    .rx  = D2,
    .rts = D13,
    .cts = D9,
    .rx_buffer_size = 512,
};


// Check parameters to see if data should be send
static bool report_needed(sensor_config_t* cfg, int32_t value, int32_t last_value, uint32_t last_report_time)
{
    switch (cfg->report_type)
    {
        case REPORT_ALWAYS:
            // Send a report at each measure
            IPRINT("Notif cause 1\r\n");
            return true;
        case REPORT_ON_DIFFERENCE:
            // Send a report when the difference between the last reported measure and the current mesure is greater than max_diff
            if (abs(last_value - value) >= cfg->max_diff)
            {
                IPRINT("Notif cause 2 last:%d new:%d max_diff:%d\r\n", last_value, value, cfg->max_diff);
                return true;
            }
            break;
        case REPORT_ON_THRESHOLD:
            // Send a report when crossing a threshold
            if (   (value >= cfg->threshold_high && last_value < cfg->threshold_high)
                || (value <= cfg->threshold_low  && last_value > cfg->threshold_low)
                || (value < cfg->threshold_high  && last_value >= cfg->threshold_high)
                || (value > cfg->threshold_low   && last_value <= cfg->threshold_low))
            {
                IPRINT("Notif cause 3 last:%d new:%d th:%d tl:%d\r\n", last_value, value, cfg->threshold_high, cfg->threshold_low);
                return true;
            }
            break;
        default:
            break;
    }
    
    // Send a report if it's been more than max_period since the last report
    if (((last_report_time/1000) >= cfg->max_period) && (cfg->max_period != 0))
    {
        IPRINT("Notif cause 4 max_period:%d time:%d\r\n", cfg->max_period, last_report_time);
        return true;
    }

    return false;
}

// Initialisation of the sensor thread's context
#define SENSOR_THREAD_CTX(_name,_vfid,_cfid,_read_func,_nb_values) \
    int32_t _name##_last_report[_nb_values];\
    int32_t _name##_current_value[_nb_values];\
    sensor_thread_ctx_t _name##_thread_ctx = {\
        .nb_values = _nb_values,\
        .data_size = _nb_values * sizeof(int32_t),\
        .read_value = _read_func,\
        .last_report_value = (int32_t*)&_name##_last_report,\
        .current_value = (int32_t*)&_name##_current_value,\
        .last_report_time = 0,\
        .value_file_id = _vfid,\
        .cfg_file_id = _cfid\
    }


SENSOR_THREAD_CTX(mag,  MAG_VALUE_FILE_ID,  MAG_CFG_FILE_ID,  mag_get_value,  3);
SENSOR_THREAD_CTX(acc,  ACC_VALUE_FILE_ID,  ACC_CFG_FILE_ID,  acc_get_value,  3);
SENSOR_THREAD_CTX(gyr,  GYR_VALUE_FILE_ID,  GYR_CFG_FILE_ID,  gyr_get_value,  3);
SENSOR_THREAD_CTX(pre,  PRE_VALUE_FILE_ID,  PRE_CFG_FILE_ID,  pre_get_value,  1);
SENSOR_THREAD_CTX(hum,  HUM_VALUE_FILE_ID,  HUM_CFG_FILE_ID,  hum_get_value,  1);
SENSOR_THREAD_CTX(tem1, TEM1_VALUE_FILE_ID, TEM1_CFG_FILE_ID, tem1_get_value, 1);
SENSOR_THREAD_CTX(tem2, TEM2_VALUE_FILE_ID, TEM2_CFG_FILE_ID, tem2_get_value, 1);

// -----------------------------------------------
// Threads
// -----------------------------------------------
void sensor_thread(const void* p)
{
    FPRINT("(id:0x%08x)\r\n", osThreadGetId());
    d7a_msg_t** resp = NULL;
    
    // Get thread context
    sensor_thread_ctx_t* th_ctx = (sensor_thread_ctx_t*)p;
    
    // Force a first notification
    bool first_notif = true;
    
    // Retrieve notification config from local file
    fs_read_file(th_ctx->cfg_file_id, 0, sizeof(sensor_config_t), (uint8_t*)&(th_ctx->cfg));

    // Declare the config file to allow it to be accessed via the modem
    d7a_declare(th_ctx->cfg_file_id, VOLATILE, RW_RW, sizeof(sensor_config_t), sizeof(sensor_config_t));
    
    // Create a file on the modem to store and notify the sensor value
    d7a_create(th_ctx->value_file_id, VOLATILE, RW_R, th_ctx->data_size, th_ctx->data_size, D7A_NOTIFICATION_FULL, D7A_ITF_REPORT_CHECKED);

    while (true)
    {
        // Read the sensor values
        bool err = th_ctx->read_value(th_ctx->current_value);
        
        ASSERT(!err, "Failed to read sensor value for FID: %d\r\n", th_ctx->value_file_id);

        // Check if data should be send
        for (uint8_t i = 0; i < th_ctx->nb_values; i++)
        {
            if (report_needed(&(th_ctx->cfg),
                th_ctx->current_value[i],
                th_ctx->last_report_value[i],
                th_ctx->last_report_time) || first_notif)
            {
                first_notif = false;
                
                PRINT("NOTIFY %3d: ", th_ctx->value_file_id);
                for (uint8_t i = 0; i < th_ctx->nb_values; i++)
                {
                    PRINT("%9ld ", (int32_t)th_ctx->current_value[i]);   
                }
                PRINT("\r\n");

                // Send data to the modem
                resp = d7a_write(th_ctx->value_file_id, 0, th_ctx->data_size, (uint8_t*)th_ctx->current_value);
                d7a_free_msg(resp);
                
                // Update last report value
                memcpy(th_ctx->last_report_value, th_ctx->current_value, th_ctx->data_size);
                // Reset last report time
                th_ctx->last_report_time = 0;
                break;
            }
        }
        
        // Update last report time
        th_ctx->last_report_time += th_ctx->cfg.period;
        
        // Wait for period
        Thread::wait(th_ctx->cfg.period);
    }
}

void file_modified_thread(const void *p)
{    
    FPRINT("(id:0x%08x)\r\n", osThreadGetId());
    
    while (true)
    {
        // Wait for a file modified event
        osEvent evt = fm_queue.get();
        
        // Retrieve FID of modified file
        uint8_t fid = (uint8_t)(uint32_t)evt.value.p;
        
        PRINT("File %d has been modified\r\n", fid);
        
        switch (fid)
        {
            // If a configuration file has been modified, update the context
            case MAG_CFG_FILE_ID:
                fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(mag_thread_ctx.cfg));
                break;
            case ACC_CFG_FILE_ID:
                fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(acc_thread_ctx.cfg));
                break;
            case GYR_CFG_FILE_ID:
                fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(gyr_thread_ctx.cfg));
                break;
            case PRE_CFG_FILE_ID:
                fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(pre_thread_ctx.cfg));
                break;
            case HUM_CFG_FILE_ID:
                fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(hum_thread_ctx.cfg));
                break;
            case TEM1_CFG_FILE_ID:
                fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(tem1_thread_ctx.cfg));
                break;
            case TEM2_CFG_FILE_ID:
                fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(tem2_thread_ctx.cfg));
                break;
            case SIMUL_FILE_ID:
                uint32_t divider;
                // Retreive the simulation parameter from local file
                fs_read_file(SIMUL_FILE_ID, 0, sizeof(uint32_t), (uint8_t*)&divider);
                // Update the simulation parameters
                update_simul_param(divider);
                PRINT("Simulation Divider is Now %d\r\n", divider);
                break;
            default:
                break;
        }
    }
}

void button_user_thread(const void *p)
{
    FPRINT("(id:0x%08x)\r\n", osThreadGetId());
    
    uint8_t alarm = 255;
    d7a_msg_t** resp = NULL;

    // Create the alarm file
    //d7a_create(ALARM_FILE_ID, VOLATILE, RW_R, sizeof(uint8_t), sizeof(uint8_t), D7A_NOTIFICATION_FULL, D7A_ITF_SINGLE);
    
    // Update it with the default value
    //resp = d7a_write(ALARM_FILE_ID, 0, sizeof(uint8_t), (uint8_t*)&alarm);
    //d7a_free_msg(resp);
    
    while (true)
    {
        // Wait for button press
        button_user.wait();
        
        // Toggle alarm state
        alarm = !alarm;
        
        PRINT("BUTTON ALARM %d\r\n", alarm);
#if 1 // Switch between Notify and Distant Read/Write example

        // Send alarm state to the modem 
        //resp = d7a_write(ALARM_FILE_ID, 0, sizeof(uint8_t), (uint8_t*)&alarm);
        //WARNING(!resp[0]->err, "BUTTON ALARM ERR %d\r\n", resp[0]->err);
        //d7a_free_msg(resp);
#else
        // Distant device that I want to acceed
#if 0 // Unicast / Broadcast
        // Unicast
        d7a_addressee_t target = {
            // Access Class
            .xcl.byte = D7A_XCL_ENDPOINT_LO,
            // Type of security you want to use
            .ctrl.bf.nls = D7A_NLS_AES_CCM_64,
            // Type of ID
            .ctrl.bf.idf = D7A_ID_UID,
            // Device ID
            .id = { 0x00, 0x1B, 0xC5, 0x0C, 0x70, 0x00, 0x07, 0xA3 },
        };
#else    
        // Broadcast
        d7a_addressee_t target = {
            // Access Class
            .xcl.byte = D7A_XCL_ENDPOINT_LO,
            // Type of security you want to use
            .ctrl.bf.nls = D7A_NLS_AES_CCM_64,
            // Type of ID
            .ctrl.bf.idf = D7A_ID_NBID,
            // Maximum number of responses (1-32)
            .id[0] = D7A_NBID(8),
        };
#endif

#if 1 // Read / Write
        // Read example
        //resp = d7a_read(MAG_CFG_FILE_ID, 12, 5, &target, D7A_ITF_ONESHOT);
        resp = d7a_read(ALARM_FILE_ID, 0, 1, NULL, &target, D7A_ITF_ONESHOT);
#else
        // Write example
        uint8_t v = 0x01;
        resp = d7a_write(ALARM_FILE_ID, 0, 1, &v, NULL, &target, D7A_ITF_ONESHOT);
#endif
        // Check received response(s)
        d7a_print_msg(resp);
        
        PRINT("Resp done.\r\n");
        d7a_free_msg(resp);
#endif


    }
}


/*** Main function ------------------------------------------------------------- ***/
int main()
{
    PinName DBG_LED = D12;
        
    // Go to sleep when idle
    //rtos_attach_idle_hook(sleep);

    // Start & initialize
    DBG_OPEN(DBG_LED);
    PRINT("\r\n--- Starting new run ---\r\n");
    FPRINT("(id:0x%08x)\r\n", osThreadGetId());
    
    DigitalOut myled(DBG_LED);
    DebouncedInterrupt user_interrupt(USER_BUTTON);
    user_interrupt.attach(button_push_isr, IRQ_FALL, 200, true);
    myled = 1;
    
    extern uint16_t const os_maxtaskrun;
    //IPRINT("Max user threads: %d\r\n", os_maxtaskrun-1-9);
#if 0
    d7a_open(&shield_config, A3, &callbacks);
    d7a_modem_print_infos();
    
    // Create the revision file for the Dash7board
    d7a_create(65, PERMANENT, RW_R, sizeof(d7a_revision_t), sizeof(d7a_revision_t), D7A_NOTIFICATION_FULL, D7A_ITF_REPORT_CHECKED);
    // Notify revision
    d7a_msg_t** resp = d7a_write(65, 0, sizeof(d7a_revision_t), (uint8_t*)&f_dev_rev);
    d7a_free_msg(resp);
#endif

#if _SENSORS_SIMU_
    PRINT("(Simulated sensors)\r\n");
    uint32_t divider;
    
    // Retreive the simulation parameter from local file
    fs_read_file(SIMUL_FILE_ID, 0, sizeof(uint32_t), (uint8_t*)&divider);
    
    // Update the simulation parameters
    update_simul_param(divider);
    
    // Declare the simulation parameter file
    //d7a_declare(SIMUL_FILE_ID, PERMANENT, RW_RW, sizeof(sensor_config_t), sizeof(sensor_config_t));

#else
    // Open I2C and initialise the sensors
    DevI2C ext_i2c(D14, D15);
    
#if (_HUM_EN_ || _TEM1_EN_)
    humidity_sensor = new HTS221(ext_i2c);
    ASSERT(Init_HTS221(humidity_sensor), "Failed to init HTS221\r\n");
    temp_sensor1 = humidity_sensor;
#endif // _TEM_EN_
#if _MAG_EN_
    magnetometer = new LIS3MDL(ext_i2c);
    ASSERT(Init_LIS3MDL(magnetometer), "Failed to init LIS3MDL\r\n");
#endif // _MAG_EN_
#if (_ACC_EN_ || _GYR_EN_)
    accelerometer = new LSM6DS0(ext_i2c);
    ASSERT(Init_LSM6DS0(accelerometer), "Failed to init LSM6DS0\r\n");
    gyroscope = accelerometer;
#endif // _ACC_EN_ || _GYR_EN_
#if (_PRE_EN_ || _TEM2_EN_)
    pressure_sensor = new LPS25H(ext_i2c);
    ASSERT(Init_LPS25H(pressure_sensor), "Failed to init LPS25H\r\n");
    temp_sensor2 = pressure_sensor;
#endif // _PRE_EN_

#endif // _SENSORS_SIMU_

    // File modified thread
    Thread fm_th(file_modified_thread);

#if _MAG_EN_
    Thread mag_th(sensor_thread, (void*)&mag_thread_ctx, osPriorityNormal, DEFAULT_STACK_SIZE*2);
#endif // _MAG_EN_
#if _ACC_EN_
    Thread acc_th(sensor_thread, (void*)&acc_thread_ctx, osPriorityNormal, DEFAULT_STACK_SIZE*2);
#endif // _ACC_EN_
#if _GYR_EN_
    Thread gyr_th(sensor_thread, (void*)&gyr_thread_ctx, osPriorityNormal, DEFAULT_STACK_SIZE*2);
#endif // _GYR_EN_
#if _PRE_EN_
    Thread pre_th(sensor_thread, (void*)&pre_thread_ctx, osPriorityNormal, DEFAULT_STACK_SIZE*2);
#endif // _PRE_EN_
#if _HUM_EN_
    Thread hum_th(sensor_thread, (void*)&hum_thread_ctx, osPriorityNormal, DEFAULT_STACK_SIZE*2);
#endif // _HUM_EN_
#if _TEM1_EN_
    Thread tem1_th(sensor_thread, (void*)&tem1_thread_ctx, osPriorityNormal, DEFAULT_STACK_SIZE*2);
#endif // _TEM1_EN_
#if _TEM2_EN_
    Thread tem2_th(sensor_thread, (void*)&tem2_thread_ctx, osPriorityNormal, DEFAULT_STACK_SIZE*2);
#endif // _TEM2_EN_

    // For button irq
    Thread but_th(button_user_thread, NULL, osPriorityNormal, DEFAULT_STACK_SIZE*4);
    
    // Set main task to lowest priority
    osThreadSetPriority(osThreadGetId(), osPriorityIdle);
    while(true)
    {
        // Wait to avoid beeing stuck in loop
        Thread::wait(200);
        myled = !myled;
    }
}