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*)÷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; } }