Demo fro training
Dependencies: X_NUCLEO_IKS01A1 d7a_1x mbed-rtos mbed wizzi-utils
Diff: main.cpp
- 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*)÷r); + // 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*)÷r); + + // 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