/*- * 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, CMD_ADV = 6, CMD_CLEAR = 7, }; /* * 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; #include 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 && size <= sizeof rxpkt) { memcpy(rxpkt, payload, size); rxpktbuf = (struct pktbuf){ .pkt = rxpkt, .pktlen = size, }; rxpktavail = false; } Radio.Rx(0); } void rxtimeout(void) { Radio.Rx(0); } void rxerr(void) { Radio.Rx(0); } 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 1 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) | ((unsigned)v[3] << 24); } struct chaninfo { GPIO_TypeDef *bank; uint16_t pinnum; bool init; bool invert; } chans[] = { [0] = { .bank = GPIOB, .pinnum = GPIO_PIN_5, .invert = true, }, [1] = { .bank = GPIOB, .pinnum = GPIO_PIN_6, .invert = true, }, [2] = { .bank = GPIOB, .pinnum = GPIO_PIN_7, .invert = true, }, [3] = { .bank = GPIOB, .pinnum = GPIO_PIN_9, .invert = true, }, /* Turn on LED at start */ [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 ^ ci.invert ? 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); } } static struct sched { uint32_t cmd; uint32_t end_wait_tick; /* end if running, otherwise how long to wait */ uint32_t chan; } schedule[20]; static int schedpos; /* position in schedule, % nitems(schedule)*/ static int schedcnt; /* total items waiting */ #define SCHED_ITEM(x) (schedule[(schedpos + x) % nitems(schedule)]) #define SCHED_HEAD SCHED_ITEM(0) #define SCHED_TAIL SCHED_ITEM(schedcnt) static void start_sched(struct sched *sched) { sched->end_wait_tick += uwTick; if (sched->cmd == CMD_RUNFOR) set_chan(sched->chan, 1); } static bool canproc_sched() { /* nothing to do? */ if (schedcnt == 0) return false; /* not yet expired */ if (uwTick < SCHED_HEAD.end_wait_tick) return false; return true; } static void process_sched() { if (!canproc_sched()) return; if (SCHED_HEAD.cmd == CMD_RUNFOR) set_chan(SCHED_HEAD.chan, 0); /* we are done, advance */ schedpos++; schedcnt--; if (schedcnt) start_sched(&SCHED_HEAD); } static void enqueue_sched(uint32_t cmd, uint32_t ticks, uint32_t chan) { if (schedcnt >= nitems(schedule)) return; SCHED_TAIL = (struct sched){ .cmd = cmd, .end_wait_tick = ticks, .chan = chan, }; if (schedcnt == 0) start_sched(&SCHED_HEAD); schedcnt++; } static void procmsg(struct pktbuf inbuf, struct pktbuf *outbuf) { uint32_t args[5]; int i, apos, cnt; 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_WAITFOR: if (apos == 1) enqueue_sched(CMD_WAITFOR, args[0], -1); break; case CMD_RUNFOR: if (apos == 2) enqueue_sched(CMD_RUNFOR, args[0], args[1]); break; case CMD_PING: break; case CMD_SETUNSET: if (apos == 2) set_chan(args[0], args[1]); break; case CMD_ADV: cnt = 1; if (apos == 1) cnt = args[0]; for (i = 0; i < cnt && i < schedcnt; i++) SCHED_ITEM(i).end_wait_tick = 0; break; case CMD_CLEAR: if (schedcnt) schedcnt = 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 = 8 /* 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, NULL, NULL); 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: while (canproc_sched()) process_sched(); 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; }