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.
 
 
 
 
 
 

429 lines
8.7 KiB

  1. /*-
  2. * Copyright 2022 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 <si_usb.h>
  27. #include <rs485frame.h>
  28. #include <misc.h>
  29. #include <stdbool.h>
  30. #include <stdint.h>
  31. #include <string.h>
  32. #include <sysinit.h>
  33. SYSINIT(hal_init, SI_SUB_HAL, SI_ORDER_FIRST, (void (*)(const void *))HAL_Init, NULL);
  34. static int dorx = 0;
  35. static uint8_t rxbuf[128];
  36. static int gotrxdone = 0;
  37. static int gottxdone = 0;
  38. static int goterr = 0;
  39. static int rxbufsize = 0;
  40. static int rxbuftrunc = 0;
  41. static void
  42. c13led(const void *none)
  43. {
  44. GPIO_InitTypeDef GPIO_InitStruct;
  45. __HAL_RCC_GPIOB_CLK_ENABLE();
  46. __HAL_RCC_GPIOC_CLK_ENABLE();
  47. GPIO_InitStruct = (GPIO_InitTypeDef){
  48. .Pin = GPIO_PIN_13,
  49. .Mode = GPIO_MODE_OUTPUT_PP,
  50. .Pull = GPIO_NOPULL,
  51. .Speed = GPIO_SPEED_FREQ_LOW,
  52. };
  53. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  54. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
  55. }
  56. SYSINIT(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led, NULL);
  57. #if 0
  58. #undef usb_printf
  59. #define usb_printf debug_printf
  60. #endif
  61. /*
  62. * Referenced from:
  63. * Projects/STM32F103RB-Nucleo/Applications/USB_Device/HID_Standalone/Src/main.c
  64. */
  65. static void
  66. oscconfig(const void *none)
  67. {
  68. RCC_ClkInitTypeDef clkinitstruct;
  69. RCC_OscInitTypeDef oscinitstruct;
  70. RCC_PeriphCLKInitTypeDef rccperiphclkinit;
  71. __HAL_RCC_PWR_CLK_ENABLE();
  72. oscinitstruct = (RCC_OscInitTypeDef){
  73. .OscillatorType = RCC_OSCILLATORTYPE_HSE,
  74. .HSEState = RCC_HSE_ON,
  75. .HSEPredivValue = RCC_HSE_PREDIV_DIV1,
  76. .PLL.PLLMUL = RCC_PLL_MUL9,
  77. .PLL.PLLState = RCC_PLL_ON,
  78. .PLL.PLLSource = RCC_PLLSOURCE_HSE,
  79. };
  80. HAL_RCC_OscConfig(&oscinitstruct);
  81. /* USB clock selection */
  82. rccperiphclkinit = (RCC_PeriphCLKInitTypeDef){
  83. .PeriphClockSelection = RCC_PERIPHCLK_USB,
  84. .UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5,
  85. };
  86. HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit);
  87. /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
  88. clocks dividers */
  89. clkinitstruct = (RCC_ClkInitTypeDef){
  90. .ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2),
  91. .SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK,
  92. .AHBCLKDivider = RCC_SYSCLK_DIV1,
  93. .APB1CLKDivider = RCC_HCLK_DIV2,
  94. .APB2CLKDivider = RCC_HCLK_DIV1,
  95. };
  96. HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2);
  97. }
  98. SYSINIT(oscconfig, SI_SUB_HAL, SI_ORDER_THIRD, oscconfig, NULL);
  99. char *
  100. findeol(char *pos, size_t len)
  101. {
  102. while (len) {
  103. if (*pos == '\r' || *pos == '\n')
  104. return pos;
  105. pos++;
  106. len--;
  107. }
  108. return NULL;
  109. }
  110. void
  111. hexdump(const uint8_t *ptr, size_t len)
  112. {
  113. int i;
  114. for (i = 0; i < len; i++)
  115. usb_printf("%02x", ptr[i]);
  116. }
  117. void
  118. txdone(void)
  119. {
  120. gottxdone = 1;
  121. }
  122. void
  123. errfunc(void)
  124. {
  125. goterr = 1;
  126. }
  127. void
  128. rxdone(const uint8_t *payload, size_t size)
  129. {
  130. if (size > sizeof rxbuf) {
  131. size = sizeof rxbuf;
  132. rxbuftrunc = 1;
  133. }
  134. memcpy(rxbuf, payload, size);
  135. rxbufsize = size;
  136. gotrxdone = 1;
  137. }
  138. void
  139. rxtimeout(void)
  140. {
  141. usb_printf("rxtimeout\r\n");
  142. }
  143. void
  144. rxerr(void)
  145. {
  146. usb_printf("rxerr\r\n");
  147. }
  148. static uint8_t
  149. hexchartonib(char s)
  150. {
  151. switch (s) {
  152. case '0'...'9':
  153. return s - '0';
  154. case 'a'...'f':
  155. return s - 'a' + 10;
  156. case 'A'...'F':
  157. return s - 'A' + 10;
  158. default:
  159. return -1;
  160. }
  161. }
  162. static bool
  163. hexdecode(char *buf, size_t len, uint8_t *out)
  164. {
  165. uint8_t topchr, botchr;
  166. if (len % 2)
  167. return false;
  168. /* NB: only needed to silence a bad gcc warning */
  169. topchr = -1;
  170. while (len) {
  171. if (len % 2) {
  172. /* bottom nibble */
  173. botchr = hexchartonib(*buf);
  174. if (topchr == -1 || botchr == -1)
  175. return false;
  176. *out = topchr << 4 | botchr;
  177. out++;
  178. } else {
  179. /* top nibble */
  180. topchr = hexchartonib(*buf);
  181. }
  182. len--;
  183. buf++;
  184. }
  185. return true;
  186. }
  187. static const char pktstart[] = "pkt:";
  188. static const size_t pktstartlen = sizeof pktstart - 1;
  189. static uint8_t pktbuf[128];
  190. static void
  191. process_line(char *start, char *end)
  192. {
  193. size_t len;
  194. /* trim off leading CR/NL */
  195. while (start < end && (*start == '\r' || *start == '\n'))
  196. start++;
  197. len = end - start;
  198. if (len >= pktstartlen && memcmp(start, pktstart, sizeof pktstart - 1) == 0) {
  199. start += pktstartlen;
  200. len -= pktstartlen;
  201. if (len % 2) {
  202. usb_printf("invalid pkt len\r\n");
  203. return;
  204. }
  205. if (!hexdecode(start, len, pktbuf)) {
  206. usb_printf("invalid pkt\r\n");
  207. return;
  208. }
  209. rs485_starttx(pktbuf, len / 2);
  210. return;
  211. }
  212. usb_printf("line: %.*s", end - start, start);
  213. fflush(vcp_usb);
  214. }
  215. static void
  216. WaitForIRQ()
  217. {
  218. __disable_irq();
  219. HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
  220. __enable_irq( );
  221. }
  222. int
  223. main(void)
  224. {
  225. sysinit_run();
  226. debug_printf("starting...\n");
  227. #if 1
  228. int i;
  229. for (i = 0; i < 5; i++) {
  230. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
  231. HAL_Delay(250);
  232. HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
  233. HAL_Delay(250);
  234. }
  235. #endif
  236. setlinebuf(vcp_usb);
  237. #if 1
  238. wait_for_vcp();
  239. /*
  240. * This is required to use w/ FreeBSD. This is an issue w/ the
  241. * STM32 Core USB library:
  242. * https://github.com/STMicroelectronics/STM32CubeL1/issues/10
  243. */
  244. HAL_Delay(50);
  245. usb_printf("starting...\r\n");
  246. #endif
  247. char inpbuf[1024];
  248. char *lastcheck;
  249. char *endchr;
  250. int inpbufpos = 0;
  251. int cpylen;
  252. rs485_register(txdone, rxdone, errfunc);
  253. rs485_startrx();
  254. unsigned lasttick = -1;
  255. uint32_t gt;
  256. loop:
  257. /*
  258. * This is disabled as when it's enabled, it causes a hang
  259. * when sending USB data. The USB CDC end point never becomes
  260. * unbusy to allow the next send to progress.
  261. */
  262. #if 0
  263. /*
  264. * A ms delay isn't a big deal, so no special locking is
  265. * used to make sure we don't got to sleep w/ data pending.
  266. */
  267. WaitForIRQ();
  268. #else
  269. (void)WaitForIRQ;
  270. #endif
  271. gt = HAL_GetTick();
  272. if (lasttick != gt / 1000) {
  273. lasttick = gt / 1000;
  274. //usb_printf("tick: %u\r\n", lasttick);
  275. }
  276. if (goterr) {
  277. //usb_printf("error\r\n");
  278. goterr = 0;
  279. dorx = 1;
  280. }
  281. if (gottxdone) {
  282. usb_printf("txdone\r\n");
  283. dorx = 1;
  284. gottxdone = 0;
  285. }
  286. if (gotrxdone) {
  287. if (rxbuftrunc) {
  288. rxbuftrunc = 0;
  289. //usb_printf("rxdone: buffer overflow\r\n");
  290. } else {
  291. usb_printf("rxdone: size: %u\r\ndata: ", rxbufsize);
  292. hexdump(rxbuf, rxbufsize);
  293. usb_printf("\r\n");
  294. }
  295. dorx = 1;
  296. gotrxdone = 0;
  297. }
  298. if (dorx) {
  299. //usb_printf("dorx\r\n");
  300. rs485_startrx();
  301. //usb_printf("startrx res: %s\r\n", rs485err);
  302. dorx = 0;
  303. }
  304. /* while we have data */
  305. while (CDC_RX_LEN) {
  306. /* store last position */
  307. lastcheck = &inpbuf[inpbufpos];
  308. /* calculate how much space left */
  309. cpylen = MIN(sizeof inpbuf - inpbufpos, CDC_RX_LEN);
  310. /* copy into buffer */
  311. memcpy(&inpbuf[inpbufpos], CDC_RX_BUFFER, cpylen);
  312. /* and point to end of buffer */
  313. inpbufpos += cpylen;
  314. do {
  315. /* find first end of line characters */
  316. endchr = findeol(lastcheck, cpylen);
  317. if (endchr != NULL) {
  318. /* if so, process it */
  319. process_line(inpbuf, endchr);
  320. /* skip end of line char */
  321. endchr++;
  322. /* move remaining buffer to the beginning */
  323. memmove(inpbuf, endchr, inpbufpos - (endchr - inpbuf));
  324. /* and store new length */
  325. inpbufpos = inpbufpos - (endchr - inpbuf);
  326. /* mark begining of stream as last checked */
  327. lastcheck = inpbuf;
  328. /* and try to process another line */
  329. continue;
  330. } else if (inpbufpos == sizeof inpbuf) {
  331. /* we overflowed the buffer */
  332. /* XXX - best way is to throw away this line */
  333. inpbufpos = 0;
  334. }
  335. } while (0);
  336. /* if we copied all the data */
  337. if (cpylen == CDC_RX_LEN) {
  338. /* declare that we are ready to receive more data */
  339. CDC_RX_LEN = 0;
  340. USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  341. } else {
  342. /* if not, move the remaining to the begining and try again */
  343. memmove(CDC_RX_BUFFER, &CDC_RX_BUFFER[cpylen], CDC_RX_LEN - cpylen);
  344. CDC_RX_LEN -= cpylen;
  345. }
  346. }
  347. goto loop;
  348. }