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;
}
}