Simon Hawe / D7_MLX_AND_BAT

Dependencies:   X_NUCLEO_IKS01A1 MLX90614 d7a_1x wizzi-utils

Fork of D7A_1x_demo_sensors_OS5 by WizziLab

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 // This project is a demo of the DASH7 1.x stack
00002 // @autor: jeremie@wizzilab.com
00003 // @date: 2016-12-20
00004 //
00005 // ----- SETUP -----
00006 // This programm has been tested with the following hardware:
00007 // --> NUCLEO-L152RE + DASH7 enabled SHIELD_SP1ML_v1.0
00008 // For this demo to work, you need the previously described hardware.
00009 // A DASH7 1.x gateway have to be available in your area in order to retreive the data.
00010 //
00011 // ----- FILE SYSTEM -----
00012 // DASH7 is a file system based protocol. You read and write in files that are present on your device or your modem.
00013 // 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)
00014 // The file system is user defined and can be changed to fit your needs.
00015 // This demo uses a simple RAM file system.
00016 // The files are defined in files.h
00017 // The files values are initialized in files.cpp
00018 // 
00019 // ----- SENSORS -----
00020 // The sensors can be simulated (random data).
00021 //
00022 // ----- DEMO -----
00023 // This demo starts 7 threads (one for each sensor value) that will periodically read the sensor value
00024 // and depending on the sensor configuration file, will send the data to the DASH7 network (notification)
00025 // You can also report the alarm status by pushing the board's button (blue)
00026 // 
00027 // ----- DEBUG -----
00028 // Several debugging options are available in wizzi-utils/dbg/dbg.h
00029 // An ASSERT is a fatal error. By default, this error will reboot the device.
00030 #include "mbed.h"
00031 #include "rtos.h"
00032 #include "rtos_idle.h"
00033 #include "d7a.h"
00034 #include "sensors.h"
00035 #include "simul.h"
00036 #include "dbg.h"
00037 #include "DebouncedInterrupt.h"
00038 #include "files.h"
00039 #include "MLX90614.h"
00040 #include "DevI2C.h"
00041 
00042 #if defined(TARGET_STM32L152RE)
00043     #define D7A_PIN_TX              (D10)
00044     #define D7A_PIN_RX              (D2)
00045     #define D7A_PIN_RTS             (D13)
00046     #define D7A_PIN_CTS             (D9)
00047     #define D7A_PIN_RESET           (A3)
00048     #define DEBUG_LED               (LED1)
00049     #define DEBUG_BUTTON            (USER_BUTTON)
00050     #define SENSOR_I2C_SDA          (D14)
00051     #define SENSOR_I2C_SCL          (D15) 
00052     #define VOLT_PIN                (A0)
00053     
00054 #elif defined(TARGET_STM32L432KC)
00055     // -----------------------------------------------
00056     // Hardware configuration for sh2050
00057     // -----------------------------------------------
00058     #define D7A_PIN_TX              (D5)
00059     #define D7A_PIN_RX              (D4)
00060     #define D7A_PIN_RTS             (D11)
00061     #define D7A_PIN_CTS             (D10)
00062     #define D7A_PIN_RESET           (D12)
00063     #define DEBUG_LED               (D13) // LED1
00064     #define DEBUG_BUTTON            (D9)
00065     #define SENSOR_I2C_SDA          (D0)
00066     #define SENSOR_I2C_SCL          (D1)
00067     // TODO Check which pin to use on the small board for voltage stuff
00068     #define VOLT_PIN                (A0)
00069     
00070 #else
00071     #error "Please choose or add the right platform."
00072 #endif
00073 
00074 #define DGB_LED_BLINK_PERIOD        (500)
00075 #define _TEM1_EN_           (1)
00076 #define _VOLTAGE_EN_        (1)
00077 #if (_VOLTAGE_EN_ == 1)
00078 AnalogIn volt_pin(VOLT_PIN);
00079 #endif
00080 
00081 MLX90614 *mlxSensor;
00082 Semaphore button_user(0);
00083 Semaphore thread_ready(0);
00084 
00085 // Interrupt Service Routine on button press.
00086 void button_push_isr( void )
00087 {
00088     button_user.release();
00089 }
00090 
00091 // file modified queue
00092 Queue<void, 8> fm_queue;
00093 
00094 // -----------------------------------------------
00095 // callbacks
00096 // -----------------------------------------------
00097 /**
00098     Write data into a file.
00099 
00100     @param const uint8_t            File ID
00101     @param const uint16_t           Offset from start of data
00102     @param const uint16_t           Size of data to be written
00103     @param const uint8_t* const     Pointer to the data to write
00104     @return uint32_t                Size of written data
00105 */
00106 uint32_t write_file_callback(const uint8_t file_id,
00107                              const uint16_t offset,
00108                              const uint16_t size,
00109                              const uint8_t* const content)
00110 {
00111     uint32_t ret = 0;
00112     
00113     IPRINT("Write file %d offset:%d size:%d\r\n", file_id, offset, size);
00114     
00115     ret = fs_write_file(file_id, offset, size, content);
00116 
00117     WARNING(ret, "File %d not found\r\n", file_id);
00118     
00119     // Indicate that the file has been modified
00120     fm_queue.put((void*)file_id);
00121     
00122     return ret;
00123 }
00124 
00125 /**
00126     Read data from a file.
00127 
00128     @param const uint8_t            File ID
00129     @param const uint16_t           Offset from start of data
00130     @param const uint16_t           Size of data to be read
00131     @param const uint8_t* const     Pointer to the reading buffer
00132     @return uint32_t                Size of read data
00133 */
00134 uint32_t read_file_callback(const uint8_t file_id,
00135                             const uint16_t offset,
00136                             const uint16_t size,
00137                             uint8_t* buf)
00138 {
00139     PRINT("Read file %d offset:%d size:%d\r\n", file_id, offset, size);
00140     uint32_t ret = fs_read_file(file_id, offset, size, buf);
00141     WARNING(ret, "File %d not found\r\n", file_id);
00142     return ret;
00143 }
00144 
00145 /**
00146     Called when a notification is finished
00147 
00148     @param const uint8_t            File ID
00149     @param const uint8_t            Error code
00150     @return void
00151 */
00152 void notif_done_callback(const uint8_t file_id, const uint8_t error)
00153 {
00154     PRINT("Notif FID %d done. err %d\r\n", file_id, error);
00155 }
00156 
00157 void unsolicited_callback(d7a_msg_t** uns)
00158 {
00159     PRINT("UNSOLICITED MESSAGE:\r\n");
00160     d7a_print_msg(uns);
00161     d7a_free_msg(uns);
00162 }
00163 
00164 // callbacks structure
00165 const d7a_callbacks_t callbacks = {
00166     .write_file = write_file_callback,   // If NULL you cannot declare files
00167     .read_file = read_file_callback,     // If NULL you cannot declare files
00168     .notif_done = notif_done_callback,
00169     .unsolicited_msg = unsolicited_callback,
00170 };
00171 
00172 // Com configuration for the DASH7 shield
00173 const d7a_com_config_t shield_config = {
00174     .tx  = D7A_PIN_TX,
00175     .rx  = D7A_PIN_RX,
00176     .rts = D7A_PIN_RTS,
00177     .cts = D7A_PIN_CTS,
00178 };
00179 
00180 // Check parameters to see if data should be send
00181 static bool report_needed(sensor_config_t* cfg, int32_t value, int32_t last_value, uint32_t last_report_time)
00182 {
00183     switch (cfg->report_type)
00184     {
00185         case REPORT_ALWAYS:
00186             // Send a report at each measure
00187             IPRINT("Notif cause 1\r\n");
00188             return true;
00189         case REPORT_ON_DIFFERENCE:
00190             // Send a report when the difference between the last reported measure and the current mesure is greater than max_diff
00191             if (abs(last_value - value) >= cfg->max_diff)
00192             {
00193                 IPRINT("Notif cause 2 last:%d new:%d max_diff:%d\r\n", last_value, value, cfg->max_diff);
00194                 return true;
00195             }
00196             break;
00197         case REPORT_ON_THRESHOLD:
00198             // Send a report when crossing a threshold
00199             if (   (value >= cfg->threshold_high && last_value < cfg->threshold_high)
00200                 || (value <= cfg->threshold_low  && last_value > cfg->threshold_low)
00201                 || (value < cfg->threshold_high  && last_value >= cfg->threshold_high)
00202                 || (value > cfg->threshold_low   && last_value <= cfg->threshold_low))
00203             {
00204                 IPRINT("Notif cause 3 last:%d new:%d th:%d tl:%d\r\n", last_value, value, cfg->threshold_high, cfg->threshold_low);
00205                 return true;
00206             }
00207             break;
00208         default:
00209             break;
00210     }
00211     
00212     // Send a report if it's been more than max_period since the last report
00213     if (((last_report_time/1000) >= cfg->max_period) && (cfg->max_period != 0))
00214     {
00215         IPRINT("Notif cause 4 max_period:%d time:%d\r\n", cfg->max_period, last_report_time);
00216         return true;
00217     }
00218 
00219     return false;
00220 }
00221 
00222 // -----------------------------------------------
00223 // Sensor Threads
00224 // -----------------------------------------------
00225 typedef struct
00226 {
00227     // Number of data fields
00228     uint32_t nb_values;
00229     // Total size of data
00230     uint32_t data_size;
00231     // Read value function
00232     bool (*read_value)(int32_t*);
00233     // Last reported value
00234     int32_t* last_report_value;
00235     // Current measured value
00236     int32_t* current_value;
00237     
00238     // File ID of the sensor value file
00239     uint8_t value_file_id;
00240     // Sensor configuration file ID
00241     uint8_t cfg_file_id;
00242     // Sensor configuration context
00243     sensor_config_t cfg;
00244     
00245 } sensor_thread_ctx_t;
00246 
00247 sensor_thread_ctx_t* g_thread_ctx;
00248 
00249 // Initialisation of the sensor thread's context
00250 #define SENSOR_THREAD_CTX(_name,_vfid,_cfid,_read_func,_nb_values) \
00251     int32_t _name##_last_report[_nb_values];\
00252     int32_t _name##_current_value[_nb_values];\
00253     sensor_thread_ctx_t _name##_thread_ctx = {\
00254         .nb_values = _nb_values,\
00255         .data_size = _nb_values * sizeof(int32_t),\
00256         .read_value = _read_func,\
00257         .last_report_value = (int32_t*)&_name##_last_report,\
00258         .current_value = (int32_t*)&_name##_current_value,\
00259         .value_file_id = _vfid,\
00260         .cfg_file_id = _cfid\
00261     }
00262 
00263 __inline int32_t float2_to_int(float v)
00264 {
00265     return (int32_t)(v*100);
00266 }
00267 
00268 bool tem1_get_value(int32_t* buf)
00269 {
00270 #if (_TEM1_EN_ == 0)
00271     return simul_sensor_value(buf, 1, 1100, 3900);
00272 #elif (_TEM1_EN_ == 1)
00273     float ambient = mlxSensor->ambientTemp();
00274     float object = mlxSensor->objectTemp();
00275     PRINT("Got %f || %f\r\n", ambient, object);
00276     buf[0] = float2_to_int(object);    
00277     buf[1] = float2_to_int(ambient);    
00278     return false;
00279 #endif
00280 }
00281 SENSOR_THREAD_CTX(tem1, TEM1_VALUE_FILE_ID, TEM1_CFG_FILE_ID, tem1_get_value, 2);
00282 
00283 bool volt_get_value(int32_t* buf)
00284 {
00285 #if (_VOLTAGE_EN_ == 0)
00286     return simul_sensor_value(buf, 1, 0, 1000);
00287 #elif (_VOLTAGE_EN_ == 1)
00288     float voltage = volt_pin*1000;
00289     PRINT("Voltage value %f\r\n", voltage);
00290     buf[0] = float2_to_int(voltage);
00291     return false;
00292 #else
00293     return false;
00294 #endif
00295 }
00296 SENSOR_THREAD_CTX(volt, VOLT_VALUE_FILE_ID, VOLT_CFG_FILE_ID, volt_get_value,  1);
00297 
00298 
00299 void sensor_thread()
00300 {
00301     FPRINT("(id:0x%08x)\r\n", osThreadGetId());
00302     
00303     // Get thread context
00304     sensor_thread_ctx_t* th_ctx = g_thread_ctx;
00305         
00306     d7a_msg_t** resp = NULL;
00307     uint32_t last_report_time = 0;
00308 
00309     // Force a first notification
00310     bool first_notif = true;
00311     
00312     // Retrieve notification config from local file
00313     fs_read_file(th_ctx->cfg_file_id, 0, sizeof(sensor_config_t), (uint8_t*)&(th_ctx->cfg));
00314 
00315     // Declare the config file to allow it to be accessed via the modem
00316     d7a_declare(th_ctx->cfg_file_id, VOLATILE, RW_RW, sizeof(sensor_config_t), sizeof(sensor_config_t));
00317     
00318     // Create a file on the modem to store and notify the sensor value
00319     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);
00320 
00321     thread_ready.release();
00322 
00323     while (true)
00324     {
00325         // Read the sensor values
00326         bool err = th_ctx->read_value(th_ctx->current_value);
00327         
00328         ASSERT(!err, "Failed to read sensor value for FID: %d\r\n", th_ctx->value_file_id);
00329 
00330         // Check if data should be send
00331         for (uint8_t i = 0; i < th_ctx->nb_values; i++)
00332         {
00333             if (report_needed(&(th_ctx->cfg),
00334                 th_ctx->current_value[i],
00335                 th_ctx->last_report_value[i],
00336                 last_report_time) || first_notif)
00337             {
00338                 first_notif = false;
00339                 
00340                 PRINT("NOTIFY %3d: ", th_ctx->value_file_id);
00341                 for (uint8_t i = 0; i < th_ctx->nb_values; i++)
00342                 {
00343                     PRINT("%9ld ", (int32_t)th_ctx->current_value[i]);   
00344                 }
00345                 PRINT("\r\n");
00346 
00347                 // Send data to the modem
00348                 resp = d7a_write(th_ctx->value_file_id, 0, th_ctx->data_size, (uint8_t*)th_ctx->current_value);
00349                 d7a_free_msg(resp);
00350                 
00351                 // Update last report value
00352                 memcpy(th_ctx->last_report_value, th_ctx->current_value, th_ctx->data_size);
00353                 // Reset last report time
00354                 last_report_time = 0;
00355                 break;
00356             }
00357         }
00358         
00359         // Update last report time
00360         last_report_time += th_ctx->cfg.period;
00361         
00362         // Wait for period
00363         Thread::wait(th_ctx->cfg.period);
00364     }
00365 }
00366 
00367 void file_modified_thread()
00368 {    
00369     FPRINT("(id:0x%08x)\r\n", osThreadGetId());
00370     
00371     while (true)
00372     {
00373         // Wait for a file modified event
00374         osEvent evt = fm_queue.get(); 
00375         // Retrieve FID of modified file
00376         uint8_t fid = (uint8_t)(uint32_t)evt.value.p;
00377         PRINT("File %d has been modified\r\n", fid);
00378         
00379         switch (fid)
00380         {
00381             // If a configuration file has been modified, update the context
00382             case VOLT_CFG_FILE_ID:
00383                 fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(volt_thread_ctx.cfg));
00384                 break;
00385             case TEM1_CFG_FILE_ID:
00386                 fs_read_file(fid, 0, sizeof(sensor_config_t), (uint8_t*)&(tem1_thread_ctx.cfg));
00387                 break;            
00388             default:
00389                 break;
00390         }
00391     }
00392 }
00393 
00394 void button_user_thread()
00395 {
00396     FPRINT("(id:0x%08x)\r\n", osThreadGetId());
00397     
00398     uint8_t alarm = 255;
00399     d7a_msg_t** resp = NULL;
00400 
00401     // Create the alarm file
00402     d7a_create(ALARM_FILE_ID, VOLATILE, RW_R, sizeof(uint8_t), sizeof(uint8_t), D7A_NOTIFICATION_FULL, D7A_ITF_SINGLE);
00403     
00404     // Update it with the default value
00405     resp = d7a_write(ALARM_FILE_ID, 0, sizeof(uint8_t), (uint8_t*)&alarm);
00406     d7a_free_msg(resp);
00407     
00408     while (true)
00409     {
00410         // Wait for button press
00411         button_user.wait();
00412         
00413         // Toggle alarm state
00414         alarm = !alarm;
00415         
00416         PRINT("BUTTON ALARM %d\r\n", alarm);
00417 #if 1 // Switch between Notify and Distant Read/Write example
00418         // Send alarm state to the modem 
00419         resp = d7a_write(ALARM_FILE_ID, 0, sizeof(uint8_t), (uint8_t*)&alarm);
00420         WARNING(!resp[0]->err, "BUTTON ALARM ERR %d\r\n", resp[0]->err);
00421         d7a_free_msg(resp);
00422 #else
00423         // Distant device that I want to acceed
00424 #if 0 // Unicast / Broadcast
00425         // Unicast
00426         d7a_addressee_t target = {
00427             // Access Class
00428             .xcl.byte = D7A_XCL_ENDPOINT_NO,
00429             // Type of security you want to use
00430             .ctrl.bf.nls = D7A_NLS_AES_CCM_64,
00431             // Type of ID
00432             .ctrl.bf.idf = D7A_ID_UID,
00433             // Device ID
00434             .id = { 0x00, 0x1B, 0xC5, 0x0C, 0x70, 0x00, 0x07, 0xA3 },
00435         };
00436 #else    
00437         // Broadcast
00438         d7a_addressee_t target = {
00439             // Access Class
00440             .xcl.byte = D7A_XCL_ENDPOINT_NO,
00441             // Type of security you want to use
00442             .ctrl.bf.nls = D7A_NLS_AES_CCM_64,
00443             // Type of ID
00444             .ctrl.bf.idf = D7A_ID_NBID,
00445             // Maximum number of responses (1-32)
00446             .id[0] = D7A_NBID(8),
00447         };
00448 #endif
00449 
00450 #if 1 // Read / Write
00451         // Read example
00452         //resp = d7a_read(MAG_CFG_FILE_ID, 12, 5, &target, D7A_ITF_ONESHOT);
00453         resp = d7a_read(ALARM_FILE_ID, 0, 1, NULL, &target, D7A_ITF_ONESHOT);
00454 #else
00455         // Write example
00456         uint8_t v = 0x01;
00457         resp = d7a_write(ALARM_FILE_ID, 0, 1, &v, NULL, &target, D7A_ITF_ONESHOT);
00458 #endif
00459         // Check received response(s)
00460         d7a_print_msg(resp);
00461         
00462         PRINT("Resp done.\r\n");
00463         d7a_free_msg(resp);
00464 #endif
00465     }
00466 }
00467 
00468 /*** Main function ------------------------------------------------------------- ***/
00469 int main()
00470 {
00471     // Start & initialize
00472     DBG_OPEN(DEBUG_LED);
00473     PRINT("\r\n--- Starting new run ---\r\n");
00474     
00475     extern uint16_t const os_maxtaskrun;
00476     //IPRINT("Max user threads: %d\r\n", os_maxtaskrun-1-9);
00477 
00478     d7a_open(&shield_config, D7A_PIN_RESET, &callbacks);
00479     d7a_modem_print_infos();
00480     
00481     // Create the revision file for the Dash7board
00482     d7a_create(65, PERMANENT, RW_R, sizeof(d7a_revision_t), sizeof(d7a_revision_t), D7A_NOTIFICATION_FULL, D7A_ITF_REPORT_CHECKED);
00483     // Notify revision
00484     d7a_msg_t** resp = d7a_write(65, 0, sizeof(d7a_revision_t), (uint8_t*)&f_dev_rev);
00485     d7a_free_msg(resp);
00486 
00487     uint32_t divider;
00488     
00489     // Retreive the simulation parameter from local file
00490     fs_read_file(SIMUL_FILE_ID, 0, sizeof(uint32_t), (uint8_t*)&divider);
00491     
00492     // Update the simulation parameters
00493     simul_update_param(divider);
00494     
00495     // Declare the simulation parameter file
00496     d7a_declare(SIMUL_FILE_ID, PERMANENT, RW_RW, sizeof(sensor_config_t), sizeof(sensor_config_t));
00497 
00498 #if (_TEM1_EN_ == 1)
00499     // Open I2C and initialise the sensors
00500     DevI2C ext_i2c(SENSOR_I2C_SDA, SENSOR_I2C_SCL);
00501     ext_i2c.frequency(100000);
00502     mlxSensor = new MLX90614(&ext_i2c);
00503 #endif
00504 
00505     osStatus status;
00506 
00507     // Start sensors threads
00508 #define THREAD_START(_name) Thread _name##_th(osPriorityNormal, 1024, NULL);\
00509                             g_thread_ctx = &_name##_thread_ctx;\
00510                             status = _name##_th.start(sensor_thread);\
00511                             ASSERT(status == osOK, "Failed to start ##_name## thread (err: %d)\r\n", status);\
00512                             thread_ready.wait();
00513                             
00514 #if (_TEM1_EN_ >= 0)
00515     THREAD_START(tem1);
00516 #endif // _TEM1_EN_
00517 
00518 #if (_VOLTAGE_EN_ >=0)
00519     THREAD_START(volt);
00520 #endif
00521 
00522     // File modified thread
00523     Thread fm_th(osPriorityNormal, 512, NULL);
00524     status = fm_th.start(file_modified_thread);
00525     ASSERT(status == osOK, "Failed to start fm thread (err: %d)\r\n", status);
00526 
00527     // For button
00528 #ifdef DEBUG_BUTTON
00529     DebouncedInterrupt user_interrupt(DEBUG_BUTTON);
00530     user_interrupt.attach(button_push_isr, IRQ_FALL, 500, true);
00531 
00532     Thread but_th(osPriorityNormal, 512, NULL);
00533     status = but_th.start(button_user_thread);
00534     ASSERT(status == osOK, "Failed to start but thread (err: %d)\r\n", status);
00535 #endif
00536 
00537 #ifdef DGB_LED_BLINK_PERIOD
00538     DigitalOut my_led(DEBUG_LED);
00539 #endif
00540     
00541     // Set main task to lowest priority
00542     osThreadSetPriority(osThreadGetId(), osPriorityIdle);
00543     while(true)
00544     {
00545 #ifdef DGB_LED_BLINK_PERIOD
00546         // Wait to avoid beeing stuck in loop
00547         Thread::wait(DGB_LED_BLINK_PERIOD);
00548         my_led = !my_led;
00549 #else
00550         Thread::wait(osWaitForever);
00551 #endif
00552     }
00553 }