├── CitizenSensor-STM32 └── CitizenSensor-STM32.ino ├── README.md └── media ├── CS40-Tisch600.jpg ├── CitizenSensor-LoRa-STM32-v4-01-BME680.png ├── CitizenSensorLora20.jpg ├── STM32LoraNode.png └── pcb40.png /CitizenSensor-STM32/CitizenSensor-STM32.ino: -------------------------------------------------------------------------------- 1 | 2 | /******************************************************************************* 3 | Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman 4 | (c) 2017 Tom Vijlbrief 5 | (c) 2017,18 Hartmut John 6 | 7 | TTN Stuttgart Version - Hardware Küche #3, 17.05.2017 8 | works with stm32duiono.com bootloader for generic boards with led on PC13 9 | aka 'blue pill' stm32f103c8t6 10 | Importat: USB Serial is not working together with sleep mode 11 | 12 | install adapted STM32 Arduino integration from here 13 | https://github.com/tomtor/Arduino_STM32 14 | 15 | optional: install adapted power saving lmic version from here 16 | https://github.com/tomtor/arduino-lmic 17 | otherwise you have to comment out skipRX 18 | 19 | non arduino mbed.org version can be found here 20 | https://developer.mbed.org/users/orangeway/code/STM32F103C8T6_LoRaWAN-lmic-app/ 21 | 22 | Permission is hereby granted, free of charge, to anyone 23 | obtaining a copy of this document and accompanying files, 24 | to do whatever they want with them without any restriction, 25 | including, but not limited to, copying, modification and redistribution. 26 | NO WARRANTY OF ANY KIND IS PROVIDED. 27 | 28 | This example sends a valid LoRaWAN packet with static payload, 29 | using frequency and encryption settings matching those of 30 | the (early prototype version of) The Things Network. 31 | 32 | Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in g1, 0.1% in g2). 33 | 34 | ToDo before use: 35 | - set NWKSKEY (value from console.thethingsnetwork.com) 36 | - set APPKSKEY (value from console.thethingsnetwork.com) 37 | - set DEVADDR (value from console.thethingsnetwork.com) 38 | - optionally comment #define DEBUG 39 | - optionally comment #define SLEEP 40 | 41 | 42 | --- Replace the default on ttn console with this payload decoder 43 | --- serving Cayenne integration and generic MQTT --- 44 | 45 | function Decoder(bytes, port) { 46 | // Decode an uplink message from a buffer 47 | // (array) of bytes to an object of fields. 48 | var decoded = {}; 49 | 50 | if (port === 1) { // Citizen Sensor first generation 51 | decoded.temp = (bytes[1]*256+bytes[0])/100.0; 52 | decoded.pres = (bytes[3]*256+bytes[2])/10.0; 53 | decoded.humi = (bytes[5]*256+bytes[4])/100.0; 54 | decoded.power = bytes[6]; 55 | decoded.rate = bytes[7]; 56 | } else if (port === 2) { // Citizen Sensor with static Cayenne support 57 | decoded.temp = (bytes[2]*256+bytes[3])/10.0; 58 | decoded.pres = (bytes[9]*256+bytes[10])/10.0; 59 | decoded.humi = (bytes[6]/2.0); 60 | decoded.power = 100; // not used 61 | decoded.rate = 0; // not used 62 | } 63 | 64 | return decoded; 65 | } 66 | 67 | *******************************************************************************/ 68 | 69 | #include 70 | 71 | #include 72 | #include 73 | #include 74 | 75 | #include 76 | #include 77 | #include 78 | 79 | #define USE_CAYENNE 80 | 81 | //PCB Version 2.0 - undefine for PCB Version 4.0 and higher 82 | //#define PCB_VER 20 83 | 84 | // use BME280 Sensor on I2C Pins 85 | #define USE_SENSOR 86 | 87 | // swap ic2 pins SCL/SDA for compatibility with some bme280 breakout boards - not required on PCB >=V4.0 88 | //#define SWAPE_I2C 89 | 90 | // send to single channel gateway? Stay on 868.1MHz but use different SF 91 | #define SINGLE_CHN 1 92 | 93 | // show debug statements; comment next line to disable debug statements 94 | #define DEBUG 95 | 96 | // Blink a led 97 | #define BLINK 98 | 99 | // Enable OTAA? - work in progress 100 | //#define OTAA 101 | 102 | // Enable down link - required for OTAA 103 | //#define RECEIVE 1 104 | 105 | // use low power sleep - serial debug not working 106 | #define SLEEP 107 | // blue pill board with power led removed + RFM95 + BME280 consumes 370 uA during deep sleep 108 | // RAM is lost and reboots on wakeup. 109 | // We safe some data in the RTC backup ram which survives DeepSleep. 110 | #define DEEP_SLEEP true 111 | 112 | #ifdef SLEEP 113 | // TODO: should be dynamic - after successfull OTAA deep sleep should be an option 114 | #if DEEP_SLEEP 115 | #undef OTAA 116 | #endif 117 | #endif 118 | 119 | #ifndef OTAA 120 | // LoRaWAN NwkSKey, your network session key, 16 bytes (from console.thethingsnetwork.org) 121 | static unsigned char NWKSKEY[16] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; 122 | 123 | // LoRaWAN AppSKey, application session key, 16 bytes (from console.thethingsnetwork.org) 124 | static unsigned char APPSKEY[16] = { 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0 }; 125 | 126 | // LoRaWAN end-device address (DevAddr), ie 0xABB481F1 (from console.thethingsnetwork.org) 127 | 128 | //#include "deviceids.h" - for bulk configuration 129 | #ifdef DEVICEID 130 | static const u4_t DEVADDR = DEVICEID ; // id from config include 131 | #else 132 | static const u4_t DEVADDR = 0x2601FFFF ; // placeholder <-- Change this address for every node! 133 | #endif 134 | 135 | #else // if use OTAA (over the air activation) - work in progress 136 | // reversed (LSB) 8 bytes of AppEUI registered with console.thethingsnetwork.org 137 | static const u1_t APPEUI[8] = { 0xEE, 0x32, 0x00, 0xF0, 0x7E, 0xD5, 0xB3, 0x70 }; 138 | // MSB 16 bytes of the APPKEY used when registering a device with ttnctl register DevEUI AppKey 139 | static const unsigned char APPKEY[16] = { 0x92, 0x5B, 0xD0, 0x03, 0xBC, 0x78, 0x3C, 0xE2, 0x53, 0xA1, 0x17, 0xD0, 0x08, 0x47, 0x83, 0xCF }; 140 | #endif 141 | 142 | 143 | // ##### ----------------- board definitions ----------------------- #### 144 | 145 | #define led LED_BUILTIN 146 | 147 | // port for RFM95 LoRa Radio 148 | #define USE_SPI 1 149 | 150 | //Global Adafruit sensor object 151 | Adafruit_BME280 bme280; 152 | 153 | static const uint16 MAGICNB = 0xbeaf; 154 | 155 | // STM32 Unique Chip IDs - used as device id in OTAA scenario 156 | #define STM32_ID ((u1_t *) 0x1FFFF7E8) 157 | 158 | SPIClass mySPI(USE_SPI); 159 | 160 | extern SPIClass *SPIp; 161 | 162 | void blinkN2(int n, int d, int t); 163 | 164 | // Schedule TX every this many seconds (might become longer due to duty 165 | // cycle limitations). Note that the LED flashing takes some time 166 | int txInterval = 60*5; 167 | 168 | #define RATE DR_SF9 169 | 170 | // generic payload object 171 | struct { 172 | unsigned short temp; 173 | unsigned short pres; 174 | unsigned short humi; 175 | byte power; 176 | byte rate2; 177 | 178 | } payload; 179 | 180 | // Cayenne Low Power Payload (LPP) object 181 | byte cayenne_lpp[11]; 182 | 183 | void initBME280Sparkfun(void); 184 | 185 | // Defined for power and sleep functions pwr.h and scb.h 186 | #include 187 | #include 188 | #include 189 | 190 | #include 191 | 192 | //RTClock rt(RTCSEL_LSI, 1000); // 10 milli second alarm 193 | RTClock rt(RTCSEL_LSI,33); // 10 milli second alarm 194 | //RTClock rt(RTCSEL_LSE,33); // 10 milli second alarm 195 | 196 | // Define the Base address of the RTC registers (battery backed up CMOS Ram), so we can use them for config of touch screen or whatever. 197 | // See http://stm32duino.com/viewtopic.php?f=15&t=132&hilit=rtc&start=40 for a more details about the RTC NVRam 198 | // 10x 16 bit registers are available on the STM32F103CXXX more on the higher density device. 199 | #define BKP_REG_BASE ((uint32_t *)(0x40006C00 +0x04)) 200 | 201 | void iwdg_feed_debug(){ 202 | //iwdg_feed(); 203 | blinkN2(1,20,0); 204 | } 205 | 206 | void storeBR(int i, uint32_t v) { 207 | BKP_REG_BASE[2 * i] = (v << 16); 208 | BKP_REG_BASE[2 * i + 1] = (v & 0xFFFF); 209 | } 210 | 211 | uint32_t readBR(int i) { 212 | return ((BKP_REG_BASE[2 * i] & 0xFFFF) >> 16) | (BKP_REG_BASE[2 * i + 1] & 0xFFFF); 213 | } 214 | 215 | bool next = false; 216 | 217 | void enterSleepMode(bool deepSleepFlag) 218 | { 219 | // Clear PDDS and LPDS bits and wakeup pin and set Clear WUF flag (required per datasheet): 220 | PWR_BASE->CR &= PWR_CR_LPDS | PWR_CR_PDDS | PWR_CR_CWUF | PWR_CSR_EWUP; 221 | 222 | 223 | 224 | // magic https://community.particle.io/t/how-to-put-spark-core-to-sleep-and-wakeup-on-interrupt-signal-on-a-pin/5947/56 225 | // Enable wakeup pin bit. 226 | PWR_BASE->CSR |= PWR_CSR_EWUP; 227 | PWR_BASE->CR |= PWR_CR_CWUF; 228 | SCB_BASE->SCR |= SCB_SCR_SLEEPDEEP; 229 | 230 | // System Control Register Bits. See... 231 | // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/Cihhjgdh.html 232 | if (deepSleepFlag) { // standby mode 233 | PWR_BASE->CR |= PWR_CR_CWUF; // FIX to save power on subsequent runs too 234 | // Set PDDS and LPDS bits for standby, 235 | // Set Power down deepsleep bit. 236 | PWR_BASE->CR |= PWR_CR_PDDS; 237 | // Unset Low-power deepsleep. 238 | PWR_BASE->CR &= ~PWR_CR_LPDS; 239 | } else { 240 | adc_disable(ADC1); 241 | adc_disable(ADC2); 242 | #if STM32_HAVE_DAC 243 | dac_disable_channel(DAC, 1); 244 | dac_disable_channel(DAC, 2); 245 | #endif 246 | // Unset Power down deepsleep bit. 247 | PWR_BASE->CR &= ~PWR_CR_PDDS; 248 | // set Low-power deepsleep. 249 | PWR_BASE->CR |= PWR_CR_LPDS; 250 | } 251 | 252 | // Now go into stop mode, wake up on interrupt 253 | asm(" wfi"); 254 | 255 | // Clear SLEEPDEEP bit so we can use SLEEP mode 256 | SCB_BASE->SCR &= ~SCB_SCR_SLEEPDEEP; 257 | } 258 | 259 | uint32 sleepTime; 260 | 261 | void AlarmFunction () { 262 | // We always wake up with the 8Mhz HSI clock! 263 | // So adjust the clock if needed... 264 | 265 | #if F_CPU == 8000000UL 266 | // nothing to do, using about 8 mA 267 | #elif F_CPU == 16000000UL 268 | rcc_clk_init(RCC_CLKSRC_HSI, RCC_PLLSRC_HSE , RCC_PLLMUL_2); 269 | #elif F_CPU == 48000000UL 270 | rcc_clk_init(RCC_CLKSRC_HSI, RCC_PLLSRC_HSE , RCC_PLLMUL_6); 271 | #elif F_CPU == 72000000UL 272 | rcc_clk_init(RCC_CLKSRC_HSI, RCC_PLLSRC_HSE , RCC_PLLMUL_9); 273 | #else 274 | #error "Unknown F_CPU!?" 275 | #endif 276 | 277 | extern volatile uint32 systick_uptime_millis; 278 | systick_uptime_millis += sleepTime; 279 | 280 | } 281 | 282 | 283 | void blinkN2(int n, int d = 400, int t = 800) 284 | { 285 | pinMode(LED_BUILTIN, OUTPUT); 286 | for (int i = 0; i < n; i++) { 287 | digitalWrite(LED_BUILTIN, 0); 288 | delay(5); 289 | digitalWrite(LED_BUILTIN, 1); 290 | delay(d); 291 | } 292 | pinMode(LED_BUILTIN, INPUT_ANALOG); 293 | delay(t); 294 | } 295 | 296 | 297 | #ifndef OTAA 298 | // These callbacks are only used in over-the-air activation, so they are 299 | // left empty here (we cannot leave them out completely unless 300 | // DISABLE_JOIN is set in config.h, otherwise the linker will complain). 301 | void os_getArtEui (u1_t* buf) { } 302 | void os_getDevEui (u1_t* buf) { } 303 | void os_getDevKey (u1_t* buf) { } 304 | 305 | #else // OTAA 306 | 307 | void os_getArtEui (u1_t* buf) { 308 | memcpy(buf, APPEUI, 8); 309 | } 310 | 311 | void os_getDevKey (u1_t* buf) { 312 | memcpy(buf, APPKEY, 16); 313 | 314 | #if 0 // use chip id - not used yet 315 | u1_t* p = STM32_ID; 316 | buf[0] = (p[0] & 0x7) + 1; 317 | buf[1] = (p[1] & 0x7) + 1; 318 | buf[2] = (p[2] & 0x7) + 1; 319 | buf[3] = (p[3] & 0x7) + 1; 320 | buf[4] = (p[4] & 0x7) + 1; 321 | buf[5] = (p[5] & 0x7) + 1; 322 | buf[6] = (p[6] & 0x7) + 1; 323 | buf[7] = (p[7] & 0x7) + 1; 324 | #endif 325 | } 326 | 327 | //static const u1_t DEVEUI[8]={}; // for OTAA - reversed 8 bytes of DevEUI registered with console.thethingsnetwork.org 328 | void os_getDevEui (u1_t* buf) { 329 | // use chip ID: 330 | memcpy(buf, &STM32_ID[1], 8); 331 | // Make locally registered: 332 | buf[0] = buf[0] & ~0x3 | 0x1; 333 | } 334 | #endif 335 | 336 | static osjob_t sendjob; 337 | 338 | // Pin mapping 339 | const lmic_pinmap lmic_pins = { 340 | #if USE_SPI == 1 341 | .nss = PA4, 342 | .rxtx = LMIC_UNUSED_PIN, 343 | #if PCB_VER == 20 344 | .rst = PB1, 345 | .dio = {PB3, PB4, LMIC_UNUSED_PIN} // different IOs on pcb version 2.0 346 | #else // PCB version >2.0 347 | .rst = PB0, 348 | .dio = {PA3, PB5, LMIC_UNUSED_PIN} // we dont use dio2 349 | #endif 350 | #else // USE_SPI == 2 351 | .nss = PB12, 352 | .rxtx = LMIC_UNUSED_PIN, 353 | .rst = PA8, 354 | .dio = {PB1, PB10, PB11} 355 | #endif 356 | }; 357 | 358 | 359 | bool TX_done = false; 360 | bool joined = false; 361 | 362 | void onEvent (ev_t ev) { 363 | #ifdef DEBUG 364 | Serial.println(F("Enter onEvent")); 365 | #endif 366 | 367 | switch (ev) { 368 | case EV_SCAN_TIMEOUT: 369 | Serial.println(F("EV_SCAN_TIMEOUT")); 370 | break; 371 | case EV_BEACON_FOUND: 372 | Serial.println(F("EV_BEACON_FOUND")); 373 | break; 374 | case EV_BEACON_MISSED: 375 | Serial.println(F("EV_BEACON_MISSED")); 376 | break; 377 | case EV_BEACON_TRACKED: 378 | Serial.println(F("EV_BEACON_TRACKED")); 379 | break; 380 | case EV_JOINING: 381 | Serial.println(F("EV_JOINING")); 382 | break; 383 | case EV_JOINED: 384 | Serial.println(F("EV_JOINED")); 385 | joined = true; 386 | break; 387 | case EV_RFU1: 388 | Serial.println(F("EV_RFU1")); 389 | break; 390 | case EV_JOIN_FAILED: 391 | Serial.println(F("EV_JOIN_FAILED")); 392 | break; 393 | case EV_REJOIN_FAILED: 394 | Serial.println(F("EV_REJOIN_FAILED")); 395 | break; 396 | case EV_TXCOMPLETE: 397 | TX_done = true; 398 | Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); 399 | if (LMIC.dataLen) { 400 | // data received in rx slot after tx 401 | Serial.print(F("Data Received: ")); 402 | Serial.write(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); 403 | Serial.println(); 404 | payload.rate2 = (LMIC.frame + LMIC.dataBeg)[0]; 405 | txInterval = (1 << payload.rate2); 406 | if (LMIC.dataLen > 1) { 407 | Serial.print(F("...change SF to: ")); 408 | Serial.println((LMIC.frame + LMIC.dataBeg)[1]); 409 | switch ((LMIC.frame + LMIC.dataBeg)[1]) { 410 | case 7: LMIC_setDrTxpow(DR_SF7, 14); break; 411 | case 8: LMIC_setDrTxpow(DR_SF8, 14); break; 412 | case 9: LMIC_setDrTxpow(DR_SF9, 14); break; 413 | case 10: LMIC_setDrTxpow(DR_SF10, 14); break; 414 | case 11: LMIC_setDrTxpow(DR_SF11, 14); break; 415 | case 12: LMIC_setDrTxpow(DR_SF12, 14); break; 416 | } 417 | } 418 | } 419 | // Schedule next transmission 420 | #ifndef SLEEP 421 | os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(txInterval), do_send); 422 | #else 423 | next = true; // handling of going to deep sleep in loop() 424 | #endif 425 | 426 | break; 427 | case EV_LOST_TSYNC: 428 | Serial.println(F("EV_LOST_TSYNC")); 429 | break; 430 | case EV_RESET: 431 | Serial.println(F("EV_RESET")); 432 | break; 433 | case EV_RXCOMPLETE: 434 | // data received in ping slot 435 | Serial.println(F("EV_RXCOMPLETE")); 436 | break; 437 | case EV_LINK_DEAD: 438 | Serial.println(F("EV_LINK_DEAD")); 439 | break; 440 | case EV_LINK_ALIVE: 441 | Serial.println(F("EV_LINK_ALIVE")); 442 | break; 443 | default: 444 | Serial.println(F("Unknown event")); 445 | break; 446 | } 447 | #ifdef DEBUG 448 | Serial.println(F("Leave onEvent")); 449 | #endif 450 | 451 | } 452 | 453 | void do_send(osjob_t* j) { 454 | 455 | #ifdef DEBUG 456 | Serial.println(F("Enter do_send")); 457 | #endif 458 | 459 | // Check if there is not a current TX/RX job running 460 | if (LMIC.opmode & OP_TXRXPEND) { 461 | Serial.println(F("OP_TXRXPEND, not sending")); 462 | } else { 463 | readData(); 464 | #ifdef SLEEP 465 | // Disable link check validation 466 | LMIC_setLinkCheckMode(0); 467 | #endif 468 | // Prepare upstream data transmission at the next possible time. 469 | #ifdef USE_CAYENNE 470 | Serial.println(F("cayenne payload queued")); 471 | LMIC_setTxData2(2, (unsigned char *)&cayenne_lpp, sizeof(cayenne_lpp), 0); 472 | #else 473 | Serial.println(F("generic payload queued")); 474 | LMIC_setTxData2(1, (unsigned char *)&payload, sizeof(payload), 0); 475 | #endif 476 | } 477 | // Next TX is scheduled after TX_COMPLETE event. 478 | #ifdef DEBUG 479 | Serial.println(F("Leave do_send")); 480 | #endif 481 | TX_done = false; 482 | 483 | } 484 | 485 | void readData() 486 | { 487 | adc_enable(ADC1); 488 | 489 | adc_reg_map *regs = ADC1->regs; 490 | regs->CR2 |= ADC_CR2_TSVREFE; // enable VREFINT and temp sensor 491 | regs->SMPR1 = (ADC_SMPR1_SMP17 /* | ADC_SMPR1_SMP16 */); // sample rate for VREFINT ADC channel 492 | 493 | delay(50); 494 | 495 | int vref = 1200 * 4096 / adc_read(ADC1, 17); // ADC sample to millivolts 496 | regs->CR2 &= ~ADC_CR2_TSVREFE; // disable VREFINT and temp sensor 497 | 498 | adc_disable(ADC1); 499 | 500 | vref += 5; 501 | 502 | /** 503 | if (vref < 2000 || vref >= 3000) 504 | blinkN(vref / 1000); 505 | 506 | blinkN(vref % 1000 / 100); 507 | blinkN(vref % 100 / 10); 508 | **/ 509 | 510 | payload.power = (vref / 10) - 200; 511 | 512 | 513 | #ifdef USE_SENSOR 514 | 515 | delay(100); // give BME some time FIXME 516 | 517 | bme280.takeForcedMeasurement(); 518 | payload.temp= int(bme280.readTemperature() * 100.00F); 519 | payload.pres= int(bme280.readPressure() / 10.0F); 520 | payload.humi= int(bme280.readHumidity() * 100.00F); 521 | 522 | cayenne_lpp[0] = 0x01; // channel 523 | cayenne_lpp[1] = 0x67; // Chayenne Type "Temp" 524 | cayenne_lpp[2] = ((int((payload.temp / 10))) >> 8) & 255; 525 | cayenne_lpp[3] = (int((bme280.readTemperature() * 10))) & 255; 526 | cayenne_lpp[4] = 0x02; // channel 527 | cayenne_lpp[5] = 0x68; // Cayenne Type "Humidity" 528 | cayenne_lpp[6] = (int(round(payload.humi / 50))) & 255; 529 | cayenne_lpp[7] = 0x03; // Channel 530 | cayenne_lpp[8] = 0x73; // Cayenne Type "Pressure" 531 | cayenne_lpp[9] = ((int(payload.pres)) >> 8) & 255; 532 | cayenne_lpp[10] = (int(payload.pres)) & 255; 533 | 534 | pinMode(PB6, INPUT_ANALOG); //SCL save energy 535 | pinMode(PB7, INPUT_ANALOG); //SDA save energy 536 | 537 | #else // simulate sensor 538 | payload.temp= (19.15) * 100.00; 539 | payload.humi= (52.45) * 100.00; 540 | payload.pres= (1096) * 1; 541 | cayenne_lpp[0] = 0x01; // channel 542 | cayenne_lpp[1] = 0x67; // Chayenne Type "Temp" 543 | cayenne_lpp[2] = 0x00; 544 | cayenne_lpp[3] = 0xFF; 545 | cayenne_lpp[4] = 0x02; // channel 546 | cayenne_lpp[5] = 0x68; // Cayenne Type "Humidity" 547 | cayenne_lpp[6] = 0xFF; 548 | cayenne_lpp[7] = 0x03; // Channel 549 | cayenne_lpp[8] = 0x73; // Cayenne Type "Pressure" 550 | cayenne_lpp[9] = 0x00; 551 | cayenne_lpp[10] = 0xFF; 552 | #endif 553 | 554 | #ifdef DEBUG 555 | Serial.print("temp: "); 556 | Serial.println(payload.temp); 557 | Serial.print("humi: "); 558 | Serial.println(payload.humi); // Serial.println(mySensor.readFloatHumidity()); 559 | Serial.print("pres: "); 560 | Serial.println(payload.pres); // Serial.println( Serial.println(payload.pres)); 561 | Serial.println(); 562 | #endif 563 | 564 | } 565 | 566 | void allInput() 567 | { 568 | adc_disable(ADC1); 569 | adc_disable(ADC2); 570 | 571 | pinMode(PA0, INPUT_ANALOG); 572 | pinMode(PA1, INPUT_ANALOG); 573 | pinMode(PA2, INPUT_ANALOG); 574 | pinMode(PA3, INPUT_ANALOG); 575 | pinMode(PA4, INPUT_ANALOG); 576 | pinMode(PA5, INPUT_ANALOG); 577 | pinMode(PA6, INPUT_ANALOG); 578 | pinMode(PA7, INPUT_ANALOG); 579 | pinMode(PA8, INPUT_ANALOG); 580 | pinMode(PA9, INPUT_ANALOG); 581 | pinMode(PA10, INPUT_ANALOG); 582 | 583 | pinMode(PA11, INPUT_ANALOG); 584 | pinMode(PA12, INPUT_ANALOG); 585 | pinMode(PA13, INPUT_ANALOG); 586 | pinMode(PA14, INPUT_ANALOG); 587 | pinMode(PA15, INPUT_ANALOG); 588 | 589 | pinMode(PB0, INPUT_ANALOG); 590 | pinMode(PB1, INPUT_ANALOG); 591 | pinMode(PB2, INPUT_ANALOG); 592 | pinMode(PB3, INPUT_ANALOG); 593 | pinMode(PB4, INPUT_ANALOG); 594 | pinMode(PB5, INPUT_ANALOG); 595 | pinMode(PB6, INPUT_ANALOG); 596 | pinMode(PB7, INPUT_ANALOG); 597 | pinMode(PB8, INPUT_ANALOG); 598 | pinMode(PB9, INPUT_ANALOG); 599 | pinMode(PB10, INPUT_ANALOG); 600 | pinMode(PB11, INPUT_ANALOG); 601 | pinMode(PB12, INPUT_ANALOG); 602 | pinMode(PB13, INPUT_ANALOG); 603 | pinMode(PB14, INPUT_ANALOG); 604 | pinMode(PB15, INPUT_ANALOG); 605 | } 606 | 607 | void scanI2C() { 608 | byte error, address; 609 | int nDevices; 610 | 611 | Serial.println("Scanning..."); 612 | 613 | Wire.begin(); 614 | 615 | nDevices = 0; 616 | for(address = 1; address < 127; address++) { 617 | // The i2c_scanner uses the return value of 618 | // the Write.endTransmisstion to see if 619 | // a device did acknowledge to the address. 620 | 621 | Wire.beginTransmission(address); 622 | error = Wire.endTransmission(); 623 | 624 | if (error == 0) { 625 | Serial.print("I2C device found at address 0x"); 626 | if (address < 16) 627 | Serial.print("0"); 628 | Serial.println(address, HEX); 629 | 630 | if (address == 1) { 631 | Serial.print("Hint: unknown device - missing pullups on i2c bus?"); 632 | } 633 | 634 | nDevices++; 635 | } 636 | else if (error == 4) { 637 | Serial.print("Unknown error at address 0x"); 638 | if (address < 16) 639 | Serial.print("0"); 640 | Serial.println(address, HEX); 641 | } 642 | } 643 | if (nDevices == 0) 644 | Serial.println("No I2C devices found"); 645 | else 646 | Serial.println("done"); 647 | } 648 | 649 | 650 | /***** SETUP 651 | * 652 | */ 653 | void setup() { 654 | 655 | // as soon as possible 656 | rt.createAlarm(&AlarmFunction, rt.getTime() + txInterval*1000); 657 | 658 | // some bme280 breakouts need software patch for hardware difference 659 | #ifdef SWAPE_I2C 660 | Wire.scl_pin=PB7; 661 | Wire.sda_pin=PB6; 662 | #endif 663 | 664 | SPIp = &mySPI; 665 | 666 | pinMode(led, OUTPUT); 667 | #ifdef DEBUG 668 | digitalWrite(led, LOW); 669 | delay(20); 670 | digitalWrite(led, HIGH); 671 | #endif 672 | 673 | Serial.begin(115200); 674 | Serial.println(F("Wait 1sec")); 675 | delay(1000); // give usb port the chance to be regognized by host driver 676 | 677 | allInput(); // save energy 678 | 679 | int cnt = 0; 680 | Serial.println(F("wait 3sec for USB before going to sleep.")); 681 | while (cnt++ < 3) { 682 | Serial.print(F(".")); 683 | digitalWrite(led, LOW); 684 | delay(10); 685 | digitalWrite(led, HIGH); 686 | delay(290); 687 | } 688 | Serial.println(F("go")); 689 | delay(100); 690 | 691 | #ifdef BLINK 692 | digitalWrite(led, HIGH); 693 | pinMode(led, INPUT_ANALOG); 694 | #endif 695 | 696 | #ifdef DEBUG 697 | Serial.print("LMIC.seqnoUp: "); 698 | Serial.println(LMIC.seqnoUp); 699 | #endif 700 | 701 | if (DEEP_SLEEP) { 702 | if (readBR(2) != MAGICNB) { 703 | LMIC.seqnoUp = 0; 704 | storeBR(0, LMIC.seqnoUp); 705 | storeBR(2, MAGICNB); 706 | } 707 | } 708 | 709 | // LMIC init 710 | os_init(); 711 | // Reset the MAC state. Session and pending data transfers will be discarded. 712 | LMIC_reset(); 713 | 714 | // only to test power mode - not used 715 | #ifdef TESTSLEEP 716 | while(true) { 717 | SPIp->end(); 718 | 719 | pinMode(PB6, INPUT_ANALOG); //SCL 720 | pinMode(PB7, INPUT_ANALOG); //SDA 721 | 722 | digitalWrite(PA5, LOW); // SCK 723 | pinMode(PA5, OUTPUT); 724 | 725 | digitalWrite(PA7, LOW); // MOSI 726 | pinMode(PA7, OUTPUT); 727 | 728 | pinMode(PA6, INPUT_ANALOG); // MISO 729 | 730 | digitalWrite(lmic_pins.nss, LOW); // NSS 731 | pinMode(lmic_pins.nss, OUTPUT); 732 | 733 | // DIO Inputs 734 | pinMode(lmic_pins.dio[0], INPUT_ANALOG); 735 | pinMode(lmic_pins.dio[1], INPUT_ANALOG); 736 | pinMode(lmic_pins.dio[2], INPUT_ANALOG); 737 | 738 | pinMode(lmic_pins.rst, INPUT_ANALOG); 739 | 740 | // Serial 741 | pinMode(PA9, INPUT_ANALOG); 742 | pinMode(PA10, INPUT_ANALOG); 743 | 744 | enterSleepMode(DEEP_SLEEP); 745 | } 746 | #endif 747 | 748 | #ifndef OTAA 749 | // Set static session parameters. Instead of dynamically establishing a session 750 | // by joining the network, precomputed session parameters are be provided. 751 | LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY); 752 | #endif 753 | 754 | #ifdef SINGLE_CHN 755 | // only to support non standard single channel geteways 756 | LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 757 | LMIC_disableChannel(1); 758 | LMIC_disableChannel(2); 759 | LMIC_disableChannel(3); 760 | LMIC_disableChannel(4); 761 | LMIC_disableChannel(5); 762 | LMIC_disableChannel(6); 763 | LMIC_disableChannel(7); 764 | LMIC_disableChannel(8); 765 | #else 766 | // Set up the channels used by the Things Network, which corresponds 767 | // to the defaults of most gateways. Without this, only three base 768 | // channels from the LoRaWAN specification are used, which certainly 769 | // works, so it is good for debugging, but can overload those 770 | // frequencies, so be sure to configure the full frequency range of 771 | // your network here (unless your network autoconfigures them). 772 | // Setting up channels should happen after LMIC_setSession, as that 773 | // configures the minimal channel set. 774 | LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 775 | LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band 776 | LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 777 | LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 778 | LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 779 | LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 780 | LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 781 | LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band 782 | LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band 783 | // TTN defines an additional channel at 869.525Mhz using SF9 for class B 784 | // devices' ping slots. LMIC does not have an easy way to define set this 785 | // frequency and support for class B is spotty and untested, so this 786 | // frequency is not configured here. 787 | #endif 788 | 789 | #if F_CPU == 8000000UL 790 | // HSI is less accurate 791 | LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); 792 | #endif 793 | 794 | // TTN uses SF9 for its RX2 window. 795 | LMIC.dn2Dr = DR_SF9; 796 | 797 | Serial.print("LMIC.dn2Freq: "); 798 | Serial.println(LMIC.dn2Freq/1000000.0); 799 | 800 | // Set data rate and transmit power (note: txpow seems to be ignored by the library) 801 | LMIC_setDrTxpow(RATE, 14); 802 | 803 | if (DEEP_SLEEP) 804 | LMIC.seqnoUp = readBR(0); 805 | 806 | #ifdef USE_SENSOR 807 | bme280.begin(0x77); 808 | initBME280Adafruit(); 809 | #endif 810 | 811 | #ifndef RECEIVE 812 | LMIC.skipRX = 1; // Do NOT wait for downstream data! 813 | #endif 814 | 815 | // Start job 816 | do_send(&sendjob); 817 | 818 | #ifdef DEBUG 819 | Serial.println(F("Leave setup")); 820 | #endif 821 | 822 | } 823 | 824 | 825 | void loop() { 826 | 827 | if (next == false) { // still in LMIC state maschine eg. wait for sending, RX ... 828 | 829 | #ifdef BLINK 830 | pinMode(led, OUTPUT); 831 | digitalWrite(led, LOW); 832 | #endif 833 | 834 | iwdg_feed_debug(); 835 | os_runloop_once(); 836 | 837 | } else { // LMIC cycle fnished including receive 838 | 839 | #ifdef BLINK 840 | digitalWrite(led, HIGH); 841 | pinMode(led, INPUT_ANALOG); 842 | #endif 843 | 844 | #ifdef DEBUG 845 | Serial.print("LMIC.seqnoUp:"); 846 | Serial.println(LMIC.seqnoUp); 847 | #endif 848 | 849 | if (DEEP_SLEEP) // keep seqNo in RTC ram 850 | storeBR(0, LMIC.seqnoUp); 851 | 852 | SPIp->end(); 853 | 854 | pinMode(PB6, INPUT_ANALOG); //SCL 855 | pinMode(PB7, INPUT_ANALOG); //SDA 856 | 857 | digitalWrite(PA5, LOW); // SCK 858 | pinMode(PA5, OUTPUT); 859 | 860 | digitalWrite(PA7, LOW); // MOSI 861 | pinMode(PA7, OUTPUT); 862 | 863 | pinMode(PA6, INPUT_ANALOG); // MISO 864 | 865 | digitalWrite(lmic_pins.nss, LOW); // NSS 866 | pinMode(lmic_pins.nss, OUTPUT); 867 | 868 | // DIO Inputs 869 | pinMode(lmic_pins.dio[0], INPUT_ANALOG); 870 | pinMode(lmic_pins.dio[1], INPUT_ANALOG); 871 | pinMode(lmic_pins.dio[2], INPUT_ANALOG); 872 | 873 | pinMode(lmic_pins.rst, INPUT_ANALOG); 874 | 875 | // Serial 876 | pinMode(PA9, INPUT_ANALOG); 877 | pinMode(PA10, INPUT_ANALOG); 878 | 879 | enterSleepMode(DEEP_SLEEP); 880 | 881 | // should not be reached, board reboots on wakeup 882 | blinkN2(8,300,100); 883 | } 884 | } 885 | 886 | 887 | void initBME280Adafruit(void) { 888 | 889 | bme280.setSampling(Adafruit_BME280::MODE_FORCED, 890 | Adafruit_BME280::SAMPLING_X1, // temperature 891 | Adafruit_BME280::SAMPLING_X1, // pressure 892 | Adafruit_BME280::SAMPLING_X1, // humidity 893 | Adafruit_BME280::FILTER_OFF ); 894 | } 895 | 896 | void sleepBME280Adafruit(void) { 897 | 898 | bme280.setSampling(Adafruit_BME280::MODE_FORCED, 899 | Adafruit_BME280::SAMPLING_NONE, // temperature 900 | Adafruit_BME280::SAMPLING_NONE, // pressure 901 | Adafruit_BME280::SAMPLING_NONE, // humidity 902 | Adafruit_BME280::FILTER_OFF ); 903 | } 904 | 905 | void PrintHex83(uint8_t *data, uint8_t length) // prints 8-bit data in hex 906 | { 907 | char tmp[length*2+1]; 908 | byte first ; 909 | int j=0; 910 | for (uint8_t i=0; i> 4) | 48; 913 | if (first > 57) tmp[j] = first + (byte)39; 914 | else tmp[j] = first ; 915 | j++; 916 | 917 | first = (data[i] & 0x0F) | 48; 918 | if (first > 57) tmp[j] = first + (byte)39; 919 | else tmp[j] = first; 920 | j++; 921 | } 922 | tmp[length*2] = 0; 923 | Serial.println(tmp); 924 | } 925 | 926 | 927 | 928 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CitizenSensor 2 | A low budget battery powered LoRa node to smart up your city 3 | 4 |

