Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
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.
 
 
 
 
 
 

449 lines
9.1 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. #include <sysinit.h>
  38. enum {
  39. CMD_TERMINATE = 1,
  40. CMD_WAITFOR = 2,
  41. CMD_RUNFOR = 3,
  42. CMD_PING = 4,
  43. CMD_SETUNSET = 5,
  44. CMD_ADV = 6,
  45. CMD_CLEAR = 7,
  46. };
  47. /*
  48. * rxpktavail is initialized to true meaning that the data in rxpkt
  49. * can be over written. When a packet is received, the data is copied
  50. * to rxpkt, and then rxpktavail is set to false. Once the packet has
  51. * been processed, it is set back to true.
  52. */
  53. static uint8_t rxpkt[128];
  54. static struct pktbuf rxpktbuf;
  55. static volatile bool rxpktavail;
  56. #include <shared_key.h>
  57. static struct comms_state cs;
  58. void
  59. txdone(void)
  60. {
  61. /* restart Rx when Tx done */
  62. Radio.Rx(0);
  63. }
  64. void
  65. txtimeout(void)
  66. {
  67. /* restart Rx when Tx done */
  68. Radio.Rx(0);
  69. }
  70. void
  71. rxdone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
  72. {
  73. if (rxpktavail && size <= sizeof rxpkt) {
  74. memcpy(rxpkt, payload, size);
  75. rxpktbuf = (struct pktbuf){
  76. .pkt = rxpkt,
  77. .pktlen = size,
  78. };
  79. rxpktavail = false;
  80. }
  81. Radio.Rx(0);
  82. }
  83. void
  84. rxtimeout(void)
  85. {
  86. Radio.Rx(0);
  87. }
  88. void
  89. rxerr(void)
  90. {
  91. Radio.Rx(0);
  92. }
  93. RadioEvents_t revents = {
  94. .TxDone = txdone,
  95. .TxTimeout = txtimeout,
  96. .RxDone = rxdone,
  97. .RxTimeout = rxtimeout,
  98. .RxError = rxerr,
  99. };
  100. /*
  101. * Seed the randomness from the radio. This is not a great
  102. * seed, and is hard to gauge how much randomness is really
  103. * there. Assuming about 1 bit per 8 bits looks pretty safe,
  104. * so add 256 * 8 / 32 words.
  105. */
  106. static void
  107. radio_seed_rng(void)
  108. {
  109. #if 1
  110. uint32_t v;
  111. int i;
  112. for (i = 0; i < 256 * 8 / 32; i++) {
  113. v = Radio.Random();
  114. strobe_seed_prng((uint8_t *)&v, sizeof v);
  115. }
  116. #endif
  117. }
  118. SYSINIT_VF(radio_seed_rng, SI_SUB_STANDARD, SI_ORDER_ANY, radio_seed_rng);
  119. static void
  120. analog_seed_rng(void)
  121. {
  122. #if 1
  123. uint16_t v;
  124. int i;
  125. for (i = 0; i < 256 / 2; i++) {
  126. /*
  127. * Capture some ADC data. If pin is floating, 0xfff
  128. * happens frequently, if pin is grounded, 0 happens
  129. * frequently, filter these values out.
  130. */
  131. do {
  132. v = AdcReadChannel(&Adc, ADC_CHANNEL_21);
  133. } while (v == 0 || v == 0xfff);
  134. strobe_seed_prng((uint8_t *)&v, sizeof v);
  135. }
  136. #endif
  137. }
  138. SYSINIT_VF(analog_seed_rng, SI_SUB_STANDARD, SI_ORDER_ANY, analog_seed_rng);
  139. static inline uint32_t
  140. letoh_32(uint8_t *v)
  141. {
  142. return v[0] | (v[1] << 8) | (v[2] << 16) | ((unsigned)v[3] << 24);
  143. }
  144. struct chaninfo {
  145. GPIO_TypeDef *bank;
  146. uint16_t pinnum;
  147. bool init;
  148. bool invert;
  149. } chans[] = {
  150. [0] = { .bank = GPIOB, .pinnum = GPIO_PIN_5, .invert = true, },
  151. [1] = { .bank = GPIOB, .pinnum = GPIO_PIN_6, .invert = true, },
  152. [2] = { .bank = GPIOB, .pinnum = GPIO_PIN_7, .invert = true, },
  153. [3] = { .bank = GPIOB, .pinnum = GPIO_PIN_9, .invert = true, },
  154. /* Turn on LED at start */
  155. [4] = { .bank = GPIOB, .pinnum = GPIO_PIN_8, .init = true, },
  156. };
  157. #define nitems(x) (sizeof(x) / sizeof *(x))
  158. static void
  159. set_chan(uint32_t chan, bool val)
  160. {
  161. struct chaninfo ci;
  162. if (chan < nitems(chans)) {
  163. ci = chans[chan];
  164. HAL_GPIO_WritePin(ci.bank, ci.pinnum, val ^ ci.invert ?
  165. GPIO_PIN_SET : GPIO_PIN_RESET);
  166. }
  167. }
  168. static void
  169. setup_gpio()
  170. {
  171. GPIO_InitTypeDef GPIO_InitStruct;
  172. int i;
  173. for (i = 0; i < nitems(chans); i++) {
  174. GPIO_InitStruct = (GPIO_InitTypeDef){
  175. .Pin = chans[i].pinnum,
  176. .Mode = GPIO_MODE_OUTPUT_PP,
  177. .Pull = GPIO_NOPULL,
  178. .Speed = GPIO_SPEED_FREQ_LOW,
  179. };
  180. HAL_GPIO_Init(chans[i].bank, &GPIO_InitStruct);
  181. set_chan(i, chans[i].init);
  182. }
  183. }
  184. SYSINIT_VF(setup_gpio, SI_SUB_HAL, SI_ORDER_LAST, setup_gpio);
  185. static struct sched {
  186. uint32_t cmd;
  187. uint32_t end_wait_tick; /* end if running, otherwise how long to wait */
  188. uint32_t chan;
  189. } schedule[20];
  190. static int schedpos; /* position in schedule, % nitems(schedule)*/
  191. static int schedcnt; /* total items waiting */
  192. #define SCHED_ITEM(x) (schedule[(schedpos + x) % nitems(schedule)])
  193. #define SCHED_HEAD SCHED_ITEM(0)
  194. #define SCHED_TAIL SCHED_ITEM(schedcnt)
  195. static void
  196. start_sched(struct sched *sched)
  197. {
  198. sched->end_wait_tick += uwTick;
  199. if (sched->cmd == CMD_RUNFOR)
  200. set_chan(sched->chan, 1);
  201. }
  202. static bool
  203. canproc_sched()
  204. {
  205. /* nothing to do? */
  206. if (schedcnt == 0)
  207. return false;
  208. /* not yet expired */
  209. if (uwTick < SCHED_HEAD.end_wait_tick)
  210. return false;
  211. return true;
  212. }
  213. static void
  214. process_sched()
  215. {
  216. if (!canproc_sched())
  217. return;
  218. if (SCHED_HEAD.cmd == CMD_RUNFOR)
  219. set_chan(SCHED_HEAD.chan, 0);
  220. /* we are done, advance */
  221. schedpos++;
  222. schedcnt--;
  223. if (schedcnt)
  224. start_sched(&SCHED_HEAD);
  225. }
  226. static void
  227. enqueue_sched(uint32_t cmd, uint32_t ticks, uint32_t chan)
  228. {
  229. if (schedcnt >= nitems(schedule))
  230. return;
  231. SCHED_TAIL = (struct sched){
  232. .cmd = cmd,
  233. .end_wait_tick = ticks,
  234. .chan = chan,
  235. };
  236. if (schedcnt == 0)
  237. start_sched(&SCHED_HEAD);
  238. schedcnt++;
  239. }
  240. static void
  241. procmsg(struct pktbuf inbuf, struct pktbuf *outbuf)
  242. {
  243. uint32_t args[5];
  244. int i, apos, cnt;
  245. i = 1;
  246. apos = 0;
  247. while (i < inbuf.pktlen) {
  248. if (i + 4 <= inbuf.pktlen) {
  249. args[apos++] = letoh_32(&inbuf.pkt[i]);
  250. i += 4;
  251. }
  252. }
  253. outbuf->pkt[0] = inbuf.pkt[0];
  254. switch (inbuf.pkt[0]) {
  255. case CMD_WAITFOR:
  256. if (apos == 1)
  257. enqueue_sched(CMD_WAITFOR, args[0], -1);
  258. break;
  259. case CMD_RUNFOR:
  260. if (apos == 2)
  261. enqueue_sched(CMD_RUNFOR, args[0], args[1]);
  262. break;
  263. case CMD_PING:
  264. break;
  265. case CMD_SETUNSET:
  266. if (apos == 2)
  267. set_chan(args[0], args[1]);
  268. break;
  269. case CMD_ADV:
  270. cnt = 1;
  271. if (apos == 1)
  272. cnt = args[0];
  273. for (i = 0; i < cnt && i < schedcnt; i++)
  274. SCHED_ITEM(i).end_wait_tick = 0;
  275. break;
  276. case CMD_CLEAR:
  277. if (schedcnt)
  278. schedcnt = 1;
  279. break;
  280. default:
  281. outbuf->pkt[0] = 0;
  282. break;
  283. }
  284. outbuf->pktlen = 1;
  285. }
  286. void
  287. radio_init(void)
  288. {
  289. Radio.Init(&revents);
  290. }
  291. SYSINIT_VF(radio_init, SI_SUB_HAL, SI_ORDER_MIDDLE, radio_init);
  292. int
  293. main()
  294. {
  295. /* turn on LED */
  296. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  297. Radio.SetModem(MODEM_LORA);
  298. Radio.SetChannel(914350 * 1000);
  299. /* RX/TX parameters */
  300. const uint8_t modem = MODEM_LORA;
  301. const uint8_t bandwidth = 0 /* 128 kHz */;
  302. const uint8_t datarate = 7 /* 128 chips */;
  303. const uint8_t coderate = 1 /* 4/5 */;
  304. const uint8_t preambleLen = 8 /* symbols */;
  305. const uint8_t fixLen = 0 /* variable */;
  306. const uint8_t crcOn = 1 /* on */;
  307. const uint8_t freqHopOn = 0 /* off */;
  308. const bool iqInverted = false /* not inverted */;
  309. Radio.SetRxConfig(modem, bandwidth, datarate, coderate, 0/*afc*/,
  310. preambleLen, 5/*symTimeout*/, fixLen, 0/*payloadlen*/, crcOn,
  311. freqHopOn, 0/*hopPeriod*/, iqInverted, true/*rxcont*/);
  312. Radio.SetTxConfig(modem, 11/*power*/, 0/*fdev*/, bandwidth, datarate,
  313. coderate, preambleLen, fixLen, crcOn, freqHopOn, 0/*hopPeriod*/,
  314. iqInverted, 1000/*timeout*/);
  315. /* blink led */
  316. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
  317. DelayMs(300);
  318. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  319. Radio.Rx(0);
  320. comms_init(&cs, procmsg, &shared_key_buf, NULL, NULL);
  321. uint8_t txbuf[128] = "i'mhere";
  322. struct pktbuf txpktbuf;
  323. txpktbuf = (struct pktbuf){
  324. .pkt = txbuf,
  325. .pktlen = 8,
  326. };
  327. Radio.Send(txpktbuf.pkt, txpktbuf.pktlen);
  328. rxpktavail = true;
  329. //Radio.Rx(0);
  330. loop:
  331. while (canproc_sched())
  332. process_sched();
  333. BoardLowPowerHandler();
  334. if (Radio.IrqProcess != NULL)
  335. Radio.IrqProcess();
  336. if (!rxpktavail) {
  337. txpktbuf = (struct pktbuf){
  338. .pkt = txbuf,
  339. .pktlen = sizeof txbuf,
  340. };
  341. /* process available packet */
  342. comms_process(&cs, rxpktbuf, &txpktbuf);
  343. rxpktavail = true;
  344. if (txpktbuf.pktlen) {
  345. int i;
  346. for (i = 0; i < 1; i++) {
  347. DelayMs(20);
  348. Radio.Send(txpktbuf.pkt, txpktbuf.pktlen);
  349. }
  350. #if 0
  351. /* blink led */
  352. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
  353. DelayMs(300);
  354. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  355. DelayMs(300);
  356. #endif
  357. }
  358. #if 0
  359. /* blink led */
  360. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
  361. DelayMs(300);
  362. HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
  363. #endif
  364. }
  365. goto loop;
  366. }