Complete sensor demo.
Dependencies: modem_ref_helper CRC X_NUCLEO_IKS01A1 DebouncedInterrupt
main.cpp
- Committer:
- marin_wizzi
- Date:
- 2021-10-29
- Revision:
- 18:51b15d8bf2fe
- Parent:
- 17:3e6083d76bc6
File content as of revision 18:51b15d8bf2fe:
// This project is a demo of the DASH7 1.x stack
// @autor: jeremie@wizzilab.com
// @date: 2016-12-20
#include "DebouncedInterrupt.h"
#include "sensors.h"
#include "sensors_cfg.h"
#include "simul.h"
#include "modem_d7a.h"
#include "modem_callbacks.h"
#include "files.h"
#include "crc.h"
#define MIN_REPORT_PERIOD (10) // Seconds
enum {
MODEM_RESP_NO,
MODEM_RESP_TERMINAL,
MODEM_RESP_DONE,
};
Semaphore button_user(1);
Semaphore thread_ready(0);
sensor_config_t g_light_config;
int sensor_id = 0;
Queue<touch_t, 8> g_file_modified;
static bool report_ok(uint32_t last_report_time)
{
// Do not send a report if it's been less than MIN_REPORT_PERIOD since the last report
if ((last_report_time/1000) < MIN_REPORT_PERIOD)
{
PRINT("Report Skipped. (Locked for %ds)\n", MIN_REPORT_PERIOD - (last_report_time/1000));
return false;
}
return true;
}
// 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, uint8_t thread_id)
{
char thread_name[10];
switch (thread_id)
{
case 0: strcpy(thread_name,"Magnetometer"); break;
case 1: strcpy(thread_name,"Accelerometer"); break;
case 2: strcpy(thread_name,"Pressure"); break;
case 3: strcpy(thread_name,"Humidity"); break;
case 4: strcpy(thread_name,"Temperature sensor 1"); break;
case 5: strcpy(thread_name,"Temperature sensor 2"); break;
case 6: strcpy(thread_name,"Light sensor"); break;
default: strcpy(thread_name,"Unknown"); break;
}
switch (config->report_type)
{
case REPORT_ALWAYS:
// Send a report at each measure
PRINT("Report[%d] : %s always\r\n", thread_id, thread_name);
return report_ok(last_report_time);
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)
{
PRINT("Report[%d] : %s on difference (last:%d new:%d max_diff:%d)\r\n", thread_id, thread_name, last_value, value, config->max_diff);
return report_ok(last_report_time);
}
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))
{
PRINT("Report[%d] : %s on threshold (last:%d new:%d th:%d tl:%d)\r\n", thread_id, thread_name, last_value, value, config->threshold_high, config->threshold_low);
return report_ok(last_report_time);
}
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)
{
PRINT("Report[%d] : %s on period (max_period:%d time:%d)\r\n", thread_id, thread_name, config->max_period, last_report_time);
return report_ok(last_report_time);
}
return false;
}
// Interrupt Service Routine on button press.
void button_push_isr( void )
{
button_user.release();
}
modem_ref_callbacks_t callbacks = {
.read = my_read,
.write = my_write,
.read_fprop = my_read_fprop,
.flush = my_flush,
.remove = my_delete,
.udata = my_udata,
.lqual = my_lqual,
.ldown = my_ldown,
.reset = my_reset,
.boot = my_boot,
.busy = my_busy,
};
// -----------------------------------------------
// 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);
void thread_sensor()
{
int thread_id = sensor_id++;
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;
// Get the sensor configuration
ram_fs_read(ctx->config_file_id, (uint8_t*)&(ctx->config), 0, sizeof(sensor_config_t));
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, thread_id))
{
// Send notification
modem_write_file(ctx->value_file_id, ctx->current_value, 0, ctx->data_size);
// 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;
ThisThread::sleep_for(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, (uint8_t*)&(mag_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
case FID_SENSOR_CONFIG_ACC:
ram_fs_read(fid, (uint8_t*)&(mag_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
case FID_SENSOR_CONFIG_GYR:
ram_fs_read(fid, (uint8_t*)&(gyr_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
case FID_SENSOR_CONFIG_PRE:
ram_fs_read(fid, (uint8_t*)&(gyr_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
case FID_SENSOR_CONFIG_HUM:
ram_fs_read(fid, (uint8_t*)&(gyr_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
case FID_SENSOR_CONFIG_TEM1:
ram_fs_read(fid, (uint8_t*)&(gyr_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
case FID_SENSOR_CONFIG_TEM2:
ram_fs_read(fid, (uint8_t*)&(gyr_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
case FID_SENSOR_CONFIG_LIGHT:
ram_fs_read(fid, (uint8_t*)&(gyr_thread_ctx.config), 0, sizeof(sensor_config_t));
break;
default:
break;
}
}
}
// Callback for button
void my_response_callback(uint8_t terminal, int8_t err, uint8_t id)
{
UNUSED(id);
if (ALP_ERR_NONE != err)
{
modem_print_error(ALP_ITF_TYPE_D7A, err);
}
if (terminal)
{
g_file_modified.put((touch_t*)MODEM_RESP_TERMINAL);
}
else
{
if (ALP_ERR_NONE == err)
{
g_file_modified.put((touch_t*)MODEM_RESP_DONE);
}
}
}
void thread_user_button()
{
FPRINT("(id:0x%08x)\r\n", osThreadGetId());
osEvent evt;
uint32_t resp;
uint8_t alarm;
d7a_sp_res_t istat;
alp_payload_t* alp;
alp_payload_t* alp_rsp;
uint8_t nb = 0;
int err;
alp_d7a_itf_t alarm_itf = {
.type = ALP_ITF_TYPE_D7A,
.cfg.to.byte = D7A_CTF_ENCODE(0),
.cfg.te.byte = D7A_CTF_ENCODE(0),
.cfg.qos.bf.resp = D7A_RESP_PREFERRED,
.cfg.qos.bf.retry = ALP_RPOL_ONESHOT_RETRY,
.cfg.addressee.ctrl.bf.nls = D7A_NLS_AES_CCM_64,
.cfg.addressee.ctrl.bf.idf = D7A_ID_NBID,
.cfg.addressee.xcl.bf = {.s = 2, .m = 0x1},// XXX D7A_XCL_GW,
.cfg.addressee.id[0] = D7A_CTF_ENCODE(4),
};
// Load alarm value
ram_fs_read(FID_ALARM, &alarm, 0, 1);
while (true)
{
// Wait for button press
button_user.acquire();
// load/save value to keep coherence in case of remote access...
ram_fs_read(FID_ALARM, &alarm, 0, 1);
// Initial value
if (alarm != 255)
{
// Toggle alarm state
alarm = !alarm;
ram_fs_write(FID_ALARM, &alarm, 0, 1);
}
PRINT("BUTTON ALARM %d\r\n", alarm);
nb = 0;
alp = NULL;
alp = alp_payload_rsp_f_data(alp, FID_ALARM, &alarm, 0, 1);
err = modem_remote_raw_alp((void*)&alarm_itf, alp, &alp_rsp, (uint32_t)15000);
do
{
evt = g_file_modified.get();
resp = (evt.status == osEventMessage)? (uint32_t)evt.value.p : MODEM_RESP_NO;
if (MODEM_RESP_DONE == resp)
{
nb++;
PRINT("%d: ", nb);
PRINT_DATA("UID:", "%02X", istat.addressee.id, 8, " ");
PRINT("SNR: %3ddB RXLEV: -%-3ddBm LB: %3ddB\n", istat.snr, istat.rxlev, istat.lb);
// Reset variable
memset(&istat, 0, sizeof(d7a_sp_res_t));
}
} while (MODEM_RESP_TERMINAL != resp);
if (alarm == 255)
{
// Toggle alarm state
alarm = !!alarm;
ram_fs_write(FID_ALARM, &alarm, 0, 1);
}
}
}
// Todo for each sensor
#define SENSOR_SETUP(NAME,name) ram_fs_new(FID_SENSOR_VALUE_##NAME, (uint8_t*)&h_sensor_value_##name, (uint8_t*)f_sensor_value_##name); modem_declare_file(FID_SENSOR_VALUE_##NAME, (alp_file_header_t*)&h_sensor_value_##name);\
ram_fs_new(FID_SENSOR_CONFIG_##NAME, (uint8_t*)&h_sensor_config_##name, (uint8_t*)&f_sensor_config_##name);\
modem_declare_file(FID_SENSOR_CONFIG_##NAME, (alp_file_header_t*)&h_sensor_config_##name);\
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.acquire()
/*** Main function ------------------------------------------------------------- ***/
int main()
{
// Start & initialize
#ifdef DEBUG_LED
DBG_OPEN(DEBUG_LED);
#else
DBG_OPEN(NC);
#endif
PRINT("\n"
"-----------------------------------------\n"
"------------- Demo Sensors --------------\n"
"-----------------------------------------\n");
FPRINT("(id:0x%08x)\r\n", osThreadGetId());
modem_open(&callbacks);
PRINT("Register Files\n");
ram_fs_new(FID_ALARM, (uint8_t*)&h_alarm, (uint8_t*)&f_alarm);
modem_declare_file(FID_ALARM, (alp_file_header_t*)&h_alarm);
PRINT("Enable D7A interface\n");
modem_d7a_enable_itf();
// Host revision file is in the modem. Update it.
PRINT("Update host revision\n");
modem_write_file(FID_HOST_REV, (void*)&f_rev, 0, sizeof(revision_t));
// Retrieve modem revision
PRINT("Send revision\n");
revision_t rev;
modem_read_file(FID_WM_REV, &rev, 0, sizeof(revision_t));
// 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
// 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
#ifdef DEBUG_LED
DigitalOut my_led(DEBUG_LED);
#endif
// Set main task to lowest priority
osThreadSetPriority(osThreadGetId(), osPriorityLow);
while(true)
{
// Wait to avoid beeing stuck in loop
ThisThread::sleep_for(500);
#ifdef DEBUG_LED
my_led = !my_led;
#endif
}
}