Smart-Home am Beispiel der Präsenzerkennung im Raum Projektarbeit Lennart Heimbs, Johannes Krug, Sebastian Dohle und Kevin Holzschuh bei Prof. Oliver Hofmann SS2019
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MyHwAVR.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * The MySensors Arduino library handles the wireless radio link and protocol
  3. * between your home built sensors/actuators and HA controller of choice.
  4. * The sensors forms a self healing radio network with optional repeaters. Each
  5. * repeater and gateway builds a routing tables in EEPROM which keeps track of the
  6. * network topology allowing messages to be routed to nodes.
  7. *
  8. * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
  9. * Copyright (C) 2013-2018 Sensnology AB
  10. * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
  11. *
  12. * Documentation: http://www.mysensors.org
  13. * Support Forum: http://forum.mysensors.org
  14. *
  15. * This program is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU General Public License
  17. * version 2 as published by the Free Software Foundation.
  18. */
  19. #include "MyHwAVR.h"
  20. #include "avr/boot.h"
  21. bool hwInit(void)
  22. {
  23. #if !defined(MY_DISABLED_SERIAL)
  24. MY_SERIALDEVICE.begin(MY_BAUD_RATE);
  25. #if defined(MY_GATEWAY_SERIAL)
  26. while (!MY_SERIALDEVICE) {}
  27. #endif
  28. #endif
  29. return true;
  30. }
  31. #define WDTO_SLEEP_FOREVER (0xFFu)
  32. #define INVALID_INTERRUPT_NUM (0xFFu)
  33. volatile uint8_t _wokeUpByInterrupt =
  34. INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu.
  35. volatile uint8_t _wakeUp1Interrupt =
  36. INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp1-callback.
  37. volatile uint8_t _wakeUp2Interrupt =
  38. INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
  39. void wakeUp1(void)
  40. {
  41. // Disable sleep. When an interrupt occurs after attachInterrupt,
  42. // but before sleeping the CPU would not wake up.
  43. // Ref: http://playground.arduino.cc/Learning/ArduinoSleepCode
  44. sleep_disable();
  45. detachInterrupt(_wakeUp1Interrupt);
  46. // First interrupt occurred will be reported only
  47. if (INVALID_INTERRUPT_NUM == _wokeUpByInterrupt) {
  48. _wokeUpByInterrupt = _wakeUp1Interrupt;
  49. }
  50. }
  51. void wakeUp2(void)
  52. {
  53. sleep_disable();
  54. detachInterrupt(_wakeUp2Interrupt);
  55. // First interrupt occurred will be reported only
  56. if (INVALID_INTERRUPT_NUM == _wokeUpByInterrupt) {
  57. _wokeUpByInterrupt = _wakeUp2Interrupt;
  58. }
  59. }
  60. inline bool interruptWakeUp(void)
  61. {
  62. return _wokeUpByInterrupt != INVALID_INTERRUPT_NUM;
  63. }
  64. void clearPendingInterrupt(const uint8_t interrupt)
  65. {
  66. EIFR = _BV(interrupt);
  67. }
  68. // Watchdog Timer interrupt service routine. This routine is required
  69. // to allow automatic WDIF and WDIE bit clearance in hardware.
  70. ISR (WDT_vect)
  71. {
  72. }
  73. void hwPowerDown(const uint8_t wdto)
  74. {
  75. // Let serial prints finish (debug, log etc)
  76. #ifndef MY_DISABLED_SERIAL
  77. MY_SERIALDEVICE.flush();
  78. #endif
  79. // disable ADC for power saving
  80. ADCSRA &= ~(1 << ADEN);
  81. // save WDT settings
  82. const uint8_t WDTsave = WDTCSR;
  83. if (wdto != WDTO_SLEEP_FOREVER) {
  84. wdt_enable(wdto);
  85. // enable WDT interrupt before system reset
  86. WDTCSR |= (1 << WDCE) | (1 << WDIE);
  87. } else {
  88. // if sleeping forever, disable WDT
  89. wdt_disable();
  90. }
  91. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  92. cli();
  93. sleep_enable();
  94. #if defined(__AVR_ATmega328P__)
  95. sleep_bod_disable();
  96. #endif
  97. // Enable interrupts & sleep until WDT or ext. interrupt
  98. sei();
  99. // Directly sleep CPU, to prevent race conditions!
  100. // Ref: chapter 7.7 of ATMega328P datasheet
  101. sleep_cpu();
  102. sleep_disable();
  103. // restore previous WDT settings
  104. cli();
  105. wdt_reset();
  106. // enable WDT changes
  107. WDTCSR |= (1 << WDCE) | (1 << WDE);
  108. // restore saved WDT settings
  109. WDTCSR = WDTsave;
  110. sei();
  111. // enable ADC
  112. ADCSRA |= (1 << ADEN);
  113. }
  114. void hwInternalSleep(uint32_t ms)
  115. {
  116. // Sleeping with watchdog only supports multiples of 16ms.
  117. // Round up to next multiple of 16ms, to assure we sleep at least the
  118. // requested amount of time. Sleep of 0ms will not sleep at all!
  119. ms += 15u;
  120. while (!interruptWakeUp() && ms >= 16) {
  121. for (uint8_t period = 9u; ; --period) {
  122. const uint16_t comparatorMS = 1 << (period + 4);
  123. if ( ms >= comparatorMS) {
  124. hwPowerDown(period); // 8192ms => 9, 16ms => 0
  125. ms -= comparatorMS;
  126. break;
  127. }
  128. }
  129. }
  130. }
  131. int8_t hwSleep(uint32_t ms)
  132. {
  133. // Return what woke the mcu.
  134. // Default: no interrupt triggered, timer wake up
  135. int8_t ret = MY_WAKE_UP_BY_TIMER;
  136. if (ms > 0u) {
  137. // sleep for defined time
  138. hwInternalSleep(ms);
  139. } else {
  140. // sleep until ext interrupt triggered
  141. hwPowerDown(WDTO_SLEEP_FOREVER);
  142. }
  143. if (interruptWakeUp()) {
  144. ret = static_cast<int8_t>(_wokeUpByInterrupt);
  145. }
  146. // Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
  147. _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
  148. return ret;
  149. }
  150. int8_t hwSleep(const uint8_t interrupt, const uint8_t mode, uint32_t ms)
  151. {
  152. return hwSleep(interrupt, mode, INVALID_INTERRUPT_NUM, 0u, ms);
  153. }
  154. int8_t hwSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2,
  155. const uint8_t mode2,
  156. uint32_t ms)
  157. {
  158. // ATMega328P supports following modes to wake from sleep: LOW, CHANGE, RISING, FALLING
  159. // Datasheet states only LOW can be used with INT0/1 to wake from sleep, which is incorrect.
  160. // Ref: http://gammon.com.au/interrupts
  161. // Disable interrupts until going to sleep, otherwise interrupts occurring between attachInterrupt()
  162. // and sleep might cause the ATMega to not wakeup from sleep as interrupt has already be handled!
  163. cli();
  164. // attach interrupts
  165. _wakeUp1Interrupt = interrupt1;
  166. _wakeUp2Interrupt = interrupt2;
  167. // Attach external interrupt handlers, and clear any pending interrupt flag
  168. // to prevent waking immediately again.
  169. // Ref: https://forum.arduino.cc/index.php?topic=59217.0
  170. if (interrupt1 != INVALID_INTERRUPT_NUM) {
  171. clearPendingInterrupt(interrupt1);
  172. attachInterrupt(interrupt1, wakeUp1, mode1);
  173. }
  174. if (interrupt2 != INVALID_INTERRUPT_NUM) {
  175. clearPendingInterrupt(interrupt2);
  176. attachInterrupt(interrupt2, wakeUp2, mode2);
  177. }
  178. if (ms > 0u) {
  179. // sleep for defined time
  180. hwInternalSleep(ms);
  181. } else {
  182. // sleep until ext interrupt triggered
  183. hwPowerDown(WDTO_SLEEP_FOREVER);
  184. }
  185. // Assure any interrupts attached, will get detached when they did not occur.
  186. if (interrupt1 != INVALID_INTERRUPT_NUM) {
  187. detachInterrupt(interrupt1);
  188. }
  189. if (interrupt2 != INVALID_INTERRUPT_NUM) {
  190. detachInterrupt(interrupt2);
  191. }
  192. // Return what woke the mcu.
  193. // Default: no interrupt triggered, timer wake up
  194. int8_t ret = MY_WAKE_UP_BY_TIMER;
  195. if (interruptWakeUp()) {
  196. ret = static_cast<int8_t>(_wokeUpByInterrupt);
  197. }
  198. // Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
  199. _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
  200. return ret;
  201. }
  202. inline void hwRandomNumberInit(void)
  203. {
  204. // This function initializes the random number generator with a seed
  205. // of 32 bits. This method is good enough to earn FIPS 140-2 conform
  206. // random data. This should reach to generate 32 Bit for randomSeed().
  207. uint32_t seed = 0;
  208. uint32_t timeout = millis() + 20;
  209. // Trigger floating effect of an unconnected pin
  210. pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT_PULLUP);
  211. pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT);
  212. delay(10);
  213. // Generate 32 bits of datas
  214. for (uint8_t i=0; i<32; i++) {
  215. const int pinValue = analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN);
  216. // Wait until the analog value has changed
  217. while ((pinValue == analogRead(MY_SIGNING_SOFT_RANDOMSEED_PIN)) && (timeout>=millis())) {
  218. seed ^= (millis() << i);
  219. // Check of data generation is slow
  220. if (timeout<=millis()) {
  221. // Trigger pin again
  222. pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT_PULLUP);
  223. pinMode(MY_SIGNING_SOFT_RANDOMSEED_PIN, INPUT);
  224. // Pause a short while
  225. delay(seed % 10);
  226. timeout = millis() + 20;
  227. }
  228. }
  229. }
  230. randomSeed(seed);
  231. }
  232. bool hwUniqueID(unique_id_t *uniqueID)
  233. {
  234. // padding
  235. (void)memset(uniqueID, MY_HWID_PADDING_BYTE, sizeof(unique_id_t));
  236. // no unique ID for non-PB AVR, use HW specifics for diversification
  237. *((uint8_t *)uniqueID) = boot_signature_byte_get(0x00);
  238. *((uint8_t *)uniqueID + 1) = boot_signature_byte_get(0x02);
  239. *((uint8_t *)uniqueID + 2) = boot_signature_byte_get(0x04);
  240. *((uint8_t *)uniqueID + 3) = boot_signature_byte_get(0x01); //OSCCAL
  241. #if defined(__AVR_ATmega328PB__)
  242. // ATMEGA328PB specifics, has unique ID
  243. for(uint8_t idx = 0; idx < 10; idx++) {
  244. *((uint8_t *)uniqueID + 4 + idx) = boot_signature_byte_get(0xE + idx);
  245. }
  246. return true; // unique ID returned
  247. #else
  248. return false; // no unique ID returned
  249. #endif
  250. }
  251. uint16_t hwCPUVoltage(void)
  252. {
  253. // Measure Vcc against 1.1V Vref
  254. #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  255. ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
  256. #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  257. ADMUX = (_BV(MUX5) | _BV(MUX0));
  258. #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  259. ADMUX = (_BV(MUX3) | _BV(MUX2));
  260. #else
  261. ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
  262. #endif
  263. // Vref settle
  264. delay(70);
  265. // Do conversion
  266. ADCSRA |= _BV(ADSC);
  267. while (bit_is_set(ADCSRA,ADSC)) {};
  268. // return Vcc in mV
  269. return (1125300UL) / ADC;
  270. }
  271. uint16_t hwCPUFrequency(void)
  272. {
  273. cli();
  274. // save WDT & timer settings
  275. const uint8_t WDTsave = WDTCSR;
  276. const uint8_t TCCR1Asave = TCCR1A;
  277. const uint8_t TCCR1Csave = TCCR1C;
  278. // setup timer1
  279. TIFR1 = 0xFF;
  280. TCNT1 = 0;
  281. TCCR1A = 0;
  282. TCCR1C = 0;
  283. // set wdt
  284. wdt_enable(WDTO_500MS);
  285. // enable WDT interrupt mode => first timeout WDIF, 2nd timeout reset
  286. WDTCSR |= (1 << WDIE);
  287. wdt_reset();
  288. // start timer1 with 1024 prescaling
  289. TCCR1B = _BV(CS12) | _BV(CS10);
  290. // wait until wdt interrupt
  291. while (bit_is_clear(WDTCSR,WDIF)) {};
  292. // stop timer
  293. TCCR1B = 0;
  294. // restore WDT settings
  295. wdt_reset();
  296. WDTCSR |= (1 << WDCE) | (1 << WDE);
  297. WDTCSR = WDTsave;
  298. sei();
  299. // restore timer settings
  300. TCCR1A = TCCR1Asave;
  301. TCCR1C = TCCR1Csave;
  302. // return frequency in 1/10MHz (accuracy +- 10%)
  303. return TCNT1 * 2048UL / 100000UL;
  304. }
  305. int8_t hwCPUTemperature(void)
  306. {
  307. #if defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328BP__) || defined(__AVR_ATmega32U4__)
  308. // Set the internal reference and mux.
  309. ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
  310. ADCSRA |= _BV(ADEN); // enable the ADC
  311. delay(20); // wait for voltages to become stable.
  312. ADCSRA |= _BV(ADSC); // Start the ADC
  313. // Wait until conversion done
  314. while (bit_is_set(ADCSRA, ADSC));
  315. // temperature is in degrees Celsius
  316. return static_cast<int8_t>((ADCW - MY_AVR_TEMPERATURE_OFFSET) / MY_AVR_TEMPERATURE_GAIN);
  317. #else
  318. return -127; // not available
  319. #endif
  320. }
  321. uint16_t hwFreeMem(void)
  322. {
  323. extern int __heap_start, *__brkval;
  324. int v;
  325. return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
  326. }
  327. void hwDebugPrint(const char *fmt, ... )
  328. {
  329. #ifndef MY_DISABLED_SERIAL
  330. char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
  331. #ifdef MY_GATEWAY_SERIAL
  332. // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
  333. snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "),
  334. C_INTERNAL, I_LOG_MESSAGE, hwMillis());
  335. MY_DEBUGDEVICE.print(fmtBuffer);
  336. #else
  337. // prepend timestamp
  338. MY_DEBUGDEVICE.print(hwMillis());
  339. MY_DEBUGDEVICE.print(F(" "));
  340. #endif
  341. va_list args;
  342. va_start(args, fmt);
  343. vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args);
  344. #ifdef MY_GATEWAY_SERIAL
  345. // Truncate message if this is gateway node
  346. fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
  347. fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
  348. #endif
  349. va_end(args);
  350. MY_DEBUGDEVICE.print(fmtBuffer);
  351. MY_DEBUGDEVICE.flush();
  352. #else
  353. (void)fmt;
  354. #endif
  355. }