/*- * 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 #include /* LoRaMac headers */ #include #include #include #include /* lora-irr headers */ #include #include char * findeol(char *pos, size_t len) { while (len) { if (*pos == '\r' || *pos == '\n') return pos; pos++; len--; } return NULL; } void hexdump(const uint8_t *ptr, size_t len) { int i; for (i = 0; i < len; i++) usb_printf("%02x", ptr[i]); } void txdone(void) { usb_printf("txdone\r\n"); } void txtimeout(void) { usb_printf("txtimeout\r\n"); } void rxdone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) { usb_printf("rxdone: size: %hu, rssi: %hd, snr: %d\r\ndata: ", size, rssi, snr); hexdump(payload, size); usb_printf("\r\n"); } void rxtimeout(void) { usb_printf("rxtimeout\r\n"); } void rxerr(void) { usb_printf("rxerr\r\n"); } RadioEvents_t revents = { .TxDone = txdone, .TxTimeout = txtimeout, .RxDone = rxdone, .RxTimeout = rxtimeout, .RxError = rxerr, }; static uint8_t hexchartonib(char s) { switch (s) { case '0'...'9': return s - '0'; case 'a'...'f': return s - 'a' + 10; case 'A'...'F': return s - 'A' + 10; default: return -1; } } static bool hexdecode(char *buf, size_t len, uint8_t *out) { uint8_t topchr, botchr; if (len % 2) return false; /* NB: only needed to silence a bad gcc warning */ topchr = -1; while (len) { if (len % 2) { /* bottom nibble */ botchr = hexchartonib(*buf); if (topchr == -1 || botchr == -1) return false; *out = topchr << 4 | botchr; out++; } else { /* top nibble */ topchr = hexchartonib(*buf); } len--; buf++; } return true; } static const char pktstart[] = "pkt:"; static const size_t pktstartlen = sizeof pktstart - 1; static uint8_t pktbuf[128]; static void process_line(char *start, char *end) { size_t len; /* trim off leading CR/NL */ while (start < end && (*start == '\r' || *start == '\n')) start++; len = end - start; if (len >= pktstartlen && memcmp(start, pktstart, sizeof pktstart - 1) == 0) { start += pktstartlen; len -= pktstartlen; if (len % 2) { usb_printf("invalid pkt len\r\n"); return; } if (!hexdecode(start, len, pktbuf)) { usb_printf("invalid pkt\r\n"); return; } Radio.Send(pktbuf, len / 2); return; } usb_printf("line: %.*s", end - start, start); } /* * 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 } int main(void) { uint8_t bytes[8]; strobe_rng_init(); BoardInitMcu(); Radio.Init(&revents); analog_seed_rng(); radio_seed_rng(); strobe_rng_save(); /* turn on LED */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); #if 1 wait_for_vcp(); usb_printf("starting...\r\n"); bare_strobe_randomize(bytes, sizeof bytes); hexdump(bytes, sizeof bytes); usb_printf("\r\n"); #endif uint32_t v; usb_printf("gs: %#x\r\n", Radio.GetStatus()); usb_printf("set modem\r\n"); Radio.SetModem(MODEM_LORA); usb_printf("check rffreq: %d\r\n", (int)Radio.CheckRfFrequency(914350 * 1000)); usb_printf("set channel\r\n"); Radio.SetChannel(914350 * 1000); v = Radio.Random(); usb_printf("rr: %#x\r\n", v); usb_printf("rssi: %#hx\r\n", Radio.Rssi(MODEM_LORA)); /* 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*/); uint8_t sendmsg[] = "testing lora123"; usb_printf("sending...\r\n"); Radio.Send(sendmsg, sizeof sendmsg); DelayMs(200); Radio.Send(sendmsg, sizeof sendmsg); DelayMs(200); Radio.Send(sendmsg, sizeof sendmsg); DelayMs(200); usb_printf("rx(0)...\r\n"); Radio.Rx(0); char inpbuf[1024]; char *lastcheck; char *endchr; int inpbufpos = 0; int cpylen; loop: BoardLowPowerHandler(); if (Radio.IrqProcess != NULL) Radio.IrqProcess(); /* while we have data */ while (CDC_RX_LEN) { /* store last position */ lastcheck = &inpbuf[inpbufpos]; /* calculate how much space left */ cpylen = MIN(sizeof inpbuf - inpbufpos, CDC_RX_LEN); /* copy into buffer */ memcpy(&inpbuf[inpbufpos], CDC_RX_BUFFER, cpylen); /* and point to end of buffer */ inpbufpos += cpylen; do { /* find first end of line characters */ endchr = findeol(lastcheck, cpylen); if (endchr != NULL) { /* if so, process it */ process_line(inpbuf, endchr); /* skip end of line char */ endchr++; /* move remaining buffer to the beginning */ memmove(inpbuf, endchr, inpbufpos - (endchr - inpbuf)); /* and store new length */ inpbufpos = inpbufpos - (endchr - inpbuf); /* mark begining of stream as last checked */ lastcheck = inpbuf; /* and try to process another line */ continue; } else if (inpbufpos == sizeof inpbuf) { /* we overflowed the buffer */ /* XXX - best way is to throw away this line */ inpbufpos = 0; } } while (0); /* if we copied all the data */ if (cpylen == CDC_RX_LEN) { /* declare that we are ready to receive more data */ CDC_RX_LEN = 0; USBD_CDC_ReceivePacket(&hUsbDeviceFS); } else { /* if not, move the remaining to the begining and try again */ memmove(CDC_RX_BUFFER, &CDC_RX_BUFFER[cpylen], CDC_RX_LEN - cpylen); CDC_RX_LEN -= cpylen; } } goto loop; }