├── Firmware ├── CHANGELOG.md ├── RAK2270 │ ├── LoRaWAN.cpp │ ├── LoRaWAN.h │ ├── RAK2270.h │ ├── RAK2270.ino │ ├── custom.h │ ├── custom_at.cpp │ ├── custom_at.h │ ├── lis3dh_md.cpp │ ├── lis3dh_md.h │ ├── ntc.cpp │ └── ntc.h ├── README.md └── Release │ ├── RAK2270.v1.0.0.Beta15.ForStandard │ ├── RAK2270.v1.0.0.Beta15.ForStandard.hex │ └── RAK2270_firmware.json │ ├── RAK2270_1.1.0_Generic │ ├── RAK2270_1.1.0_Generic.hex │ └── RAK2270_firmware.json │ ├── RAK2270_1.1.2_Generic │ ├── RAK2270_1.1.2_Generic.hex │ ├── RAK2270_1.1.2_Generic_firmware.json │ └── forUpgrade │ │ └── RAK2270_1.1.2_Generic.bin │ ├── RAK2270_firmware_v1.0.0.RC1.ForStandard │ ├── RAK2270.v1.0.0.RC1.ForStandard.hex │ └── RAK2270_firmware.json │ ├── RAK2270_firmware_v1.0.0.RC5.ForStandard │ ├── RAK2270.v1.0.0.RC5.ForStandard.hex │ └── RAK2270_firmware.json │ └── RAK2270_firmware_v1.0.0.RC6.ForStandard │ ├── RAK2270.v1.0.0.RC6.ForStandard.hex │ └── RAK2270_firmware.json ├── Hardware ├── BOM of RAK2270(FPCB-WITHOUT EEPROM-OPENSOURCE).xls ├── Cadence │ ├── RAK2270_FPC_VC_2023_0628_1.brd │ └── RAK2270_VC_2023_0531.DSN ├── GerberFiles_RAK2270_FPC_VC_2023_0615_1.zip ├── RAK2270-bareboard-1.jpg ├── RAK2270-bareboard-2.png ├── RAK2270-sticker-1.png ├── RAK2270-sticker-2.png ├── RAK2270_FPC_VC_2023_0628_1_placement.zip ├── Schematic_RAK2270_VD_2024_0731.pdf ├── Testpoints_RAK2270_FPC_VC.pdf └── pins_location.png ├── LICENSE ├── RAK2270 Estimate Battery Life V1.xlsx └── README.md /Firmware/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] - 2025-04-10 2 | 3 | ### Added 4 | 5 | ### Changed 6 | 7 | ### Fixed 8 | 9 | ## [1.1.2] - 2025-04-10 10 | 11 | Use RUI Arduino BSP RAK3172-SiP v4.1.1 12 | 13 | ### Added 14 | - add AT and Downlink command for re-join. 15 | - add AT and Downlink command for development testing. 16 | 17 | ### Changed 18 | - Optimize motion detection functionality. 19 | - Add stage management to optimize the re-join mechanism. 20 | - Configure the activation GPIO pin as pull-none, because add a pull-down resistor in the hardware to prevent excessive power consumption caused by software misjudgment. 21 | - Cancel the downlink command ACK to avoid conflicts caused by the RUI mechanism of automatically replying with an null packet. 22 | 23 | ### Fixed 24 | - Add a debounce mechanism to prevent accidental device activation during production. 25 | - Optimize the linkcheck mechanism to avoid conflicts caused by multiple timers. 26 | 27 | ## [1.1.1] - 2024-12-20 28 | 29 | Use RUI Arduino BSP RAK3172-SiP v4.1.1 30 | 31 | ### Added 32 | - support Motion Detection by the accelerometer. 33 | - add AT and Downlink command for Motion Detection. 34 | 35 | ### Changed 36 | 37 | ### Fixed 38 | 39 | 40 | ## [1.1.0] - 2024-02-22 41 | 42 | Use RUI Arduino BSP RAK3172-SiP v4.1.0 43 | 44 | ### Added 45 | - first release for RAK2270_1.1.0_Generic. 46 | 47 | ### Changed 48 | 49 | ### Fixed 50 | 51 | -------------------------------------------------------------------------------- /Firmware/RAK2270/LoRaWAN.cpp: -------------------------------------------------------------------------------- 1 | #include "service_lora.h" 2 | #include "udrv_flash.h" 3 | #include "./custom_at.h" 4 | #include "./LoRaWAN.h" 5 | #include "./RAK2270.h" 6 | #include "./lis3dh_md.h" 7 | #include "./ntc.h" 8 | 9 | 10 | static uint8_t retry = LORAWAN_RETRY; 11 | 12 | /** Packet buffer for sending */ 13 | uint8_t collected_data[64] = { 0 }; 14 | 15 | bool has_downlink_cmd = false; 16 | DOWNLINK_CMD_t downlink_cmd; 17 | DOWNLINK_ACK_t downlink_ack; 18 | RETURN_PRE_SETTING_t g_return_pre_setting; 19 | bool f_cmd_rejoin = false; 20 | 21 | bool send_downlink_ack(bool confirmed_ack) 22 | { 23 | if (api.lorawan.send(downlink_ack.length, downlink_ack.data, FPORT_DOWNLINK_ACK, confirmed_ack, retry)) { 24 | Serial.println("Sending is requested"); 25 | return true; 26 | } 27 | else { 28 | Serial.println("Sending failed"); 29 | return false; 30 | } 31 | } 32 | 33 | void downlink_cmd_handle() 34 | { 35 | g_return_pre_setting.check = false; 36 | g_return_pre_setting.enable = false; 37 | has_downlink_cmd = false; 38 | 39 | switch (downlink_cmd.command) 40 | { 41 | case DLCMD_DR: // Setting data rate 42 | { 43 | uint8_t dr_org = api.lorawan.dr.get(); 44 | if (!api.lorawan.dr.set(downlink_cmd.data_8)) { 45 | Serial.printf("DLCMD_DR(%02X), Failed to set DR to %d!\r\n", DLCMD_DR, downlink_cmd.data_8); 46 | } 47 | else { 48 | Serial.printf("Set DR to %d\r\n", downlink_cmd.data_8); 49 | g_return_pre_setting.check = true; 50 | g_return_pre_setting.cmd = DLCMD_DR; 51 | g_return_pre_setting.data_8 = dr_org; 52 | send_downlink_ack(true); 53 | } 54 | return; 55 | } 56 | case DLCMD_ADR: // Setting adr 57 | { 58 | uint8_t adr_org = api.lorawan.adr.get(); 59 | if (!api.lorawan.adr.set(downlink_cmd.data_8)) { 60 | Serial.printf("DLCMD_ADR(%02X), Failed to set ADR to %d!\r\n", DLCMD_ADR, downlink_cmd.data_8); 61 | } 62 | else { 63 | Serial.printf("Set ADR to %d\r\n", downlink_cmd.data_8); 64 | g_return_pre_setting.check = true; 65 | g_return_pre_setting.cmd = DLCMD_ADR; 66 | g_return_pre_setting.data_8 = adr_org; 67 | send_downlink_ack(true); 68 | } 69 | return; 70 | } 71 | case DLCMD_UL_INTERVAL: // Modify the interval of periodic uplink, range: 1~1440 mintues 72 | { 73 | g_txInterval = (uint32_t)downlink_cmd.data_16; 74 | save_at_setting(SEND_FREQ_OFFSET); 75 | Serial.printf("Set period uplink interval = %d\r\n",g_txInterval); 76 | 77 | api.system.timer.stop(TIMER_PERIODIC_UPLINK); 78 | while (api.system.timer.create(TIMER_PERIODIC_UPLINK, (RAK_TIMER_HANDLER)periodic_uplink_handler, RAK_TIMER_PERIODIC) != true) { 79 | Serial.printf("Failed to creat timer.\r\n"); 80 | delay(100); 81 | } 82 | while (api.system.timer.start(TIMER_PERIODIC_UPLINK, g_txInterval * 60000, NULL) != true) { 83 | Serial.printf("Failed to start timer.\r\n"); 84 | delay(100); 85 | } 86 | return; 87 | } 88 | case DLCMD_MD_ENABLE: // Setting md_enable 89 | { 90 | if (get_md_enable() != downlink_cmd.data_8) { 91 | set_md_enable(downlink_cmd.data_8); 92 | if (get_md_enable()) { 93 | lis3dh_md_begin(); 94 | } 95 | else { 96 | lis3dh_md_end(); 97 | } 98 | } 99 | Serial.printf("Set md enable = %d\r\n", downlink_cmd.data_8); 100 | return; 101 | } 102 | case DLCMD_MD_THRESHOLD: // Setting md_threshold 103 | { 104 | if (get_md_threshold() != downlink_cmd.data_16) { 105 | set_md_threshold(downlink_cmd.data_16); 106 | if (get_md_enable()) { 107 | lis3dh_md_pause(); 108 | lis3dh_md_resume(); 109 | } 110 | } 111 | Serial.printf("Set md threshold = %d\r\n", downlink_cmd.data_16); 112 | return; 113 | } 114 | case DLCMD_MD_SAMPLE_RATE: // Setting md_sample_rate 115 | { 116 | if (get_md_sample_rate() != downlink_cmd.data_16) { 117 | set_md_sample_rate(downlink_cmd.data_16); 118 | if (get_md_enable()) { 119 | lis3dh_md_pause(); 120 | lis3dh_md_resume(); 121 | } 122 | } 123 | Serial.printf("Set md sample rate = %d\r\n", downlink_cmd.data_16); 124 | return; 125 | } 126 | case DLCMD_CFM: // Enable or disable for sending confirmed package 127 | { 128 | if (!api.lorawan.cfm.set(downlink_cmd.data_8)) 129 | Serial.printf("DLCMD_CFM(%02X), Failed to set CFM to %d!\r\n", DLCMD_CFM, downlink_cmd.data_8); 130 | else { 131 | Serial.printf("Set CFM to %d\r\n", downlink_cmd.data_8); 132 | } 133 | return; 134 | } 135 | case DLCMD_MD_DURATION: // Setting md_duration 136 | { 137 | if (get_md_duration() != downlink_cmd.data_16) { 138 | set_md_duration(downlink_cmd.data_16); 139 | if (get_md_enable()) { 140 | lis3dh_md_pause(); 141 | lis3dh_md_resume(); 142 | } 143 | } 144 | Serial.printf("Set md duration = %d\r\n", downlink_cmd.data_16); 145 | return; 146 | } 147 | case DLCMD_MD_NUMBER: // Setting md_number 148 | { 149 | if (get_md_number() != downlink_cmd.data_8) { 150 | set_md_number(downlink_cmd.data_8); 151 | if (get_md_enable()) { 152 | lis3dh_md_pause(); 153 | lis3dh_md_resume(); 154 | } 155 | } 156 | Serial.printf("Set md number = %d\r\n", downlink_cmd.data_8); 157 | return; 158 | } 159 | case DLCMD_MD_SILENT_PERIOD: // Setting md_silent_period 160 | { 161 | if (get_md_silent_period() != downlink_cmd.data_16) { 162 | set_md_silent_period(downlink_cmd.data_16); 163 | if (get_md_enable()) { 164 | lis3dh_md_pause(); 165 | lis3dh_md_resume(); 166 | } 167 | } 168 | Serial.printf("Set md silent period = %d\r\n", downlink_cmd.data_16); 169 | return; 170 | } 171 | case DLCMD_REJION: // return to join stage 172 | { 173 | f_cmd_rejoin = true; 174 | return; 175 | } 176 | case DLCMD_LINKCHECK_TIMEOUT: // Modify the timeout of link check timeout to return to join stage, range: 0~1440 mintues 177 | { 178 | set_linkcheck_timeout((uint32_t)downlink_cmd.data_16); 179 | reset_linkcheck_start(); 180 | reset_last_send_ok_time(); 181 | Serial.printf("Set link check timeout = %d\r\n", downlink_cmd.data_16); 182 | return; 183 | } 184 | } 185 | } 186 | 187 | /* 188 | * @note: recvCallback, downlink command handler 189 | */ 190 | void recvCallback(SERVICE_LORA_RECEIVE_T * data) 191 | { 192 | uint8_t command; 193 | uint8_t tmp_8; 194 | uint16_t tmp_16; 195 | uint32_t tmp_32; 196 | 197 | if (data->BufferSize > 0) { 198 | Serial.printf("Received Downlink:"); 199 | for (int i = 0; i < data->BufferSize; i++) { 200 | Serial.printf(" %02x", data->Buffer[i]); 201 | } 202 | Serial.printf("\r\n"); 203 | 204 | command = data->Buffer[0] & 0xFF; 205 | switch(command) 206 | { 207 | case DLCMD_DR: // Setting data rate 208 | { 209 | if (data->BufferSize != 2) 210 | Serial.printf("DLCMD_DR(%02X), Parameter error!\r\n", DLCMD_DR); 211 | else 212 | { 213 | tmp_8 = (data->Buffer[1] & 0xFF); 214 | if(tmp_8 > 7) 215 | Serial.printf("DLCMD_DR(%02X), Parameter error!\r\n", DLCMD_DR); 216 | else { 217 | downlink_cmd.length = data->BufferSize; 218 | downlink_cmd.command = command; 219 | downlink_cmd.data_8 = tmp_8; 220 | 221 | downlink_ack.length = data->BufferSize; 222 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 223 | has_downlink_cmd = true; 224 | } 225 | } 226 | break; 227 | } 228 | case DLCMD_ADR: // Setting adr 229 | { 230 | if (data->BufferSize != 2) 231 | Serial.printf("DLCMD_ADR(%02X) Parameter error\r\n", DLCMD_ADR); 232 | else 233 | { 234 | tmp_8 = (data->Buffer[1] & 0xFF); 235 | if (tmp_8 != 0 && tmp_8 != 1) 236 | Serial.printf("DLCMD_ADR(%02X) Parameter error\r\n", DLCMD_ADR); 237 | else { 238 | downlink_cmd.length = data->BufferSize; 239 | downlink_cmd.command = command; 240 | downlink_cmd.data_8 = tmp_8; 241 | 242 | downlink_ack.length = data->BufferSize; 243 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 244 | has_downlink_cmd = true; 245 | } 246 | } 247 | break; 248 | } 249 | case DLCMD_UL_INTERVAL: // Modify the interval of periodic uplink, range: 1~1440 mintues 250 | { 251 | if (data->BufferSize != 3) 252 | Serial.printf("DLCMD_UL_INTERVAL(%02X), Parameter error!\r\n", DLCMD_UL_INTERVAL); 253 | else 254 | { 255 | tmp_16 = (data->Buffer[2] & 0xFF) + ((data->Buffer[1] & 0xFF) << 8); 256 | if (tmp_16 < 1 || tmp_16 > 1440) 257 | Serial.printf("DLCMD_UL_INTERVAL(%02X), Parameter error!\r\n", DLCMD_UL_INTERVAL); 258 | else { 259 | downlink_cmd.length = data->BufferSize; 260 | downlink_cmd.command = command; 261 | downlink_cmd.data_16 = tmp_16; 262 | 263 | downlink_ack.length = data->BufferSize; 264 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 265 | has_downlink_cmd = true; 266 | } 267 | } 268 | break; 269 | } 270 | case DLCMD_MD_ENABLE: // Setting md_enable 271 | { 272 | if(data->BufferSize != 2) 273 | Serial.printf("DLCMD_MD_ENABLE(%02X), Parameter error!\r\n", DLCMD_MD_ENABLE); 274 | else 275 | { 276 | tmp_8 = (data->Buffer[1] & 0xFF); 277 | if(tmp_8 > 1) 278 | Serial.printf("DLCMDLCMD_MD_ENABLED_CFM(%02X), Parameter error!\r\n", DLCMD_MD_ENABLE); 279 | else { 280 | downlink_cmd.length = data->BufferSize; 281 | downlink_cmd.command = command; 282 | downlink_cmd.data_8 = tmp_8; 283 | 284 | downlink_ack.length = data->BufferSize; 285 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 286 | has_downlink_cmd = true; 287 | } 288 | } 289 | break; 290 | } 291 | case DLCMD_MD_THRESHOLD: // Setting md_threshold 292 | { 293 | if (data->BufferSize != 3) 294 | Serial.printf("DLCMD_MD_THRESHOLD(%02X), Parameter error!\r\n", DLCMD_MD_THRESHOLD); 295 | else 296 | { 297 | tmp_16 = (data->Buffer[2] & 0xFF) + ((data->Buffer[1] & 0xFF) << 8); 298 | if (tmp_16 < 50 || tmp_16 > 16000) 299 | Serial.printf("DLCMD_MD_THRESHOLD(%02X), Parameter error!\r\n", DLCMD_MD_THRESHOLD); 300 | else { 301 | downlink_cmd.length = data->BufferSize; 302 | downlink_cmd.command = command; 303 | downlink_cmd.data_16 = tmp_16; 304 | 305 | downlink_ack.length = data->BufferSize; 306 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 307 | has_downlink_cmd = true; 308 | } 309 | } 310 | break; 311 | } 312 | case DLCMD_MD_SAMPLE_RATE: // Setting md_sample_rate 313 | { 314 | if (data->BufferSize != 3) 315 | Serial.printf("DLCMD_MD_SAMPLE_RATE(%02X), Parameter error!\r\n", DLCMD_MD_SAMPLE_RATE); 316 | else 317 | { 318 | tmp_16 = (data->Buffer[2] & 0xFF) + ((data->Buffer[1] & 0xFF) << 8); 319 | if (tmp_16 > 1) 320 | Serial.printf("DLCMD_MD_SAMPLE_RATE(%02X), Parameter error!\r\n", DLCMD_MD_SAMPLE_RATE); 321 | else { 322 | downlink_cmd.length = data->BufferSize; 323 | downlink_cmd.command = command; 324 | downlink_cmd.data_16 = tmp_16; 325 | 326 | downlink_ack.length = data->BufferSize; 327 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 328 | has_downlink_cmd = true; 329 | } 330 | } 331 | break; 332 | } 333 | case DLCMD_CFM: // Enable or disable for sending confirmed package 334 | { 335 | if(data->BufferSize != 2) 336 | Serial.printf("DLCMD_CFM(%02X), Parameter error!\r\n", DLCMD_CFM); 337 | else 338 | { 339 | tmp_8 = (data->Buffer[1] & 0xFF); 340 | if(tmp_8 != 0 && tmp_8 != 1) 341 | Serial.printf("DLCMD_CFM(%02X), Parameter error!\r\n", DLCMD_CFM); 342 | else { 343 | downlink_cmd.length = data->BufferSize; 344 | downlink_cmd.command = command; 345 | downlink_cmd.data_8 = tmp_8; 346 | 347 | downlink_ack.length = data->BufferSize; 348 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 349 | has_downlink_cmd = true; 350 | } 351 | } 352 | break; 353 | } 354 | case DLCMD_MD_DURATION: // Setting md_duration 355 | { 356 | if (data->BufferSize != 3) 357 | Serial.printf("DLCMD_MD_DURATION(%02X), Parameter error!\r\n", DLCMD_MD_DURATION); 358 | else 359 | { 360 | tmp_16 = (data->Buffer[2] & 0xFF) + ((data->Buffer[1] & 0xFF) << 8); 361 | if (tmp_16 < 300 || tmp_16 > 15000) 362 | Serial.printf("DLCMD_MD_DURATION(%02X), Parameter error!\r\n", DLCMD_MD_DURATION); 363 | else { 364 | downlink_cmd.length = data->BufferSize; 365 | downlink_cmd.command = command; 366 | downlink_cmd.data_16 = tmp_16; 367 | 368 | downlink_ack.length = data->BufferSize; 369 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 370 | has_downlink_cmd = true; 371 | } 372 | } 373 | break; 374 | } 375 | case DLCMD_MD_NUMBER: // Setting md_number 376 | { 377 | if(data->BufferSize != 2) 378 | Serial.printf("DLCMD_MD_NUMBER(%02X), Parameter error!\r\n", DLCMD_MD_NUMBER); 379 | else 380 | { 381 | tmp_8 = (data->Buffer[1] & 0xFF); 382 | if (tmp_8 < 1 || tmp_8 > 10) 383 | Serial.printf("DLCMD_MD_NUMBER(%02X), Parameter error!\r\n", DLCMD_MD_NUMBER); 384 | else { 385 | downlink_cmd.length = data->BufferSize; 386 | downlink_cmd.command = command; 387 | downlink_cmd.data_8 = tmp_8; 388 | 389 | downlink_ack.length = data->BufferSize; 390 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 391 | has_downlink_cmd = true; 392 | } 393 | } 394 | break; 395 | } 396 | case DLCMD_MD_SILENT_PERIOD: // Setting md_silent_period 397 | { 398 | if(data->BufferSize != 3) 399 | Serial.printf("DLCMD_MD_SILENT_PERIOD(%02X), Parameter error!\r\n", DLCMD_MD_SILENT_PERIOD); 400 | else 401 | { 402 | tmp_16 = (data->Buffer[2] & 0xFF) + ((data->Buffer[1] & 0xFF) << 8); 403 | if (tmp_16 < 10 || tmp_16 > 3600) 404 | Serial.printf("DLCMD_MD_DURATION(%02X), Parameter error!\r\n", DLCMD_MD_DURATION); 405 | else { 406 | downlink_cmd.length = data->BufferSize; 407 | downlink_cmd.command = command; 408 | downlink_cmd.data_16 = tmp_16; 409 | 410 | downlink_ack.length = data->BufferSize; 411 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 412 | has_downlink_cmd = true; 413 | } 414 | } 415 | break; 416 | } 417 | case DLCMD_REJION: // return to join stage 418 | { 419 | if (data->BufferSize != 1) 420 | Serial.printf("DLCMD_REJION(%02X), Parameter error!\r\n", DLCMD_REJION); 421 | else 422 | { 423 | downlink_cmd.length = data->BufferSize; 424 | downlink_cmd.command = command; 425 | 426 | downlink_ack.length = data->BufferSize; 427 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 428 | has_downlink_cmd = true; 429 | } 430 | break; 431 | } 432 | case DLCMD_LINKCHECK_TIMEOUT: // Modify the timeout of link check timeout to return to join stage, range: 0~1440 mintues 433 | { 434 | if (data->BufferSize != 3) 435 | Serial.printf("DLCMD_LINKCHECK_TIMEOUT(%02X), Parameter error!\r\n", DLCMD_LINKCHECK_TIMEOUT); 436 | else 437 | { 438 | tmp_16 = (data->Buffer[2] & 0xFF) + ((data->Buffer[1] & 0xFF) << 8); 439 | if (tmp_16 > 1440) 440 | Serial.printf("DLCMD_UL_INTERVAL(%02X), Parameter error!\r\n", DLCMD_UL_INTERVAL); 441 | else { 442 | downlink_cmd.length = data->BufferSize; 443 | downlink_cmd.command = command; 444 | downlink_cmd.data_16 = tmp_16; 445 | 446 | downlink_ack.length = data->BufferSize; 447 | memcpy(downlink_ack.data, data->Buffer, data->BufferSize); 448 | has_downlink_cmd = true; 449 | } 450 | } 451 | break; 452 | } 453 | default: 454 | { 455 | Serial.printf("DLCMD is invalid!\r\n"); 456 | } 457 | } 458 | } 459 | } 460 | 461 | uint32_t linkcheck_start = 0; 462 | 463 | void reset_linkcheck_start() 464 | { 465 | linkcheck_start = millis(); 466 | } 467 | 468 | void do_linkcheck() 469 | { 470 | if (get_linkcheck_timeout() == 0) 471 | return; 472 | 473 | if ((millis()-linkcheck_start) >= (INTERVAL_LINKCHECK*60*1000)) 474 | { 475 | service_lora_set_linkcheck(1); 476 | } 477 | } 478 | 479 | /* 480 | * @note: sending data for periodic uplink 481 | */ 482 | void loraSendData(uint8_t *bufPtr, uint8_t data_len) 483 | { 484 | if (api.lorawan.njs.get()) 485 | { 486 | memcpy(collected_data , bufPtr , data_len); 487 | 488 | Serial.println("Data Packet:"); 489 | for (int i = 0; i < data_len; i++) 490 | { 491 | Serial.printf("0x%02X ", collected_data[i]); 492 | } 493 | Serial.println(""); 494 | 495 | do_linkcheck(); 496 | 497 | if (api.lorawan.send(data_len, (uint8_t *) & collected_data, FPORT_PERIODIC_UPLINK, api.lorawan.cfm.get(), 1)) 498 | { 499 | Serial.println("Sending is requested"); 500 | } 501 | else 502 | { 503 | Serial.println("Sending failed"); 504 | } 505 | } 506 | } 507 | 508 | void loraSendMDEvent() 509 | { 510 | if (api.lorawan.njs.get()) 511 | { 512 | uint8_t data = 0xE0; 513 | if (api.lorawan.send(1, &data, FPORT_MD_EVENT_UPLINK, api.lorawan.cfm.get(), 1)) 514 | { 515 | Serial.println("MD event sending is requested"); 516 | } 517 | else 518 | { 519 | Serial.println("MD event sending failed"); 520 | } 521 | } 522 | } 523 | 524 | 525 | /* 526 | * @note: sendCallback 527 | */ 528 | void sendCallback(int32_t status) 529 | { 530 | Serial.printf("send_cb(%d)\r\n", status); 531 | 532 | if (g_return_pre_setting.check) 533 | { 534 | if (api.lorawan.cfs.get() == false) 535 | { 536 | g_return_pre_setting.enable = true; 537 | } 538 | g_return_pre_setting.check = false; 539 | } 540 | } 541 | 542 | 543 | uint8_t lora_network_status = NOT_JOINED; 544 | uint8_t get_lora_network_status(void) 545 | { 546 | return lora_network_status; 547 | } 548 | 549 | void set_lora_network_status(uint8_t status) 550 | { 551 | lora_network_status = status; 552 | } 553 | 554 | uint32_t last_send_ok_time = 0; 555 | void reset_last_send_ok_time(void) 556 | { 557 | last_send_ok_time = millis(); 558 | } 559 | 560 | bool get_trigger_rejoin_status(void) 561 | { 562 | if (get_linkcheck_timeout() == 0) 563 | return false; 564 | 565 | if ((millis() - last_send_ok_time) >= (get_linkcheck_timeout()*60*1000)) 566 | return true; 567 | else 568 | return false; 569 | } 570 | 571 | 572 | /* 573 | * @note: linkcheck function callbak for trigger re-join mechanism 574 | * re-join after node cannot get linkcheck status over 12 hr 575 | */ 576 | void linkcheckCallback(SERVICE_LORA_LINKCHECK_T *data) 577 | { 578 | //Serial.printf("linkcheck %d:%d:%d:%d:%d\r\n", data->State, data->DemodMargin, data->NbGateways, data->Rssi, data->Snr); 579 | if (data->State == 0) { 580 | set_lora_network_status(SEND_OK); 581 | reset_last_send_ok_time(); 582 | } 583 | else { 584 | set_lora_network_status(SEND_NG); 585 | if (get_trigger_rejoin_status() == true) { 586 | set_lora_network_status(NOT_JOINED); 587 | return_to_join_stage(); 588 | } 589 | } 590 | } 591 | 592 | 593 | /* 594 | * @note: joinCallback, reserve 595 | */ 596 | void joinCallback(int32_t status) 597 | { 598 | set_lora_network_status(NOT_JOINED); 599 | 600 | Serial.printf("Join status: %d\r\n", status); 601 | if (status == 0) { 602 | set_lora_network_status(JOINED); 603 | reset_last_send_ok_time(); 604 | 605 | goto_periodic_uplink_stage(); 606 | } 607 | } 608 | 609 | 610 | /* 611 | * @note: initialize LoRaWAN, register callback function 612 | */ 613 | void loraWanInit() 614 | { 615 | api.lorawan.registerRecvCallback(recvCallback); 616 | api.lorawan.registerJoinCallback(joinCallback); 617 | api.lorawan.registerSendCallback(sendCallback); 618 | service_lora_register_linkcheck_cb(linkcheckCallback); 619 | } 620 | -------------------------------------------------------------------------------- /Firmware/RAK2270/LoRaWAN.h: -------------------------------------------------------------------------------- 1 | #ifndef __LORAWAN_H__ 2 | #define __LORAWAN_H__ 3 | 4 | #include 5 | 6 | 7 | typedef enum 8 | { 9 | DLCMD_DR = 0x00, //LoRaWAN, DR 10 | DLCMD_ADR = 0x01, //LoRaWAN, ADR 11 | DLCMD_UL_INTERVAL = 0x02, //Period uplink interval 12 | DLCMD_MD_ENABLE = 0x03, //Motion detect, enable to detect 13 | DLCMD_MD_THRESHOLD = 0x04, //Motion detect, the threshold of motion detection 14 | DLCMD_MD_SAMPLE_RATE = 0x05, //Motion detect, the sample rate of lis3dh 15 | DLCMD_CFM = 0x06, //LoRaWAN, use confirmed packet 16 | DLCMD_MD_DURATION = 0x07, //Motion detect, the detected duration for event trigger 17 | DLCMD_MD_NUMBER = 0x08, //Motion detect, the detected number for event trigger 18 | DLCMD_MD_SILENT_PERIOD = 0x09, //Motion detect, the silent period after event trigger 19 | DLCMD_REJION = 0x0A, //LORAWAN, do re-join 20 | DLCMD_LINKCHECK_TIMEOUT = 0x0B, //LORAWAN, link check timeout for trigger to re-join 21 | } DOWNLINK_CMD_ID_t; 22 | 23 | typedef struct DOWNLINK_CMD_s 24 | { 25 | uint8_t length; 26 | uint8_t command; 27 | uint8_t data_8; 28 | uint16_t data_16; 29 | uint32_t data_32; 30 | } DOWNLINK_CMD_t; 31 | 32 | typedef struct DOWNLINK_ACK_s 33 | { 34 | uint8_t length; 35 | uint8_t data[16]; 36 | } DOWNLINK_ACK_t; 37 | 38 | typedef struct RETURN_PRE_SETTING_s 39 | { 40 | bool check; 41 | bool enable; 42 | DOWNLINK_CMD_ID_t cmd; 43 | uint8_t data_8; 44 | //uint16_t data_16; 45 | //uint32_t data_32; 46 | } RETURN_PRE_SETTING_t; 47 | 48 | void loraWanInit(); 49 | void loraSendData(uint8_t *bufPtr, uint8_t data_len); 50 | void loraSendTempData(uint8_t len, uint8_t *buf); 51 | void loraSendMDEvent(); 52 | void reset_linkcheck_start(); 53 | void reset_last_send_ok_time(); 54 | void downlink_cmd_handle(); 55 | 56 | extern bool has_downlink_cmd; 57 | extern RETURN_PRE_SETTING_t g_return_pre_setting; 58 | extern bool f_cmd_rejoin; 59 | 60 | typedef enum 61 | { 62 | NOT_JOINED = 0x0, 63 | JOINED = 0x1, 64 | SEND_OK = 0x2, 65 | SEND_NG = 0x3, 66 | } LORA_NETWORK_STAUS_t; 67 | 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /Firmware/RAK2270/RAK2270.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file RAK2270.h 3 | @author rakwireless.com 4 | @brief Used to include required header files. 5 | @version 0.1 6 | @date 2023-10-3 7 | @copyright Copyright (c) 2023 8 | **/ 9 | #ifndef __RAK2270_H__ 10 | #define __RAK2270_H__ 11 | 12 | 13 | #include 14 | 15 | typedef enum 16 | { 17 | ACTIVATE_STAGE = 0x0, 18 | JOIN_STAGE = 0x1, 19 | PERIODIC_UPLINK_STAGE = 0x2, 20 | } SYSTEM_STAGE_t; 21 | 22 | uint8_t get_system_stage(); 23 | void goto_join_stage(); 24 | void goto_periodic_uplink_stage(); 25 | void return_to_join_stage(); 26 | void join_handler(void); 27 | void periodic_uplink_handler(void); 28 | 29 | 30 | #define TIMER_PERIODIC_UPLINK RAK_TIMER_0 31 | #define TIMER_MD_SILENT_PERIOD RAK_TIMER_1 32 | #define TIMER_DOWNLINK_CHECK RAK_TIMER_2 33 | 34 | #define ACTIVATE_DEBOUNCE_TIMEOUT 5000 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Firmware/RAK2270/RAK2270.ino: -------------------------------------------------------------------------------- 1 | /** 2 | @file RAK2270.h 3 | @author rakwireless.com 4 | @brief For RAK2270. 5 | @version 1.1.2 6 | @date 2025-03-05 7 | @copyright Copyright (c) 2023 8 | **/ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "./custom_at.h" 14 | #include "./ntc.h" 15 | #include "./LoRaWAN.h" 16 | #include "./custom.h" 17 | #include "./lis3dh_md.h" 18 | #include "./RAK2270.h" 19 | 20 | 21 | CayenneLPP g_solution_data(255); 22 | 23 | #define ACT_CHECK WB_IO1 24 | 25 | void factory_mode_setup(); 26 | void normal_mode_setup(); 27 | 28 | void setup() 29 | { 30 | Serial.begin(115200); 31 | Serial.printf("RAK2270 SW Version: %s\r\n", CUSTOME_VER); 32 | delay(500); 33 | 34 | //Initialize ATC command and flash memory 35 | init_frequency_at(); 36 | init_factory_mode(); 37 | init_ntc_calibration_mode(); 38 | init_development_mode(); 39 | register_rejoin_atcmd(); 40 | init_join_interval(); 41 | init_linkcheck_timeout(); 42 | init_md_atcmd(); 43 | 44 | //Initialize NTC, GPIO/output/low 45 | pinMode(WB_A1, OUTPUT); 46 | pinMode(WB_A0, OUTPUT); 47 | digitalWrite(WB_A1, LOW); 48 | digitalWrite(WB_A0, LOW); // 设置 NTC 采样电路关断 49 | 50 | uint8_t factoryFlag = get_factory_mode(); 51 | Serial.printf("FACTORY_MODE: %s(%d)\r\n", factoryFlag?"ON":"OFF", factoryFlag); 52 | get_at_setting(SEND_FREQ_OFFSET); 53 | Serial.printf("Uplink period is %u minutes\r\n", g_txInterval); 54 | 55 | if (factoryFlag == FACTORY_MODE_ON){ 56 | factory_mode_setup(); 57 | } 58 | else { 59 | normal_mode_setup(); 60 | } 61 | } 62 | 63 | void factory_mode_setup() 64 | { 65 | float voltage = api.system.bat.get(); 66 | Serial.printf("Battery Voltage: %2.2f\r\n", voltage); 67 | float tempT = getTemperature(); 68 | float temperature = calibrateTemperature(tempT); 69 | Serial.printf("NTC_Temp.: %4.2f\r\n", temperature); 70 | bool ret = lis3dh_factory_test(); 71 | Serial.printf("LIS3DH: %s\r\n", ret? "OK" : "FAIL"); 72 | 73 | pinMode(ACT_CHECK, INPUT); 74 | udrv_gpio_set_pull((uint32_t)ACT_CHECK, GPIO_PULL_NONE); 75 | if(digitalRead(ACT_CHECK) == HIGH) { 76 | Serial.println("ACT_CHECK: Inactivated(1)"); 77 | } 78 | else { 79 | Serial.println("ACT_CHECK: Activated(0)!!!!!!!!"); 80 | } 81 | 82 | api.lorawan.dcs.set(0); 83 | } 84 | 85 | void normal_mode_setup() 86 | { 87 | lis3dh_init(); 88 | 89 | pinMode(ACT_CHECK, INPUT); 90 | udrv_gpio_set_pull((uint32_t)ACT_CHECK, GPIO_PULL_NONE); 91 | if(digitalRead(ACT_CHECK) == HIGH) { 92 | Serial.println("ACT_CHECK: Inactivated(1)"); 93 | } 94 | else { 95 | Serial.println("ACT_CHECK: Activated(0)!!!!!!!!"); 96 | } 97 | 98 | api.lorawan.dcs.set(1); 99 | loraWanInit(); 100 | } 101 | 102 | void factory_mode_loop(); 103 | void normal_mode_loop(); 104 | 105 | void loop() 106 | { 107 | if (get_factory_mode() == FACTORY_MODE_ON) { //FACTORY_MODE 108 | factory_mode_loop(); 109 | } 110 | else { 111 | normal_mode_loop(); 112 | } 113 | } 114 | 115 | void factory_mode_loop() 116 | { 117 | if (get_ntc_calibration_mode() && (api.lorawan.nwm.get()==1)) 118 | { 119 | if (api.lorawan.njs.get() == 0) 120 | { 121 | Serial.print("Waiting for Lorawan join...\r\n"); 122 | api.lorawan.join(1,0,7,0); 123 | delay(10000); 124 | } 125 | else 126 | { 127 | periodic_uplink_handler(); 128 | delay(30000); 129 | } 130 | } 131 | } 132 | 133 | uint8_t g_system_stage = ACTIVATE_STAGE; 134 | 135 | uint8_t get_system_stage() 136 | { 137 | return g_system_stage; 138 | } 139 | 140 | bool activate_debounce_check() 141 | { 142 | uint32_t timeout = ACTIVATE_DEBOUNCE_TIMEOUT; 143 | uint32_t start = millis(); 144 | do 145 | { 146 | api.system.sleep.all(500); 147 | if(digitalRead(ACT_CHECK) == HIGH) 148 | return false; 149 | } while ((millis()-start) < timeout); 150 | return true; 151 | } 152 | 153 | void downlink_check_handle() 154 | { 155 | if (g_return_pre_setting.enable) { 156 | if (g_return_pre_setting.cmd == DLCMD_DR) { 157 | api.lorawan.dr.set(g_return_pre_setting.data_8); 158 | Serial.printf("Return DR to %d\r\n", g_return_pre_setting.data_8); 159 | } 160 | else if (g_return_pre_setting.cmd == DLCMD_ADR) { 161 | api.lorawan.adr.set(g_return_pre_setting.data_8); 162 | Serial.printf("Return ADR to %d\r\n", g_return_pre_setting.data_8); 163 | } 164 | g_return_pre_setting.enable = false; 165 | } 166 | } 167 | 168 | void normal_mode_loop() 169 | { 170 | switch (g_system_stage) { 171 | case ACTIVATE_STAGE: 172 | if (get_development_mode()) 173 | { 174 | Serial.println("Device activated for development."); 175 | goto_join_stage(); 176 | } 177 | 178 | if(digitalRead(ACT_CHECK) == HIGH) 179 | { 180 | api.system.sleep.setup(RUI_WAKEUP_FALLING_EDGE, ACT_CHECK); 181 | api.system.sleep.all(); 182 | udrv_gpio_set_wakeup_disable(ACT_CHECK); 183 | 184 | if (activate_debounce_check()) 185 | { 186 | Serial.println("Device activated."); 187 | api.system.reboot(); 188 | //goto_join_stage(); 189 | } 190 | } 191 | else 192 | { 193 | if (activate_debounce_check()) 194 | { 195 | Serial.println("Device activated."); 196 | goto_join_stage(); 197 | } 198 | } 199 | break; 200 | case JOIN_STAGE: 201 | api.system.sleep.all(); 202 | break; 203 | case PERIODIC_UPLINK_STAGE: 204 | api.system.sleep.all(); 205 | 206 | //downlink command handle 207 | if (has_downlink_cmd) 208 | { 209 | downlink_cmd_handle(); 210 | 211 | if (f_cmd_rejoin) 212 | { 213 | f_cmd_rejoin = false; 214 | return_to_join_stage(); 215 | } 216 | 217 | if (g_return_pre_setting.check) 218 | { 219 | api.system.timer.stop(TIMER_DOWNLINK_CHECK); 220 | if (api.system.timer.create(TIMER_DOWNLINK_CHECK, (RAK_TIMER_HANDLER)downlink_check_handle, RAK_TIMER_ONESHOT) != true) 221 | { 222 | Serial.printf("Creating timer failed.\r\n"); 223 | } 224 | else if (api.system.timer.start(TIMER_DOWNLINK_CHECK, 5000, NULL) != true) 225 | { 226 | Serial.printf("Starting timer failed.\r\n"); 227 | } 228 | } 229 | } 230 | break; 231 | } 232 | } 233 | 234 | void goto_join_stage() 235 | { 236 | Serial.println("Goto join stage."); 237 | 238 | //create timer for join 239 | api.system.timer.stop(TIMER_PERIODIC_UPLINK); 240 | if (api.system.timer.create(TIMER_PERIODIC_UPLINK, (RAK_TIMER_HANDLER)join_handler, RAK_TIMER_PERIODIC) != true) 241 | { 242 | Serial.printf("Creating timer failed.\r\n"); 243 | } 244 | else if (api.system.timer.start(TIMER_PERIODIC_UPLINK, get_join_interval() *60 * 1000, NULL) != true) 245 | { 246 | Serial.printf("Starting timer failed.\r\n"); 247 | } 248 | 249 | join_handler(); 250 | 251 | g_system_stage = JOIN_STAGE; 252 | } 253 | 254 | void return_to_join_stage() 255 | { 256 | if (get_md_enable()) 257 | { 258 | lis3dh_md_end(); 259 | } 260 | goto_join_stage(); 261 | } 262 | 263 | void goto_periodic_uplink_stage() 264 | { 265 | Serial.println("Goto periodic uplink stage."); 266 | 267 | //create timer for periodic uplink 268 | api.system.timer.stop(TIMER_PERIODIC_UPLINK); 269 | if (api.system.timer.create(TIMER_PERIODIC_UPLINK, (RAK_TIMER_HANDLER)periodic_uplink_handler, RAK_TIMER_PERIODIC) != true) 270 | { 271 | Serial.printf("Creating timer failed.\r\n"); 272 | } 273 | else if (api.system.timer.start(TIMER_PERIODIC_UPLINK, g_txInterval * 60 * 1000, NULL) != true) 274 | { 275 | Serial.printf("Starting timer failed.\r\n"); 276 | } 277 | 278 | reset_linkcheck_start(); 279 | reset_last_send_ok_time(); 280 | periodic_uplink_handler(); 281 | 282 | //start lis3dh for motion detection 283 | if (get_md_enable()) 284 | { 285 | lis3dh_md_begin(); 286 | } 287 | 288 | g_system_stage = PERIODIC_UPLINK_STAGE; 289 | } 290 | 291 | void inactivate_protect_check() 292 | { 293 | if (get_development_mode()) 294 | { 295 | return; 296 | } 297 | 298 | if (digitalRead(ACT_CHECK) == HIGH) 299 | { 300 | Serial.println("activate_protect_check: Inactivated!!!!"); 301 | api.system.reboot(); 302 | } 303 | } 304 | 305 | void join_handler(void) 306 | { 307 | inactivate_protect_check(); 308 | 309 | //join lorawan network immediately 310 | Serial.println("Wait for LoRaWAN join...\r\n"); 311 | if (!api.lorawan.join(1,0,7,3)) // Join to Gateway 312 | { 313 | Serial.printf("LoRaWan OTAA - join fail! \r\n"); 314 | } 315 | } 316 | 317 | void periodic_uplink_handler(void) 318 | { 319 | inactivate_protect_check(); 320 | 321 | Serial.println("\r\nperiodic_uplink_handler"); 322 | float temperature = 0; 323 | float voltage = 0; 324 | float tempT = getTemperature(); 325 | 326 | temperature = calibrateTemperature(tempT); 327 | voltage = api.system.bat.get(); 328 | Serial.printf("temperature = %4.2f\r\n", temperature); 329 | Serial.printf("voltage = %2.2f\r\n", voltage); 330 | 331 | g_solution_data.reset(); 332 | g_solution_data.addTemperature(TEMP_CH, temperature); 333 | g_solution_data.addAnalogInput(BAT_CH, voltage); 334 | 335 | loraSendData(g_solution_data.getBuffer(), g_solution_data.getSize()); 336 | } 337 | -------------------------------------------------------------------------------- /Firmware/RAK2270/custom.h: -------------------------------------------------------------------------------- 1 | #ifndef __CUSTOM_H__ 2 | #define __CUSTOM_H__ 3 | 4 | 5 | //Version string 6 | #define FW_VERSION "1.1.2" 7 | #define CUSTOM_MODEL "RAK2270" 8 | #define CUSTOMER "Generic" 9 | #define CUSTOME_VER CUSTOM_MODEL"_"FW_VERSION"_"CUSTOMER 10 | 11 | //the channel for Cayenne LPP 12 | #define TEMP_CH 0 13 | #define BAT_CH 0 14 | 15 | //LoRaWAN configure 16 | #define LORAWAN_RETRY 0 17 | //FPort 18 | #define FPORT_PERIODIC_UPLINK 10 19 | #define FPORT_DOWNLINK_ACK 11 20 | #define FPORT_MD_EVENT_UPLINK 20 21 | 22 | //Interval 23 | #define INTERVAL_PERIODIC_UPLINK 60 24 | #define INTERVAL_JOIN 240 25 | #define INTERVAL_LINKCHECK 60 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /Firmware/RAK2270/custom_at.cpp: -------------------------------------------------------------------------------- 1 | #include "./custom_at.h" 2 | #include "./lis3dh_md.h" 3 | #include "./LoRaWAN.h" 4 | #include "./RAK2270.h" 5 | 6 | 7 | uint32_t g_txInterval; 8 | 9 | /** 10 | * @brief Get setting from flash 11 | */ 12 | bool get_at_setting(uint32_t setting_type) 13 | { 14 | uint8_t flash_value[18]; 15 | switch (setting_type) 16 | { 17 | case SEND_FREQ_OFFSET: 18 | if (!api.system.flash.get(SEND_FREQ_OFFSET, flash_value, 5)) 19 | { 20 | g_txInterval = DEFAULT_TXINTERVAL; 21 | save_at_setting(SEND_FREQ_OFFSET); 22 | return false; 23 | } 24 | if (flash_value[4] != 0xAA) 25 | { 26 | g_txInterval = DEFAULT_TXINTERVAL; 27 | save_at_setting(SEND_FREQ_OFFSET); 28 | return false; 29 | } 30 | 31 | g_txInterval = 0; 32 | g_txInterval |= flash_value[0] << 0; 33 | g_txInterval |= flash_value[1] << 8; 34 | g_txInterval |= flash_value[2] << 16; 35 | g_txInterval |= flash_value[3] << 24; 36 | 37 | if((g_txInterval > 1440) || (g_txInterval == 0)) 38 | { 39 | g_txInterval = DEFAULT_TXINTERVAL; 40 | save_at_setting(SEND_FREQ_OFFSET); 41 | return false; 42 | } 43 | 44 | return true; 45 | break; 46 | 47 | default: 48 | return false; 49 | } 50 | } 51 | 52 | /** 53 | * @brief Save setting to flash 54 | */ 55 | bool save_at_setting(uint32_t setting_type) 56 | { 57 | uint8_t flash_value[18] = {0}; 58 | bool wr_result = false; 59 | switch (setting_type) 60 | { 61 | case SEND_FREQ_OFFSET: 62 | flash_value[0] = (uint8_t)(g_txInterval >> 0); 63 | flash_value[1] = (uint8_t)(g_txInterval >> 8); 64 | flash_value[2] = (uint8_t)(g_txInterval >> 16); 65 | flash_value[3] = (uint8_t)(g_txInterval >> 24); 66 | flash_value[4] = 0xAA; // dirty byte for checking 67 | 68 | wr_result = api.system.flash.set(SEND_FREQ_OFFSET, flash_value, 5); 69 | wr_result = true; 70 | return wr_result; 71 | break; 72 | default: 73 | return false; 74 | break; 75 | } 76 | return false; 77 | } 78 | 79 | /** 80 | * @brief Handler for send frequency AT commands 81 | * 82 | */ 83 | int freq_send_handler(SERIAL_PORT port, char *cmd, stParam *param) 84 | { 85 | // char ble_data[50]; 86 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) 87 | { 88 | Serial.print(cmd); 89 | Serial.printf("=%ld minutes\r\n", g_txInterval); 90 | } 91 | else if (param->argc == 1) 92 | { 93 | for (int i = 0; i < strlen(param->argv[0]); i++) 94 | { 95 | if (!isdigit(*(param->argv[0] + i))) 96 | { 97 | Serial.printf("%d is no digit", i); 98 | return AT_PARAM_ERROR; 99 | } 100 | } 101 | 102 | uint32_t new_send_freq = strtoul(param->argv[0], NULL, 10); 103 | 104 | if((new_send_freq > 1440) || (new_send_freq == 0)) 105 | { 106 | Serial.printf("Does not exceed 1-1440"); 107 | return AT_PARAM_ERROR; 108 | } 109 | 110 | g_txInterval = new_send_freq; 111 | save_at_setting(SEND_FREQ_OFFSET); 112 | 113 | if (get_system_stage() == PERIODIC_UPLINK_STAGE) 114 | { 115 | api.system.timer.stop(TIMER_PERIODIC_UPLINK); 116 | if (api.system.timer.create(TIMER_PERIODIC_UPLINK, (RAK_TIMER_HANDLER)periodic_uplink_handler, RAK_TIMER_PERIODIC) != true) { 117 | Serial.printf("Failed to creat timer.\r\n"); 118 | } 119 | else if (api.system.timer.start(TIMER_PERIODIC_UPLINK, g_txInterval * 60000, NULL) != true) { 120 | Serial.printf("Failed to start timer.\r\n"); 121 | } 122 | } 123 | } 124 | else 125 | { 126 | return AT_PARAM_ERROR; 127 | } 128 | 129 | return AT_OK; 130 | } 131 | 132 | /** 133 | * @brief Add send-frequency AT command 134 | */ 135 | bool init_frequency_at(void) 136 | { 137 | return api.system.atMode.add((char *)"SENDFREQ", 138 | (char *)"Set/Get the periodic uplink interval (1-1440 minutes)", 139 | (char *)"SENDFREQ", freq_send_handler); 140 | } 141 | 142 | uint8_t g_FACTORY_MODE = DEFAULT_FACTORY_MODE; 143 | 144 | /** 145 | * @brief get factory mode 146 | */ 147 | uint8_t get_factory_mode(void) 148 | { 149 | return g_FACTORY_MODE; 150 | } 151 | 152 | /** 153 | * @brief set factory mode 154 | */ 155 | bool set_factory_mode(uint8_t mode) 156 | { 157 | if (api.system.flash.set(FACTORY_MODE_OFFSET, &mode, 1) == false) { 158 | return false; 159 | } 160 | g_FACTORY_MODE = mode; 161 | return true; 162 | } 163 | 164 | /** 165 | * @brief handle factory mode 166 | */ 167 | int factory_mode_handle(SERIAL_PORT port, char *cmd, stParam *param) 168 | { 169 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 170 | Serial.print(cmd); 171 | Serial.print("="); 172 | Serial.println(get_factory_mode()?"ON":"OFF"); 173 | } else if (param->argc == 1) { 174 | if (!isdigit(*(param->argv[0]))) { 175 | return AT_PARAM_ERROR; 176 | } 177 | 178 | uint8_t tmp = strtoul(param->argv[0], NULL, 10); 179 | if (tmp != FACTORY_MODE_OFF && tmp != FACTORY_MODE_ON) { 180 | return AT_PARAM_ERROR; 181 | } 182 | 183 | if (set_factory_mode(tmp) == false) { 184 | return AT_ERROR; 185 | } 186 | } 187 | else { 188 | return AT_PARAM_ERROR; 189 | } 190 | 191 | return AT_OK; 192 | } 193 | 194 | /** 195 | * @brief Register ATC Command for factory mode 196 | */ 197 | void register_factory_mode_atcmd(void) 198 | { 199 | api.system.atMode.add("FMODE", "Set/Get the Factory Mode status (0: OFF, 1: ON)", "FMODE", factory_mode_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 200 | } 201 | 202 | /** 203 | * @brief Initialize factory mode 204 | */ 205 | void init_factory_mode() 206 | { 207 | if (api.system.flash.get(FACTORY_MODE_OFFSET, &g_FACTORY_MODE, 1) == false) { 208 | Serial.print("Reading flash data failure...\r\n"); 209 | } 210 | else 211 | { 212 | if (g_FACTORY_MODE == 0xFF) 213 | { 214 | set_factory_mode(DEFAULT_FACTORY_MODE); 215 | } 216 | } 217 | register_factory_mode_atcmd(); 218 | } 219 | 220 | uint8_t g_NTC_CALIBRATION_MODE = DEFAULT_NTC_CALIBRATION_MODE; 221 | 222 | /** 223 | * @brief get ntc calibration mode 224 | */ 225 | uint8_t get_ntc_calibration_mode(void) 226 | { 227 | return g_NTC_CALIBRATION_MODE; 228 | } 229 | 230 | /** 231 | * @brief set ntc calibration mode 232 | */ 233 | bool set_ntc_calibration_mode(uint8_t mode) 234 | { 235 | if (api.system.flash.set(NTC_CALIBRATION_MODE_OFFSET, &mode, 1) == false) { 236 | return false; 237 | } 238 | g_NTC_CALIBRATION_MODE = mode; 239 | return true; 240 | } 241 | 242 | /** 243 | * @brief handle ntc calibration mode 244 | */ 245 | int ntc_calibration_mode_handle(SERIAL_PORT port, char *cmd, stParam *param) 246 | { 247 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 248 | Serial.print(cmd); 249 | Serial.print("="); 250 | Serial.printf("%d\r\n",get_ntc_calibration_mode()); 251 | } else if (param->argc == 1) { 252 | if (!isdigit(*(param->argv[0]))) { 253 | return AT_PARAM_ERROR; 254 | } 255 | 256 | uint8_t tmp = strtoul(param->argv[0], NULL, 10); 257 | if (tmp != 0 && tmp != 1) { 258 | return AT_PARAM_ERROR; 259 | } 260 | 261 | if (set_ntc_calibration_mode(tmp) == false) { 262 | return AT_ERROR; 263 | } 264 | } 265 | else { 266 | return AT_PARAM_ERROR; 267 | } 268 | 269 | return AT_OK; 270 | } 271 | 272 | /** 273 | * @brief Register ATC Command for ntc calibration mode 274 | */ 275 | void register_ntc_calibration_mode_atcmd(void) 276 | { 277 | api.system.atMode.add("NCMODE", "Set/Get the NTC Calibration Mode status (0: OFF, 1: ON)", "NCMODE", ntc_calibration_mode_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 278 | } 279 | 280 | /** 281 | * @brief Initialize ntc calibration mode 282 | */ 283 | void init_ntc_calibration_mode() 284 | { 285 | if (api.system.flash.get(NTC_CALIBRATION_MODE_OFFSET, &g_NTC_CALIBRATION_MODE, 1) == false) 286 | { 287 | Serial.print("Reading flash data failure...\r\n"); 288 | } 289 | else 290 | { 291 | if (g_NTC_CALIBRATION_MODE == 0xFF) 292 | { 293 | set_ntc_calibration_mode(DEFAULT_NTC_CALIBRATION_MODE); 294 | } 295 | } 296 | register_ntc_calibration_mode_atcmd(); 297 | } 298 | 299 | uint8_t g_DEVELOPMENT_MODE = DEFAULT_DEVELOPMENT_MODE; 300 | 301 | /** 302 | * @brief get development mode 303 | */ 304 | uint8_t get_development_mode(void) 305 | { 306 | return g_DEVELOPMENT_MODE; 307 | } 308 | 309 | /** 310 | * @brief set development mode 311 | */ 312 | bool set_development_mode(uint8_t mode) 313 | { 314 | g_DEVELOPMENT_MODE = mode; 315 | return true; 316 | } 317 | 318 | /** 319 | * @brief handle development mode 320 | */ 321 | int development_mode_handle(SERIAL_PORT port, char *cmd, stParam *param) 322 | { 323 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 324 | Serial.print(cmd); 325 | Serial.print("="); 326 | Serial.printf("%d\r\n",get_development_mode()); 327 | } else if (param->argc == 1) { 328 | if (!isdigit(*(param->argv[0]))) { 329 | return AT_PARAM_ERROR; 330 | } 331 | 332 | uint8_t tmp = strtoul(param->argv[0], NULL, 10); 333 | if (tmp != 0 && tmp != 1) { 334 | return AT_PARAM_ERROR; 335 | } 336 | 337 | if (set_development_mode(tmp) == false) { 338 | return AT_ERROR; 339 | } 340 | } 341 | else { 342 | return AT_PARAM_ERROR; 343 | } 344 | 345 | return AT_OK; 346 | } 347 | 348 | /** 349 | * @brief Register ATC Command for development mode 350 | */ 351 | void register_development_mode_atcmd(void) 352 | { 353 | api.system.atMode.add("DMODE", "Set/Get the Development Mode (0: OFF, 1: ON)", "DMODE", development_mode_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 354 | } 355 | 356 | /** 357 | * @brief Initialize development mode 358 | */ 359 | void init_development_mode() 360 | { 361 | set_development_mode(DEFAULT_DEVELOPMENT_MODE); 362 | register_development_mode_atcmd(); 363 | } 364 | 365 | 366 | /** 367 | * @brief handle re-join 368 | */ 369 | int rejoin_handle(SERIAL_PORT port, char *cmd, stParam *param) 370 | { 371 | if (param->argc == 0) { 372 | if (get_factory_mode() == FACTORY_MODE_OFF) 373 | return_to_join_stage(); 374 | return AT_OK; 375 | } 376 | else { 377 | return AT_PARAM_ERROR; 378 | } 379 | } 380 | 381 | /** 382 | * @brief Register ATC Command for re-join 383 | */ 384 | void register_rejoin_atcmd(void) 385 | { 386 | api.system.atMode.add("REJOIN", "Return To Join Stage", "JOIN", rejoin_handle, RAK_ATCMD_PERM_READ); 387 | } 388 | 389 | uint32_t g_JOIN_INTERVAL = DEFAULT_JOIN_INTERVAL; 390 | 391 | /** 392 | * @brief get join interval 393 | */ 394 | uint32_t get_join_interval(void) 395 | { 396 | return g_JOIN_INTERVAL; 397 | } 398 | 399 | /** 400 | * @brief set join interval 401 | */ 402 | bool set_join_interval(uint32_t interval) 403 | { 404 | if (api.system.flash.set(JOIN_INTERVAL_OFFSET, (uint8_t*)&interval, 4) == false) { 405 | return false; 406 | } 407 | g_JOIN_INTERVAL = interval; 408 | return true; 409 | } 410 | 411 | /** 412 | * @brief handle join interval 413 | */ 414 | int join_interval_handle(SERIAL_PORT port, char *cmd, stParam *param) 415 | { 416 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 417 | Serial.print(cmd); 418 | Serial.print("="); 419 | Serial.println(get_join_interval()); 420 | } else if (param->argc == 1) { 421 | if (!isdigit(*(param->argv[0]))) { 422 | return AT_PARAM_ERROR; 423 | } 424 | 425 | uint32_t tmp = strtoul(param->argv[0], NULL, 10); 426 | if (tmp < 1 || tmp > 1440) { 427 | return AT_PARAM_ERROR; 428 | } 429 | 430 | if (set_join_interval(tmp) == false) { 431 | return AT_ERROR; 432 | } 433 | } 434 | else { 435 | return AT_PARAM_ERROR; 436 | } 437 | 438 | return AT_OK; 439 | } 440 | 441 | /** 442 | * @brief Register ATC Command for join interval 443 | */ 444 | void register_join_interval_atcmd(void) 445 | { 446 | api.system.atMode.add("JINTV", "Set/Get the periodic join interval (1-1440 minutes)", "JOIN", join_interval_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 447 | } 448 | 449 | /** 450 | * @brief Initialize join interval 451 | */ 452 | void init_join_interval() 453 | { 454 | if (api.system.flash.get(JOIN_INTERVAL_OFFSET, (uint8_t*)&g_JOIN_INTERVAL, 4) == false) { 455 | Serial.print("Reading flash data failure...\r\n"); 456 | } 457 | else 458 | { 459 | if (g_JOIN_INTERVAL == 0xFFFFFFFF) 460 | { 461 | set_join_interval(DEFAULT_JOIN_INTERVAL); 462 | } 463 | } 464 | register_join_interval_atcmd(); 465 | } 466 | 467 | uint32_t g_LINKCHECK_TIMEOUT = DEFAULT_LINKCHECK_TIMEOUT; 468 | 469 | /** 470 | * @brief get linkcheck_timeout 471 | */ 472 | uint32_t get_linkcheck_timeout(void) 473 | { 474 | return g_LINKCHECK_TIMEOUT; 475 | } 476 | 477 | /** 478 | * @brief set linkcheck_timeout 479 | */ 480 | bool set_linkcheck_timeout(uint32_t timeout) 481 | { 482 | if (api.system.flash.set(LINKCHECK_TIMEOUT_OFFSET, (uint8_t*)&timeout, 4) == false) { 483 | return false; 484 | } 485 | g_LINKCHECK_TIMEOUT = timeout; 486 | return true; 487 | } 488 | 489 | /** 490 | * @brief handle linkcheck_timeout 491 | */ 492 | int linkcheck_timeout_handle(SERIAL_PORT port, char *cmd, stParam *param) 493 | { 494 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 495 | Serial.print(cmd); 496 | Serial.print("="); 497 | Serial.println(get_linkcheck_timeout()); 498 | } else if (param->argc == 1) { 499 | if (!isdigit(*(param->argv[0]))) { 500 | return AT_PARAM_ERROR; 501 | } 502 | 503 | uint32_t tmp = strtoul(param->argv[0], NULL, 10); 504 | if (tmp > 1440) { 505 | return AT_PARAM_ERROR; 506 | } 507 | 508 | if (set_linkcheck_timeout(tmp) == false) { 509 | return AT_ERROR; 510 | } 511 | reset_linkcheck_start(); 512 | reset_last_send_ok_time(); 513 | } 514 | else { 515 | return AT_PARAM_ERROR; 516 | } 517 | 518 | return AT_OK; 519 | } 520 | 521 | /** 522 | * @brief Register ATC Command for linkcheck_timeout 523 | */ 524 | void register_linkcheck_timeout_atcmd(void) 525 | { 526 | api.system.atMode.add("LCTIMEOUT", "Set/Get the linkcheck timeout for re-join (0-1440 minutes, 0: Disable)", "JOIN", linkcheck_timeout_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 527 | } 528 | 529 | /** 530 | * @brief Initialize linkcheck_timeout 531 | */ 532 | void init_linkcheck_timeout() 533 | { 534 | if (api.system.flash.get(LINKCHECK_TIMEOUT_OFFSET, (uint8_t*)&g_LINKCHECK_TIMEOUT, 4) == false) { 535 | Serial.print("Reading flash data failure...\r\n"); 536 | } 537 | else 538 | { 539 | if (g_LINKCHECK_TIMEOUT == 0xFFFFFFFF) 540 | { 541 | set_linkcheck_timeout(DEFAULT_LINKCHECK_TIMEOUT); 542 | } 543 | } 544 | register_linkcheck_timeout_atcmd(); 545 | } 546 | 547 | 548 | //md_threshold, md_duration, md_number, md_silent_period 549 | uint8_t g_md_enable; //Motion detect, enable 550 | uint16_t g_md_threshold; //Motion detect, detect threshold 551 | uint16_t g_md_sample_rate; //Motion detect, lis3dh sample 552 | uint16_t g_md_duration; //Motion detect, the duration for event trigger 553 | uint8_t g_md_number; //Motion detect, the number of deteted for event trigger 554 | uint16_t g_md_silent_period; //Motion detect, the silent period after event trigger 555 | 556 | /** 557 | * @brief get motion detect enable 558 | */ 559 | uint8_t get_md_enable(void) 560 | { 561 | return g_md_enable; 562 | } 563 | 564 | /** 565 | * @brief set motion detect enable 566 | */ 567 | bool set_md_enable(uint8_t enable) 568 | { 569 | if (api.system.flash.set(MD_ENABLE_OFFSET, &enable, 1) == false) { 570 | return false; 571 | } 572 | g_md_enable = enable; 573 | return true; 574 | } 575 | 576 | /** 577 | * @brief get motion detect threshold 578 | */ 579 | uint16_t get_md_threshold(void) 580 | { 581 | return g_md_threshold; 582 | } 583 | 584 | /** 585 | * @brief set motion detect threshold 586 | */ 587 | bool set_md_threshold(uint16_t threshold) 588 | { 589 | if (api.system.flash.set(MD_THRESHOLD_OFFSET, (uint8_t*)&threshold, 2) == false) { 590 | return false; 591 | } 592 | g_md_threshold = threshold; 593 | return true; 594 | } 595 | 596 | /** 597 | * @brief get lis3dh sample rate 598 | */ 599 | uint16_t get_md_sample_rate(void) 600 | { 601 | return g_md_sample_rate; 602 | } 603 | 604 | /** 605 | * @brief set lis3dh sample rate 606 | */ 607 | bool set_md_sample_rate(uint16_t sample_rate) 608 | { 609 | if (api.system.flash.set(MD_SAMPLE_RATE_OFFSET, (uint8_t*)&sample_rate, 2) == false) { 610 | return false; 611 | } 612 | g_md_sample_rate = sample_rate; 613 | return true; 614 | } 615 | 616 | /** 617 | * @brief get motion detect duration 618 | */ 619 | uint16_t get_md_duration(void) 620 | { 621 | return g_md_duration; 622 | } 623 | 624 | /** 625 | * @brief set motion detect duration 626 | */ 627 | bool set_md_duration(uint16_t duration) 628 | { 629 | if (api.system.flash.set(MD_DURATION_OFFSET, (uint8_t*)&duration, 2) == false) { 630 | return false; 631 | } 632 | g_md_duration = duration; 633 | return true; 634 | } 635 | 636 | /** 637 | * @brief get motion detect number 638 | */ 639 | uint8_t get_md_number(void) 640 | { 641 | return g_md_number; 642 | } 643 | 644 | /** 645 | * @brief set motion detect number 646 | */ 647 | bool set_md_number(uint8_t number) 648 | { 649 | if (api.system.flash.set(MD_NUMBER_OFFSET, (uint8_t*)&number, 1) == false) { 650 | return false; 651 | } 652 | g_md_number = number; 653 | return true; 654 | } 655 | 656 | /** 657 | * @brief get motion detect silent period 658 | */ 659 | uint16_t get_md_silent_period(void) 660 | { 661 | return g_md_silent_period; 662 | } 663 | 664 | /** 665 | * @brief set motion detect silent period 666 | */ 667 | bool set_md_silent_period(uint16_t silent_period) 668 | { 669 | if (api.system.flash.set(MD_SILENT_PERIOD_OFFSET, (uint8_t*)&silent_period, 2) == false) { 670 | return false; 671 | } 672 | g_md_silent_period = silent_period; 673 | return true; 674 | } 675 | 676 | /** 677 | * @brief the handler of ATC Command for motion detect enable 678 | */ 679 | int md_enable_handle(SERIAL_PORT port, char *cmd, stParam *param) 680 | { 681 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 682 | Serial.print(cmd); 683 | Serial.print("="); 684 | Serial.println(get_md_enable()); 685 | } else if (param->argc == 1) { 686 | if (!isdigit(*(param->argv[0]))) { 687 | return AT_PARAM_ERROR; 688 | } 689 | 690 | uint8_t tmp = (uint8_t)strtoul(param->argv[0], NULL, 10); 691 | if (tmp > 1) { 692 | return AT_PARAM_ERROR; 693 | } 694 | 695 | if (get_md_enable() == tmp) { 696 | return AT_OK; 697 | } 698 | 699 | if (set_md_enable(tmp) == false) { 700 | return AT_ERROR; 701 | } 702 | else { 703 | if (get_md_enable()) { 704 | lis3dh_md_begin(); 705 | } 706 | else { 707 | lis3dh_md_end(); 708 | } 709 | } 710 | } 711 | else { 712 | return AT_PARAM_ERROR; 713 | } 714 | 715 | return AT_OK; 716 | } 717 | 718 | /** 719 | * @brief Register ATC Command for motion detect status 720 | */ 721 | void register_md_enable_atcmd(void) 722 | { 723 | api.system.atMode.add("MDEN", "Set/Get the Motion Detection status (0: Disable, 1: Enable)", "MD", md_enable_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 724 | } 725 | 726 | /** 727 | * @brief the handler of ATC Command for motion detect threshold 728 | */ 729 | int md_threshold_handle(SERIAL_PORT port, char *cmd, stParam *param) 730 | { 731 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 732 | Serial.print(cmd); 733 | Serial.print("="); 734 | Serial.println(get_md_threshold()); 735 | } else if (param->argc == 1) { 736 | if (!isdigit(*(param->argv[0]))) { 737 | return AT_PARAM_ERROR; 738 | } 739 | 740 | uint16_t tmp = (uint16_t)strtoul(param->argv[0], NULL, 10); 741 | if(tmp < 50 || tmp > 16000) 742 | return AT_PARAM_ERROR; 743 | 744 | if (get_md_threshold() == tmp) { 745 | return AT_OK; 746 | } 747 | 748 | if (set_md_threshold(tmp) == false) { 749 | return AT_ERROR; 750 | } 751 | else { 752 | if (get_md_enable()) { 753 | lis3dh_md_pause(); 754 | lis3dh_md_resume(); 755 | } 756 | } 757 | } 758 | else { 759 | return AT_PARAM_ERROR; 760 | } 761 | 762 | return AT_OK; 763 | } 764 | 765 | /** 766 | * @brief Register ATC Command for motion detect threshold 767 | */ 768 | void register_md_threshold_atcmd(void) 769 | { 770 | api.system.atMode.add("MDTH", "Set/Get the detection threshold of the accelerometer (50~16000 mG)", "MD", md_threshold_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 771 | } 772 | 773 | /** 774 | * @brief the handler of ATC Command for lis3dh sample rate 775 | */ 776 | int md_sample_rate_handle(SERIAL_PORT port, char *cmd, stParam *param) 777 | { 778 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 779 | Serial.print(cmd); 780 | Serial.print("="); 781 | Serial.println(get_md_sample_rate()); 782 | } else if (param->argc == 1) { 783 | if (!isdigit(*(param->argv[0]))) { 784 | return AT_PARAM_ERROR; 785 | } 786 | 787 | uint16_t tmp = (uint16_t)strtoul(param->argv[0], NULL, 10); 788 | if(tmp > 1) 789 | return AT_PARAM_ERROR; 790 | 791 | if (get_md_sample_rate() == tmp) { 792 | return AT_OK; 793 | } 794 | 795 | if (set_md_sample_rate(tmp) == false) { 796 | return AT_ERROR; 797 | } 798 | else { 799 | if (get_md_enable()) { 800 | lis3dh_md_pause(); 801 | lis3dh_md_resume(); 802 | } 803 | } 804 | } 805 | else { 806 | return AT_PARAM_ERROR; 807 | } 808 | 809 | return AT_OK; 810 | } 811 | 812 | /** 813 | * @brief Register ATC Command for lis3dh sample rate 814 | */ 815 | void register_md_sample_rate_atcmd(void) 816 | { 817 | api.system.atMode.add("MDSR", "Set/Get the sample rate of the accelerometer (0: 1 Hz, 1: 10 Hz)", "MD", md_sample_rate_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 818 | } 819 | 820 | /** 821 | * @brief the handler of ATC Command for motion detect duration 822 | */ 823 | int md_duration_handle(SERIAL_PORT port, char *cmd, stParam *param) 824 | { 825 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 826 | Serial.print(cmd); 827 | Serial.print("="); 828 | Serial.println(get_md_duration()); 829 | } else if (param->argc == 1) { 830 | if (!isdigit(*(param->argv[0]))) { 831 | return AT_PARAM_ERROR; 832 | } 833 | 834 | uint16_t tmp = (uint16_t)strtoul(param->argv[0], NULL, 10); 835 | if(tmp < 300 || tmp > 15000) 836 | return AT_PARAM_ERROR; 837 | 838 | if (get_md_duration() == tmp) { 839 | return AT_OK; 840 | } 841 | 842 | if (set_md_duration(tmp) == false) { 843 | return AT_ERROR; 844 | } 845 | else { 846 | if (get_md_enable()) { 847 | lis3dh_md_pause(); 848 | lis3dh_md_resume(); 849 | } 850 | } 851 | } 852 | else { 853 | return AT_PARAM_ERROR; 854 | } 855 | 856 | return AT_OK; 857 | } 858 | 859 | /** 860 | * @brief Register ATC Command for motion detect duration 861 | */ 862 | void register_md_duration_atcmd(void) 863 | { 864 | api.system.atMode.add("MDDUR", "Set/Get the detect duration of the Motion Detection (300~15000 mseconds)", "MD", md_duration_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 865 | } 866 | 867 | /** 868 | * @brief the handler of ATC Command for motion detect number 869 | */ 870 | int md_number_handle(SERIAL_PORT port, char *cmd, stParam *param) 871 | { 872 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 873 | Serial.print(cmd); 874 | Serial.print("="); 875 | Serial.println(get_md_number()); 876 | } else if (param->argc == 1) { 877 | if (!isdigit(*(param->argv[0]))) { 878 | return AT_PARAM_ERROR; 879 | } 880 | 881 | uint8_t tmp = (uint8_t)strtoul(param->argv[0], NULL, 10); 882 | if (tmp < 1 || tmp > 10) { 883 | return AT_PARAM_ERROR; 884 | } 885 | 886 | if (get_md_number() == tmp) { 887 | return AT_OK; 888 | } 889 | 890 | if (set_md_number(tmp) == false) { 891 | return AT_ERROR; 892 | } 893 | else { 894 | if (get_md_enable()) { 895 | lis3dh_md_pause(); 896 | lis3dh_md_resume(); 897 | } 898 | } 899 | } 900 | else { 901 | return AT_PARAM_ERROR; 902 | } 903 | 904 | return AT_OK; 905 | } 906 | 907 | /** 908 | * @brief Register ATC Command for motion detect number 909 | */ 910 | void register_md_number_atcmd(void) 911 | { 912 | api.system.atMode.add("MDNUM", "Set/Get the number of triggers required to send an event uplink (1~10)", "MD", md_number_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 913 | } 914 | 915 | /** 916 | * @brief the handler of ATC Command for motion detect silent period 917 | */ 918 | int md_silent_period_handle(SERIAL_PORT port, char *cmd, stParam *param) 919 | { 920 | if (param->argc == 1 && !strcmp(param->argv[0], "?")) { 921 | Serial.print(cmd); 922 | Serial.print("="); 923 | Serial.println(get_md_silent_period()); 924 | } else if (param->argc == 1) { 925 | if (!isdigit(*(param->argv[0]))) { 926 | return AT_PARAM_ERROR; 927 | } 928 | 929 | uint16_t tmp = (uint16_t)strtoul(param->argv[0], NULL, 10); 930 | if(tmp < 10 || tmp > 3600) 931 | return AT_PARAM_ERROR; 932 | 933 | if (get_md_silent_period() == tmp) { 934 | return AT_OK; 935 | } 936 | 937 | if (set_md_silent_period(tmp) == false) { 938 | return AT_ERROR; 939 | } 940 | else { 941 | if (get_md_enable()) { 942 | lis3dh_md_pause(); 943 | lis3dh_md_resume(); 944 | } 945 | } 946 | } 947 | else { 948 | return AT_PARAM_ERROR; 949 | } 950 | 951 | return AT_OK; 952 | } 953 | 954 | /** 955 | * @brief Register ATC Command for motion detect silent period 956 | */ 957 | void register_md_silent_period_atcmd(void) 958 | { 959 | api.system.atMode.add("MDSP", "Set/Get the silent period after sending an event uplink (10~3600 seconds)", "MD", md_silent_period_handle, RAK_ATCMD_PERM_WRITE | RAK_ATCMD_PERM_READ); 960 | } 961 | 962 | /** 963 | * @brief Initialize the parameters for motion detect 964 | */ 965 | void init_md_atcmd() 966 | { 967 | if (api.system.flash.get(MD_ENABLE_OFFSET, &g_md_enable, 1) == false) { 968 | Serial.print("Reading flash data failure...\r\n"); 969 | } 970 | if (g_md_enable == 0xFF) { 971 | g_md_enable = DEFAULT_MD_ENABLE; 972 | set_md_enable(g_md_enable); 973 | } 974 | //Serial.printf("md_enable: %d(%d)\r\n", g_md_enable, get_md_enable()); 975 | 976 | if (api.system.flash.get(MD_THRESHOLD_OFFSET, (uint8_t*)&g_md_threshold, 2) == false) { 977 | Serial.print("Reading flash data failure...\r\n"); 978 | } 979 | if (g_md_threshold == 0xFFFF) { 980 | g_md_threshold = DEFAULT_MD_THRESHOLD; 981 | set_md_threshold(g_md_threshold); 982 | } 983 | //Serial.printf("md_threshold: %d(%d)\r\n", g_md_threshold, get_md_threshold()); 984 | 985 | if (api.system.flash.get(MD_SAMPLE_RATE_OFFSET, (uint8_t*)&g_md_sample_rate, 2) == false) { 986 | Serial.print("Reading flash data failure...\r\n"); 987 | } 988 | if (g_md_sample_rate == 0xFFFF) { 989 | g_md_sample_rate = DEFAULT_MD_SAMPLE_RATE; 990 | set_md_sample_rate(g_md_sample_rate); 991 | } 992 | //Serial.printf("g_md_sample_rate: %d(%d)\r\n", g_md_sample_rate, get_md_sample_rate()); 993 | 994 | if (api.system.flash.get(MD_DURATION_OFFSET, (uint8_t*)&g_md_duration, 2) == false) { 995 | Serial.print("Reading flash data failure...\r\n"); 996 | } 997 | if (g_md_duration == 0xFFFF) { 998 | g_md_duration = DEFAULT_MD_DURATION; 999 | set_md_duration(g_md_duration); 1000 | } 1001 | //Serial.printf("md_duration: %d(%d)\r\n", g_md_duration, get_md_duration()); 1002 | 1003 | if (api.system.flash.get(MD_NUMBER_OFFSET, (uint8_t*)&g_md_number, 1) == false) { 1004 | Serial.print("Reading flash data failure...\r\n"); 1005 | } 1006 | if (g_md_number == 0xFF) { 1007 | g_md_number = DEFAULT_MD_NUMBER; 1008 | set_md_number(g_md_number); 1009 | } 1010 | //Serial.printf("md_number: %d(%d)\r\n", g_md_number, get_md_number()); 1011 | 1012 | if (api.system.flash.get(MD_SILENT_PERIOD_OFFSET, (uint8_t*)&g_md_silent_period, 2) == false) { 1013 | Serial.print("Reading flash data failure...\r\n"); 1014 | } 1015 | if (g_md_silent_period == 0xFFFF) { 1016 | g_md_silent_period = DEFAULT_MD_SILENT_PERIOD; 1017 | set_md_silent_period(g_md_silent_period); 1018 | } 1019 | //Serial.printf("md_silent_period: %d(%d)\r\n", g_md_silent_period, get_md_silent_period()); 1020 | 1021 | register_md_enable_atcmd(); 1022 | register_md_threshold_atcmd(); 1023 | register_md_sample_rate_atcmd(); 1024 | register_md_duration_atcmd(); 1025 | register_md_number_atcmd(); 1026 | register_md_silent_period_atcmd(); 1027 | } 1028 | 1029 | -------------------------------------------------------------------------------- /Firmware/RAK2270/custom_at.h: -------------------------------------------------------------------------------- 1 | #ifndef __CUSTOM_AT_H__ 2 | #define __CUSTOM_AT_H__ 3 | 4 | #include 5 | #include "./custom.h" 6 | 7 | /* The offset of configure in flash memory */ 8 | #define SEND_FREQ_OFFSET (0x0) //5 9 | #define FACTORY_MODE_OFFSET (0x5) //1 10 | #define NTC_CALIBRATION_MODE_OFFSET (0x6) //1 11 | #define MD_ENABLE_OFFSET (0x8) //1 12 | #define MD_THRESHOLD_OFFSET (0xC) //2 13 | #define MD_DURATION_OFFSET (0x10) //2 14 | #define MD_NUMBER_OFFSET (0x14) //1 15 | #define MD_SILENT_PERIOD_OFFSET (0x18) //2 16 | #define MD_SAMPLE_RATE_OFFSET (0x1A) //2 17 | #define JOIN_INTERVAL_OFFSET (0x1C) //4 18 | #define LINKCHECK_TIMEOUT_OFFSET (0x20) //4 19 | 20 | 21 | /* Sending frequency for periodic uplink */ 22 | extern uint32_t g_txInterval; 23 | 24 | #define DEFAULT_TXINTERVAL INTERVAL_PERIODIC_UPLINK //60 mins 25 | 26 | bool init_frequency_at(void); 27 | bool get_at_setting(uint32_t setting_type); 28 | bool save_at_setting(uint32_t setting_type); 29 | 30 | /* operate mode */ 31 | #define DEFAULT_FACTORY_MODE FACTORY_MODE_ON 32 | #define DEFAULT_NTC_CALIBRATION_MODE 0x0 33 | #define DEFAULT_DEVELOPMENT_MODE 0x0 34 | 35 | /* Enable or disable for factory mode */ 36 | typedef enum 37 | { 38 | FACTORY_MODE_OFF = 0x0, 39 | FACTORY_MODE_ON = 0x1 40 | } FACTORY_MODE_t; 41 | 42 | uint8_t get_factory_mode(void); 43 | void init_factory_mode(); 44 | uint8_t get_ntc_calibration_mode(); 45 | void init_ntc_calibration_mode(); 46 | uint8_t get_development_mode(); 47 | void init_development_mode(); 48 | 49 | /* Join and re-join */ 50 | #define DEFAULT_JOIN_INTERVAL INTERVAL_JOIN //480 mins (1-1440) 51 | #define DEFAULT_LINKCHECK_TIMEOUT 0 //0: disable, v1.1.0=12*60 mins (0-1440) 52 | 53 | void register_rejoin_atcmd(); 54 | uint32_t get_join_interval(); 55 | void init_join_interval(); 56 | uint32_t get_linkcheck_timeout(); 57 | bool set_linkcheck_timeout(uint32_t timeout); 58 | void init_linkcheck_timeout(); 59 | 60 | /* Motion detection */ 61 | #define DEFAULT_MD_ENABLE 0 62 | #define DEFAULT_MD_THRESHOLD 400 //400 mg (50~16000) 63 | #define DEFAULT_MD_SAMPLE_RATE 1 //10 Hz (0: 1 HZ, 1: 10 Hz) 64 | #define DEFAULT_MD_DURATION 1000 //1000 ms (300~15000) 65 | #define DEFAULT_MD_NUMBER 2 //2 (1~10) 66 | #define DEFAULT_MD_SILENT_PERIOD 3600 //10 s (10~3600) 67 | 68 | uint8_t get_md_enable(void); 69 | bool set_md_enable(uint8_t enable); 70 | uint16_t get_md_threshold(void); 71 | bool set_md_threshold(uint16_t threshold); 72 | uint16_t get_md_sample_rate(void); 73 | bool set_md_sample_rate(uint16_t sample_rate); 74 | uint16_t get_md_duration(void); 75 | bool set_md_duration(uint16_t duration); 76 | uint8_t get_md_number(void); 77 | bool set_md_number(uint8_t number); 78 | uint16_t get_md_silent_period(void); 79 | bool set_md_silent_period(uint16_t silent_period); 80 | void init_md_atcmd(); 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /Firmware/RAK2270/lis3dh_md.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "./LoRaWAN.h" 4 | #include "./lis3dh_md.h" 5 | #include "./custom_at.h" 6 | #include "./RAK2270.h" 7 | 8 | #define USED_INT2 0 //INT2 reserve 9 | #if USED_INT2 10 | #define LIS3DH_INT2_CFG 0x34 11 | #define LIS3DH_INT2_SRC 0x35 12 | #define LIS3DH_INT2_THS 0x36 13 | #define LIS3DH_INT2_DURATION 0x37 14 | #endif 15 | 16 | LIS3DH myIMU(I2C_MODE, 0x19); 17 | 18 | void lis3dh_showConfigReg(); 19 | void lis3dh_setReg(); 20 | void lis3dh_get_md_data(); 21 | void lis3dh_INT1_handle(); 22 | void lis3dh_INT2_handle(); 23 | 24 | 25 | /* 26 | BEGIN of linked list queue for MD event 27 | */ 28 | struct Node { 29 | uint32_t data; 30 | struct Node* next; 31 | }; 32 | 33 | struct Node* head = NULL; 34 | uint8_t send_event_flag = 0; 35 | 36 | struct Node* createNode(uint32_t value); 37 | void append(struct Node** head, uint32_t value); 38 | void deleteNode(struct Node** head, struct Node* target); 39 | struct Node* firstNode(struct Node* head); 40 | struct Node* lastNode(struct Node* head); 41 | int NodeSize(struct Node* head); 42 | void display(struct Node* head); 43 | void freeList(struct Node** head); 44 | /* 45 | END of linked list queue for MD event 46 | */ 47 | 48 | void lis3dh_showConfigReg() 49 | { 50 | uint8_t readData = 0; 51 | 52 | myIMU.readRegister(&readData, LIS3DH_CTRL_REG1); 53 | Serial.printf("LIS3DH_CTRL_REG1: 0x%02X\r\n", readData); 54 | myIMU.readRegister(&readData, LIS3DH_CTRL_REG2); 55 | Serial.printf("LIS3DH_CTRL_REG2: 0x%02X\r\n", readData); 56 | myIMU.readRegister(&readData, LIS3DH_CTRL_REG3); 57 | Serial.printf("LIS3DH_CTRL_REG3: 0x%02X\r\n", readData); 58 | myIMU.readRegister(&readData, LIS3DH_CTRL_REG4); 59 | Serial.printf("LIS3DH_CTRL_REG4: 0x%02X\r\n", readData); 60 | myIMU.readRegister(&readData, LIS3DH_CTRL_REG5); 61 | Serial.printf("LIS3DH_CTRL_REG5: 0x%02X\r\n", readData); 62 | myIMU.readRegister(&readData, LIS3DH_CTRL_REG6); 63 | Serial.printf("LIS3DH_CTRL_REG6: 0x%02X\r\n", readData); 64 | 65 | myIMU.readRegister(&readData, LIS3DH_INT1_CFG); 66 | Serial.printf("LIS3DH_INT1_CFG: 0x%02X\r\n", readData); 67 | myIMU.readRegister(&readData, LIS3DH_INT1_THS); 68 | Serial.printf("LIS3DH_INT1_THS: 0x%02X\r\n", readData); 69 | myIMU.readRegister(&readData, LIS3DH_INT1_DURATION); 70 | Serial.printf("LIS3DH_INT1_DURATION: 0x%02X\r\n", readData); 71 | 72 | #if USED_INT2 73 | myIMU.readRegister(&readData, LIS3DH_INT2_CFG); 74 | Serial.printf("LIS3DH_INT2_CFG: 0x%02X\r\n", readData); 75 | myIMU.readRegister(&readData, LIS3DH_INT2_THS); 76 | Serial.printf("LIS3DH_INT2_THS: 0x%02X\r\n", readData); 77 | myIMU.readRegister(&readData, LIS3DH_INT2_DURATION); 78 | Serial.printf("LIS3DH_INT2_DURATION: 0x%02X\r\n", readData); 79 | #endif 80 | } 81 | 82 | void lis3dh_setReg() 83 | { 84 | uint8_t dataToWrite; 85 | uint16_t md_threshold = get_md_threshold(); 86 | uint16_t md_sample_rate = get_md_sample_rate(); 87 | Serial.printf("\r\nMD th: %d sr: %d\r\n", md_threshold, md_sample_rate); 88 | 89 | //LIS3DH_CTRL_REG1 90 | dataToWrite = 0; 91 | //dataToWrite |= 0x20; //ODR3-0, Data rate selection. Default value 00 92 | //0000: Power down mode, 0001: 1Hz(N/L), 0010: 10 Hz(N/L), 0011: 25Hz(N/L), 0100: 50Hz(N/L) 93 | //0101: 100 Hz(N/L), 0110: 200 Hz(N/L), 0111: 400 Hz(N/L), 1000: 1.6KHz(L), 1001: 1.25K Hz(N)/5K Hz(L) 94 | if (md_sample_rate == 0) { 95 | dataToWrite |= 0x10; //1Hz 96 | } 97 | else if (md_sample_rate == 1) { 98 | dataToWrite |= 0x20; //10Hz 99 | } 100 | else { 101 | dataToWrite |= 0x20; //10Hz 102 | } 103 | dataToWrite |= 0x08; //LPen, 0: normal mode, 1: low power mode 104 | dataToWrite |= 0x04; //Zen, 0: Z axis disable, 1: Z axis enable 105 | dataToWrite |= 0x02; //Yen, 0: Y axis disable, 1: Y axis enable 106 | dataToWrite |= 0x01; //Xen, 0: X axis disable, 1: X axis enable 107 | myIMU.writeRegister(LIS3DH_CTRL_REG1, dataToWrite); 108 | 109 | //LIS3DH_CTRL_REG2 110 | dataToWrite = 0; 111 | //dataToWrite |= 0x80; //HPM1-HPM0, High pass filter mode selection. 112 | //dataToWrite |= 0x30; //HPCF2-HPCF1, High pass filter cut off frequency selection 113 | dataToWrite |= 0x08; //Filtered data selection. 0: internal filter bypassed; 1: data from internal filter sent to output register and FIFO. 114 | //dataToWrite |= 0x04; //High pass filter enabled for CLICK function 115 | //dataToWrite |= 0x02; //High pass filter enabled for AOI function on interrupt 2 116 | dataToWrite |= 0x01; //High pass filter enabled for AOI function on interrupt 1 117 | myIMU.writeRegister(LIS3DH_CTRL_REG2, dataToWrite); 118 | 119 | //LIS3DH_CTRL_REG3 120 | //Choose source for pin 1 121 | dataToWrite = 0; 122 | //dataToWrite |= 0x80; //Click interrupt on INT1 123 | dataToWrite |= 0x40; //IA1 interrupt on INT1 124 | //dataToWrite |= 0x20; //IA2 interrupt on INT1 125 | //dataToWrite |= 0x10; //ZYXDA interrupt on INT1 126 | //dataToWrite |= 0x08; //321DA interrupt on INT1 127 | //dataToWrite |= 0x04; //FIFO watermark interrupt on INT1 128 | //dataToWrite |= 0x02; //FIFO overrun interrupt on INT1 129 | myIMU.writeRegister(LIS3DH_CTRL_REG3, dataToWrite); 130 | 131 | //LIS3DH_CTRL_REG4 132 | dataToWrite = 0; 133 | //dataToWrite |= 0x80; //Block data update 134 | //dataToWrite |= 0x40; //Big/little endian data selection 135 | //dataToWrite |= 0x10; //FS1-FS0, Full scale selection. (00: +/- 2G; 01: +/- 4G; 10: +/- 8G; 11: +/- 16G) 136 | if (md_threshold >= 8000) { 137 | dataToWrite |= 0x30; //16g 138 | } 139 | else if (md_threshold >= 4000) { 140 | dataToWrite |= 0x20; //8g 141 | } 142 | else if (md_threshold >= 2000) { 143 | dataToWrite |= 0x10; //4g 144 | } 145 | else { 146 | dataToWrite |= 0x00; //2g 147 | } 148 | //dataToWrite |= 0x08; //High resolution output mode. 149 | //dataToWrite |= 0x04; //ST1-ST0, Self test enable. 150 | //dataToWrite |= 0x01; //SPI serial interface mode selection 151 | myIMU.writeRegister(LIS3DH_CTRL_REG4, dataToWrite); 152 | 153 | //LIS3DH_CTRL_REG5 154 | dataToWrite = 0; 155 | //Int1 latch interrupt and 4D on int1 (preserve fifo en) 156 | //dataToWrite &= 0xF3; //Clear bits of interest 157 | dataToWrite |= 0x08; //Latch interrupt (Cleared by reading int1_src) 158 | //dataToWrite |= 0x04; //Pipe 4D detection from 6D recognition to int1? 159 | myIMU.writeRegister(LIS3DH_CTRL_REG5, dataToWrite); 160 | 161 | //LIS3DH_CTRL_REG6 162 | //Choose source for pin 2 and both pin output inversion state 163 | dataToWrite = 0; 164 | //dataToWrite |= 0x80; //Click interrupt on pin2 165 | //dataToWrite |= 0x40; //IA1 interrupt on INT2 166 | //dataToWrite |= 0x20; //IA2 interrupt on INT2 167 | //dataToWrite |= 0x10; //boot on INT2 pin 168 | //dataToWrite |= 0x08; //activity interrupt on INT2 pin 169 | //dataToWrite |= 0x02; //INT1 and INT2 pin polarity 170 | myIMU.writeRegister(LIS3DH_CTRL_REG6, dataToWrite); 171 | 172 | //LIS3DH_INT1_CFG 173 | dataToWrite = 0; 174 | //dataToWrite |= 0x80;//AOI, 0 = OR 1 = AND 175 | //dataToWrite |= 0x40;//6D, 0 = interrupt source, 1 = 6 direction source 176 | //Set these to enable individual axes of generation source (or direction) 177 | // -- high and low are used generically 178 | dataToWrite |= 0x20;//Z high 179 | //dataToWrite |= 0x10;//Z low 180 | dataToWrite |= 0x08;//Y high 181 | //dataToWrite |= 0x04;//Y low 182 | dataToWrite |= 0x02;//X high 183 | //dataToWrite |= 0x01;//X low 184 | myIMU.writeRegister(LIS3DH_INT1_CFG, dataToWrite); 185 | 186 | //LIS3DH_INT1_THS 187 | dataToWrite = 0; 188 | //Provide 7 bit value, 0x7F always equals max range by accelRange setting 189 | uint8_t val = 0; 190 | if (md_threshold >= 8000) { 191 | val = md_threshold / 125; 192 | } 193 | else if (md_threshold >= 4000) { 194 | val = md_threshold / 63; 195 | } 196 | else if (md_threshold >= 2000) { 197 | val = md_threshold / 31; 198 | } 199 | else { 200 | val = md_threshold / 16; 201 | } 202 | if (val > 127) val = 127; 203 | dataToWrite |= val; 204 | myIMU.writeRegister(LIS3DH_INT1_THS, dataToWrite); 205 | 206 | //LIS3DH_INT1_DURATION 207 | dataToWrite = 0; 208 | //minimum duration of the interrupt, duration = value * (1000 ms / (sample rate)) 209 | if (md_sample_rate == 0) 210 | dataToWrite |= 0x0; 211 | else if (md_sample_rate == 1) 212 | dataToWrite |= 0x0; 213 | myIMU.writeRegister(LIS3DH_INT1_DURATION, dataToWrite); 214 | 215 | #if USED_INT2 //INT2 reserve 216 | //LIS3DH_CTRL_REG6 217 | //Choose source for pin 2 and both pin output inversion state 218 | dataToWrite = 0; 219 | //dataToWrite |= 0x80; //Click interrupt on pin2 220 | //dataToWrite |= 0x40; //IA1 interrupt on INT2 221 | //dataToWrite |= 0x20; //IA2 interrupt on INT2 222 | //dataToWrite |= 0x10; //boot on INT2 pin 223 | //dataToWrite |= 0x08; //activity interrupt on INT2 pin 224 | //dataToWrite |= 0x02; //INT1 and INT2 pin polarity 225 | myIMU.writeRegister(LIS3DH_CTRL_REG6, dataToWrite); 226 | 227 | //LIS3DH_INT2_CFG 228 | dataToWrite = 0; 229 | //dataToWrite |= 0x80;//AOI, 0 = OR 1 = AND 230 | //dataToWrite |= 0x40;//6D, 0 = interrupt source, 1 = 6 direction source 231 | //Set these to enable individual axes of generation source (or direction) 232 | // -- high and low are used generically 233 | //dataToWrite |= 0x20;//Z high 234 | //dataToWrite |= 0x10;//Z low 235 | //dataToWrite |= 0x08;//Y high 236 | //dataToWrite |= 0x04;//Y low 237 | //dataToWrite |= 0x02;//X high 238 | //dataToWrite |= 0x01;//X low 239 | myIMU.writeRegister(LIS3DH_INT2_CFG, dataToWrite); 240 | 241 | //LIS3DH_INT2_THS 242 | dataToWrite = 0; 243 | //Provide 7 bit value, 0x7F always equals max range by accelRange setting 244 | //dataToWrite |= 0x10; // 1/8 range 245 | //dataToWrite |= 0x1F; // Threshold = 6000mg. 246 | myIMU.writeRegister(LIS3DH_INT2_THS, dataToWrite); 247 | 248 | //LIS3DH_INT2_DURATION 249 | dataToWrite = 0; 250 | //minimum duration of the interrupt 251 | //dataToWrite |= 0x0; 252 | myIMU.writeRegister(LIS3DH_INT2_DURATION, dataToWrite); 253 | #endif 254 | 255 | //write CTRL_REG5 again after setting INT(AN3308 doc) 256 | //LIS3DH_CTRL_REG5 257 | dataToWrite = 0; 258 | //Int1 latch interrupt and 4D on int1 (preserve fifo en) 259 | //dataToWrite &= 0xF3; //Clear bits of interest 260 | dataToWrite |= 0x08; //Latch interrupt (Cleared by reading int1_src) 261 | //dataToWrite |= 0x04; //Pipe 4D detection from 6D recognition to int1? 262 | myIMU.writeRegister(LIS3DH_CTRL_REG5, dataToWrite); 263 | 264 | return ; 265 | } 266 | 267 | void lis3dh_get_md_data() 268 | { 269 | uint8_t dataRead; 270 | Serial.print("\r\nLIS3DH_INT1_SRC: 0x"); 271 | myIMU.readRegister(&dataRead, LIS3DH_INT1_SRC);//cleared by reading 272 | Serial.println(dataRead, HEX); 273 | if(dataRead & 0x40) { 274 | Serial.print("Even detected:"); 275 | if(dataRead & 0x02) Serial.print(" X+ "); 276 | if(dataRead & 0x01) Serial.print(" X- "); 277 | if(dataRead & 0x08) Serial.print(" Y+ "); 278 | if(dataRead & 0x04) Serial.print(" Y- "); 279 | if(dataRead & 0x20) Serial.print(" Z+ "); 280 | if(dataRead & 0x10) Serial.print(" Z- "); 281 | Serial.println(); 282 | } 283 | 284 | //Get 3-axis data 285 | Serial.print("Accelerometer:"); 286 | Serial.print(" X="); 287 | Serial.print(myIMU.readFloatAccelX(), 4); 288 | Serial.print(" Y="); 289 | Serial.print(myIMU.readFloatAccelY(), 4); 290 | Serial.print(" Z="); 291 | Serial.print(myIMU.readFloatAccelZ(), 4); 292 | Serial.println(); 293 | } 294 | 295 | void lis3dh_INT1_handle() 296 | { 297 | Serial.printf("\r\nlis3dh_INT1_handle\r\n"); 298 | uint16_t md_duration = get_md_duration(); 299 | uint8_t md_number = get_md_number(); 300 | uint32_t md_silent_period = (uint32_t)get_md_silent_period(); 301 | uint8_t dataRead; 302 | 303 | Wire.begin(); //contorl i2c after system wake up 304 | lis3dh_get_md_data(); 305 | 306 | append(&head, millis()); 307 | //Serial.printf("append\r\n"); 308 | //Serial.printf("NodeSize %d\r\n", NodeSize(head)); 309 | //Serial.printf("firstNode %d\r\n",firstNode(head)->data); 310 | //Serial.printf("lastNode %d\r\n",lastNode(head)->data); 311 | if (NodeSize(head) >= md_number) 312 | { 313 | //Serial.printf("%d <= %d ?\r\n",(lastNode(head)->data - firstNode(head)->data),md_duration); 314 | if ((lastNode(head)->data - firstNode(head)->data) <= md_duration) { 315 | send_event_flag = 1; 316 | freeList(&head); 317 | 318 | if (md_silent_period) { 319 | lis3dh_md_pause(); 320 | 321 | if (api.system.timer.create(TIMER_MD_SILENT_PERIOD, (RAK_TIMER_HANDLER)lis3dh_md_resume, RAK_TIMER_ONESHOT) != true) 322 | { 323 | Serial.printf("Creating timer failed.\r\n"); 324 | lis3dh_md_resume(); 325 | } 326 | else if (api.system.timer.start(TIMER_MD_SILENT_PERIOD, md_silent_period*1000, NULL) != true) 327 | { 328 | Serial.printf("Starting timer failed.\r\n"); 329 | lis3dh_md_resume(); 330 | } 331 | } 332 | } 333 | else 334 | { 335 | //Serial.printf("deleteNode\r\n"); 336 | deleteNode(&head, firstNode(head)); 337 | } 338 | } 339 | 340 | if (send_event_flag == 1) 341 | { 342 | //Serial.printf("loraSendMDEvent\r\n"); 343 | loraSendMDEvent(); 344 | send_event_flag = 0; 345 | } 346 | } 347 | 348 | #if USED_INT2 //INT2 reserve 349 | void lis3dh_INT2_handle() 350 | { 351 | Serial.printf("\r\nlis3dh_INT2_handle\r\n"); 352 | } 353 | #endif 354 | 355 | void lis3dh_md_pause() 356 | { 357 | api.system.timer.stop(TIMER_MD_SILENT_PERIOD); 358 | freeList(&head); 359 | 360 | Wire.begin(); 361 | uint8_t dataRead; 362 | myIMU.readRegister(&dataRead, LIS3DH_INT1_SRC);//cleared by reading 363 | myIMU.writeRegister(LIS3DH_CTRL_REG1, 0x0); 364 | } 365 | 366 | void lis3dh_md_resume() 367 | { 368 | Wire.begin(); 369 | lis3dh_setReg(); 370 | lis3dh_showConfigReg(); 371 | } 372 | 373 | bool lis3dh_begin() 374 | { 375 | //Accel sample rate and range effect interrupt time and threshold values!!! 376 | myIMU.settings.accelSampleRate = 0; //Hz. Can be: 0,1,10,25,50,100,200,400,1600,5000 Hz 377 | myIMU.settings.accelRange = 2; //Max G force readable. Can be: 2, 4, 8, 16 378 | 379 | myIMU.settings.adcEnabled = 0; 380 | myIMU.settings.tempEnabled = 0; 381 | myIMU.settings.xAccelEnabled = 1; 382 | myIMU.settings.yAccelEnabled = 1; 383 | myIMU.settings.zAccelEnabled = 1; 384 | 385 | if (myIMU.begin() != IMU_SUCCESS) 386 | return false; 387 | else 388 | return true; 389 | } 390 | 391 | void lis3dh_md_end() 392 | { 393 | pinMode(LIS3D_INT1_PIN, INPUT); 394 | detachInterrupt(LIS3D_INT1_PIN); 395 | 396 | Wire.begin(); 397 | lis3dh_md_pause(); 398 | Wire.end(); 399 | } 400 | 401 | void lis3dh_md_begin() 402 | { 403 | if (lis3dh_begin()) 404 | { 405 | lis3dh_md_pause(); 406 | 407 | pinMode(LIS3D_INT1_PIN, INPUT_PULLDOWN); 408 | attachInterrupt(LIS3D_INT1_PIN, lis3dh_INT1_handle, RISING); 409 | 410 | lis3dh_md_resume(); 411 | } 412 | else 413 | { 414 | Serial.println("LIS3DH initialize fail..."); 415 | } 416 | } 417 | 418 | void lis3dh_init() 419 | { 420 | if (lis3dh_begin()) 421 | { 422 | lis3dh_md_end(); 423 | } 424 | else 425 | { 426 | Serial.println("LIS3DH initialize fail..."); 427 | } 428 | } 429 | 430 | bool lis3dh_factory_test() 431 | { 432 | bool ret = lis3dh_begin(); 433 | if (ret) 434 | { 435 | lis3dh_md_pause(); 436 | Wire.end(); 437 | } 438 | return ret; 439 | } 440 | 441 | /* 442 | BEGIN of linked list queue for MD event 443 | */ 444 | // Function to create a new node with a given value 445 | struct Node* createNode(uint32_t value) { 446 | struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); 447 | if (!newNode) { 448 | printf("Memory allocation error\n"); 449 | return NULL; 450 | } 451 | newNode->data = value; 452 | newNode->next = NULL; 453 | return newNode; 454 | } 455 | 456 | // Function to append a node to the end of the linked list 457 | void append(struct Node** head, uint32_t value) { 458 | struct Node* newNode = createNode(value); 459 | if (*head == NULL) { // If list is empty, make new node the head 460 | *head = newNode; 461 | } else { 462 | struct Node* temp = *head; 463 | while (temp->next != NULL) { 464 | temp = temp->next; 465 | } 466 | temp->next = newNode; 467 | } 468 | } 469 | 470 | // Function to delete a node with a specific value 471 | void deleteNode(struct Node** head, struct Node* target) { 472 | struct Node* temp = *head; 473 | struct Node* prev = NULL; 474 | 475 | // Check if head node holds the value to be deleted 476 | if (temp != NULL && temp == target) { 477 | *head = temp->next; // Change head 478 | free(temp); // Free old head 479 | return; 480 | } 481 | 482 | // Search for the node to delete, keeping track of the previous node 483 | while (temp->next != NULL && temp->next != target) { 484 | prev = temp; 485 | temp = temp->next; 486 | } 487 | 488 | // If value not found in the list 489 | if (temp == NULL) { 490 | //Serial.printf("target not found in the list.\r\n"); 491 | return; 492 | } 493 | 494 | // Unlink the node from the linked list and free its memory 495 | prev->next = temp->next; 496 | free(temp); 497 | } 498 | 499 | // Function to search for the first node 500 | struct Node* firstNode(struct Node* head) { 501 | return head; 502 | } 503 | 504 | // Function to search for the last node 505 | struct Node* lastNode(struct Node* head) { 506 | struct Node* temp = head; 507 | struct Node* prev = NULL; 508 | while (temp != NULL) { 509 | prev = temp; 510 | temp = temp->next; 511 | } 512 | return prev; 513 | } 514 | 515 | // Function to get the size of the linked list 516 | int NodeSize(struct Node* head) { 517 | int count = 0; 518 | struct Node* temp = head; 519 | while (temp != NULL) { 520 | count++; 521 | temp = temp->next; 522 | } 523 | return count; 524 | } 525 | 526 | // Function to display the linked list 527 | void display(struct Node* head) { 528 | struct Node* temp = head; 529 | while (temp != NULL) { 530 | printf("%d -> ", temp->data); 531 | temp = temp->next; 532 | } 533 | printf("NULL\n"); 534 | } 535 | 536 | // Function to free all nodes in the list 537 | void freeList(struct Node** head) { 538 | struct Node* temp = *head; 539 | struct Node* prev = NULL; 540 | while (temp != NULL) { 541 | prev = temp; 542 | temp = temp->next; 543 | free(prev); 544 | } 545 | *head = temp; 546 | } 547 | /* 548 | END of linked list queue for MD event 549 | */ 550 | -------------------------------------------------------------------------------- /Firmware/RAK2270/lis3dh_md.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIS3DH_MD_H__ 2 | #define __LIS3DH_MD_H__ 3 | 4 | 5 | #define LIS3D_INT1_PIN PA10 6 | #define LIS3D_INT2_PIN PB14 7 | 8 | void lis3dh_md_pause(); 9 | void lis3dh_md_resume(); 10 | void lis3dh_md_begin(); 11 | void lis3dh_md_end(); 12 | void lis3dh_init(); 13 | bool lis3dh_factory_test(); 14 | #endif 15 | -------------------------------------------------------------------------------- /Firmware/RAK2270/ntc.cpp: -------------------------------------------------------------------------------- 1 | #include "./ntc.h" 2 | 3 | 4 | #define NTC_EN WB_A1 5 | #define NTC_ADC WB_A0 6 | 7 | /** 8 | * @brief Get Temperature from NTC 9 | * Here is a ln relationship. T = 84.987-25.18*ln(10*V/(3.3-V)) 10 | */ 11 | float getTemperature() 12 | { 13 | float max, ref; 14 | 15 | pinMode(NTC_EN, OUTPUT); 16 | digitalWrite(NTC_EN, HIGH); 17 | delay(200); 18 | 19 | analogReadResolution(10); 20 | pinMode(NTC_EN, OUTPUT); 21 | 22 | switch (udrv_adc_get_resolution()) { 23 | case UDRV_ADC_RESOLUTION_6BIT: 24 | { 25 | max = 64.0; 26 | break; 27 | } 28 | case UDRV_ADC_RESOLUTION_8BIT: 29 | { 30 | max = 256.0; 31 | break; 32 | } 33 | case UDRV_ADC_RESOLUTION_10BIT: 34 | default: 35 | { 36 | max = 1024.0; 37 | break; 38 | } 39 | case UDRV_ADC_RESOLUTION_12BIT: 40 | { 41 | max = 4096.0; 42 | break; 43 | } 44 | case UDRV_ADC_RESOLUTION_14BIT: 45 | { 46 | max = 16384.0; 47 | break; 48 | } 49 | } 50 | 51 | switch (udrv_adc_get_mode()) { 52 | case UDRV_ADC_MODE_DEFAULT: 53 | default: 54 | { 55 | #ifdef rak11720 56 | ref = 2.0; 57 | #else 58 | ref = 3.6; 59 | #endif 60 | break; 61 | } 62 | #ifdef rak11720 63 | case UDRV_ADC_MODE_1_5: 64 | { 65 | ref = 1.5; 66 | break; 67 | } 68 | #else 69 | case UDRV_ADC_MODE_3_3: 70 | { 71 | ref = 3.3; 72 | break; 73 | } 74 | case UDRV_ADC_MODE_3_0: 75 | { 76 | ref = 3.0; 77 | break; 78 | } 79 | case UDRV_ADC_MODE_2_4: 80 | { 81 | ref = 2.4; 82 | break; 83 | } 84 | case UDRV_ADC_MODE_1_8: 85 | { 86 | ref = 1.8; 87 | break; 88 | } 89 | case UDRV_ADC_MODE_1_2: 90 | { 91 | ref = 1.2; 92 | break; 93 | } 94 | #endif 95 | } 96 | 97 | float vbat = api.system.bat.get(); 98 | uint16_t tv = analogRead(NTC_ADC); 99 | float tv2 = (tv * vbat)/max; 100 | float tmp = 84.987 - 25.18 *log(10*tv2/(vbat-tv2)); 101 | 102 | delay(100); 103 | digitalWrite(NTC_EN, LOW); 104 | 105 | return tmp; 106 | } 107 | 108 | /** 109 | * @brief Calibrate Temperature 110 | * 1st: y = (0.00312f * x^2) + (0.927f * x) - 1.2f 111 | * 2nd: y = (0.976f * x) - 1.11 112 | */ 113 | float calibrateTemperature(float temp) 114 | { 115 | float temp2 = (0.00312f * temp * temp) + (0.927f * temp) - 1.2f; 116 | temp2 = (0.976f * temp2) - 1.11; 117 | return temp2; 118 | } 119 | -------------------------------------------------------------------------------- /Firmware/RAK2270/ntc.h: -------------------------------------------------------------------------------- 1 | #ifndef __NTC_H__ 2 | #define __NTC_H__ 3 | 4 | #include "Arduino.h" 5 | 6 | float getTemperature(); 7 | float calibrateTemperature(float temp); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /Firmware/README.md: -------------------------------------------------------------------------------- 1 | # RAK2270-Sticker-Tracker 2 | 3 | ## Software 4 | 5 | The main chip used on the RAK2270 is the WisDuo Module RAK3172-SiP/RAK3172LP-SiP (pin-to-pin and software compatible). This chip has STM32WL SoC which is a combination of MCU and LoRa/SubGhz transceiver. The firmware written on this is based on [RUI3](https://docs.rakwireless.com/RUI3/). You can start creating your own firmware using this [RAK3172-SiP guide](https://docs.rakwireless.com/Product-Categories/WisDuo/RAK3172-SiP/Quickstart/#rak3172-sip-as-a-stand-alone-device-using-rui3). 6 | 7 | ### Install 8 | 9 | [Install RUI3 Board Support Package in Arduino IDE](https://docs.rakwireless.com/RUI3/Supported-IDE/#software-setup) 10 | Board: RAKwireless RUI STM32 Boards, WisDuo RAK3272-SiP Board, version 4.1.0 or later 11 | 12 | ### Upgrade firmware 13 | 14 | - Use "Upload" by Arduino IDE to upgrade firmware 15 | - Use J-Link to upgrade firmware 16 | Firmware image will be generated in C:\Users\$USER\AppData\Local\Temp\arduino_build_XXXXX 17 | "$USER" is your user name on Windows 18 | "arduino_build_XXXXX" will be generate when Verify by Arduino IDE 19 | Programming "RAK2270.ino.hex" by J-Link 20 | 21 | ### Setting LoRaWAN parameters 22 | 23 | Use [RUI3 AT Command](https://docs.rakwireless.com/RUI3/Serial-Operating-Modes/AT-Command-Manual/) 24 | 25 | ### Factory default value 26 | 27 | - LoRaWAN Region: US915, AT+BAND=5 28 | - Channel mask: use channel 8-15, AT+MASK=0002 29 | - ADR disable, AT+ADR=0 30 | - Unconfirmed package, AT+CFM=0 31 | - TX Power: 0, AT+TXP=0 32 | - Sending interval: 60 mintues, ATC+SENDFREQ=60 33 | - DEVEUI, APPEUI, APPKEY will be generated during factory production 34 | 35 | ### Uplink payload format 36 | 37 | The data packet is encoded by CayenneLPP format based on the [_**CayenneLPP format**_](https://github.com/ElectronicCats/CayenneLPP) provided by ElectronicCats. 38 | 39 | The channel ID's used for the different values are: 40 | 41 | | Channel Name | Channel ID | Type | Value | 42 | | ---------------- | ---------- | ----------- | ---------------------------------- | 43 | | LPP_TEMPERATURE | 0 | Temperature | RAK2270 Temperature in 1/10 °C | 44 | | LPP_ANALOG_INPUT | 0 | Analog | RAK2270 Bettery voltage in 1/100 V | 45 | 46 | The packet sent out displayed as hex values looks like this: 47 | 0x00 0x67 0x01 0x11 0x00 0x02 0x01 0x2a 48 | 49 | | Bytes | Meaning | Value in Hex | Value | 50 | | ----- | ------------------------ | ------------ | ------- | 51 | | 1 | RAK2270 Channel ID | 0x00 | | 52 | | 2 | Temperature Channel Type | 0x67 | | 53 | | 3, 4 | Temperature Value | 0x01 0x11 | 27.3 °C | 54 | | 5 | RAK2270 Channel ID | 0x00 | | 55 | | 6 | Analog Channel Type | 0x02 | | 56 | | 7, 8 | Battery Voltage Value | 0x01 0x2a | 2.98 V | 57 | 58 | ### Downlink command format 59 | 60 | - Modify Periodic Uplink Interval Command 61 | 62 | | Field | Size | Description | 63 | | ----- | ---- | ------------------------------ | 64 | | Type | 1 | 0x02 | 65 | | Data | 2 | Range: 0x0001 - 0x05A0, mintue | 66 | 67 | - Modify Confirmed Command 68 | 69 | | Field | Size | Description | 70 | | ----- | ---- | ---------------------------------- | 71 | | Type | 1 | 0x06 | 72 | | Data | 1 | 0x00: Uncomfirmed, 0x01: Comfirmed | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Firmware/Release/RAK2270.v1.0.0.Beta15.ForStandard/RAK2270_firmware.json: -------------------------------------------------------------------------------- 1 | { 2 | "File":"RAK2270.v1.0.0.Beta15.ForStandard.hex", 3 | "Version":"v1.0.0.Beta15.ForStandard", 4 | "MD5":"53fb65f70e75f1c53dd08864b3954c66" 5 | } -------------------------------------------------------------------------------- /Firmware/Release/RAK2270_1.1.0_Generic/RAK2270_firmware.json: -------------------------------------------------------------------------------- 1 | { 2 | "File":"RAK2270_1.1.0_Generic.hex", 3 | "Version":"RAK2270_1.1.0_Generic", 4 | "MD5":"b737c16fe89acf081f8cbff2ad686b75" 5 | } 6 | -------------------------------------------------------------------------------- /Firmware/Release/RAK2270_1.1.2_Generic/RAK2270_1.1.2_Generic_firmware.json: -------------------------------------------------------------------------------- 1 | { 2 | "File":"RAK2270_1.1.2_Generic.hex", 3 | "Version":"RAK2270_1.1.2_Generic", 4 | "MD5":"2be3c91d57f186b8963a0393e21d3ae3" 5 | } 6 | -------------------------------------------------------------------------------- /Firmware/Release/RAK2270_1.1.2_Generic/forUpgrade/RAK2270_1.1.2_Generic.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Firmware/Release/RAK2270_1.1.2_Generic/forUpgrade/RAK2270_1.1.2_Generic.bin -------------------------------------------------------------------------------- /Firmware/Release/RAK2270_firmware_v1.0.0.RC1.ForStandard/RAK2270_firmware.json: -------------------------------------------------------------------------------- 1 | { 2 | "File":"RAK2270.v1.0.0.RC1.ForStandard.hex", 3 | "Version":"v1.0.0.RC1.ForStandard", 4 | "MD5":"fa29c6c8b0e4c1ff5f79dfb8ef11fcf9" 5 | } -------------------------------------------------------------------------------- /Firmware/Release/RAK2270_firmware_v1.0.0.RC5.ForStandard/RAK2270_firmware.json: -------------------------------------------------------------------------------- 1 | { 2 | "File":"RAK2270.v1.0.0.RC5.ForStandard.hex", 3 | "Version":"v1.0.0.RC5.ForStandard", 4 | "MD5":"322e907e4452164691b7fdbf7b846782" 5 | } 6 | -------------------------------------------------------------------------------- /Firmware/Release/RAK2270_firmware_v1.0.0.RC6.ForStandard/RAK2270_firmware.json: -------------------------------------------------------------------------------- 1 | { 2 | "File":"RAK2270.v1.0.0.RC6.ForStandard.hex", 3 | "Version":"v1.0.0.RC6.ForStandard", 4 | "MD5":"7c83d74db136bf3439e756054a2708f7" 5 | } 6 | -------------------------------------------------------------------------------- /Hardware/BOM of RAK2270(FPCB-WITHOUT EEPROM-OPENSOURCE).xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/BOM of RAK2270(FPCB-WITHOUT EEPROM-OPENSOURCE).xls -------------------------------------------------------------------------------- /Hardware/Cadence/RAK2270_FPC_VC_2023_0628_1.brd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/Cadence/RAK2270_FPC_VC_2023_0628_1.brd -------------------------------------------------------------------------------- /Hardware/Cadence/RAK2270_VC_2023_0531.DSN: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/Cadence/RAK2270_VC_2023_0531.DSN -------------------------------------------------------------------------------- /Hardware/GerberFiles_RAK2270_FPC_VC_2023_0615_1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/GerberFiles_RAK2270_FPC_VC_2023_0615_1.zip -------------------------------------------------------------------------------- /Hardware/RAK2270-bareboard-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/RAK2270-bareboard-1.jpg -------------------------------------------------------------------------------- /Hardware/RAK2270-bareboard-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/RAK2270-bareboard-2.png -------------------------------------------------------------------------------- /Hardware/RAK2270-sticker-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/RAK2270-sticker-1.png -------------------------------------------------------------------------------- /Hardware/RAK2270-sticker-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/RAK2270-sticker-2.png -------------------------------------------------------------------------------- /Hardware/RAK2270_FPC_VC_2023_0628_1_placement.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/RAK2270_FPC_VC_2023_0628_1_placement.zip -------------------------------------------------------------------------------- /Hardware/Schematic_RAK2270_VD_2024_0731.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/Schematic_RAK2270_VD_2024_0731.pdf -------------------------------------------------------------------------------- /Hardware/Testpoints_RAK2270_FPC_VC.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/Testpoints_RAK2270_FPC_VC.pdf -------------------------------------------------------------------------------- /Hardware/pins_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/Hardware/pins_location.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 RAKwireless 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RAK2270 Estimate Battery Life V1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAKWireless/RAK2270-Sticker-Tracker/696275ff653136eb516009f36edfc3228ed4db71/RAK2270 Estimate Battery Life V1.xlsx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RAK2270-Sticker-Tracker 2 | 3 | Open source files for RAK2270 Sticker Tracker 4 | 5 | ## Product Description 6 | 7 | RAK2270 is a sleek sticker tracker for LoRaWAN® designed like a label, making it easy to attach to any product. It offers real-time location tracking through the Helium network server's advanced features. With a built-in temperature sensor and an integrated 3-axis accelerometer, it provides accurate temperature monitoring and optimized device usage. It supports multiple LoRaWAN® bands, ensuring versatility for various regions and applications. The RAK2270 is a compact and powerful solution for precise and efficient tracking needs. 8 | 9 | [RAK2270 works with Helium network and Trackpac platform](https://store.rakwireless.com/products/rak2270-rak-sticker-tracker) which allows you to quickly use the device with out doing any on-boarding process. Just scan the QR code and wait for the device to show on the Trackpac platform. 10 | 11 | ## Design Files 12 | 13 | In this repository, we provide hardware and software files so you can have 100% access on the internals of RAK2270 Tracker Sticker. 14 | 15 | ### Hardware 16 | 17 | The RAK2270 is desiged using Cadence Allegro EDA software. We provided Cadence compatible files as well as the generated gerber file. The full schematic is in PDF file as well. 18 | 19 | ### Software 20 | 21 | The main chip used on the RAK2270 is the WisDuo Module RAK3172-SiP/RAK3172LP-SiP (pin-to-pin and software compatible). This chip has STM32WL SoC which is a combination of MCU and LoRa/SubGhz transceiver. The firmware written on this is based on [RUI3](https://docs.rakwireless.com/RUI3/). You can start creating your own firmware using this [RAK3172-SiP guide](https://docs.rakwireless.com/Product-Categories/WisDuo/RAK3172-SiP/Quickstart/#rak3172-sip-as-a-stand-alone-device-using-rui3). 22 | 23 | ## Direct order on JLCPCB 24 | 25 | RAKwireless and JLCPCB collaborate together so that you can order directly a RAK2270 with complete components from JLCPCB. You just have to use [this link](https://cart.jlcpcb.com/quote/?from=rak&fileUrl=https%3A%2F%2Fs3.eu-central-1.amazonaws.com%2Frakwireless-downloads-center-prod%2FLoRa%2FRAK2270%2Fjlcpcb%2FGERBER_FPC_RAK2270.zip&bomUrl=https%3A%2F%2Fs3.eu-central-1.amazonaws.com%2Frakwireless-downloads-center-prod%2FLoRa%2FRAK2270%2Fjlcpcb%2FBOM_PCB_RAK2270.xlsx&cplUrl=https%3A%2F%2Fs3.eu-central-1.amazonaws.com%2Frakwireless-downloads-center-prod%2FLoRa%2FRAK2270%2Fjlcpcb%2FPLACEMENT_FPC_RAK2270.xlsx) and just proceed each step of the order. The files will be automatically uploaded as you go on each step. This makes ordering of RAK2270 directly from JLCPCB easier and faster. --------------------------------------------------------------------------------