Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

449 Zeilen
8.9 KiB

  1. /*-
  2. * Copyright 2021 John-Mark Gurney.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  14. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  17. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  19. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  20. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  23. * SUCH DAMAGE.
  24. *
  25. */
  26. #include "stm32l1xx.h"
  27. #include "stm32l1xx_hal.h"
  28. /* LoRaMac headers */
  29. #include <board.h>
  30. #include <adc.h>
  31. #include <radio.h>
  32. #include <delay.h>
  33. /* lora-irr headers */
  34. #include <misc.h>
  35. #include <strobe_rng_init.h>
  36. #include <comms.h>
  37. enum {
  38. CMD_TERMINATE = 1,
  39. CMD_WAITFOR = 2,
  40. CMD_RUNFOR = 3,
  41. CMD_PING = 4,
  42. CMD_SETUNSET = 5,
  43. CMD_ADV = 6,
  44. CMD_CLEAR = 7,
  45. };
  46. /*
  47. * rxpktavail is initialized to true meaning that the data in rxpkt
  48. * can be over written. When a packet is received, the data is copied
  49. * to rxpkt, and then rxpktavail is set to false. Once the packet has
  50. * been processed, it is set back to true.
  51. */
  52. static uint8_t rxpkt[128];
  53. static struct pktbuf rxpktbuf;
  54. static volatile bool rxpktavail;
  55. #include <shared_key.h>
  56. static struct comms_state cs;
  57. void
  58. txdone(void)
  59. {
  60. /* restart Rx when Tx done */
  61. Radio.Rx(0);
  62. }
  63. void
  64. txtimeout(void)
  65. {
  66. /* restart Rx when Tx done */
  67. Radio.Rx(0);
  68. }
  69. void
  70. rxdone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
  71. {
  72. if (rxpktavail && size <= sizeof rxpkt) {
  73. memcpy(rxpkt, payload, size);
  74. rxpktbuf = (struct pktbuf){
  75. .pkt = rxpkt,
  76. .pktlen = size,
  77. };
  78. rxpktavail = false;
  79. }
  80. Radio.Rx(0);
  81. }
  82. void
  83. rxtimeout(void)
  84. {
  85. Radio.Rx(0);
  86. }
  87. void
  88. rxerr(void)
  89. {
  90. Radio.Rx(0);
  91. }
  92. RadioEvents_t revents = {
  93. .TxDone = txdone,
  94. .TxTimeout = txtimeout,
  95. .RxDone = rxdone,
  96. .RxTimeout = rxtimeout,
  97. .RxError = rxerr,
  98. };
  99. /*
  100. * Seed the randomness from the radio. This is not a great
  101. * seed, and is hard to gauge how much randomness is really
  102. * there. Assuming about 1 bit per 8 bits looks pretty safe,
  103. * so add 256 * 8 / 32 words.
  104. */
  105. static void
  106. radio_seed_rng(void)
  107. {
  108. #if 1
  109. uint32_t v;
  110. int i;
  111. for (i = 0; i < 256 * 8 / 32; i++) {
  112. v = Radio.Random();
  113. strobe_seed_prng((uint8_t *)&v, sizeof v);
  114. }
  115. #endif
  116. }
  117. static void
  118. analog_seed_rng(void)
  119. {
  120. #if 1
  121. uint16_t v;
  122. int i;
  123. for (i = 0; i < 256 / 2; i++) {
  124. /*
  125. * Capture some ADC data. If pin is floating, 0xfff
  126. * happens frequently, if pin is grounded, 0 happens
  127. * frequently, filter these values out.
  128. */
  129. do {
  130. v = AdcReadChannel(&Adc, ADC_CHANNEL_21);
  131. } while (v == 0 || v == 0xfff);
  132. strobe_seed_prng((uint8_t *)&v, sizeof v);
  133. }
  134. #endif
  135. }
  136. static inline uint32_t
  137. letoh_32(uint8_t *v)
  138. {
  139. return v[0] | (v[1] << 8) | (v[2] << 16) | (v[3] << 24);
  140. }
  141. struct chaninfo {
  142. GPIO_TypeDef *bank;
  143. uint16_t pinnum;
  144. bool init;
  145. bool invert;
  146. } chans[] = {
  147. [0] = { .bank = GPIOB, .pinnum = GPIO_PIN_5, .invert = true, },
  148. [1] = { .bank = GPIOB, .pinnum = GPIO_PIN_6, .invert = true, },
  149. [2] = { .bank = GPIOB, .pinnum = GPIO_PIN_7, .invert = true, },
  150. [3] = { .bank = GPIOB, .pinnum = GPIO_PIN_9, .invert = true, },
  151. /* Turn on LED at start */
  152. [4] = { .bank = GPIOB, .pinnum = GPIO_PIN_8, .init = true, },
  153. };
  154. #define nitems(x) (sizeof(x) / sizeof *(x))
  155. static void
  156. set_chan(uint32_t chan, bool val)
  157. {
  158. struct chaninfo ci;
  159. if (chan < nitems(chans)) {
  160. ci = chans[chan];
  161. HAL_GPIO_WritePin(ci.bank, ci.pinnum, val ^ ci.invert ?
  162. GPIO_PIN_SET : GPIO_PIN_RESET);
  163. }
  164. }
  165. static void
  166. setup_gpio()
  167. {
  168. GPIO_InitTypeDef GPIO_InitStruct;
  169. int i;
  170. for (i = 0; i < nitems(chans); i++) {
  171. GPIO_InitStruct = (GPIO_InitTypeDef){
  172. .Pin = chans[i].pinnum,
  173. .Mode = GPIO_MODE_OUTPUT_PP,
  174. .Pull = GPIO_NOPULL,
  175. .Speed = GPIO_SPEED_FREQ_LOW,
  176. };
  177. HAL_GPIO_Init(chans[i].bank, &GPIO_InitStruct);
  178. set_chan(i, chans[i].init);
  179. }
  180. }
  181. static struct sched {
  182. uint32_t cmd;
  183. uint32_t end_wait_tick; /* end if running, otherwise how long to wait */
  184. uint32_t chan;
  185. } schedule[20];
  186. static int schedpos; /* position in schedule, % nitems(schedule)*/
  187. static int schedcnt; /* total items waiting */
  188. #define SCHED_ITEM(x) (schedule[(schedpos + x) % nitems(schedule)])
  189. #define SCHED_HEAD SCHED_ITEM(0)
  190. #define SCHED_TAIL SCHED_ITEM(schedcnt)
  191. static void
  192. start_sched(struct sched *sched)
  193. {
  194. sched->end_wait_tick += uwTick;
  195. if (sched->cmd == CMD_RUNFOR)
  196. set_chan(sched->chan, 1);
  197. }
  198. static bool
  199. canproc_sched()
  200. {
  201. /* nothing to do? */
  202. if (schedcnt == 0)
  203. return false;
  204. /* not yet expired */
  205. if (uwTick < SCHED_HEAD.end_wait_tick)
  206. return false;
  207. return true;
  208. }
  209. static void
  210. process_sched()
  211. {
  212. if (!canproc_sched())
  213. return;
  214. if (SCHED_HEAD.cmd == CMD_RUNFOR)
  215. set_chan(SCHED_HEAD.chan, 0);
  216. /* we are done, advance */
  217. schedpos++;
  218. schedcnt--;
  219. if (schedcnt)
  220. start_sched(&SCHED_HEAD);
  221. }
  222. static void
  223. enqueue_sched(uint32_t cmd, uint32_t ticks, uint32_t chan)
  224. {
  225. if (schedcnt >= nitems(schedule))
  226. return;
  227. SCHED_TAIL = (struct sched){
  228. .cmd = cmd,
  229. .end_wait_tick = ticks,
  230. .chan = chan,
  231. };
  232. if (schedcnt == 0)
  233. start_sched(&SCHED_HEAD);
  234. schedcnt++;
  235. }
  236. static void
  237. procmsg(struct pktbuf inbuf, struct pktbuf *outbuf)
  238. {
  239. uint32_t args[5];
  240. int i, apos, cnt;
  241. i = 1;
  242. apos = 0;
  243. while (i < inbuf.pktlen) {
  244. if (i + 4 <= inbuf.pktlen) {
  245. args[apos++] = letoh_32(&inbuf.pkt[i]);
  246. i += 4;
  247. }
  248. }
  249. outbuf->pkt[0] = inbuf.pkt[0];
  250. switch (inbuf.pkt[0]) {
  251. case CMD_WAITFOR:
  252. if (apos == 1)
  253. enqueue_sched(CMD_WAITFOR, args[0], -1);
  254. break;
  255. case CMD_RUNFOR:
  256. if (apos == 2)
  257. enqueue_sched(CMD_RUNFOR, args[0], args[1]);
  258. break;
  259. case CMD_PING:
  260. break;
  261. case CMD_SETUNSET:
  262. if (apos == 2)
  263. set_chan(args[0], args[1]);
  264. break;
  265. case CMD_ADV:
  266. cnt = 1;
  267. if (apos == 1)
  268. cnt = args[0];
  269. for (i = 0; i < cnt && i < schedcnt; i++)
  270. SCHED_ITEM(i).end_wait_tick = 0;
  271. break;
  272. case CMD_CLEAR:
  273. if (schedcnt)
  274. schedcnt = 1;
  275. break;
  276. default:
  277. outbuf->pkt[0] = 0;
  278. break;
  279. }
  280. outbuf->pktlen = 1;
  281. }
  282. int
  283. main()
  284. {
  285. strobe_rng_init();
  286. BoardInitMcu();
  287. Radio.Init(&revents);
  288. analog_seed_rng();
  289. radio_seed_rng();
  290. strobe_rng_save();
  291. setup_gpio();
  292. /* turn on LED */
  293. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  294. Radio.SetModem(MODEM_LORA);
  295. Radio.SetChannel(914350 * 1000);
  296. /* RX/TX parameters */
  297. const uint8_t modem = MODEM_LORA;
  298. const uint8_t bandwidth = 0 /* 128 kHz */;
  299. const uint8_t datarate = 7 /* 128 chips */;
  300. const uint8_t coderate = 1 /* 4/5 */;
  301. const uint8_t preambleLen = 8 /* symbols */;
  302. const uint8_t fixLen = 0 /* variable */;
  303. const uint8_t crcOn = 1 /* on */;
  304. const uint8_t freqHopOn = 0 /* off */;
  305. const bool iqInverted = false /* not inverted */;
  306. Radio.SetRxConfig(modem, bandwidth, datarate, coderate, 0/*afc*/,
  307. preambleLen, 5/*symTimeout*/, fixLen, 0/*payloadlen*/, crcOn,
  308. freqHopOn, 0/*hopPeriod*/, iqInverted, true/*rxcont*/);
  309. Radio.SetTxConfig(modem, 11/*power*/, 0/*fdev*/, bandwidth, datarate,
  310. coderate, preambleLen, fixLen, crcOn, freqHopOn, 0/*hopPeriod*/,
  311. iqInverted, 1000/*timeout*/);
  312. /* blink led */
  313. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
  314. DelayMs(300);
  315. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  316. Radio.Rx(0);
  317. comms_init(&cs, procmsg, &shared_key_buf);
  318. uint8_t txbuf[128] = "i'mhere";
  319. struct pktbuf txpktbuf;
  320. txpktbuf = (struct pktbuf){
  321. .pkt = txbuf,
  322. .pktlen = 8,
  323. };
  324. Radio.Send(txpktbuf.pkt, txpktbuf.pktlen);
  325. rxpktavail = true;
  326. //Radio.Rx(0);
  327. loop:
  328. while (canproc_sched())
  329. process_sched();
  330. BoardLowPowerHandler();
  331. if (Radio.IrqProcess != NULL)
  332. Radio.IrqProcess();
  333. if (!rxpktavail) {
  334. txpktbuf = (struct pktbuf){
  335. .pkt = txbuf,
  336. .pktlen = sizeof txbuf,
  337. };
  338. /* process available packet */
  339. comms_process(&cs, rxpktbuf, &txpktbuf);
  340. rxpktavail = true;
  341. if (txpktbuf.pktlen) {
  342. int i;
  343. for (i = 0; i < 1; i++) {
  344. DelayMs(20);
  345. Radio.Send(txpktbuf.pkt, txpktbuf.pktlen);
  346. }
  347. #if 0
  348. /* blink led */
  349. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
  350. DelayMs(300);
  351. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  352. DelayMs(300);
  353. #endif
  354. }
  355. #if 0
  356. /* blink led */
  357. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
  358. DelayMs(300);
  359. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  360. #endif
  361. }
  362. goto loop;
  363. }