|
|
@@ -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 <board.h> |
|
|
|
#include <adc.h> |
|
|
|
#include <radio.h> |
|
|
|
#include <delay.h> |
|
|
|
|
|
|
|
/* lora-irr headers */ |
|
|
|
#include <misc.h> |
|
|
|
#include <strobe_rng_init.h> |
|
|
|
#include <comms.h> |
|
|
|
|
|
|
|
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; |
|
|
|
} |