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.
 
 
 
 
 
 

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