5 | LoraNode Fritzing 6 |

7 | 8 | ## bill of materials 9 | | Parts List for boxed version | 10 | |-------------------------------------------| 11 | | STM32F103C8T6 Minimum System Dev Board (blue pill) | 12 | | RFM95 868Mhz Modul | 13 | | SMA PCB Connector female | 14 | | Antenna SMA male 868mhz 2dbi | 15 | | BME 280 Breakout 6pin 3.3v only w/ pullup | 16 | | Battery Holder 2xAA | 17 | | Header 2.54mm 12pin | 18 | | Abzweigdose IP54 grau Aufputz 75x75x40 mm | 19 | | CitizenSensor V4.0 PCB | 20 | CS40-Tisch600.jpg 21 | ## Prototyp 22 |

23 | Prototype 24 |

25 | 26 | ## Citizen Sensor Kit (PCB 4.0) 27 |

28 | Kit 29 |

30 | 31 | ## Schematic PCB V4.0 32 |

33 | Schematic 34 |

35 | 36 | ## PCB V4.0 top 37 |

38 | pcb 39 |

40 | -------------------------------------------------------------------------------- /media/CS40-Tisch600.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangewaylab/CitizenSensor/12ff18b8b91a2a069a529801a68a9e944a0606f8/media/CS40-Tisch600.jpg -------------------------------------------------------------------------------- /media/CitizenSensor-LoRa-STM32-v4-01-BME680.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangewaylab/CitizenSensor/12ff18b8b91a2a069a529801a68a9e944a0606f8/media/CitizenSensor-LoRa-STM32-v4-01-BME680.png -------------------------------------------------------------------------------- /media/CitizenSensorLora20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangewaylab/CitizenSensor/12ff18b8b91a2a069a529801a68a9e944a0606f8/media/CitizenSensorLora20.jpg -------------------------------------------------------------------------------- /media/STM32LoraNode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangewaylab/CitizenSensor/12ff18b8b91a2a069a529801a68a9e944a0606f8/media/STM32LoraNode.png -------------------------------------------------------------------------------- /media/pcb40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangewaylab/CitizenSensor/12ff18b8b91a2a069a529801a68a9e944a0606f8/media/pcb40.png --------------------------------------------------------------------------------