read and push
Dependencies: X_NUCLEO_IKS01A1 MLX90614 d7a_1x wizzi-utils
Fork of D7A_1x_demo_sensors_OS5 by
main.cpp
- Committer:
- shawe
- Date:
- 2017-02-27
- Revision:
- 16:ad7842fcd376
- Parent:
- 15:f8e5805c696c
File content as of revision 16:ad7842fcd376:
// This project is a demo of the DASH7 1.x stack // @autor: jeremie@wizzilab.com // @date: 2016-12-20 // // ----- 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" #include "MLX90614.h" #include "DevI2C.h" #if defined(TARGET_STM32L152RE) #define D7A_PIN_TX (D10) #define D7A_PIN_RX (D2) #define D7A_PIN_RTS (D13) #define D7A_PIN_CTS (D9) #define D7A_PIN_RESET (A3) #define DEBUG_LED (LED1) #define DEBUG_BUTTON (USER_BUTTON) #define SENSOR_I2C_SDA (D14) #define SENSOR_I2C_SCL (D15) #define VOLT_PIN (A0) #elif defined(TARGET_STM32L432KC) // ----------------------------------------------- // Hardware configuration for sh2050 // ----------------------------------------------- #define D7A_PIN_TX (D5) #define D7A_PIN_RX (D4) #define D7A_PIN_RTS (D11) #define D7A_PIN_CTS (D10) #define D7A_PIN_RESET (D12) #define DEBUG_LED (D13) // LED1 #define DEBUG_BUTTON (D9) #define SENSOR_I2C_SDA (D0) #define SENSOR_I2C_SCL (D1) // TODO Check which pin to use on the small board for voltage stuff #define VOLT_PIN (A0) #else #error "Please choose or add the right platform." #endif #define DGB_LED_BLINK_PERIOD (500) #define _TEM1_EN_ (1) #define _VOLTAGE_EN_ (1) #if (_VOLTAGE_EN_ == 1) AnalogIn volt_pin(VOLT_PIN); #endif MLX90614 *mlxSensor; Semaphore button_user(0); Semaphore thread_ready(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) { PRINT("Read file %d offset:%d size:%d\r\n", file_id, offset, size); uint32_t 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 = D7A_PIN_TX, .rx = D7A_PIN_RX, .rts = D7A_PIN_RTS, .cts = D7A_PIN_CTS, }; // 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; } // ----------------------------------------------- // Sensor Threads // ----------------------------------------------- typedef struct { // Number of data fields uint32_t nb_values; // Total size of data uint32_t data_size; // Read value function bool (*read_value)(int32_t*); // Last reported value int32_t* last_report_value; // Current measured value int32_t* current_value; // File ID of the sensor value file uint8_t value_file_id; // Sensor configuration file ID uint8_t cfg_file_id; // Sensor configuration context sensor_config_t cfg; } sensor_thread_ctx_t; sensor_thread_ctx_t* g_thread_ctx; // 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,\ .value_file_id = _vfid,\ .cfg_file_id = _cfid\ } __inline int32_t float2_to_int(float v) { return (int32_t)(v*100); } bool tem1_get_value(int32_t* buf) { #if (_TEM1_EN_ == 0) return simul_sensor_value(buf, 1, 1100, 3900); #elif (_TEM1_EN_ == 1) float ambient = mlxSensor->ambientTemp(); float object = mlxSensor->objectTemp(); PRINT("Got %f || %f\r\n", ambient, object); buf[0] = float2_to_int(object); buf[1] = float2_to_int(ambient); return false; #endif } SENSOR_THREAD_CTX(tem1, TEM1_VALUE_FILE_ID, TEM1_CFG_FILE_ID, tem1_get_value, 2); bool volt_get_value(int32_t* buf) { #if (_VOLTAGE_EN_ == 0) return simul_sensor_value(buf, 1, 0, 1000); #elif (_VOLTAGE_EN_ == 1) float voltage = volt_pin*1000; PRINT("Voltage value %f\r\n", voltage); buf[0] = float2_to_int(voltage); return false; #else return false; #endif } SENSOR_THREAD_CTX(volt, VOLT_VALUE_FILE_ID, VOLT_CFG_FILE_ID, volt_get_value, 1); void sensor_thread() { FPRINT("(id:0x%08x)\r\n", osThreadGetId()); // Get thread context sensor_thread_ctx_t* th_ctx = g_thread_ctx; d7a_msg_t** resp = NULL; uint32_t last_report_time = 0; // 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); thread_ready.release(); 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], 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 last_report_time = 0; break; } } // Update last report time last_report_time += th_ctx->cfg.period; // Wait for period Thread::wait(th_ctx->cfg.period); } } void file_modified_thread() { 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 VOLT_CFG_FILE_ID: fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(volt_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; default: break; } } } void button_user_thread() { 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_NO, // 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_NO, // 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() { // Start & initialize DBG_OPEN(DEBUG_LED); PRINT("\r\n--- Starting new run ---\r\n"); extern uint16_t const os_maxtaskrun; //IPRINT("Max user threads: %d\r\n", os_maxtaskrun-1-9); d7a_open(&shield_config, D7A_PIN_RESET, &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); 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 simul_update_param(divider); // Declare the simulation parameter file d7a_declare(SIMUL_FILE_ID, PERMANENT, RW_RW, sizeof(sensor_config_t), sizeof(sensor_config_t)); #if (_TEM1_EN_ == 1) // Open I2C and initialise the sensors DevI2C ext_i2c(SENSOR_I2C_SDA, SENSOR_I2C_SCL); ext_i2c.frequency(100000); mlxSensor = new MLX90614(&ext_i2c); #endif osStatus status; // Start sensors threads #define THREAD_START(_name) Thread _name##_th(osPriorityNormal, 1024, NULL);\ g_thread_ctx = &_name##_thread_ctx;\ status = _name##_th.start(sensor_thread);\ ASSERT(status == osOK, "Failed to start ##_name## thread (err: %d)\r\n", status);\ thread_ready.wait(); #if (_TEM1_EN_ >= 0) THREAD_START(tem1); #endif // _TEM1_EN_ #if (_VOLTAGE_EN_ >=0) THREAD_START(volt); #endif // File modified thread Thread fm_th(osPriorityNormal, 512, NULL); status = fm_th.start(file_modified_thread); ASSERT(status == osOK, "Failed to start fm thread (err: %d)\r\n", status); // For button #ifdef DEBUG_BUTTON DebouncedInterrupt user_interrupt(DEBUG_BUTTON); user_interrupt.attach(button_push_isr, IRQ_FALL, 500, true); Thread but_th(osPriorityNormal, 512, NULL); status = but_th.start(button_user_thread); ASSERT(status == osOK, "Failed to start but thread (err: %d)\r\n", status); #endif #ifdef DGB_LED_BLINK_PERIOD DigitalOut my_led(DEBUG_LED); #endif // Set main task to lowest priority osThreadSetPriority(osThreadGetId(), osPriorityIdle); while(true) { #ifdef DGB_LED_BLINK_PERIOD // Wait to avoid beeing stuck in loop Thread::wait(DGB_LED_BLINK_PERIOD); my_led = !my_led; #else Thread::wait(osWaitForever); #endif } }