Demo fro training

Dependencies:   X_NUCLEO_IKS01A1 d7a_1x mbed-rtos mbed wizzi-utils

Revision:
0:429446fe396d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Nov 21 07:24:34 2016 +0000
@@ -0,0 +1,535 @@
+// 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;
+    }
+}
\ No newline at end of file