From 62ddf5d2432e88cf4ece725a4a6fdf7613567564 Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Wed, 5 May 2021 22:17:36 -0700 Subject: [PATCH] start implementing the core part of the irrigation controller... This gets basic comms working and setunset working as well... It makes the LED blink! --- Makefile | 6 +- irr_main.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 868e5da..bec1b56 100644 --- a/Makefile +++ b/Makefile @@ -45,10 +45,12 @@ PROGS = lora.gw lora.irr SRCS.lora.gw = main.c SRCS.lora.irr = irr_main.c +SRCS.lora.irr+= comms.c +SRCS.lora.irr+= strobe_rng_init.c +SRCS.lora.irr+= $(STROBE_SRCS) SRCS+= board.c SRCS+= misc.c -SRCS+= strobe_rng_init.c .if 0 SRCS+= rng_save.c .endif @@ -60,7 +62,7 @@ CFLAGS+= -g # Strobe .PATH: $(.CURDIR)/strobe CFLAGS+= -I$(.CURDIR)/strobe -SRCS+= strobe.c \ +STROBE_SRCS+= strobe.c \ x25519.c # LoRamac (SX1276) radio code diff --git a/irr_main.c b/irr_main.c index df67b91..92c77ff 100644 --- a/irr_main.c +++ b/irr_main.c @@ -1,4 +1,339 @@ +/*- + * Copyright 2021 John-Mark Gurney. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "stm32l1xx.h" +#include "stm32l1xx_hal.h" + +/* LoRaMac headers */ +#include +#include +#include +#include + +/* lora-irr headers */ +#include +#include +#include + +enum { + CMD_TERMINATE = 1, + CMD_WAITFOR = 2, + CMD_RUNFOR = 3, + CMD_PING = 4, + CMD_SETUNSET = 5, +}; + +/* + * rxpktavail is initialized to true meaning that the data in rxpkt + * can be over written. When a packet is received, the data is copied + * to rxpkt, and then rxpktavail is set to false. Once the packet has + * been processed, it is set back to true. + */ +static uint8_t rxpkt[128]; +static struct pktbuf rxpktbuf; +static volatile bool rxpktavail; + +static uint8_t shared_key[] = "foobar"; +static struct pktbuf shared_key_buf = (struct pktbuf){ + .pkt = shared_key, + .pktlen = sizeof shared_key - 1, +}; + +static struct comms_state cs; + +void +txdone(void) +{ + + /* restart Rx when Tx done */ + Radio.Rx(0); +} + +void +txtimeout(void) +{ + + /* restart Rx when Tx done */ + Radio.Rx(0); +} + +void +rxdone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) +{ + + if (rxpktavail) { + memcpy(rxpkt, payload, MIN(sizeof rxpkt, size)); + rxpktbuf = (struct pktbuf){ + .pkt = rxpkt, + .pktlen = size, + }; + rxpktavail = false; + } +} + +void +rxtimeout(void) +{ + +} + +void +rxerr(void) +{ +} + +RadioEvents_t revents = { + .TxDone = txdone, + .TxTimeout = txtimeout, + .RxDone = rxdone, + .RxTimeout = rxtimeout, + .RxError = rxerr, +}; + +/* + * Seed the randomness from the radio. This is not a great + * seed, and is hard to gauge how much randomness is really + * there. Assuming about 1 bit per 8 bits looks pretty safe, + * so add 256 * 8 / 32 words. + */ +static void +radio_seed_rng(void) +{ +#if 0 + uint32_t v; + int i; + + for (i = 0; i < 256 * 8 / 32; i++) { + v = Radio.Random(); + strobe_seed_prng((uint8_t *)&v, sizeof v); + } +#endif +} + +static void +analog_seed_rng(void) +{ +#if 1 + uint16_t v; + int i; + + for (i = 0; i < 256 / 2; i++) { + /* + * Capture some ADC data. If pin is floating, 0xfff + * happens frequently, if pin is grounded, 0 happens + * frequently, filter these values out. + */ + do { + v = AdcReadChannel(&Adc, ADC_CHANNEL_21); + } while (v == 0 || v == 0xfff); + strobe_seed_prng((uint8_t *)&v, sizeof v); + } +#endif +} + +static inline uint32_t +letoh_32(uint8_t *v) +{ + return v[0] | (v[1] << 8) | (v[2] << 16) | (v[3] << 24); +} + +struct chaninfo { + GPIO_TypeDef *bank; + uint16_t pinnum; + bool init; +} chans[] = { + [0] = { .bank = GPIOB, .pinnum = GPIO_PIN_5, .init = true, }, + [1] = { .bank = GPIOB, .pinnum = GPIO_PIN_6, .init = true, }, + [2] = { .bank = GPIOB, .pinnum = GPIO_PIN_7, .init = true, }, + [3] = { .bank = GPIOB, .pinnum = GPIO_PIN_9, .init = true, }, + [4] = { .bank = GPIOB, .pinnum = GPIO_PIN_8, .init = true, }, +}; +#define nitems(x) (sizeof(x) / sizeof *(x)) + +static void +set_chan(uint32_t chan, bool val) +{ + struct chaninfo ci; + + if (chan < nitems(chans)) { + ci = chans[chan]; + HAL_GPIO_WritePin(ci.bank, ci.pinnum, val ? GPIO_PIN_SET : GPIO_PIN_RESET); + } +} + +static void +setup_gpio() +{ + GPIO_InitTypeDef GPIO_InitStruct; + int i; + + for (i = 0; i < nitems(chans); i++) { + GPIO_InitStruct = (GPIO_InitTypeDef){ + .Pin = chans[i].pinnum, + .Mode = GPIO_MODE_OUTPUT_PP, + .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_FREQ_LOW, + }; + HAL_GPIO_Init(chans[i].bank, &GPIO_InitStruct); + set_chan(i, chans[i].init); + } +} + +void +procmsg(struct pktbuf inbuf, struct pktbuf *outbuf) +{ + uint32_t args[5]; + int i, apos; + + i = 1; + apos = 0; + while (i < inbuf.pktlen) { + if (i + 4 <= inbuf.pktlen) { + args[apos++] = letoh_32(&inbuf.pkt[i]); + i += 4; + } + } + + outbuf->pkt[0] = inbuf.pkt[0]; + + switch (inbuf.pkt[0]) { + case CMD_SETUNSET: + if (apos == 2) + set_chan(args[0], args[1]); + break; + + default: + outbuf->pkt[0] = 0; + break; + } + + outbuf->pktlen = 1; +} + int main() { + + strobe_rng_init(); + + BoardInitMcu(); + + Radio.Init(&revents); + + analog_seed_rng(); + + radio_seed_rng(); + + strobe_rng_save(); + + setup_gpio(); + + /* turn on LED */ + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); + + Radio.SetModem(MODEM_LORA); + Radio.SetChannel(914350 * 1000); + + /* RX/TX parameters */ + const uint8_t modem = MODEM_LORA; + const uint8_t bandwidth = 0 /* 128 kHz */; + const uint8_t datarate = 7 /* 128 chips */; + const uint8_t coderate = 1 /* 4/5 */; + const uint8_t preambleLen = 4 /* symbols */; + const uint8_t fixLen = 0 /* variable */; + const uint8_t crcOn = 1 /* on */; + const uint8_t freqHopOn = 0 /* off */; + const bool iqInverted = false /* not inverted */; + + Radio.SetRxConfig(modem, bandwidth, datarate, coderate, 0/*afc*/, + preambleLen, 5/*symTimeout*/, fixLen, 0/*payloadlen*/, crcOn, + freqHopOn, 0/*hopPeriod*/, iqInverted, true/*rxcont*/); + Radio.SetTxConfig(modem, 11/*power*/, 0/*fdev*/, bandwidth, datarate, + coderate, preambleLen, fixLen, crcOn, freqHopOn, 0/*hopPeriod*/, + iqInverted, 1000/*timeout*/); + + /* blink led */ + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); + DelayMs(300); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); + + Radio.Rx(0); + + comms_init(&cs, procmsg, &shared_key_buf); + + uint8_t txbuf[128] = "i'mhere"; + struct pktbuf txpktbuf; + + txpktbuf = (struct pktbuf){ + .pkt = txbuf, + .pktlen = 8, + }; + Radio.Send(txpktbuf.pkt, txpktbuf.pktlen); + + rxpktavail = true; + + //Radio.Rx(0); + +loop: + BoardLowPowerHandler(); + if (Radio.IrqProcess != NULL) + Radio.IrqProcess(); + + if (!rxpktavail) { + txpktbuf = (struct pktbuf){ + .pkt = txbuf, + .pktlen = sizeof txbuf, + }; + /* process available packet */ + comms_process(&cs, rxpktbuf, &txpktbuf); + + rxpktavail = true; + + if (txpktbuf.pktlen) { + int i; + for (i = 0; i < 1; i++) { + DelayMs(20); + Radio.Send(txpktbuf.pkt, txpktbuf.pktlen); + } + +#if 0 + /* blink led */ + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); + DelayMs(300); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); + DelayMs(300); +#endif + } + +#if 0 + /* blink led */ + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); + DelayMs(300); + HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); +#endif + } + goto loop; }