Complete sensor demo.
Dependencies: modem_ref_helper CRC X_NUCLEO_IKS01A1 DebouncedInterrupt
Diff: main.cpp
- Revision:
- 0:87c57e1b1e1c
- Child:
- 1:4d3968b2941b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon May 15 16:00:25 2017 +0000 @@ -0,0 +1,596 @@ +// This project is a demo of the DASH7 1.x stack +// @autor: jeremie@wizzilab.com +// @date: 2016-12-20 + +#include "mbed.h" +#include "rtos.h" +#include "sensors.h" +#include "WizziDebug.h" +#include "WizziCom.h" +#include "DebouncedInterrupt.h" + +#include "files.h" +#include "ram_fs.h" +#include "hwcfg.h" +#include "modem_callbacks.h" +#include "simul.h" + +#include "revision.h" +#include "alp_spec.h" +#include "alp_helpers.h" +#include "modem_ref.h" +#include "d7a_1x.h" +#include "d7a_1x_fs.h" +#include "alp.h" + + +WizziCom* g_modem_com; +Semaphore button_user(0); +Semaphore modem_ready[MAX_USER_NB]; +Semaphore thread_ready(0); +sensor_config_t g_light_config; +Queue<void, 8> g_file_modified; + +TYPEDEF_STRUCT_PACKED { + uint8_t type; + d7a_sp_cfg_t cfg; +} alp_d7a_itf_t; + +#define D7A_CTF_VAL(mant,exp) ((uint8_t)(mant|(exp<<5))) +#define ALP_ITF_TYPE_D7A 0xD7 +#define MY_D7_ITF_SIZE(_itf) (1+my_alp_itf_d7a_cfg_size(&(_itf)->cfg)) +int my_alp_itf_d7a_cfg_size(d7a_sp_cfg_t* cfg) +{ + int size = sizeof(d7a_sp_cfg_t) - sizeof(d7a_addressee_t); + size += D7A_ADDR_LEN(cfg->addressee.ctrl); + return size; +} + +// Check parameters to see if data should be send +static bool report_needed(sensor_config_t* config, int32_t value, int32_t last_value, uint32_t last_report_time) +{ + switch (config->report_type) + { + case REPORT_ALWAYS: + // Send a report at each measure + IPRINT("Report always\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) >= config->max_diff && config->max_diff) + { + IPRINT("Report on difference (last:%d new:%d max_diff:%d)\r\n", last_value, value, config->max_diff); + return true; + } + break; + case REPORT_ON_THRESHOLD: + // Send a report when crossing a threshold + if ( (value >= config->threshold_high && last_value < config->threshold_high) + || (value <= config->threshold_low && last_value > config->threshold_low) + || (value < config->threshold_high && last_value >= config->threshold_high) + || (value > config->threshold_low && last_value <= config->threshold_low)) + { + IPRINT("Rerport on threshold (last:%d new:%d th:%d tl:%d)\r\n", last_value, value, config->threshold_high, config->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) >= config->max_period) && config->max_period) + { + IPRINT("Report on period (max_period:%d time:%d)\r\n", config->max_period, last_report_time); + return true; + } + + return false; +} + +// Interrupt Service Routine on button press. +void button_push_isr( void ) +{ + button_user.release(); +} + +void my_get_alp_file_props(uint8_t fid, alp_file_header_t* hdr) +{ + memcpy(hdr, ram_fs_get_header(fid), sizeof(alp_file_header_t)); +} + +void print_status(int status) +{ + switch (status) + { + case ALP_ERR_NONE: + PRINT("Status: OK\n"); + break; + case ALP_ERR_FILE_EXIST: + PRINT("Status: Already registered\n"); + break; + default: + PRINT("Status: error %d\n", status); + break; + } +} + +void print_resp(int status) +{ + switch (status) + { + case ALP_ERR_NONE: + PRINT("Resp: OK\n"); + break; + case ALP_ERR_FILE_EXIST: + PRINT("Resp: Already registered\n"); + break; + default: + PRINT("Resp: error %d\n", status); + break; + } +} + +// ============================================================}}} + +// Serial adapters to WizziLab's own architecture +// ============================================================{{{ + +void my_serial_input(WizziCom* com, WizziComPacket_t* pkt) +{ + modem_input(wizzicom_type_to_flow(pkt->type), pkt->data, pkt->length); + FREE(pkt); +} + +int my_serial_send(uint8_t* data1, uint8_t size1, uint8_t* data2, uint8_t size2) +{ + (void)size1; + + // Retrieve Flow ID from header and send packet + g_modem_com->send((WizziComPacketType)wizzicom_flow_to_type(data1[4]), size2, data2); + + return (size1 + size2); +} + +modem_callbacks_t callbacks = { + .read = my_read, + .write = my_write, + .read_fprop = my_read_fprop, + .flush = my_flush, + .remove = my_delete, + .lqual = my_lqual, + .ldown = my_ldown, + .reset = my_reset, + .boot = my_boot +}; + +// Callback for main_id User +void my_main_callback(uint8_t terminal, int8_t err, uint8_t id) +{ + if (terminal) + { + print_status(err); + modem_ready[id].release(); + } + else + { + print_resp(err); + } +} + +// ----------------------------------------------- +// 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 config_file_id; + // Sensor configuration context + sensor_config_t config; + +} sensor_thread_ctx_t; + +sensor_thread_ctx_t* g_thread_ctx; + +// Initialisation of the sensor thread's context +#define SENSOR_THREAD_CTX(name,NAME,_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 = name##_get_value,\ + .last_report_value = (int32_t*)&name##_last_report,\ + .current_value = (int32_t*)&name##_current_value,\ + .value_file_id = FID_SENSOR_VALUE_##NAME,\ + .config_file_id = FID_SENSOR_CONFIG_##NAME\ + } + + +SENSOR_THREAD_CTX(mag, MAG, 3); +SENSOR_THREAD_CTX(acc, ACC, 3); +SENSOR_THREAD_CTX(gyr, GYR, 3); +SENSOR_THREAD_CTX(pre, PRE, 1); +SENSOR_THREAD_CTX(hum, HUM, 1); +SENSOR_THREAD_CTX(tem1, TEM1, 1); +SENSOR_THREAD_CTX(tem2, TEM2, 1); +SENSOR_THREAD_CTX(light, LIGHT, 1); + + sensor_thread_ctx_t* ctx = g_thread_ctx; + + +void thread_sensor() +{ + FPRINT("(id:0x%08x)\r\n", osThreadGetId()); + + // To force a first report + uint32_t last_report_time = 0xFFFFFFFF; + sensor_thread_ctx_t* ctx = g_thread_ctx; + uint8_t user_id = modem_get_id(my_main_callback); + + // Get the sensor configuration + ram_fs_read(ctx->config_file_id, 0, sizeof(sensor_config_t), (uint8_t*)&(ctx->config)); + + PRINT("Start sensor thread %d\n", user_id); + + thread_ready.release(); + + while (true) + { + bool err = ctx->read_value(ctx->current_value); + + ASSERT(err == 0, "Failed to read sensor\n"); + + PRINT("Got %3d: ", ctx->value_file_id); + for (uint8_t i = 0; i < ctx->nb_values; i++) + { + PRINT("%9ld ", (int32_t)ctx->current_value[i]); + } + PRINT("\r\n"); + + for (uint8_t i = 0; i < ctx->nb_values; i++) + { + if (report_needed(&(ctx->config), + ctx->current_value[i], + ctx->last_report_value[i], + last_report_time)) + { + // Send notification + modem_write_file(ctx->value_file_id, ctx->current_value, 0, ctx->data_size, user_id); + modem_ready[user_id].wait(); + + // Update last report value + memcpy(ctx->last_report_value, ctx->current_value, ctx->data_size); + // Reset last report time + last_report_time = 0; + } + } + + // Update last report time + last_report_time += ctx->config.read_period; + + Thread::wait(ctx->config.read_period); + } +} + +void thread_file_modified() +{ + uint8_t fid; + osEvent evt; + + while (true) + { + evt = g_file_modified.get(); + fid = (evt.status == osEventMessage)? (uint8_t)(uint32_t)evt.value.p : NULL; + + switch (fid) + { + // If a configuration file has been modified, update the context + case FID_SENSOR_CONFIG_MAG: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(mag_thread_ctx.config)); + break; + case FID_SENSOR_CONFIG_ACC: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(acc_thread_ctx.config)); + break; + case FID_SENSOR_CONFIG_GYR: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(gyr_thread_ctx.config)); + break; + case FID_SENSOR_CONFIG_PRE: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(pre_thread_ctx.config)); + break; + case FID_SENSOR_CONFIG_HUM: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(hum_thread_ctx.config)); + break; + case FID_SENSOR_CONFIG_TEM1: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(tem1_thread_ctx.config)); + break; + case FID_SENSOR_CONFIG_TEM2: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(tem2_thread_ctx.config)); + break; + case FID_SENSOR_CONFIG_LIGHT: + ram_fs_read(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(light_thread_ctx.config)); + break; + default: + break; + } + } +} + +void thread_user_button() +{ + FPRINT("(id:0x%08x)\r\n", osThreadGetId()); + + uint8_t alarm; + d7a_sp_res_t istat; + uint8_t user_id = modem_get_id(my_main_callback); + + + alp_d7a_itf_t alarm_itf = { + .type = ALP_ITF_TYPE_D7A, + .cfg.to = 0, + .cfg.te = 0, + .cfg.qos.bf.record = 0, + .cfg.qos.bf.stop_on_err = 0, + .cfg.qos.bf.resp = D7A_RESP_PREFERRED, + .cfg.qos.bf.retry = 1, // XXX WM_RPOL_RARE_SINGLE_CHECK, + .cfg.addressee.ctrl.bf.nls = D7A_NLS_AES_CCM_64, + .cfg.addressee.ctrl.bf.idf = D7A_ID_NBID, + .cfg.addressee.xcl.bf = {.s = 0x2, .m = 0x1},// XXX D7A_XCL_GW, + .cfg.addressee.id[0] = D7A_CTF_VAL(1,1) + }; + + // Load alarm value + ram_fs_read(FID_ALARM, 0, 1, &alarm); + + // Send initial value + modem_send_file_content((uint8_t*)&alarm_itf, MY_D7_ITF_SIZE(&alarm_itf), (void*)&istat, FID_ALARM, &alarm, 0, 1, user_id); + modem_ready[user_id].wait(); + + while (true) + { + // Wait for button press + button_user.wait(); + + // load/save value to keep choerency in case of remote access... + ram_fs_read(FID_ALARM, 0, 1, &alarm); + + // Toggle alarm state + alarm = !alarm; + + ram_fs_write(FID_ALARM, 0, 1, &alarm); + + PRINT("BUTTON ALARM %d\r\n", alarm); + + modem_send_file_content((uint8_t*)&alarm_itf, MY_D7_ITF_SIZE(&alarm_itf), (void*)&istat, FID_ALARM, &alarm, 0, 1, user_id); + modem_ready[user_id].wait(); + } +} + +void modem_update_file(uint8_t fid, alp_file_header_t* header, uint8_t* data, uint8_t id) +{ + alp_file_header_t remote_header; + + memset(&remote_header, 0, sizeof(alp_file_header_t)); + + // Read remote header + modem_read_fprop(fid, &remote_header, id); + modem_ready[id].wait(); + + // Add file in local file system + ram_fs_new(fid, (uint8_t*)header, data); + + // Update file + if (memcmp(&remote_header, header, sizeof(alp_file_header_t))) + { + PRINT("Updating file %d\n", fid); + // Delete + modem_delete_file(fid, id); + modem_ready[id].wait(); + // Restore in local file system + ram_fs_new(fid, (uint8_t*)header, data); + // Re-create + if (data) + { + modem_declare_file(fid, header, id); + } + else + { + modem_create_file(fid, header, id); + } + modem_ready[id].wait(); + } + else + { + PRINT("File %d up to date\n", fid); + } +} + +// Todo for each sensor +#define SENSOR_SETUP(NAME,name) modem_update_file(FID_SENSOR_VALUE_##NAME, (alp_file_header_t*)&h_sensor_value_##name, NULL, main_id);\ + modem_update_file(FID_SENSOR_CONFIG_##NAME, (alp_file_header_t*)&h_sensor_config_##name, (uint8_t*)&f_sensor_config_##name, main_id);\ + g_thread_ctx = &name##_thread_ctx;\ + Thread th_##name(osPriorityNormal, 1024, NULL);\ + status = th_##name.start(thread_sensor);\ + ASSERT(status == osOK, "Failed to start thread (err: %d)\r\n", status);\ + thread_ready.wait() + +/*** Main function ------------------------------------------------------------- ***/ +int main() +{ + // Start & initialize + DBG_OPEN(DEBUG_LED); + PRINT("\r\n--- Starting new run ---\r\n"); + FPRINT("(id:0x%08x)\r\n", osThreadGetId()); + + static union { + uint8_t b[8]; + uint32_t w[2]; + } uid; + revision_t rev; + uint8_t main_id; + + // Hardware reset + DigitalOut reset_low(MODEM_PIN_RESET, 0); + Thread::wait(100); + + // Release reset + DigitalIn reset_release(MODEM_PIN_RESET); + Thread::wait(2000); + + // Open modem Com port + g_modem_com = new WizziCom(MODEM_PIN_TX, MODEM_PIN_RX, MODEM_PIN_IRQ_OUT, MODEM_PIN_IRQ_IN); + + // Redirect All Port traffic to my_serial_input + g_modem_com->attach(my_serial_input, WizziComPacketOther); + + // Open driver + modem_open(my_serial_send, &callbacks); + + main_id = modem_get_id(my_main_callback); + + PRINT("Start Modem Process (id=%d)\n", main_id); + Thread::wait(1000); + + modem_read_file(D7A_FID_UID, uid.b, 0, 8, main_id); + modem_ready[main_id].wait(); + + modem_read_file(D7A_FID_FIRMWARE_VERSION, &rev, 0, sizeof(revision_t), main_id); + modem_ready[main_id].wait(); + + PRINT("------------ D7A Modem infos ------------\r\n"); + PRINT_DATA(" - UID: ", "%02X", uid.b, 8, "\r\n"); + PRINT(" - Manufacturer ID: %08X\r\n", rev.manufacturer_id); + PRINT(" - Device ID: %08X\r\n", rev.device_id); + PRINT(" - Hardware version: %08X\r\n", rev.hw_version); + PRINT(" - Firmware version: v%d.%d.%d\r\n", rev.fw_version.major, rev.fw_version.minor, rev.fw_version.patch); + PRINT(" - File system CRC: 0x%08x\r\n", rev.fs_crc); + PRINT("-----------------------------------------\r\n"); + + PRINT("Register Files\n"); + modem_update_file(FID_HOST_REV, &h_rev, (uint8_t*)&f_rev, main_id); + modem_update_file(FID_ALARM, &h_alarm, (uint8_t*)&f_alarm, main_id); + + // Configure URC: LQUAL on report file notification every 10 reports + PRINT("Setup URCs\n"); + modem_enable_urc(ALP_URC_TYPE_LQUAL, IFID_REPORT, 10, true, main_id); + modem_ready[main_id].wait(); + + PRINT("Start D7A Stack\n"); + modem_activate_itf(ALP_ITF_TYPE_D7A, 24, 0, ALP_D7A_ISTAT_RESP , true, main_id); + modem_ready[main_id].wait(); + + PRINT("Notify Modem Version\n"); + modem_notify_file(D7A_FID_FIRMWARE_VERSION, 0, SIZE_HOST_REV, main_id); + modem_ready[main_id].wait(); + + PRINT("Notify FW Version\n"); + modem_notify_file(FID_HOST_REV, 0, SIZE_HOST_REV, main_id); + modem_ready[main_id].wait(); + + // Start file modified thread + Thread th_file_modified(osPriorityNormal, 1024, NULL); + osStatus status = th_file_modified.start(thread_file_modified); + ASSERT(status == osOK, "Failed to start thread_file_modified (err: %d)\r\n", status); + +#if (_HUM_EN_ > 0 || _TEM1_EN_ > 0 || _MAG_EN_ > 0 || _ACC_EN_ > 0 || _GYR_EN_ > 0 || _PRE_EN_ > 0 || _TEM2_EN_ > 0) + // Open I2C and initialise the sensors + DevI2C ext_i2c(SENSOR_I2C_SDA, SENSOR_I2C_SCL); +#endif + +#if (_HUM_EN_ > 0 || _TEM1_EN_ > 0) + 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 (_PRE_EN_ > 0 || _TEM2_EN_ > 0) + pressure_sensor = new LPS25H(ext_i2c); + ASSERT(Init_LPS25H(pressure_sensor), "Failed to init LPS25H\r\n"); + temp_sensor2 = pressure_sensor; +#endif // _PRE_EN_ + +#if defined(TARGET_STM32L152RE) + #if (_MAG_EN_ > 0) + magnetometer = new LIS3MDL(ext_i2c); + ASSERT(Init_LIS3MDL(magnetometer), "Failed to init LIS3MDL\r\n"); + #endif // _MAG_EN_ + #if (_ACC_EN_ > 0 || _GYR_EN_ > 0) + accelerometer = new LSM6DS0(ext_i2c); + ASSERT(Init_LSM6DS0(accelerometer), "Failed to init LSM6DS0\r\n"); + gyroscope = accelerometer; + #endif // _ACC_EN_ || _GYR_EN_ +#elif defined(TARGET_STM32L432KC) + #if (_ACC_EN_ > 0) + accelerometer = new LSM303C_ACC_Sensor(ext_i2c); + ASSERT(Init_LSM303C_ACC(accelerometer), "Failed to init LSM303C_ACC\r\n"); + #endif // _ACC_EN_ + #if (_MAG_EN_ > 0) + magnetometer = new LSM303C_MAG_Sensor(ext_i2c); + ASSERT(Init_LSM303C_MAG(magnetometer), "Failed to init LSM303C_MAG\r\n"); + #endif // _MAG_EN_ +#endif + +#if (_MAG_EN_ > 0) + SENSOR_SETUP(MAG,mag); +#endif +#if (_ACC_EN_ > 0) + SENSOR_SETUP(ACC,acc); +#endif +#if (_GYR_EN_ > 0) + SENSOR_SETUP(GYR,gyr); +#endif +#if (_PRE_EN_ > 0) + SENSOR_SETUP(PRE,pre); +#endif +#if (_HUM_EN_ > 0) + SENSOR_SETUP(HUM,hum); +#endif +#if (_TEM1_EN_ > 0) + SENSOR_SETUP(TEM1,tem1); +#endif +#if (_TEM2_EN_ > 0) + SENSOR_SETUP(TEM2,tem2); +#endif +#if (_LIGHT_EN_ > 0) + SENSOR_SETUP(LIGHT,light); +#endif + + modem_free_id(main_id); + + // For button +#ifdef DEBUG_BUTTON + DebouncedInterrupt user_interrupt(DEBUG_BUTTON); + user_interrupt.attach(button_push_isr, IRQ_FALL, 500, true); + + Thread but_th(osPriorityNormal, 1024, NULL); + status = but_th.start(thread_user_button); + ASSERT(status == osOK, "Failed to start but thread (err: %d)\r\n", status); +#endif + +#if (DEBUG_LED != NC) + DigitalOut my_led(DEBUG_LED); +#endif + + // Set main task to lowest priority + osThreadSetPriority(osThreadGetId(), osPriorityIdle); + while(true) + { + // Wait to avoid beeing stuck in loop + Thread::wait(500); +#if (DEBUG_LED != NC) + my_led = !my_led; +#endif + } +} \ No newline at end of file