LoRaWAN demo.
Dependencies: modem_ref_helper DebouncedInterrupt
main.cpp
00001 // @autor: jeremie@wizzilab.com 00002 // @date: 2017-09-21 00003 00004 #include "DebouncedInterrupt.h" 00005 #include "modem_d7a.h" 00006 #include "d7a_callbacks.h" 00007 #include "lwan_callbacks.h" 00008 #include "files.h" 00009 #include "sensor.h" 00010 00011 // Minimum time between alarms 00012 #define ALARM_COOLDOWN_TIME 10000 // ms 00013 #define MIN_REPORT_PERIOD (10) // Seconds 00014 00015 Semaphore modem_urc(0); 00016 Semaphore button_user(0); 00017 sensor_config_t g_light_config; 00018 Queue<uint8_t, 8> g_file_modified; 00019 Queue<uint32_t, 8> g_urc; 00020 int itf_busy; 00021 Timer busy_tim; 00022 00023 bool alarm_ready = false; 00024 00025 #define USE_WL_TTN 00026 #ifdef USE_WL_TTN 00027 // This is WizziLab's The Things Network LoRaWAN configuration file 00028 // This device is already registered on WizziLab's APP 00029 // 00030 // You can create your own account to get custom app_id and app_key. 00031 // The device EUI is the modem's UID. 00032 // https://account.thethingsnetwork.org/register 00033 // https://console.thethingsnetwork.org/applications 00034 00035 #define MY_APP_ID {0x70, 0xB3, 0xD5, 0x7E, 0xF0, 0x00, 0x3A, 0xF1 } 00036 #define MY_APP_KEY {0x73, 0x90, 0x54, 0x78, 0xB5, 0x0B, 0xA8, 0x9A, 0x78, 0x23, 0xB7, 0x12, 0xD5, 0x5C, 0x70, 0x99 } 00037 #else 00038 // This is your APP_ID and APP_KEY, as defined on your own TTN account 00039 // https://account.thethingsnetwork.org/register 00040 // https://console.thethingsnetwork.org/applications 00041 00042 #define MY_APP_ID { 0x70, 0xB3, 0xD5, 0x7E, 0xD0, 0x00, 0xAC, 0xB2 } 00043 #define MY_APP_KEY { 0xDF, 0xF7, 0x26, 0x21, 0x22, 0xAD, 0x9A, 0xD6, 0x16, 0x84, 0xD1, 0x95, 0xBA, 0x8C, 0xD1, 0x1E } 00044 00045 #endif 00046 00047 lwan_cfg_t lwan_cfg = { 00048 // LoRaWAN device class 00049 .dev_class = LWAN_CLASS_A, 00050 // State of adaptative Datarate 00051 .adr_enable = 1, 00052 // Uplink datarate, if adr_enable is off 00053 .tx_datarate = 0, 00054 // ISM Band 00055 .ism_band = ISM_BAND_868, 00056 // maximum join attempts 00057 .join_trials = 2, 00058 // Rejoin period (hours) 00059 .rejoin_period = 24 00060 }; 00061 00062 lwan_nls_t lwan_nls = { 00063 // Application identifier 00064 .app_id = MY_APP_ID, 00065 // Application key 00066 .app_key = MY_APP_KEY, 00067 }; 00068 00069 alp_d7a_itf_t report_itf = { 00070 .type = ALP_ITF_TYPE_D7A, 00071 .cfg.to = D7A_CTF(0), 00072 .cfg.te = D7A_CTF(0), 00073 .cfg.qos.bf.resp = D7A_RESP_PREFERRED, 00074 .cfg.qos.bf.retry = ALP_RPOL_ONESHOT, 00075 .cfg.addressee.ctrl.bf.nls = D7A_NLS_AES_CCM_64, 00076 .cfg.addressee.ctrl.bf.idf = D7A_ID_NBID, 00077 .cfg.addressee.xcl.bf = {.m = 0x1, .s = 2},// XXX D7A_XCL_GW, 00078 .cfg.addressee.id = { D7A_CTF_ENCODE(2) } 00079 }; 00080 00081 modem_ref_callbacks_t callbacks = { 00082 .read = my_read, 00083 .write = my_write, 00084 .read_fprop = my_read_fprop, 00085 .flush = my_flush, 00086 .remove = my_delete, 00087 .udata = my_udata, 00088 .lqual = my_lqual, 00089 .ldown = my_ldown, 00090 .reset = my_reset, 00091 .boot = my_boot, 00092 .busy = my_busy, 00093 .itf_busy = NULL, 00094 }; 00095 00096 modem_lwan_callbacks_t lwan_callbacks = { 00097 .packet_sent = lwan_packet_sent, 00098 .itf_busy = lwan_busy, 00099 .join_failed = lwan_join_failed, 00100 }; 00101 00102 00103 // Interrupt Service Routine on button press. 00104 void button_push_isr( void ) 00105 { 00106 if (alarm_ready) 00107 { 00108 button_user.release(); 00109 } 00110 else 00111 { 00112 PRINT("YOU CAN'T SEND ALARM AGAIN SO SOON.\n"); 00113 } 00114 } 00115 00116 static bool report_ok(uint32_t last_report_time) 00117 { 00118 // Do not send a report if it's been less than MIN_REPORT_PERIOD since the last report 00119 if ((last_report_time/1000) < MIN_REPORT_PERIOD) 00120 { 00121 PRINT("Report Skipped, next in %ds min\n", MIN_REPORT_PERIOD - (last_report_time/1000)); 00122 return false; 00123 } 00124 00125 return true; 00126 } 00127 00128 // Check parameters to see if data should be send 00129 static bool report_needed(sensor_config_t* config, int32_t value, int32_t last_value, uint32_t last_report_time) 00130 { 00131 switch (config->report_type) 00132 { 00133 case REPORT_ALWAYS: 00134 // Send a report at each measure 00135 PRINT("Report always\r\n"); 00136 return report_ok(last_report_time); 00137 case REPORT_ON_DIFFERENCE: 00138 // Send a report when the difference between the last reported measure and the current mesure is greater than max_diff 00139 if (abs(last_value - value) >= config->max_diff && config->max_diff) 00140 { 00141 PRINT("Report on difference (last:%d new:%d max_diff:%d)\r\n", last_value, value, config->max_diff); 00142 return report_ok(last_report_time); 00143 } 00144 break; 00145 case REPORT_ON_THRESHOLD: 00146 // Send a report when crossing a threshold 00147 if ( (value >= config->threshold_high && last_value < config->threshold_high) 00148 || (value <= config->threshold_low && last_value > config->threshold_low) 00149 || (value < config->threshold_high && last_value >= config->threshold_high) 00150 || (value > config->threshold_low && last_value <= config->threshold_low)) 00151 { 00152 PRINT("Reporton threshold (last:%d new:%d th:%d tl:%d)\r\n", last_value, value, config->threshold_high, config->threshold_low); 00153 return report_ok(last_report_time); 00154 } 00155 break; 00156 default: 00157 break; 00158 } 00159 00160 // Send a report if it's been more than max_period since the last report 00161 if (((last_report_time/1000) >= config->max_period) && config->max_period) 00162 { 00163 PRINT("Report on period (max_period:%d time:%d)\r\n", config->max_period, last_report_time); 00164 return report_ok(last_report_time); 00165 } 00166 00167 return false; 00168 } 00169 00170 void thread_file_modified() 00171 { 00172 uint8_t fid; 00173 osEvent evt; 00174 00175 while (true) 00176 { 00177 evt = g_file_modified.get(); 00178 fid = (evt.status == osEventMessage)? (uint8_t)(uint32_t)evt.value.p : NULL; 00179 00180 switch (fid) 00181 { 00182 case FID_SENSOR_CONFIG: 00183 // Update sensor configuration 00184 ram_fs_read(FID_SENSOR_CONFIG, (uint8_t*)&g_light_config, 0, SIZE_SENSOR_CONFIG); 00185 PRINT("Sensor configuration updated\r\n"); 00186 break; 00187 default: 00188 break; 00189 } 00190 } 00191 } 00192 00193 void d7a_thread() 00194 { 00195 light_value_t light_level; 00196 light_value_t light_level_old = 0; 00197 alp_payload_t* alp = NULL; 00198 revision_t rev; 00199 00200 // To force a first report 00201 uint32_t last_report_time = 0xFFFFFFFF; 00202 00203 // Add files to local file system 00204 ram_fs_new(FID_SENSOR_CONFIG, (uint8_t*)&h_sensor_config, (uint8_t*)&f_sensor_config); 00205 ram_fs_new(FID_ALARM, (uint8_t*)&h_alarm, (uint8_t*)&f_alarm); 00206 00207 DPRINT("D7A: Register Files\n"); 00208 // Allow remote access. 00209 modem_declare_file(FID_SENSOR_CONFIG, (alp_file_header_t*)&h_sensor_config); 00210 modem_declare_file(FID_ALARM, (alp_file_header_t*)&h_alarm); 00211 00212 PRINT("D7A: Notify Revision\n"); 00213 modem_d7a_enable_itf(); 00214 00215 // Host revision file is in the modem. Update it. 00216 modem_write_file(FID_HOST_REV, &f_rev, 0, sizeof(revision_t)); 00217 00218 // Retrieve modem revision 00219 modem_read_file(FID_WM_REV, &rev, 0, sizeof(revision_t)); 00220 00221 // Send both to the server 00222 // Build payload 00223 alp = NULL; 00224 alp = alp_payload_rsp_f_data(alp, FID_WM_REV, &rev, 0, sizeof(revision_t)); 00225 alp = alp_payload_rsp_f_data(alp, FID_HOST_REV, &f_rev, 0, sizeof(revision_t)); 00226 // Send 00227 modem_remote_raw_alp((void*)&report_itf, alp, NULL, 10000); 00228 00229 // Get the sensor configuration 00230 ram_fs_read(FID_SENSOR_CONFIG, (uint8_t*)&g_light_config, 0, SIZE_SENSOR_CONFIG); 00231 00232 while (true) 00233 { 00234 light_level = sensor_get_light(); 00235 00236 //PRINT("Light %d\r\n", light_level); 00237 00238 if (report_needed(&g_light_config, light_level, light_level_old, last_report_time)) 00239 { 00240 PRINT("D7A: Light report %d\r\n", light_level); 00241 00242 // Build payload 00243 alp = NULL; 00244 alp = alp_payload_rsp_f_data(alp, FID_SENSOR_LIGHT, &light_level, 0, SIZE_SENSOR_LIGHT); 00245 // Send 00246 modem_remote_raw_alp((void*)&report_itf, alp, NULL, 1000); 00247 00248 // Update 00249 light_level_old = light_level; 00250 last_report_time = 0; 00251 } 00252 00253 // Update last report time 00254 last_report_time += g_light_config.read_period; 00255 00256 ThisThread::sleep_for(g_light_config.read_period); 00257 } 00258 } 00259 00260 void lwan_thread() 00261 { 00262 alarm_t alarm; 00263 alp_payload_t* alp = NULL; 00264 00265 DebouncedInterrupt user_interrupt(DEBUG_BUTTON); 00266 user_interrupt.attach(button_push_isr, IRQ_FALL, 500, true); 00267 00268 // Load alarm value 00269 ram_fs_read(FID_ALARM, (uint8_t*)&alarm, 0, SIZE_ALARM); 00270 00271 PRINT("LoRaWAN: Update parameters\n"); 00272 modem_lwan_set_cfg(&lwan_cfg); 00273 modem_lwan_set_nls(&lwan_nls); 00274 00275 PRINT("LoRaWAN: Start (first join)\n"); 00276 modem_lwan_open(&lwan_callbacks); 00277 00278 while (true) 00279 { 00280 // Wait for button press 00281 PRINT("PRESS BUTTON TO SEND LORAWAN ALARM...\r\n"); 00282 alarm_ready = true; 00283 button_user.acquire(); 00284 alarm_ready = false; 00285 00286 itf_busy -= (int32_t)busy_tim.read(); 00287 00288 if (itf_busy > 0) 00289 { 00290 PRINT("LoRaWAN: Still busy for %ds.\r\n", itf_busy); 00291 busy_tim.reset(); 00292 00293 lwan_status_t lwan; 00294 00295 modem_lwan_get_status(&lwan); 00296 00297 PRINT( 00298 "LoRaWAN: Joined :%d\r\n" 00299 " NetID :%d\r\n" 00300 " IsmBand :%d\r\n" 00301 " PublicNetwork :%d\r\n" 00302 " UpLinkCounter :%d\r\n" 00303 " DownLinkCounter :%d\r\n" 00304 " TxDr :%d\r\n", 00305 lwan.IsNetworkJoined, 00306 lwan.NetID, 00307 lwan.IsmBand, 00308 lwan.PublicNetwork, 00309 lwan.UpLinkCounter, 00310 lwan.DownLinkCounter, 00311 lwan.TxDr 00312 ); 00313 } 00314 else 00315 { 00316 busy_tim.stop(); 00317 00318 // load/save value to keep choerency in case of remote access... 00319 ram_fs_read(FID_ALARM, (uint8_t*)&alarm, 0, SIZE_ALARM); 00320 00321 // Toggle alarm state 00322 alarm = !alarm; 00323 00324 ram_fs_write(FID_ALARM, &alarm, 0, SIZE_ALARM); 00325 00326 PRINT("BUTTON ALARM %d\r\n", alarm); 00327 00328 // Build payload 00329 alp = NULL; 00330 alp = alp_payload_rsp_f_data(alp, FID_ALARM, &alarm, 0, sizeof(alarm_t)); 00331 00332 // Send 00333 modem_lwan_send(alp); 00334 } 00335 } 00336 } 00337 00338 00339 /*** Main function ------------------------------------------------------------- ***/ 00340 int main() 00341 { 00342 // Start & initialize 00343 #ifdef DEBUG_LED 00344 DBG_OPEN(DEBUG_LED); 00345 #else 00346 DBG_OPEN(NC); 00347 #endif 00348 PRINT("\n" 00349 "-----------------------------------------\n" 00350 "-------------- Demo LoRaWAN -------------\n" 00351 "-----------------------------------------\n"); 00352 00353 modem_open(&callbacks); 00354 00355 // Start file modified thread 00356 Thread th_file_modified(osPriorityNormal, 1024, NULL); 00357 osStatus status = th_file_modified.start(thread_file_modified); 00358 ASSERT(status == osOK, "Failed to start thread_file_modified (err: %d)\r\n", status); 00359 00360 // Start light measure thread 00361 Thread th_sensor_light(osPriorityNormal, 1024, NULL); 00362 status = th_sensor_light.start(d7a_thread); 00363 ASSERT(status == osOK, "Failed to start d7a_thread (err: %d)\r\n", status); 00364 00365 Thread but_th(osPriorityNormal, 1024, NULL); 00366 status = but_th.start(lwan_thread); 00367 ASSERT(status == osOK, "Failed to start but thread (err: %d)\r\n", status); 00368 00369 #ifdef DEBUG_LED 00370 DigitalOut my_led(DEBUG_LED); 00371 #endif 00372 00373 // Set main task to lowest priority 00374 osThreadSetPriority(osThreadGetId(), osPriorityLow); 00375 while(true) 00376 { 00377 ThisThread::sleep_for(500); 00378 #ifdef DEBUG_LED 00379 my_led = !my_led; 00380 #endif 00381 } 00382 }
Generated on Wed Jul 13 2022 08:15:37 by 1.7.2