├── media
├── pcb40.png
├── CS40-Tisch600.jpg
├── STM32LoraNode.png
├── CitizenSensorLora20.jpg
└── CitizenSensor-LoRa-STM32-v4-01-BME680.png
├── README.md
└── CitizenSensor-STM32
└── CitizenSensor-STM32.ino
/media/pcb40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangewaylab/CitizenSensor/HEAD/media/pcb40.png
--------------------------------------------------------------------------------
/media/CS40-Tisch600.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangewaylab/CitizenSensor/HEAD/media/CS40-Tisch600.jpg
--------------------------------------------------------------------------------
/media/STM32LoraNode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangewaylab/CitizenSensor/HEAD/media/STM32LoraNode.png
--------------------------------------------------------------------------------
/media/CitizenSensorLora20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangewaylab/CitizenSensor/HEAD/media/CitizenSensorLora20.jpg
--------------------------------------------------------------------------------
/media/CitizenSensor-LoRa-STM32-v4-01-BME680.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangewaylab/CitizenSensor/HEAD/media/CitizenSensor-LoRa-STM32-v4-01-BME680.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CitizenSensor
2 | A low budget battery powered LoRa node to smart up your city
3 |
4 |
5 |
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 |
24 |
25 |
26 | ## Citizen Sensor Kit (PCB 4.0)
27 |
28 |
29 |
30 |
31 | ## Schematic PCB V4.0
32 |
33 |
34 |
35 |
36 | ## PCB V4.0 top
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------