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.

MyHwNRF5.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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
  6. * the
  7. * network topology allowing messages to be routed to nodes.
  8. *
  9. * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
  10. * Copyright (C) 2013-2018 Sensnology AB
  11. * Copyright (C) 2017 Frank Holtz
  12. * Full contributor list:
  13. * https://github.com/mysensors/MySensors/graphs/contributors
  14. *
  15. * Documentation: http://www.mysensors.org
  16. * Support Forum: http://forum.mysensors.org
  17. *
  18. * This program is free software; you can redistribute it and/or
  19. * modify it under the terms of the GNU General Public License
  20. * version 2 as published by the Free Software Foundation.
  21. */
  22. #include "MyHwNRF5.h"
  23. #include <nrf.h>
  24. #define CRYPTO_LITTLE_ENDIAN
  25. #define INVALID_INTERRUPT_NUM (0xFFu)
  26. volatile uint8_t _wokeUpByInterrupt = INVALID_INTERRUPT_NUM; // Interrupt number that woke the mcu.
  27. volatile uint8_t _wakeUp1Interrupt =
  28. INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp1-callback.
  29. volatile uint8_t _wakeUp2Interrupt =
  30. INVALID_INTERRUPT_NUM; // Interrupt number for wakeUp2-callback.
  31. void wakeUp1(void) // place to send the interrupts
  32. {
  33. _wokeUpByInterrupt = _wakeUp1Interrupt;
  34. }
  35. void wakeUp2(void) // place to send the second interrupts
  36. {
  37. _wokeUpByInterrupt = _wakeUp2Interrupt;
  38. }
  39. void hwReadConfigBlock(void *buf, void *addr, size_t length)
  40. {
  41. uint8_t *dst = static_cast<uint8_t *>(buf);
  42. const int offs = reinterpret_cast<int>(addr);
  43. (void)NVRAM.read_block(dst, offs, length);
  44. }
  45. void hwWriteConfigBlock(void *buf, void *addr, size_t length)
  46. {
  47. uint8_t *src = static_cast<uint8_t *>(buf);
  48. const int offs = reinterpret_cast<int>(addr);
  49. (void)NVRAM.write_block(src, offs, length);
  50. }
  51. uint8_t hwReadConfig(const int addr)
  52. {
  53. return NVRAM.read(addr);
  54. }
  55. void hwWriteConfig(const int addr, uint8_t value)
  56. {
  57. (void)NVRAM.write(addr, value);
  58. }
  59. bool hwInit(void)
  60. {
  61. #ifdef MY_LOCK_MCU
  62. #ifdef NRF51
  63. // Lock MCU
  64. if((uint32_t)((NRF_UICR->RBPCONF & UICR_RBPCONF_PALL_Msk) >> UICR_RBPCONF_PALL_Pos) !=
  65. UICR_RBPCONF_PALL_Enabled) {
  66. Flash.write((uint32_t *)&NRF_UICR->RBPCONF, (NRF_UICR->RBPCONF & ~UICR_RBPCONF_PALL_Msk));
  67. hwReboot();
  68. }
  69. #else
  70. // Lock MCU
  71. if((uint32_t)((NRF_UICR->APPROTECT & UICR_APPROTECT_PALL_Msk) >> UICR_APPROTECT_PALL_Pos) !=
  72. UICR_APPROTECT_PALL_Enabled) {
  73. Flash.write((uint32_t *)&NRF_UICR->APPROTECT, (NRF_UICR->APPROTECT & ~UICR_APPROTECT_PALL_Msk));
  74. hwReboot();
  75. }
  76. #endif
  77. #endif
  78. #if defined(NRF51) && defined(CONFIG_ENABLE_PINRESET)
  79. // Enabling reset for NRF51 isn't handled by arduino-nrf5. Enable it, if requested.
  80. NRF_POWER->RESET = POWER_RESET_RESET_Enabled;
  81. NRF_POWER->RAMON |= (POWER_RAMON_ONRAM0_RAM0On << POWER_RAMON_ONRAM0_Pos) |
  82. (POWER_RAMON_ONRAM1_RAM1On << POWER_RAMON_ONRAM1_Pos);
  83. #endif
  84. // Clock is manged by sleep modes. Radio depends on HFCLK.
  85. // Force to start HFCLK
  86. NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
  87. NRF_CLOCK->TASKS_HFCLKSTART = 1;
  88. while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
  89. ;
  90. // Enable low latency sleep mode
  91. NRF_POWER->TASKS_CONSTLAT = 1;
  92. // Enable cache on >= NRF52
  93. #ifndef NRF51
  94. NRF_NVMC->ICACHECNF = NVMC_ICACHECNF_CACHEEN_Msk;
  95. #endif
  96. // Suspend UART
  97. NRF_UART0->TASKS_STOPRX = 1;
  98. NRF_UART0->TASKS_STOPTX = 1;
  99. NRF_UART0->TASKS_SUSPEND = 1;
  100. #ifdef MY_DISABLED_SERIAL
  101. // Disable UART, when not configured
  102. #ifdef NRF51
  103. NRF_UART0->POWER = 0;
  104. #endif
  105. #else
  106. // Configure UART
  107. MY_SERIALDEVICE.begin(MY_BAUD_RATE);
  108. #if defined(MY_GATEWAY_SERIAL)
  109. while (!MY_SERIALDEVICE) {
  110. }
  111. #endif
  112. #endif
  113. return true;
  114. }
  115. static nrf_ecb_t hwRngData;
  116. static int8_t hwRndDataReadPos = -1;
  117. void hwRandomNumberInit(void)
  118. {
  119. // Start HWRNG
  120. #ifdef NRF51
  121. NRF_RNG->POWER = 1;
  122. #endif
  123. // Enable "more random" numbers
  124. NRF_RNG->CONFIG = RNG_CONFIG_DERCEN_Enabled << RNG_CONFIG_DERCEN_Pos;
  125. NRF_RNG->TASKS_START = 1;
  126. NRF_RNG->EVENTS_VALRDY = 0;
  127. uint32_t seed = 0;
  128. for (uint8_t i = 0; i < 4; i++) {
  129. // Wait for an random number
  130. while (NRF_RNG->EVENTS_VALRDY == 0) {
  131. yield();
  132. }
  133. seed = (seed << 8) | (uint32_t)NRF_RNG->VALUE;
  134. NRF_RNG->EVENTS_VALRDY = 0;
  135. }
  136. randomSeed(seed);
  137. // Fill ESB data structure for fast random data generation
  138. uint8_t *ecbstruct = (uint8_t *)&hwRngData;
  139. for (uint8_t i = 0; i<sizeof(hwRngData); i++) {
  140. while (NRF_RNG->EVENTS_VALRDY == 0) {
  141. yield();
  142. }
  143. ecbstruct[i] = NRF_RNG->VALUE;
  144. NRF_RNG->EVENTS_VALRDY = 0;
  145. }
  146. hwRndDataReadPos = 0;
  147. // Stop HWRNG
  148. NRF_RNG->TASKS_STOP = 1;
  149. #ifdef NRF51
  150. NRF_RNG->POWER = 0;
  151. #endif
  152. }
  153. ssize_t hwGetentropy(void *__buffer, size_t __length)
  154. {
  155. if (hwRndDataReadPos<0) {
  156. // Not initialized
  157. hwRandomNumberInit();
  158. }
  159. // cut length if > 256
  160. if (__length > 256) {
  161. __length = 256;
  162. }
  163. uint8_t *dst = (uint8_t *)__buffer;
  164. // Start random number generator
  165. for (size_t i = 0; i < __length; i++) {
  166. dst[i] = hwRngData.ciphertext[hwRndDataReadPos & 0xfu];
  167. MY_CRITICAL_SECTION {
  168. if (hwRndDataReadPos >= ((int8_t)sizeof(hwRngData.ciphertext)-1))
  169. {
  170. // Retry until no error
  171. bool need_data = true;
  172. while (need_data) {
  173. // Stop if another task is running
  174. NRF_ECB->TASKS_STOPECB = 1;
  175. NRF_ECB->EVENTS_ERRORECB = 0;
  176. NRF_ECB->EVENTS_ENDECB = 0;
  177. uint32_t ptrbackup = NRF_ECB->ECBDATAPTR;
  178. NRF_ECB->ECBDATAPTR = (uint32_t)&hwRngData;
  179. NRF_ECB->TASKS_STARTECB = 1;
  180. while (!NRF_ECB->EVENTS_ENDECB);
  181. NRF_ECB->ECBDATAPTR = ptrbackup;
  182. if (NRF_ECB->EVENTS_ERRORECB == 0) {
  183. need_data = false;
  184. }
  185. }
  186. hwRndDataReadPos=0;
  187. for (uint8_t i=0; i<sizeof(hwRngData.ciphertext); i++) {
  188. hwRngData.cleartext[i] ^= hwRngData.ciphertext[i];
  189. }
  190. } else
  191. {
  192. hwRndDataReadPos++;
  193. }
  194. }
  195. }
  196. return __length;
  197. }
  198. void hwWatchdogReset(void)
  199. {
  200. NRF_WDT->RR[0] = WDT_RR_RR_Reload;
  201. }
  202. void hwReboot(void)
  203. {
  204. NVIC_SystemReset();
  205. while (true)
  206. ;
  207. }
  208. static volatile bool nrf5_rtc_event_triggered;
  209. static volatile bool nrf5_pwr_hfclk;
  210. void hwSleepPrepare(uint32_t ms)
  211. {
  212. // Enable low power sleep mode
  213. NRF_POWER->TASKS_LOWPWR = 1;
  214. // Reset RTC trigger flag
  215. nrf5_rtc_event_triggered = false;
  216. if (ms > 0) {
  217. // Configure RTC
  218. #ifdef NRF51
  219. MY_HW_RTC->POWER = 1;
  220. #endif
  221. // Reset RTC
  222. MY_HW_RTC->TASKS_CLEAR = 1;
  223. // Calculate sleep time and prescaler
  224. if (ms<512000) {
  225. // prescaler 0, 30.517 μs resolution -> max 512 s sleep
  226. MY_HW_RTC->PRESCALER = 0;
  227. // Set compare register to 1/30.517 µs to guarantee event triggering
  228. // A minimum of 2 ticks must be guaranteed
  229. // (1000/32768)<<12 == 125
  230. MY_HW_RTC->CC[0] = max(((ms << 12) / 125), 2);
  231. } else {
  232. // 8 Hz -> max 582.542 hours sleep.
  233. MY_HW_RTC->PRESCALER = 4095;
  234. // Set compare register to 1/125ms
  235. // A minimum of 2 ticks must be guaranteed
  236. MY_HW_RTC->CC[0] = max((ms / 125), 2);
  237. }
  238. MY_HW_RTC->INTENSET = RTC_INTENSET_COMPARE0_Msk;
  239. MY_HW_RTC->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
  240. MY_HW_RTC->EVENTS_COMPARE[0] = 0;
  241. MY_HW_RTC->TASKS_START = 1;
  242. NVIC_SetPriority(MY_HW_RTC_IRQN, 15);
  243. NVIC_ClearPendingIRQ(MY_HW_RTC_IRQN);
  244. NVIC_EnableIRQ(MY_HW_RTC_IRQN);
  245. } else {
  246. NRF_RTC1->TASKS_STOP = 1;
  247. }
  248. // Stop HFCLK
  249. nrf5_pwr_hfclk = NRF_CLOCK->EVENTS_HFCLKSTARTED;
  250. NRF_CLOCK->TASKS_HFCLKSTOP = 1;
  251. // Idle serial device
  252. #ifndef MY_DISABLED_SERIAL
  253. NRF_UART0->TASKS_STOPRX = 1;
  254. NRF_UART0->TASKS_STOPTX = 1;
  255. NRF_UART0->TASKS_SUSPEND = 1;
  256. #endif
  257. // Clear NVRAM log, if needed and a time frame of 4 seconds available
  258. if (ms > 40000) {
  259. // preserve some bytes for writing to NVRAM
  260. NVRAM.clean_up(32);
  261. }
  262. }
  263. void hwSleepEnd(uint32_t ms)
  264. {
  265. // Start HFCLK
  266. if (nrf5_pwr_hfclk) {
  267. NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
  268. NRF_CLOCK->TASKS_HFCLKSTART = 1;
  269. while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0)
  270. ;
  271. // Enable low latency sleep mode
  272. NRF_POWER->TASKS_CONSTLAT = 1;
  273. }
  274. if (ms > 0) {
  275. // Stop RTC
  276. #ifdef NRF51
  277. MY_HW_RTC->POWER = 0;
  278. #endif
  279. MY_HW_RTC->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
  280. MY_HW_RTC->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk;
  281. MY_HW_RTC->TASKS_STOP = 1;
  282. NVIC_DisableIRQ(MY_HW_RTC_IRQN);
  283. } else {
  284. // Start Arduino RTC for millis()
  285. NRF_RTC1->TASKS_START = 1;
  286. }
  287. // Start serial device
  288. #ifndef MY_DISABLED_SERIAL
  289. NRF_UART0->TASKS_STARTRX = 1;
  290. NRF_UART0->TASKS_STARTTX = 1;
  291. #endif
  292. }
  293. // Halt CPU until next interrupt event
  294. inline void hwWaitForInterrupt(void)
  295. {
  296. __DSB();
  297. __WFI();
  298. }
  299. // Sleep in System ON mode
  300. inline void hwSleep(void)
  301. {
  302. __WFE();
  303. __SEV();
  304. __WFE();
  305. }
  306. int8_t hwSleep(uint32_t ms)
  307. {
  308. hwSleepPrepare(ms);
  309. while (nrf5_rtc_event_triggered == false) {
  310. hwSleep();
  311. }
  312. hwSleepEnd(ms);
  313. return MY_WAKE_UP_BY_TIMER;
  314. }
  315. int8_t hwSleep(uint8_t interrupt, uint8_t mode, uint32_t ms)
  316. {
  317. return hwSleep(interrupt, mode, INVALID_INTERRUPT_NUM, 0u, ms);
  318. }
  319. int8_t hwSleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2,
  320. uint8_t mode2, uint32_t ms)
  321. {
  322. // Disable interrupts until going to sleep, otherwise interrupts occurring
  323. // between attachInterrupt()
  324. // and sleep might cause the MCU to not wakeup from sleep as interrupt has
  325. // already be handled!
  326. MY_CRITICAL_SECTION {
  327. // attach interrupts
  328. _wakeUp1Interrupt = interrupt1;
  329. _wakeUp2Interrupt = interrupt2;
  330. if (interrupt1 != INVALID_INTERRUPT_NUM)
  331. {
  332. attachInterrupt(interrupt1, wakeUp1, mode1);
  333. }
  334. if (interrupt2 != INVALID_INTERRUPT_NUM)
  335. {
  336. attachInterrupt(interrupt2, wakeUp2, mode2);
  337. }
  338. // Reset attribute
  339. _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
  340. }
  341. // Prepare Timer and Hardware
  342. hwSleepPrepare(ms);
  343. // Sleep until timeout or interrupt
  344. while ((nrf5_rtc_event_triggered == false) and
  345. (_wokeUpByInterrupt == INVALID_INTERRUPT_NUM)) {
  346. hwSleep();
  347. }
  348. // Assure any interrupts attached, will get detached when they did not occur.
  349. if (interrupt1 != INVALID_INTERRUPT_NUM) {
  350. detachInterrupt(interrupt1);
  351. }
  352. if (interrupt2 != INVALID_INTERRUPT_NUM) {
  353. detachInterrupt(interrupt2);
  354. }
  355. // Wake up Hardware
  356. hwSleepEnd(ms);
  357. // Return what woke the mcu.
  358. int8_t ret =
  359. MY_WAKE_UP_BY_TIMER; // default: no interrupt triggered, timer wake up
  360. if (_wokeUpByInterrupt != INVALID_INTERRUPT_NUM) {
  361. ret = static_cast<int8_t>(_wokeUpByInterrupt);
  362. }
  363. // Clear woke-up-by-interrupt flag, so next sleeps won't return immediately.
  364. _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
  365. return ret;
  366. }
  367. extern "C" {
  368. // RTC interrupt handler
  369. void MY_HW_RTC_IRQ_HANDLER(void)
  370. {
  371. if (MY_HW_RTC->EVENTS_COMPARE[0] > 0) {
  372. nrf5_rtc_event_triggered = true;
  373. NRF_RESET_EVENT(MY_HW_RTC->EVENTS_COMPARE[0]);
  374. }
  375. }
  376. }
  377. bool hwUniqueID(unique_id_t *uniqueID)
  378. {
  379. uint32_t *buffer = (uint32_t *)uniqueID;
  380. buffer[0] = NRF_FICR->DEVICEID[0];
  381. buffer[1] = NRF_FICR->DEVICEID[1];
  382. buffer[2] = NRF_FICR->DEVICEADDR[0];
  383. buffer[3] = NRF_FICR->DEVICEADDR[1];
  384. return true;
  385. }
  386. uint16_t hwCPUVoltage(void)
  387. {
  388. // VDD is prescaled 1/3 and compared with the internal 1.2V reference
  389. #if defined(NRF_ADC)
  390. // NRF51:
  391. // Sampling is done with lowest resolution to minimize the time
  392. // 20uS@260uA
  393. // Concurrent resource: disable
  394. uint32_t lpcomp_enabled = NRF_LPCOMP->ENABLE;
  395. NRF_LPCOMP->ENABLE = 0;
  396. // Enable and configure ADC
  397. NRF_ADC->ENABLE = 1;
  398. NRF_ADC->CONFIG = (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos) |
  399. (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
  400. (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
  401. (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
  402. (ADC_CONFIG_RES_8bit << ADC_CONFIG_RES_Pos);
  403. NRF_ADC->EVENTS_END = 0;
  404. NRF_ADC->TASKS_START = 1;
  405. while(!NRF_ADC->EVENTS_END);
  406. NRF_ADC->EVENTS_END = 0;
  407. int32_t sample = (int32_t)NRF_ADC->RESULT;
  408. NRF_ADC->TASKS_STOP = 1;
  409. NRF_ADC->ENABLE = 0;
  410. // Restore LPCOMP state
  411. NRF_LPCOMP->ENABLE = lpcomp_enabled;
  412. return (sample*3600)/255;
  413. #elif defined(NRF_SAADC)
  414. // NRF52:
  415. // Sampling time 3uS@700uA
  416. int32_t sample;
  417. NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;
  418. NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_8bit << SAADC_RESOLUTION_VAL_Pos;
  419. NRF_SAADC->CH[0].PSELP = SAADC_CH_PSELP_PSELP_VDD << SAADC_CH_PSELP_PSELP_Pos;
  420. NRF_SAADC->CH[0].CONFIG = (SAADC_CH_CONFIG_BURST_Disabled << SAADC_CH_CONFIG_BURST_Pos) |
  421. (SAADC_CH_CONFIG_MODE_SE << SAADC_CH_CONFIG_MODE_Pos) |
  422. (SAADC_CH_CONFIG_TACQ_3us << SAADC_CH_CONFIG_TACQ_Pos) |
  423. (SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
  424. (SAADC_CH_CONFIG_GAIN_Gain1_6 << SAADC_CH_CONFIG_GAIN_Pos) |
  425. (SAADC_CH_CONFIG_RESN_Bypass << SAADC_CH_CONFIG_RESN_Pos) |
  426. (SAADC_CH_CONFIG_RESP_Bypass << SAADC_CH_CONFIG_RESP_Pos);
  427. NRF_SAADC->OVERSAMPLE = SAADC_OVERSAMPLE_OVERSAMPLE_Bypass << SAADC_OVERSAMPLE_OVERSAMPLE_Pos;
  428. NRF_SAADC->SAMPLERATE = SAADC_SAMPLERATE_MODE_Task << SAADC_SAMPLERATE_MODE_Pos;
  429. NRF_SAADC->RESULT.MAXCNT = 1;
  430. NRF_SAADC->RESULT.PTR = (uint32_t)&sample;
  431. NRF_SAADC->EVENTS_STARTED = 0;
  432. NRF_SAADC->TASKS_START = 1;
  433. while (!NRF_SAADC->EVENTS_STARTED);
  434. NRF_SAADC->EVENTS_STARTED = 0;
  435. NRF_SAADC->EVENTS_END = 0;
  436. NRF_SAADC->TASKS_SAMPLE = 1;
  437. while (!NRF_SAADC->EVENTS_END);
  438. NRF_SAADC->EVENTS_END = 0;
  439. NRF_SAADC->EVENTS_STOPPED = 0;
  440. NRF_SAADC->TASKS_STOP = 1;
  441. while (!NRF_SAADC->EVENTS_STOPPED);
  442. NRF_SAADC->EVENTS_STOPPED = 1;
  443. NRF_SAADC->ENABLE = (SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos);
  444. return (sample*3600)/255;
  445. #else
  446. // unknown MCU
  447. return 0;
  448. #endif
  449. }
  450. uint16_t hwCPUFrequency(void)
  451. {
  452. #if defined(VARIANT_MCK)
  453. return (VARIANT_MCK) / 100000UL;
  454. #elif defined(F_CPU)
  455. return (F_CPU) / 100000UL;
  456. #else
  457. return 160;
  458. #endif
  459. }
  460. int8_t hwCPUTemperature(void)
  461. {
  462. return -127; // not implemented yet
  463. }
  464. uint16_t hwFreeMem(void)
  465. {
  466. // TODO: Not supported!
  467. return FUNCTION_NOT_SUPPORTED;
  468. }
  469. void hwDebugPrint(const char *fmt, ...)
  470. {
  471. #ifndef MY_DISABLED_SERIAL
  472. char fmtBuffer[MY_SERIAL_OUTPUT_SIZE];
  473. #ifdef MY_GATEWAY_SERIAL
  474. // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
  475. snprintf(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "),
  476. C_INTERNAL, I_LOG_MESSAGE, hwMillis());
  477. MY_DEBUGDEVICE.print(fmtBuffer);
  478. #else
  479. // prepend timestamp
  480. MY_DEBUGDEVICE.print(hwMillis());
  481. MY_DEBUGDEVICE.print(F(" "));
  482. #endif
  483. va_list args;
  484. va_start(args, fmt);
  485. vsnprintf(fmtBuffer, sizeof(fmtBuffer), fmt, args);
  486. #ifdef MY_GATEWAY_SERIAL
  487. // Truncate message if this is gateway node
  488. fmtBuffer[sizeof(fmtBuffer) - 2] = '\n';
  489. fmtBuffer[sizeof(fmtBuffer) - 1] = '\0';
  490. #endif
  491. va_end(args);
  492. MY_DEBUGDEVICE.print(fmtBuffer);
  493. MY_DEBUGDEVICE.flush();
  494. #else
  495. (void)fmt;
  496. #endif
  497. }