From 5556c1de03068a62d07020a666051d9ac11ea270 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 15 Nov 2016 16:47:48 +0100 Subject: [PATCH 001/137] Arduino: Add Arduino-specific files This adds an implementation of the LMIC HAL, plus some files needed for the Arduino library format and some examples. In addition, an export.sh script is added that can be used to generate an Arduino library from this repository. This script only reorders files, it does not change anything. When given the --link option, it creates symlinks instead of copies, allowing to test things in the Arduino IDE easily, while committing the original files in this repository. The HAL, examples and library.properties files are taken from the https://github.com/matthijskooijman/arduino-lmic repository, revision 90bc049 (Print downlink ack status in ttn-otaa example too). Only the configuration mechanism was changed slightly. --- target/arduino/examples/raw/raw.ino | 162 +++++++++++ target/arduino/examples/ttn-abp/ttn-abp.ino | 226 ++++++++++++++++ target/arduino/examples/ttn-otaa/ttn-otaa.ino | 173 ++++++++++++ target/arduino/export.sh | 45 ++++ target/arduino/hal/hal.cpp | 253 ++++++++++++++++++ target/arduino/hal/hal.h | 28 ++ target/arduino/hal/target-config.h | 80 ++++++ target/arduino/library.properties | 9 + target/arduino/lmic.h | 9 + 9 files changed, 985 insertions(+) create mode 100644 target/arduino/examples/raw/raw.ino create mode 100644 target/arduino/examples/ttn-abp/ttn-abp.ino create mode 100644 target/arduino/examples/ttn-otaa/ttn-otaa.ino create mode 100755 target/arduino/export.sh create mode 100644 target/arduino/hal/hal.cpp create mode 100644 target/arduino/hal/hal.h create mode 100644 target/arduino/hal/target-config.h create mode 100644 target/arduino/library.properties create mode 100644 target/arduino/lmic.h diff --git a/target/arduino/examples/raw/raw.ino b/target/arduino/examples/raw/raw.ino new file mode 100644 index 0000000..1e0382f --- /dev/null +++ b/target/arduino/examples/raw/raw.ino @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example transmits data on hardcoded channel and receives data + * when not transmitting. Running this sketch on two nodes should allow + * them to communicate. + *******************************************************************************/ + +#include +#include +#include + +#if !defined(DISABLE_INVERT_IQ_ON_RX) +#error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ + config.h in the lmic library to set it. +#endif + +// How often to send a packet. Note that this sketch bypasses the normal +// LMIC duty cycle limiting, so when you change anything in this sketch +// (payload length, frequency, spreading factor), be sure to check if +// this interval should not also be increased. +// See this spreadsheet for an easy airtime and duty cycle calculator: +// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc +#define TX_INTERVAL 2000 + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, +}; + + +// These callbacks are only used in over-the-air activation, so they are +// left empty here (we cannot leave them out completely unless +// DISABLE_JOIN is set in config.h, otherwise the linker will complain). +void os_getArtEui (u1_t* buf) { } +void os_getDevEui (u1_t* buf) { } +void os_getDevKey (u1_t* buf) { } + +void onEvent (ev_t ev) { +} + +osjob_t txjob; +osjob_t timeoutjob; +static void tx_func (osjob_t* job); + +// Transmit the given string and call the given function afterwards +void tx(const char *str, osjobcb_t func) { + os_radio(RADIO_RST); // Stop RX first + delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet + LMIC.dataLen = 0; + while (*str) + LMIC.frame[LMIC.dataLen++] = *str++; + LMIC.osjob.func = func; + os_radio(RADIO_TX); + Serial.println("TX"); +} + +// Enable rx mode and call func when a packet is received +void rx(osjobcb_t func) { + LMIC.osjob.func = func; + LMIC.rxtime = os_getTime(); // RX _now_ + // Enable "continuous" RX (e.g. without a timeout, still stops after + // receiving a packet) + os_radio(RADIO_RXON); + Serial.println("RX"); +} + +static void rxtimeout_func(osjob_t *job) { + digitalWrite(LED_BUILTIN, LOW); // off +} + +static void rx_func (osjob_t* job) { + // Blink once to confirm reception and then keep the led on + digitalWrite(LED_BUILTIN, LOW); // off + delay(10); + digitalWrite(LED_BUILTIN, HIGH); // on + + // Timeout RX (i.e. update led status) after 3 periods without RX + os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); + + // Reschedule TX so that it should not collide with the other side's + // next TX + os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); + + Serial.print("Got "); + Serial.print(LMIC.dataLen); + Serial.println(" bytes"); + Serial.write(LMIC.frame, LMIC.dataLen); + Serial.println(); + + // Restart RX + rx(rx_func); +} + +static void txdone_func (osjob_t* job) { + rx(rx_func); +} + +// log text to USART and toggle LED +static void tx_func (osjob_t* job) { + // say hello + tx("Hello, world!", txdone_func); + // reschedule job every TX_INTERVAL (plus a bit of random to prevent + // systematic collisions), unless packets are received, then rx_func + // will reschedule at half this time. + os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); +} + +// application entry point +void setup() { + Serial.begin(115200); + Serial.println("Starting"); + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + pinMode(LED_BUILTIN, OUTPUT); + + // initialize runtime env + os_init(); + + // Set up these settings once, and use them for both TX and RX + +#if defined(CFG_eu868) + // Use a frequency in the g3 which allows 10% duty cycling. + LMIC.freq = 869525000; +#elif defined(CFG_us915) + LMIC.freq = 902300000; +#endif + + // Maximum TX power + LMIC.txpow = 27; + // Use a medium spread factor. This can be increased up to SF12 for + // better range, but then the interval should be (significantly) + // lowered to comply with duty cycle limits as well. + LMIC.datarate = DR_SF9; + // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250) + LMIC.rps = updr2rps(LMIC.datarate); + + Serial.println("Started"); + Serial.flush(); + + // setup initial job + os_setCallback(&txjob, tx_func); +} + +void loop() { + // execute scheduled jobs and events + os_runloop_once(); +} diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/ttn-abp/ttn-abp.ino new file mode 100644 index 0000000..a8b4a18 --- /dev/null +++ b/target/arduino/examples/ttn-abp/ttn-abp.ino @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example sends a valid LoRaWAN packet with payload "Hello, + * world!", using frequency and encryption settings matching those of + * the The Things Network. + * + * This uses ABP (Activation-by-personalisation), where a DevAddr and + * Session keys are preconfigured (unlike OTAA, where a DevEUI and + * application key is configured, while the DevAddr and session keys are + * assigned/generated in the over-the-air-activation procedure). + * + * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in + * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably + * violated by this sketch when left running for longer)! + * + * To use this sketch, first register your application and device with + * the things network, to set or generate a DevAddr, NwkSKey and + * AppSKey. Each device should have their own unique values for these + * fields. + * + * Do not forget to define the radio type correctly in config.h. + * + *******************************************************************************/ + +#include +#include +#include + +// LoRaWAN NwkSKey, network session key +// This is the default Semtech key, which is used by the early prototype TTN +// network. +static const PROGMEM u1_t NWKSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; + +// LoRaWAN AppSKey, application session key +// This is the default Semtech key, which is used by the early prototype TTN +// network. +static const u1_t PROGMEM APPSKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; + +// LoRaWAN end-device address (DevAddr) +static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every node! + +// These callbacks are only used in over-the-air activation, so they are +// left empty here (we cannot leave them out completely unless +// DISABLE_JOIN is set in config.h, otherwise the linker will complain). +void os_getArtEui (u1_t* buf) { } +void os_getDevEui (u1_t* buf) { } +void os_getDevKey (u1_t* buf) { } + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, +}; + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + break; + case EV_RFU1: + Serial.println(F("EV_RFU1")); + break; + case EV_JOIN_FAILED: + Serial.println(F("EV_JOIN_FAILED")); + break; + case EV_REJOIN_FAILED: + Serial.println(F("EV_REJOIN_FAILED")); + break; + case EV_TXCOMPLETE: + Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); + if (LMIC.txrxFlags & TXRX_ACK) + Serial.println(F("Received ack")); + if (LMIC.dataLen) { + Serial.println(F("Received ")); + Serial.println(LMIC.dataLen); + Serial.println(F(" bytes of payload")); + } + // Schedule next transmission + os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); + break; + case EV_LOST_TSYNC: + Serial.println(F("EV_LOST_TSYNC")); + break; + case EV_RESET: + Serial.println(F("EV_RESET")); + break; + case EV_RXCOMPLETE: + // data received in ping slot + Serial.println(F("EV_RXCOMPLETE")); + break; + case EV_LINK_DEAD: + Serial.println(F("EV_LINK_DEAD")); + break; + case EV_LINK_ALIVE: + Serial.println(F("EV_LINK_ALIVE")); + break; + default: + Serial.println(F("Unknown event")); + break; + } +} + +void do_send(osjob_t* j){ + // Check if there is not a current TX/RX job running + if (LMIC.opmode & OP_TXRXPEND) { + Serial.println(F("OP_TXRXPEND, not sending")); + } else { + // Prepare upstream data transmission at the next possible time. + LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); + Serial.println(F("Packet queued")); + } + // Next TX is scheduled after TX_COMPLETE event. +} + +void setup() { + Serial.begin(115200); + Serial.println(F("Starting")); + + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + // LMIC init + os_init(); + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + + // Set static session parameters. Instead of dynamically establishing a session + // by joining the network, precomputed session parameters are be provided. + #ifdef PROGMEM + // On AVR, these values are stored in flash and only copied to RAM + // once. Copy them to a temporary buffer here, LMIC_setSession will + // copy them into a buffer of its own again. + uint8_t appskey[sizeof(APPSKEY)]; + uint8_t nwkskey[sizeof(NWKSKEY)]; + memcpy_P(appskey, APPSKEY, sizeof(APPSKEY)); + memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY)); + LMIC_setSession (0x1, DEVADDR, nwkskey, appskey); + #else + // If not running an AVR with PROGMEM, just use the arrays directly + LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY); + #endif + + #if defined(CFG_eu868) + // Set up the channels used by the Things Network, which corresponds + // to the defaults of most gateways. Without this, only three base + // channels from the LoRaWAN specification are used, which certainly + // works, so it is good for debugging, but can overload those + // frequencies, so be sure to configure the full frequency range of + // your network here (unless your network autoconfigures them). + // Setting up channels should happen after LMIC_setSession, as that + // configures the minimal channel set. + // NA-US channels 0-71 are configured automatically + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band + // TTN defines an additional channel at 869.525Mhz using SF9 for class B + // devices' ping slots. LMIC does not have an easy way to define set this + // frequency and support for class B is spotty and untested, so this + // frequency is not configured here. + #elif defined(CFG_us915) + // NA-US channels 0-71 are configured automatically + // but only one group of 8 should (a subband) should be active + // TTN recommends the second sub band, 1 in a zero based count. + // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json + LMIC_selectSubBand(1); + #endif + + // Disable link check validation + LMIC_setLinkCheckMode(0); + + // TTN uses SF9 for its RX2 window. + LMIC.dn2Dr = DR_SF9; + + // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) + LMIC_setDrTxpow(DR_SF7,14); + + // Start job + do_send(&sendjob); +} + +void loop() { + os_runloop_once(); +} diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/ttn-otaa/ttn-otaa.ino new file mode 100644 index 0000000..d667aa2 --- /dev/null +++ b/target/arduino/examples/ttn-otaa/ttn-otaa.ino @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * This example sends a valid LoRaWAN packet with payload "Hello, + * world!", using frequency and encryption settings matching those of + * the The Things Network. + * + * This uses OTAA (Over-the-air activation), where where a DevEUI and + * application key is configured, which are used in an over-the-air + * activation procedure where a DevAddr and session keys are + * assigned/generated for use with all further communication. + * + * Note: LoRaWAN per sub-band duty-cycle limitation is enforced (1% in + * g1, 0.1% in g2), but not the TTN fair usage policy (which is probably + * violated by this sketch when left running for longer)! + + * To use this sketch, first register your application and device with + * the things network, to set or generate an AppEUI, DevEUI and AppKey. + * Multiple devices can use the same AppEUI, but each device has its own + * DevEUI and AppKey. + * + * Do not forget to define the radio type correctly in config.h. + * + *******************************************************************************/ + +#include +#include +#include + +// This EUI must be in little-endian format, so least-significant-byte +// first. When copying an EUI from ttnctl output, this means to reverse +// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, +// 0x70. +static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} + +// This should also be in little endian format, see above. +static const u1_t PROGMEM DEVEUI[8]={ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} + +// This key should be in big endian format (or, since it is not really a +// number but a block of memory, endianness does not really apply). In +// practice, a key taken from ttnctl can be copied as-is. +// The key shown here is the semtech default key. +static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; +void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +static uint8_t mydata[] = "Hello, world!"; +static osjob_t sendjob; + +// Schedule TX every this many seconds (might become longer due to duty +// cycle limitations). +const unsigned TX_INTERVAL = 60; + +// Pin mapping +const lmic_pinmap lmic_pins = { + .nss = 6, + .rxtx = LMIC_UNUSED_PIN, + .rst = 5, + .dio = {2, 3, 4}, +}; + +void onEvent (ev_t ev) { + Serial.print(os_getTime()); + Serial.print(": "); + switch(ev) { + case EV_SCAN_TIMEOUT: + Serial.println(F("EV_SCAN_TIMEOUT")); + break; + case EV_BEACON_FOUND: + Serial.println(F("EV_BEACON_FOUND")); + break; + case EV_BEACON_MISSED: + Serial.println(F("EV_BEACON_MISSED")); + break; + case EV_BEACON_TRACKED: + Serial.println(F("EV_BEACON_TRACKED")); + break; + case EV_JOINING: + Serial.println(F("EV_JOINING")); + break; + case EV_JOINED: + Serial.println(F("EV_JOINED")); + + // Disable link check validation (automatically enabled + // during join, but not supported by TTN at this time). + LMIC_setLinkCheckMode(0); + break; + case EV_RFU1: + Serial.println(F("EV_RFU1")); + break; + case EV_JOIN_FAILED: + Serial.println(F("EV_JOIN_FAILED")); + break; + case EV_REJOIN_FAILED: + Serial.println(F("EV_REJOIN_FAILED")); + break; + break; + case EV_TXCOMPLETE: + Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)")); + if (LMIC.txrxFlags & TXRX_ACK) + Serial.println(F("Received ack")); + if (LMIC.dataLen) { + Serial.println(F("Received ")); + Serial.println(LMIC.dataLen); + Serial.println(F(" bytes of payload")); + } + // Schedule next transmission + os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); + break; + case EV_LOST_TSYNC: + Serial.println(F("EV_LOST_TSYNC")); + break; + case EV_RESET: + Serial.println(F("EV_RESET")); + break; + case EV_RXCOMPLETE: + // data received in ping slot + Serial.println(F("EV_RXCOMPLETE")); + break; + case EV_LINK_DEAD: + Serial.println(F("EV_LINK_DEAD")); + break; + case EV_LINK_ALIVE: + Serial.println(F("EV_LINK_ALIVE")); + break; + default: + Serial.println(F("Unknown event")); + break; + } +} + +void do_send(osjob_t* j){ + // Check if there is not a current TX/RX job running + if (LMIC.opmode & OP_TXRXPEND) { + Serial.println(F("OP_TXRXPEND, not sending")); + } else { + // Prepare upstream data transmission at the next possible time. + LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); + Serial.println(F("Packet queued")); + } + // Next TX is scheduled after TX_COMPLETE event. +} + +void setup() { + Serial.begin(9600); + Serial.println(F("Starting")); + + #ifdef VCC_ENABLE + // For Pinoccio Scout boards + pinMode(VCC_ENABLE, OUTPUT); + digitalWrite(VCC_ENABLE, HIGH); + delay(1000); + #endif + + // LMIC init + os_init(); + // Reset the MAC state. Session and pending data transfers will be discarded. + LMIC_reset(); + + // Start job (sending automatically starts OTAA too) + do_send(&sendjob); +} + +void loop() { + os_runloop_once(); +} diff --git a/target/arduino/export.sh b/target/arduino/export.sh new file mode 100755 index 0000000..d05fcc3 --- /dev/null +++ b/target/arduino/export.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +SRC=$(cd "$(dirname "$0")"; pwd) + +usage() { + echo "$0 [--link] TARGET_DIR" + echo "Create an Arduino-compatible library in the given directory, overwriting existing files. If --link is given, creates symbolic links for easy testing." +} + + +CPOPTS= +case "$1" in + --help) + usage + exit 0 + ;; + --link) + CPOPTS=--symbolic-link + shift; + ;; + --*) + echo "Unknown option: $1" 1>&2 + exit 1 + ;; +esac + +TARGET=$1 + +if [ -z "$TARGET" ]; then + usage + exit 1 +fi + +if ! [ -d "$(dirname "$TARGET")" ]; then + echo "Parent of $TARGET should exist" 1>&2 + exit 1 +fi + +mkdir -p "$TARGET"/src +cp $CPOPTS -f -v "$SRC"/library.properties "$TARGET" +cp $CPOPTS -f -v "$SRC"/lmic.h "$TARGET"/src +cp $CPOPTS -r -f -v "$SRC"/../../lmic "$TARGET"/src +cp $CPOPTS -r -f -v "$SRC"/../../aes "$TARGET"/src +cp $CPOPTS -r -f -v "$SRC"/hal "$TARGET"/src +cp $CPOPTS -r -f -v "$SRC"/examples "$TARGET" diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp new file mode 100644 index 0000000..53bf359 --- /dev/null +++ b/target/arduino/hal/hal.cpp @@ -0,0 +1,253 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ + +#include +#include +#include "../lmic.h" +#include "hal.h" +#include + +// ----------------------------------------------------------------------------- +// I/O + +static void hal_io_init () { + // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK + ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN || lmic_pins.dio[2] != LMIC_UNUSED_PIN); + + pinMode(lmic_pins.nss, OUTPUT); + if (lmic_pins.rxtx != LMIC_UNUSED_PIN) + pinMode(lmic_pins.rxtx, OUTPUT); + if (lmic_pins.rst != LMIC_UNUSED_PIN) + pinMode(lmic_pins.rst, OUTPUT); + + pinMode(lmic_pins.dio[0], INPUT); + if (lmic_pins.dio[1] != LMIC_UNUSED_PIN) + pinMode(lmic_pins.dio[1], INPUT); + if (lmic_pins.dio[2] != LMIC_UNUSED_PIN) + pinMode(lmic_pins.dio[2], INPUT); +} + +// val == 1 => tx 1 +void hal_pin_rxtx (u1_t val) { + if (lmic_pins.rxtx != LMIC_UNUSED_PIN) + digitalWrite(lmic_pins.rxtx, val); +} + +// set radio RST pin to given value (or keep floating!) +void hal_pin_rst (u1_t val) { + if (lmic_pins.rst == LMIC_UNUSED_PIN) + return; + + if(val == 0 || val == 1) { // drive pin + pinMode(lmic_pins.rst, OUTPUT); + digitalWrite(lmic_pins.rst, val); + } else { // keep pin floating + pinMode(lmic_pins.rst, INPUT); + } +} + +static bool dio_states[NUM_DIO] = {0}; + +static void hal_io_check() { + uint8_t i; + for (i = 0; i < NUM_DIO; ++i) { + if (lmic_pins.dio[i] == LMIC_UNUSED_PIN) + continue; + + if (dio_states[i] != digitalRead(lmic_pins.dio[i])) { + dio_states[i] = !dio_states[i]; + if (dio_states[i]) + radio_irq_handler(i); + } + } +} + +// ----------------------------------------------------------------------------- +// SPI + +static const SPISettings settings(10E6, MSBFIRST, SPI_MODE0); + +static void hal_spi_init () { + SPI.begin(); +} + +void hal_pin_nss (u1_t val) { + if (!val) + SPI.beginTransaction(settings); + else + SPI.endTransaction(); + + //Serial.println(val?">>":"<<"); + digitalWrite(lmic_pins.nss, val); +} + +// perform SPI transaction with radio +u1_t hal_spi (u1_t out) { + u1_t res = SPI.transfer(out); +/* + Serial.print(">"); + Serial.print(out, HEX); + Serial.print("<"); + Serial.println(res, HEX); + */ + return res; +} + +// ----------------------------------------------------------------------------- +// TIME + +static void hal_time_init () { + // Nothing to do +} + +u4_t hal_ticks () { + // Because micros() is scaled down in this function, micros() will + // overflow before the tick timer should, causing the tick timer to + // miss a significant part of its values if not corrected. To fix + // this, the "overflow" serves as an overflow area for the micros() + // counter. It consists of three parts: + // - The US_PER_OSTICK upper bits are effectively an extension for + // the micros() counter and are added to the result of this + // function. + // - The next bit overlaps with the most significant bit of + // micros(). This is used to detect micros() overflows. + // - The remaining bits are always zero. + // + // By comparing the overlapping bit with the corresponding bit in + // the micros() return value, overflows can be detected and the + // upper bits are incremented. This is done using some clever + // bitwise operations, to remove the need for comparisons and a + // jumps, which should result in efficient code. By avoiding shifts + // other than by multiples of 8 as much as possible, this is also + // efficient on AVR (which only has 1-bit shifts). + static uint8_t overflow = 0; + + // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0, + // the others will be the lower bits of our return value. + uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT; + // Most significant byte of scaled + uint8_t msb = scaled >> 24; + // Mask pointing to the overlapping bit in msb and overflow. + const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT)); + // Update overflow. If the overlapping bit is different + // between overflow and msb, it is added to the stored value, + // so the overlapping bit becomes equal again and, if it changed + // from 1 to 0, the upper bits are incremented. + overflow += (msb ^ overflow) & mask; + + // Return the scaled value with the upper bits of stored added. The + // overlapping bit will be equal and the lower bits will be 0, so + // bitwise or is a no-op for them. + return scaled | ((uint32_t)overflow << 24); + + // 0 leads to correct, but overly complex code (it could just return + // micros() unmodified), 8 leaves no room for the overlapping bit. + static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); +} + +// Returns the number of ticks until time. Negative values indicate that +// time has already passed. +static s4_t delta_time(u4_t time) { + return (s4_t)(time - hal_ticks()); +} + +void hal_waitUntil (u4_t time) { + s4_t delta = delta_time(time); + // From delayMicroseconds docs: Currently, the largest value that + // will produce an accurate delay is 16383. + while (delta > (16000 / US_PER_OSTICK)) { + delay(16); + delta -= (16000 / US_PER_OSTICK); + } + if (delta > 0) + delayMicroseconds(delta * US_PER_OSTICK); +} + +// check and rewind for target time +u1_t hal_checkTimer (u4_t time) { + // No need to schedule wakeup, since we're not sleeping + return delta_time(time) <= 0; +} + +static uint8_t irqlevel = 0; + +void hal_disableIRQs () { + noInterrupts(); + irqlevel++; +} + +void hal_enableIRQs () { + if(--irqlevel == 0) { + interrupts(); + + // Instead of using proper interrupts (which are a bit tricky + // and/or not available on all pins on AVR), just poll the pin + // values. Since os_runloop disables and re-enables interrupts, + // putting this here makes sure we check at least once every + // loop. + // + // As an additional bonus, this prevents the can of worms that + // we would otherwise get for running SPI transfers inside ISRs + hal_io_check(); + } +} + +void hal_sleep () { + // Not implemented +} + +// ----------------------------------------------------------------------------- + +#if defined(LMIC_PRINTF_TO) +static int uart_putchar (char c, FILE *) +{ + LMIC_PRINTF_TO.write(c) ; + return 0 ; +} + +void hal_printf_init() { + // create a FILE structure to reference our UART output function + static FILE uartout; + memset(&uartout, 0, sizeof(uartout)); + + // fill in the UART file descriptor with pointer to writer. + fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); + + // The uart is the standard output device STDOUT. + stdout = &uartout ; +} +#endif // defined(LMIC_PRINTF_TO) + +void hal_init () { + // configure radio I/O and interrupt handler + hal_io_init(); + // configure radio SPI + hal_spi_init(); + // configure timer and interrupt handler + hal_time_init(); +#if defined(LMIC_PRINTF_TO) + // printf support + hal_printf_init(); +#endif +} + +void hal_failed (const char *file, u2_t line) { +#if defined(LMIC_FAILURE_TO) + LMIC_FAILURE_TO.println("FAILURE "); + LMIC_FAILURE_TO.print(file); + LMIC_FAILURE_TO.print(':'); + LMIC_FAILURE_TO.println(line); + LMIC_FAILURE_TO.flush(); +#endif + hal_disableIRQs(); + while(1); +} diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h new file mode 100644 index 0000000..e096569 --- /dev/null +++ b/target/arduino/hal/hal.h @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This the HAL to run LMIC on top of the Arduino environment. + *******************************************************************************/ +#ifndef _hal_hal_h_ +#define _hal_hal_h_ + +static const int NUM_DIO = 3; + +struct lmic_pinmap { + u1_t nss; + u1_t rxtx; + u1_t rst; + u1_t dio[NUM_DIO]; +}; + +// Use this for any unused pins. +const u1_t LMIC_UNUSED_PIN = 0xff; + +// Declared here, to be defined an initialized by the application +extern const lmic_pinmap lmic_pins; + +#endif // _hal_hal_h_ diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h new file mode 100644 index 0000000..026a18c --- /dev/null +++ b/target/arduino/hal/target-config.h @@ -0,0 +1,80 @@ +#ifndef _lmic_arduino_hal_config_h_ +#define _lmic_arduino_hal_config_h_ + +#define CFG_eu868 1 +//#define CFG_us915 1 + +// This is the SX1272/SX1273 radio, which is also used on the HopeRF +// RFM92 boards. +//#define CFG_sx1272_radio 1 +// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on +// the HopeRF RFM95 boards. +#define CFG_sx1276_radio 1 + +// 16 μs per tick +// LMIC requires ticks to be 15.5μs - 100 μs long +#define US_PER_OSTICK_EXPONENT 4 +#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) +#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) + +// Enable this to allow using printf() to print to the given serial port +// (or any other Print object). This can be easy for debugging. The +// current implementation only works on AVR, though. +//#define LMIC_PRINTF_TO Serial + +// Any runtime assertion failures are printed to this serial port (or +// any other Print object). If this is unset, any failures just silently +// halt execution. +#define LMIC_FAILURE_TO Serial + +// Set this to 1 to enable some basic debug output (using printf) about +// RF settings used during transmission and reception. Set to 2 to +// enable more verbose output. Make sure that printf is actually +// configured (e.g. on AVR it is not by default), otherwise using it can +// cause crashing. +#define LMIC_DEBUG_LEVEL 0 + +// Uncomment this to disable all code related to joining +//#define DISABLE_JOIN +// Uncomment this to disable all code related to ping +#define DISABLE_PING +// Uncomment this to disable all code related to beacon tracking. +// Requires ping to be disabled too +#define DISABLE_BEACONS + +// Uncomment these to disable the corresponding MAC commands. +// Class A +//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap +//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param +//#define DISABLE_MCMD_SNCH_REQ // set new channel +// Class B +//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING +//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON + +// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the +// same on RX. This ensures that gateways can talk to nodes and vice +// versa, but gateways will not hear other gateways and nodes will not +// hear other nodes. By uncommenting this macro, this inversion is +// disabled and this node can hear other nodes. If two nodes both have +// this macro set, they can talk to each other (but they can no longer +// hear gateways). This should probably only be used when debugging +// and/or when talking to the radio directly (e.g. like in the "raw" +// example). +//#define DISABLE_INVERT_IQ_ON_RX + +// This allows choosing between multiple included AES implementations. +// Make sure exactly one of these is uncommented. +// +// This selects the original AES implementation included LMIC. This +// implementation is optimized for speed on 32-bit processors using +// fairly big lookup tables, but it takes up big amounts of flash on the +// AVR architecture. +// #define USE_ORIGINAL_AES +// +// This selects the AES implementation written by Ideetroon for their +// own LoRaWAN library. It also uses lookup tables, but smaller +// byte-oriented ones, making it use a lot less flash space (but it is +// also about twice as slow as the original). +#define USE_IDEETRON_AES + +#endif // _lmic_arduino_hal_config_h_ diff --git a/target/arduino/library.properties b/target/arduino/library.properties new file mode 100644 index 0000000..0dbf7d5 --- /dev/null +++ b/target/arduino/library.properties @@ -0,0 +1,9 @@ +name=IBM LMIC framework +version=1.5.0+arduino-1 +author=IBM +maintainer=Matthijs Kooijman +sentence=Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. +paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers +category=Communication +url=http://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html +architectures=* diff --git a/target/arduino/lmic.h b/target/arduino/lmic.h new file mode 100644 index 0000000..f2c7935 --- /dev/null +++ b/target/arduino/lmic.h @@ -0,0 +1,9 @@ +#ifdef __cplusplus +extern "C"{ +#endif + +#include "lmic/lmic.h" + +#ifdef __cplusplus +} +#endif From c722c99d369468ceefd7a2532cb2648a5d63e007 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 30 Nov 2016 12:42:09 +0100 Subject: [PATCH 002/137] Arduino: Make export script work on OSX Previously, this used GNU-specific cp options, which are not available on OSX. --- target/arduino/export.sh | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/target/arduino/export.sh b/target/arduino/export.sh index d05fcc3..03e3bd5 100755 --- a/target/arduino/export.sh +++ b/target/arduino/export.sh @@ -8,14 +8,24 @@ usage() { } -CPOPTS= +# This recursively copies $1 (file or directory) into $2 (directory) +# This is essentially cp --symbolic-link, but POSIX-compatible. +create_links() { + local TARGET=$(cd "$2" && pwd) + local SRCDIR=$(cd "$(dirname "$1")" && pwd) + local SRCNAME=$(basename "$1") + (cd "$SRCDIR" && find "$SRCNAME" -type d -exec mkdir -p "$TARGET/{}" \; -o -exec ln -s -v "$SRCDIR/{}" "$TARGET/{}" \; ) +} + + +CMD="cp -r -v" case "$1" in --help) usage exit 0 ;; --link) - CPOPTS=--symbolic-link + CMD="create_links" shift; ;; --*) @@ -37,9 +47,13 @@ if ! [ -d "$(dirname "$TARGET")" ]; then fi mkdir -p "$TARGET"/src -cp $CPOPTS -f -v "$SRC"/library.properties "$TARGET" -cp $CPOPTS -f -v "$SRC"/lmic.h "$TARGET"/src -cp $CPOPTS -r -f -v "$SRC"/../../lmic "$TARGET"/src -cp $CPOPTS -r -f -v "$SRC"/../../aes "$TARGET"/src -cp $CPOPTS -r -f -v "$SRC"/hal "$TARGET"/src -cp $CPOPTS -r -f -v "$SRC"/examples "$TARGET" + +# This copies or links the relevant directories. For the hal and lmic +# directories, the contained files are copied or linked, so that when +# linking relative includes still work as expected +$CMD "$SRC"/library.properties "$TARGET" +$CMD "$SRC"/lmic.h "$TARGET"/src +$CMD "$SRC"/../../lmic "$TARGET"/src +$CMD "$SRC"/hal "$TARGET"/src +$CMD "$SRC"/../../aes "$TARGET"/src +$CMD "$SRC"/examples "$TARGET" From 5d96aaab0d7fdb97775b183b19d413707bd307a0 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 30 Nov 2016 12:47:55 +0100 Subject: [PATCH 003/137] Arduino: Optionally remove existing directory when exporting --- target/arduino/export.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/arduino/export.sh b/target/arduino/export.sh index 03e3bd5..6b09210 100755 --- a/target/arduino/export.sh +++ b/target/arduino/export.sh @@ -46,6 +46,15 @@ if ! [ -d "$(dirname "$TARGET")" ]; then exit 1 fi + +if [ -e "$TARGET" ]; then + echo -n "$TARGET exists, remove before export? [yN]" + read answer + if [ "$answer" = "y" ]; then + rm -rf "$TARGET" + fi +fi + mkdir -p "$TARGET"/src # This copies or links the relevant directories. For the hal and lmic From 3b8bc600fd9d0917217b57bc9c2bceb95101165c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 7 Dec 2016 08:44:53 +0100 Subject: [PATCH 004/137] Arduino: Simplify examples This stops using the osjob scheduling from lmic for scheduling transmissions, since this is a bit tricky to integrate with existing code, and users now often think that using it is mandatory. By removing it, and replacing it by simple time-based checks for scheduling transmissions, it should be more clear how this works. Also, remove some code specific to the Pinoccio board. --- target/arduino/examples/ttn-abp/ttn-abp.ino | 56 +++++++++---------- target/arduino/examples/ttn-otaa/ttn-otaa.ino | 56 +++++++++---------- 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/ttn-abp/ttn-abp.ino index a8b4a18..49a27b7 100644 --- a/target/arduino/examples/ttn-abp/ttn-abp.ino +++ b/target/arduino/examples/ttn-abp/ttn-abp.ino @@ -53,12 +53,9 @@ void os_getArtEui (u1_t* buf) { } void os_getDevEui (u1_t* buf) { } void os_getDevKey (u1_t* buf) { } -static uint8_t mydata[] = "Hello, world!"; -static osjob_t sendjob; - -// Schedule TX every this many seconds (might become longer due to duty +// Schedule TX every this many milliseconds (might become longer due to duty // cycle limitations). -const unsigned TX_INTERVAL = 60; +const unsigned TX_INTERVAL = 60000; // Pin mapping const lmic_pinmap lmic_pins = { @@ -108,8 +105,6 @@ void onEvent (ev_t ev) { Serial.println(LMIC.dataLen); Serial.println(F(" bytes of payload")); } - // Schedule next transmission - os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); break; case EV_LOST_TSYNC: Serial.println(F("EV_LOST_TSYNC")); @@ -133,29 +128,10 @@ void onEvent (ev_t ev) { } } -void do_send(osjob_t* j){ - // Check if there is not a current TX/RX job running - if (LMIC.opmode & OP_TXRXPEND) { - Serial.println(F("OP_TXRXPEND, not sending")); - } else { - // Prepare upstream data transmission at the next possible time. - LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); - Serial.println(F("Packet queued")); - } - // Next TX is scheduled after TX_COMPLETE event. -} - void setup() { Serial.begin(115200); Serial.println(F("Starting")); - #ifdef VCC_ENABLE - // For Pinoccio Scout boards - pinMode(VCC_ENABLE, OUTPUT); - digitalWrite(VCC_ENABLE, HIGH); - delay(1000); - #endif - // LMIC init os_init(); // Reset the MAC state. Session and pending data transfers will be discarded. @@ -217,10 +193,34 @@ void setup() { // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) LMIC_setDrTxpow(DR_SF7,14); - // Start job - do_send(&sendjob); + // Enable this to increase the receive window size, to compensate + // for an inaccurate clock. // This compensate for +/- 10% clock + // error, a lower value will likely be more appropriate. + //LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100); + + // Queue first packet + send_packet(); } +uint32_t last_packet = 0; + void loop() { + // Let LMIC handle background tasks os_runloop_once(); + + // If TX_INTERVAL passed, *and* our previous packet is not still + // pending (which can happen due to duty cycle limitations), send + // the next packet. + if (millis() - last_packet > TX_INTERVAL && !(LMIC.opmode & (OP_JOINING|OP_TXRXPEND))) + send_packet(); +} + +void send_packet(){ + // Prepare upstream data transmission at the next possible time. + uint8_t mydata[] = "Hello, world!"; + LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); + Serial.println(F("Packet queued")); + + last_packet = millis(); } + diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/ttn-otaa/ttn-otaa.ino index d667aa2..dc5cd65 100644 --- a/target/arduino/examples/ttn-otaa/ttn-otaa.ino +++ b/target/arduino/examples/ttn-otaa/ttn-otaa.ino @@ -51,12 +51,9 @@ void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} -static uint8_t mydata[] = "Hello, world!"; -static osjob_t sendjob; - -// Schedule TX every this many seconds (might become longer due to duty +// Schedule TX every this many milliseconds (might become longer due to duty // cycle limitations). -const unsigned TX_INTERVAL = 60; +const unsigned TX_INTERVAL = 60000; // Pin mapping const lmic_pinmap lmic_pins = { @@ -111,8 +108,6 @@ void onEvent (ev_t ev) { Serial.println(LMIC.dataLen); Serial.println(F(" bytes of payload")); } - // Schedule next transmission - os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send); break; case EV_LOST_TSYNC: Serial.println(F("EV_LOST_TSYNC")); @@ -136,38 +131,41 @@ void onEvent (ev_t ev) { } } -void do_send(osjob_t* j){ - // Check if there is not a current TX/RX job running - if (LMIC.opmode & OP_TXRXPEND) { - Serial.println(F("OP_TXRXPEND, not sending")); - } else { - // Prepare upstream data transmission at the next possible time. - LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); - Serial.println(F("Packet queued")); - } - // Next TX is scheduled after TX_COMPLETE event. -} - void setup() { Serial.begin(9600); Serial.println(F("Starting")); - #ifdef VCC_ENABLE - // For Pinoccio Scout boards - pinMode(VCC_ENABLE, OUTPUT); - digitalWrite(VCC_ENABLE, HIGH); - delay(1000); - #endif - // LMIC init os_init(); - // Reset the MAC state. Session and pending data transfers will be discarded. LMIC_reset(); - // Start job (sending automatically starts OTAA too) - do_send(&sendjob); + // Enable this to increase the receive window size, to compensate + // for an inaccurate clock. // This compensate for +/- 10% clock + // error, a lower value will likely be more appropriate. + //LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100); + + // Queue first packet (sending automatically starts OTAA too) + send_packet(); } +uint32_t last_packet = 0; + void loop() { + // Let LMIC handle background tasks os_runloop_once(); + + // If TX_INTERVAL passed, *and* our previous packet is not still + // pending (which can happen due to duty cycle limitations), send + // the next packet. + if (millis() - last_packet > TX_INTERVAL && !(LMIC.opmode & (OP_JOINING|OP_TXRXPEND))) + send_packet(); +} + +void send_packet(){ + // Prepare upstream data transmission at the next possible time. + uint8_t mydata[] = "Hello, world!"; + LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0); + Serial.println(F("Packet queued")); + + last_packet = millis(); } From 3ccfb5b3dbaa68d05e13049ee26503f6febecb10 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 23 May 2019 13:22:59 +0200 Subject: [PATCH 005/137] Arduino: Adapt Arduino code to basicmac Examples are untouched, those will be updated separately. --- lmic/debug.c | 4 +- lmic/region.h | 2 + target/arduino/board.h | 1 + target/arduino/export.sh | 2 + target/arduino/hal/hal.cpp | 142 ++++++++++++++++++++++++++--- target/arduino/hal/target-config.h | 65 +++++-------- target/arduino/hw.h | 45 +++++++++ target/arduino/library.properties | 8 +- 8 files changed, 206 insertions(+), 63 deletions(-) create mode 100644 target/arduino/board.h create mode 100644 target/arduino/hw.h diff --git a/lmic/debug.c b/lmic/debug.c index df0af38..a9f8550 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -4,12 +4,12 @@ // This file is subject to the terms and conditions defined in file 'LICENSE', // which is part of this source code package. +#include "lmic.h" + #ifdef CFG_DEBUG #include -#include "lmic.h" - void debug_led (int val) { hal_debug_led(val); } diff --git a/lmic/region.h b/lmic/region.h index 11495ab..27a7917 100644 --- a/lmic/region.h +++ b/lmic/region.h @@ -6,6 +6,8 @@ #ifndef _region_h_ #define _region_h_ +#include "board.h" + // public region codes - DO NOT CHANGE! enum { REGCODE_UNDEF = 0, diff --git a/target/arduino/board.h b/target/arduino/board.h new file mode 100644 index 0000000..426077a --- /dev/null +++ b/target/arduino/board.h @@ -0,0 +1 @@ +#include "../hal/target-config.h" diff --git a/target/arduino/export.sh b/target/arduino/export.sh index 6b09210..6eb16a3 100755 --- a/target/arduino/export.sh +++ b/target/arduino/export.sh @@ -66,3 +66,5 @@ $CMD "$SRC"/../../lmic "$TARGET"/src $CMD "$SRC"/hal "$TARGET"/src $CMD "$SRC"/../../aes "$TARGET"/src $CMD "$SRC"/examples "$TARGET" +$CMD "$SRC"/board.h "$TARGET"/src/lmic +$CMD "$SRC"/hw.h "$TARGET"/src/lmic diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index 53bf359..fb65405 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -37,7 +37,7 @@ static void hal_io_init () { } // val == 1 => tx 1 -void hal_pin_rxtx (u1_t val) { +void hal_pin_rxtx (s1_t val) { if (lmic_pins.rxtx != LMIC_UNUSED_PIN) digitalWrite(lmic_pins.rxtx, val); } @@ -55,6 +55,10 @@ void hal_pin_rst (u1_t val) { } } +void hal_irqmask_set (int mask) { + // Not implemented +} + static bool dio_states[NUM_DIO] = {0}; static void hal_io_check() { @@ -66,11 +70,20 @@ static void hal_io_check() { if (dio_states[i] != digitalRead(lmic_pins.dio[i])) { dio_states[i] = !dio_states[i]; if (dio_states[i]) - radio_irq_handler(i); + radio_irq_handler(i, hal_ticks()); } } } +bool hal_pin_tcxo (u1_t val) { + // Not implemented + return false; +} + +void hal_pin_busy_wait (void) { + // Not implemented +} + // ----------------------------------------------------------------------------- // SPI @@ -80,14 +93,14 @@ static void hal_spi_init () { SPI.begin(); } -void hal_pin_nss (u1_t val) { - if (!val) +void hal_spi_select (int on) { + if (on) SPI.beginTransaction(settings); else SPI.endTransaction(); //Serial.println(val?">>":"<<"); - digitalWrite(lmic_pins.nss, val); + digitalWrite(lmic_pins.nss, !on); } // perform SPI transaction with radio @@ -154,6 +167,17 @@ u4_t hal_ticks () { static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value"); } +u8_t hal_xticks (void) { + // TODO + return hal_ticks(); +} +/* Not actually used now +s2_t hal_subticks (void) { + // TODO + return 0; +} +*/ + // Returns the number of ticks until time. Negative values indicate that // time has already passed. static s4_t delta_time(u4_t time) { @@ -201,10 +225,47 @@ void hal_enableIRQs () { } } -void hal_sleep () { +u1_t hal_sleep (u1_t type, u4_t targettime) { + // Actual sleeping not implemented, but jobs are only run when this + // function returns 0, so make sure we only do that when the + // targettime is close. When asked to sleep forever (until woken up + // by an interrupt), just return immediately to keep polling. + if (type == HAL_SLEEP_FOREVER) + return 0; + + // TODO: What value should we use for "close"? + return delta_time(targettime) < 10 ? 0 : 1; +} + +void hal_watchcount (int cnt) { // Not implemented } +// ----------------------------------------------------------------------------- +// DEBUG + +#ifdef CFG_DEBUG +static void hal_debug_init() { + #ifdef LED_BUILTIN + pinMode(LED_BUILTIN, OUTPUT); + #endif +} + +#if !defined(CFG_DEBUG_STREAM) +#error "CFG_DEBUG needs CFG_DEBUG_STREAM defined in target-config.h" +#endif + +void hal_debug_str (const char* str) { + CFG_DEBUG_STREAM.print(str); +} + +void hal_debug_led (int val) { + #ifdef LED_BUILTIN + digitalWrite(LED_BUILTIN, val); + #endif +} +#endif // CFG_DEBUG + // ----------------------------------------------------------------------------- #if defined(LMIC_PRINTF_TO) @@ -227,7 +288,7 @@ void hal_printf_init() { } #endif // defined(LMIC_PRINTF_TO) -void hal_init () { +void hal_init (void *bootarg) { // configure radio I/O and interrupt handler hal_io_init(); // configure radio SPI @@ -238,16 +299,69 @@ void hal_init () { // printf support hal_printf_init(); #endif +#ifdef CFG_DEBUG + hal_debug_init(); +#endif } -void hal_failed (const char *file, u2_t line) { -#if defined(LMIC_FAILURE_TO) - LMIC_FAILURE_TO.println("FAILURE "); - LMIC_FAILURE_TO.print(file); - LMIC_FAILURE_TO.print(':'); - LMIC_FAILURE_TO.println(line); - LMIC_FAILURE_TO.flush(); +void hal_failed () { +#ifdef CFG_DEBUG + CFG_DEBUG_STREAM.flush(); #endif hal_disableIRQs(); while(1); } + +void hal_reboot (void) { + // TODO + hal_failed(); +} + +u1_t hal_getBattLevel (void) { + // Not implemented + return 0; +} + +void hal_setBattLevel (u1_t level) { + // Not implemented +} + +void hal_fwinfo (hal_fwi* fwi) { + // Not implemented +} + +u1_t* hal_joineui (void) { + return nullptr; +} + +u1_t* hal_deveui (void) { + return nullptr; +} + +u1_t* hal_nwkkey (void) { + return nullptr; +} + +u1_t* hal_appkey (void) { + return nullptr; +} + +u1_t* hal_serial (void) { + return nullptr; +} + +u4_t hal_region (void) { + return 0; +} + +u4_t hal_hwid (void) { + return 0; +} + +u4_t hal_unique (void) { + return 0; +} + +u4_t hal_dnonce_next (void) { + return 0; +} diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index 026a18c..d1a4f8c 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -4,12 +4,14 @@ #define CFG_eu868 1 //#define CFG_us915 1 +#define CFG_autojoin + // This is the SX1272/SX1273 radio, which is also used on the HopeRF // RFM92 boards. -//#define CFG_sx1272_radio 1 +//#define BRD_sx1272_radio 1 // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on // the HopeRF RFM95 boards. -#define CFG_sx1276_radio 1 +#define BRD_sx1276_radio 1 // 16 μs per tick // LMIC requires ticks to be 15.5μs - 100 μs long @@ -17,50 +19,27 @@ #define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT) #define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK) -// Enable this to allow using printf() to print to the given serial port -// (or any other Print object). This can be easy for debugging. The -// current implementation only works on AVR, though. -//#define LMIC_PRINTF_TO Serial - -// Any runtime assertion failures are printed to this serial port (or -// any other Print object). If this is unset, any failures just silently -// halt execution. -#define LMIC_FAILURE_TO Serial - -// Set this to 1 to enable some basic debug output (using printf) about -// RF settings used during transmission and reception. Set to 2 to -// enable more verbose output. Make sure that printf is actually -// configured (e.g. on AVR it is not by default), otherwise using it can -// cause crashing. -#define LMIC_DEBUG_LEVEL 0 +// When this is defined, some debug output will be printed and +// debug_printf(...) is available (which is a slightly non-standard +// printf implementation). +// Without this, assertion failures are *not* printed! +#define CFG_DEBUG +// Debug output (and assertion failures) are printed to this Stream +#define CFG_DEBUG_STREAM Serial +// Define these to add some TX or RX specific debug output +//#define DEBUG_TX +//#define DEBUG_RX +// Uncomment to display timestamps in ticks rather than milliseconds +//#define CFG_DEBUG_RAW_TIMESTAMPS + +// When this is defined, the standard libc printf function will print to +// this Stream. You should probably use CFG_DEBUG and debug_printf() +// instead, though. +//#define LMIC_PRINTF_FO -// Uncomment this to disable all code related to joining -//#define DISABLE_JOIN -// Uncomment this to disable all code related to ping -#define DISABLE_PING // Uncomment this to disable all code related to beacon tracking. // Requires ping to be disabled too -#define DISABLE_BEACONS - -// Uncomment these to disable the corresponding MAC commands. -// Class A -//#define DISABLE_MCMD_DCAP_REQ // duty cycle cap -//#define DISABLE_MCMD_DN2P_SET // 2nd DN window param -//#define DISABLE_MCMD_SNCH_REQ // set new channel -// Class B -//#define DISABLE_MCMD_PING_SET // set ping freq, automatically disabled by DISABLE_PING -//#define DISABLE_MCMD_BCNI_ANS // next beacon start, automatical disabled by DISABLE_BEACON - -// In LoRaWAN, a gateway applies I/Q inversion on TX, and nodes do the -// same on RX. This ensures that gateways can talk to nodes and vice -// versa, but gateways will not hear other gateways and nodes will not -// hear other nodes. By uncommenting this macro, this inversion is -// disabled and this node can hear other nodes. If two nodes both have -// this macro set, they can talk to each other (but they can no longer -// hear gateways). This should probably only be used when debugging -// and/or when talking to the radio directly (e.g. like in the "raw" -// example). -//#define DISABLE_INVERT_IQ_ON_RX +#define DISABLE_CLASSB // This allows choosing between multiple included AES implementations. // Make sure exactly one of these is uncommented. diff --git a/target/arduino/hw.h b/target/arduino/hw.h new file mode 100644 index 0000000..efdffe1 --- /dev/null +++ b/target/arduino/hw.h @@ -0,0 +1,45 @@ +// __ __ ___ __ __ _________________________________ +// (_ |_ |V| | |_ / |_| (C) 2018-2018 Semtech Corporation +// __)|__| | | |__\__| | All rights reserved + +#ifndef _hw_h_ +#define _hw_h_ + +/* +#define PERIPH_EEPROM + +#define EEPROM_BASE 0x30000000 +#define EEPROM_SZ (8 * 1024) +#define EEPROM_END (EEPROM_BASE + EEPROM_SZ) + +// 0x0000-0x003f 64 B : reserved for bootloader +// 0x0040-0x005f 32 B : reserved for persistent stack data +// 0x0060-0x00ff 160 B : reserved for personalization data +// 0x0100-...... : reserved for application + +#define STACKDATA_BASE (EEPROM_BASE + 0x0040) +#define PERSODATA_BASE (EEPROM_BASE + 0x0060) +#define APPDATA_BASE (EEPROM_BASE + 0x0100) + +#define STACKDATA_SZ (PERSODATA_BASE - STACKDATA_BASE) +#define PERSODATA_SZ (APPDATA_BASE - PERSODATA_BASE) +#define APPDATA_SZ (EEPROM_END - APPDATA_BASE) + + +#define FLASH_BASE 0x20000000 +#define FLASH_SZ (128 * 1024) +#define FLASH_END (FLASH_BASE + FLASH_SZ) +#define FLASH_PAGE_SZ 128 + + +#define PERIPH_USART +#define USART_BR_9600 9600 +#define USART_BR_115200 115200 + +#define PERIPH_PIO +#define PIO_IRQ_LINE(gpio) (gpio) + +#define PERIPH_CRC +#define PERIPH_SHA256 +*/ +#endif diff --git a/target/arduino/library.properties b/target/arduino/library.properties index 0dbf7d5..9ee51d9 100644 --- a/target/arduino/library.properties +++ b/target/arduino/library.properties @@ -1,9 +1,9 @@ -name=IBM LMIC framework -version=1.5.0+arduino-1 -author=IBM +name=Basicmac LoRaWAN stack +version=2.1.0 +author=Various maintainer=Matthijs Kooijman sentence=Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers category=Communication -url=http://www.research.ibm.com/labs/zurich/ics/lrsc/lmic.html +url=https://github.com/lorabasics/basicmac architectures=* From 1505722c912c8cb0cfff2e18b115f9f2c1a62d0f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 12:00:23 +0100 Subject: [PATCH 006/137] Arduino: Update examples for BasicMAC --- target/arduino/examples/ttn-abp/ttn-abp.ino | 73 ++++++++++++------- target/arduino/examples/ttn-otaa/ttn-otaa.ino | 35 +++++++-- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/ttn-abp/ttn-abp.ino index 49a27b7..9a3f582 100644 --- a/target/arduino/examples/ttn-abp/ttn-abp.ino +++ b/target/arduino/examples/ttn-abp/ttn-abp.ino @@ -49,9 +49,10 @@ static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every no // These callbacks are only used in over-the-air activation, so they are // left empty here (we cannot leave them out completely unless // DISABLE_JOIN is set in config.h, otherwise the linker will complain). -void os_getArtEui (u1_t* buf) { } +void os_getJoinEui (u1_t* buf) { } void os_getDevEui (u1_t* buf) { } -void os_getDevKey (u1_t* buf) { } +void os_getNwkKey (u1_t* buf) { } +u1_t os_getRegion (void) { return REGCODE_EU868; } // Schedule TX every this many milliseconds (might become longer due to duty // cycle limitations). @@ -65,7 +66,7 @@ const lmic_pinmap lmic_pins = { .dio = {2, 3, 4}, }; -void onEvent (ev_t ev) { +void onLmicEvent (ev_t ev) { Serial.print(os_getTime()); Serial.print(": "); switch(ev) { @@ -122,8 +123,27 @@ void onEvent (ev_t ev) { case EV_LINK_ALIVE: Serial.println(F("EV_LINK_ALIVE")); break; + case EV_SCAN_FOUND: + Serial.println(F("EV_SCAN_FOUND")); + break; + case EV_TXSTART: + Serial.println(F("EV_TXSTART,")); + break; + case EV_TXDONE: + Serial.println(F("EV_TXDONE")); + break; + case EV_DATARATE: + Serial.println(F("EV_DATARATE")); + break; + case EV_START_SCAN: + Serial.println(F("EV_START_SCAN")); + break; + case EV_ADR_BACKOFF: + Serial.println(F("EV_ADR_BACKOFF")); + break; default: - Serial.println(F("Unknown event")); + Serial.print(F("Unknown event: ")); + Serial.println(ev); break; } } @@ -133,7 +153,7 @@ void setup() { Serial.println(F("Starting")); // LMIC init - os_init(); + os_init(nullptr); // Reset the MAC state. Session and pending data transfers will be discarded. LMIC_reset(); @@ -154,6 +174,9 @@ void setup() { #endif #if defined(CFG_eu868) + // These are defined by the LoRaWAN specification + const uint8_t EU_DR_SF12 = 0, EU_DR_SF9 = 3, EU_DR_SF7 = 5, EU_DR_SF7_BW250 = 6; + // Set up the channels used by the Things Network, which corresponds // to the defaults of most gateways. Without this, only three base // channels from the LoRaWAN specification are used, which certainly @@ -162,20 +185,24 @@ void setup() { // your network here (unless your network autoconfigures them). // Setting up channels should happen after LMIC_setSession, as that // configures the minimal channel set. - // NA-US channels 0-71 are configured automatically - LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(DR_SF12, DR_SF7B), BAND_CENTI); // g-band - LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // g-band - LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(DR_FSK, DR_FSK), BAND_MILLI); // g2-band - // TTN defines an additional channel at 869.525Mhz using SF9 for class B - // devices' ping slots. LMIC does not have an easy way to define set this - // frequency and support for class B is spotty and untested, so this - // frequency is not configured here. + LMIC_setupChannel(0, 868100000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band + LMIC_setupChannel(1, 868300000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7_BW250)); // g-band + LMIC_setupChannel(2, 868500000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band + LMIC_setupChannel(3, 867100000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band + LMIC_setupChannel(4, 867300000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band + LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band + LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band + LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(FSK, FSK)); // g2-band + + // TTN uses SF9 at 869.525Mhz for its RX2 window (frequency is + // default LoRaWAN, SF is different, but set them both to be + // explicit). + LMIC.dn2Freq = 869525000; + LMIC.dn2Dr = EU_DR_SF9; + + // Set data rate for uplink + LMIC_setDrTxpow(EU_DR_SF7, KEEP_TXPOWADJ); #elif defined(CFG_us915) // NA-US channels 0-71 are configured automatically // but only one group of 8 should (a subband) should be active @@ -187,12 +214,6 @@ void setup() { // Disable link check validation LMIC_setLinkCheckMode(0); - // TTN uses SF9 for its RX2 window. - LMIC.dn2Dr = DR_SF9; - - // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library) - LMIC_setDrTxpow(DR_SF7,14); - // Enable this to increase the receive window size, to compensate // for an inaccurate clock. // This compensate for +/- 10% clock // error, a lower value will likely be more appropriate. @@ -206,7 +227,7 @@ uint32_t last_packet = 0; void loop() { // Let LMIC handle background tasks - os_runloop_once(); + os_runstep(); // If TX_INTERVAL passed, *and* our previous packet is not still // pending (which can happen due to duty cycle limitations), send diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/ttn-otaa/ttn-otaa.ino index dc5cd65..8d990c2 100644 --- a/target/arduino/examples/ttn-otaa/ttn-otaa.ino +++ b/target/arduino/examples/ttn-otaa/ttn-otaa.ino @@ -38,7 +38,7 @@ // the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, // 0x70. static const u1_t PROGMEM APPEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} +void os_getJoinEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);} // This should also be in little endian format, see above. static const u1_t PROGMEM DEVEUI[8]={ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -49,7 +49,10 @@ void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} // practice, a key taken from ttnctl can be copied as-is. // The key shown here is the semtech default key. static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; -void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} +void os_getNwkKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} + +// The region to use +u1_t os_getRegion (void) { return REGCODE_EU868; } // Schedule TX every this many milliseconds (might become longer due to duty // cycle limitations). @@ -63,7 +66,7 @@ const lmic_pinmap lmic_pins = { .dio = {2, 3, 4}, }; -void onEvent (ev_t ev) { +void onLmicEvent (ev_t ev) { Serial.print(os_getTime()); Serial.print(": "); switch(ev) { @@ -125,8 +128,28 @@ void onEvent (ev_t ev) { case EV_LINK_ALIVE: Serial.println(F("EV_LINK_ALIVE")); break; + case EV_SCAN_FOUND: + Serial.println(F("EV_SCAN_FOUND")); + break; + case EV_TXSTART: + Serial.println(F("EV_TXSTART,")); + break; + case EV_TXDONE: + Serial.println(F("EV_TXDONE")); + break; + case EV_DATARATE: + Serial.println(F("EV_DATARATE")); + break; + case EV_START_SCAN: + Serial.println(F("EV_START_SCAN")); + break; + case EV_ADR_BACKOFF: + Serial.println(F("EV_ADR_BACKOFF")); + break; + default: - Serial.println(F("Unknown event")); + Serial.print(F("Unknown event: ")); + Serial.println(ev); break; } } @@ -136,7 +159,7 @@ void setup() { Serial.println(F("Starting")); // LMIC init - os_init(); + os_init(nullptr); LMIC_reset(); // Enable this to increase the receive window size, to compensate @@ -152,7 +175,7 @@ uint32_t last_packet = 0; void loop() { // Let LMIC handle background tasks - os_runloop_once(); + os_runstep(); // If TX_INTERVAL passed, *and* our previous packet is not still // pending (which can happen due to duty cycle limitations), send From d747876c73d07fa08ad56e5f745d8b529879386b Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 23 May 2019 13:20:21 +0200 Subject: [PATCH 007/137] Move DECL_ON_LMIC_EVENT from lmic.c to lmic.h This declares the onLmicEvent() callback function that must be defined by an application. Previously, the application was expected to define this itself, and a reference to it was declared in lmic.c. By moving the declaration into lmic.h, this prevents problems when an application uses a different function signature for the function, or when defining the callback in a C++ file (since lmic.h uses extern "C", the compiler now knows to generate a C-style function rather than C++). --- lmic/lmic.c | 1 - lmic/lmic.h | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index b3436f7..2ac2ed2 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -41,7 +41,6 @@ #endif DEFINE_LMIC; -DECL_ON_LMIC_EVENT; // Fwd decls. static void engineUpdate(void); diff --git a/lmic/lmic.h b/lmic/lmic.h index becb3e3..3b7bf45 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -472,6 +472,10 @@ const char* LMIC_addr2func (void* addr); int LMIC_arr2len (const char* name); #endif +// Declare onEvent() function, to make sure any definition will have the +// C conventions, even when in a C++ file. +DECL_ON_LMIC_EVENT; + // Special APIs - for development or testing // !!!See implementation for caveats!!! #if defined(CFG_extapi) From dc99921b43bf5c4adfae34e57449495212b60a1b Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 11 Nov 2015 08:55:36 +0100 Subject: [PATCH 008/137] Fix AES encryption on 16-bit platforms The AES_S array contains 16-bit integers, which are shifted upwards during processing. On platforms where int is 32-bit or wider, these integers are integer-promoted to 32-bit before shifting, making them work as expected. On smaller platforms, this does not happen, causing the upper 16 bits to be shifted off before the value is converted to a 32-bit integer. By manually casting these values to 32-bit before shifting, this issue is prevented. --- lmic/aes.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lmic/aes.c b/lmic/aes.c index a461ac6..84ac7c0 100644 --- a/lmic/aes.c +++ b/lmic/aes.c @@ -205,10 +205,10 @@ static const u4_t AES_E4[256] = { r0 ^= AES_E1[ (i>>24)] #define AES_expr(a,r0,r1,r2,r3,i) a = ki[i]; \ - a ^= (AES_S[ r0>>24 ]<<24); \ - a ^= (AES_S[u1(r1>>16)]<<16); \ - a ^= (AES_S[u1(r2>> 8)]<< 8); \ - a ^= AES_S[u1(r3) ] + a ^= ((u4_t)AES_S[ r0>>24 ]<<24); \ + a ^= ((u4_t)AES_S[u1(r1>>16)]<<16); \ + a ^= ((u4_t)AES_S[u1(r2>> 8)]<< 8); \ + a ^= (u4_t)AES_S[u1(r3) ] // generate 1+10 roundkeys for encryption with 128-bit key // read 128-bit key from AESKEY in MSBF, generate roundkey words in place @@ -224,10 +224,10 @@ static void aesroundkeys () { for( ; i<44; i++ ) { if( i%4==0 ) { // b = SubWord(RotWord(b)) xor Rcon[i/4] - b = (AES_S[u1(b >> 16)] << 24) ^ - (AES_S[u1(b >> 8)] << 16) ^ - (AES_S[u1(b) ] << 8) ^ - (AES_S[ b >> 24 ] ) ^ + b = ((u4_t)AES_S[u1(b >> 16)] << 24) ^ + ((u4_t)AES_S[u1(b >> 8)] << 16) ^ + ((u4_t)AES_S[u1(b) ] << 8) ^ + ((u4_t)AES_S[ b >> 24 ] ) ^ AES_RCON[(i-4)/4]; } AESKEY[i] = b ^= AESKEY[i-4]; From 81de5e1fe859210435190de9b7298aa4afec8d2e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 17 Feb 2016 18:51:04 +0100 Subject: [PATCH 009/137] Fix os_rlsbf4() and os_rmsbf4() when int is smaller than 32 bits These functions convert four individiual bytes from a buffer into a 32-bit integer. The upper two bytes were cast to u4_t before being shifted in place, but not the lower bytes. It seems that this was not needed, since even when int is 16-bits, shifting a byte by 8 will not cause any bits to "fall off". However, a problem with sign-extension would occur in some cases. The problematic expression is: return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24)); Here, `buf[1]` would be integer-promoted before shifting. It is promoted to a *signed* `int`, since the original `u1_t` type is small enough to fit in there. Now, if the MSB of `buf[1]` is 1, it is then shifted to become the MSB (i.e. sign bit) of the resulting *signed* integer. Then, before the bitwise-or operator is applied, this value is extended to 32-bits, since the right operand is 32-bit. Since the left-hand side is signed, it is sign-extended, causing all upper 16 bits to become 1, making them also 1 in the resulting value. To fix this, all bytes are first explicitely cast to `u4_t` to make sure they are already big enough and remain unsigned. For consistency, the same is done for `os_rlsbf2()`, even though this same problem cannot occur there (C guarantees that an int is at least 16-bits wide). --- lmic/lmic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 2ac2ed2..c051452 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -54,20 +54,20 @@ static void startScan (void); #if !defined(os_rlsbf2) u2_t os_rlsbf2 (const u1_t* buf) { - return (u2_t)(buf[0] | (buf[1]<<8)); + return (u2_t)((u2_t)buf[0] | ((u2_t)buf[1]<<8)); } #endif #if !defined(os_rlsbf4) u4_t os_rlsbf4 (const u1_t* buf) { - return (u4_t)(buf[0] | (buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24)); + return (u4_t)((u4_t)buf[0] | ((u4_t)buf[1]<<8) | ((u4_t)buf[2]<<16) | ((u4_t)buf[3]<<24)); } #endif #if !defined(os_rmsbf4) u4_t os_rmsbf4 (const u1_t* buf) { - return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24)); + return (u4_t)((u4_t)buf[3] | ((u4_t)buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24)); } #endif From a0d1a61f93562df8f4cdf022b2d0de2a05c7cdd6 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 10 Jun 2016 16:10:31 +0200 Subject: [PATCH 010/137] Add alternative AES implementation The original LMIC AES implementation is optimized for fast execution on 32bit processors, but it ends up taking a lot of flash space on 8-bit AVR processors. This commit adds an alternative AES implementation written by Ideetron, which is a lot smaller, but also about twice as slow as the original implementation. This new implementation is selected by default, but the original can be used by modifying config.h. --- aes/aes-common.c | 145 +++++++++++++ aes/aes-ideetron.c | 341 +++++++++++++++++++++++++++++++ lmic/aes.c => aes/aes-original.c | 6 +- projects/ex-join/Makefile | 1 + projects/projects.gmk | 5 +- 5 files changed, 495 insertions(+), 3 deletions(-) create mode 100644 aes/aes-common.c create mode 100644 aes/aes-ideetron.c rename lmic/aes.c => aes/aes-original.c (99%) diff --git a/aes/aes-common.c b/aes/aes-common.c new file mode 100644 index 0000000..020a7b6 --- /dev/null +++ b/aes/aes-common.c @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2016 Matthijs Kooijman + * + * LICENSE + * + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and + * redistribution. + * + * NO WARRANTY OF ANY KIND IS PROVIDED. + *******************************************************************************/ + +/* + * The original LMIC AES implementation integrates raw AES encryption + * with CMAC and AES-CTR in a single piece of code. Most other AES + * implementations (only) offer raw single block AES encryption, so this + * file contains an implementation of CMAC and AES-CTR, and offers the + * same API through the os_aes() function as the original AES + * implementation. This file assumes that there is an encryption + * function available with this signature: + * + * extern "C" void aes_encrypt(u1_t *data, u1_t *key); + * + * That takes a single 16-byte buffer and encrypts it wit the given + * 16-byte key. + */ + +#include "../lmic/aes.h" + +#if !defined(USE_ORIGINAL_AES) + +// This should be defined elsewhere +void aes_encrypt(u1_t *data, u1_t *key); + +// global area for passing parameters (aux, key) and for storing round keys +u4_t AESAUX[16/sizeof(u4_t)]; +u4_t AESKEY[11*16/sizeof(u4_t)]; + +// Shift the given buffer left one bit +static void shift_left(u1_t *buf, u1_t len) { + while (len--) { + u1_t next = len ? buf[1] : 0; + + u1_t val = (*buf << 1); + if (next & 0x80) + val |= 1; + *buf++ = val; + } +} + +// Apply RFC4493 CMAC, using AESKEY as the key. If prepend_aux is true, +// AESAUX is prepended to the message. AESAUX is used as working memory +// in any case. The CMAC result is returned in AESAUX as well. +static void os_aes_cmac(u1_t *buf, u2_t len, u1_t prepend_aux) { + if (prepend_aux) + aes_encrypt(AESaux, AESkey); + else + memset (AESaux, 0, 16); + + while (len > 0) { + u1_t need_padding = 0; + for (u1_t i = 0; i < 16; ++i, ++buf, --len) { + if (len == 0) { + // The message is padded with 0x80 and then zeroes. + // Since zeroes are no-op for xor, we can just skip them + // and leave AESAUX unchanged for them. + AESaux[i] ^= 0x80; + need_padding = 1; + break; + } + AESaux[i] ^= *buf; + } + + if (len == 0) { + // Final block, xor with K1 or K2. K1 and K2 are calculated + // by encrypting the all-zeroes block and then applying some + // shifts and xor on that. + u1_t final_key[16]; + memset(final_key, 0, sizeof(final_key)); + aes_encrypt(final_key, AESkey); + + // Calculate K1 + u1_t msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + + // If the final block was not complete, calculate K2 from K1 + if (need_padding) { + msb = final_key[0] & 0x80; + shift_left(final_key, sizeof(final_key)); + if (msb) + final_key[sizeof(final_key)-1] ^= 0x87; + } + + // Xor with K1 or K2 + for (u1_t i = 0; i < sizeof(final_key); ++i) + AESaux[i] ^= final_key[i]; + } + + aes_encrypt(AESaux, AESkey); + } +} + +// Run AES-CTR using the key in AESKEY and using AESAUX as the +// counter block. The last byte of the counter block will be incremented +// for every block. The given buffer will be encrypted in place. +static void os_aes_ctr (u1_t *buf, u2_t len) { + u1_t ctr[16]; + while (len) { + // Encrypt the counter block with the selected key + memcpy(ctr, AESaux, sizeof(ctr)); + aes_encrypt(ctr, AESkey); + + // Xor the payload with the resulting ciphertext + for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) + *buf ^= ctr[i]; + + // Increment the block index byte + AESaux[15]++; + } +} + +u4_t os_aes (u1_t mode, u1_t *buf, u2_t len) { + switch (mode & ~AES_MICNOAUX) { + case AES_MIC: + os_aes_cmac(buf, len, /* prepend_aux */ !(mode & AES_MICNOAUX)); + return os_rmsbf4(AESaux); + + case AES_ENC: + // TODO: Check / handle when len is not a multiple of 16 + for (u1_t i = 0; i < len; i += 16) + aes_encrypt(buf+i, AESkey); + break; + + case AES_CTR: + os_aes_ctr(buf, len); + break; + } + return 0; +} + +#endif // !defined(USE_ORIGINAL_AES) diff --git a/aes/aes-ideetron.c b/aes/aes-ideetron.c new file mode 100644 index 0000000..afa74d0 --- /dev/null +++ b/aes/aes-ideetron.c @@ -0,0 +1,341 @@ +/****************************************************************************************** +#if defined(USE_IDEETRON_AES) +* Copyright 2015, 2016 Ideetron B.V. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +******************************************************************************************/ +/****************************************************************************************** +* +* File: AES-128_V10.cpp +* Author: Gerben den Hartog +* Compagny: Ideetron B.V. +* Website: http://www.ideetron.nl/LoRa +* E-mail: info@ideetron.nl +******************************************************************************************/ +/**************************************************************************************** +* +* Created on: 20-10-2015 +* Supported Hardware: ID150119-02 Nexus board with RFM95 +* +* Firmware Version 1.0 +* First version +****************************************************************************************/ + +// This file was taken from +// https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for +// use with LMIC. It was only cosmetically modified: +// - AES_Encrypt was renamed to aes_encrypt. +// - All other functions and variables were made static +// - Tabs were converted to 2 spaces +// - An #include and #if guard was added + +#include "../lmic/oslmic.h" + +#if defined(USE_IDEETRON_AES) + +/* +******************************************************************************************** +* Global Variables +******************************************************************************************** +*/ + +static unsigned char State[4][4]; + +static unsigned char S_Table[16][16] = { + {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76}, + {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0}, + {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15}, + {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75}, + {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84}, + {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF}, + {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8}, + {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2}, + {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73}, + {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB}, + {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79}, + {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08}, + {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A}, + {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E}, + {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF}, + {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} +}; + +void aes_encrypt(unsigned char *Data, unsigned char *Key); +static void AES_Add_Round_Key(unsigned char *Round_Key); +static unsigned char AES_Sub_Byte(unsigned char Byte); +static void AES_Shift_Rows(); +static void AES_Mix_Collums(); +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); +static void Send_State(); + +/* +***************************************************************************************** +* Description : Function for encrypting data using AES-128 +* +* Arguments : *Data Data to encrypt is a 16 byte long arry +* *Key Key to encrypt data with is a 16 byte long arry +***************************************************************************************** +*/ +void aes_encrypt(unsigned char *Data, unsigned char *Key) +{ + unsigned char i; + unsigned char Row,Collum; + unsigned char Round = 0x00; + unsigned char Round_Key[16]; + + //Copy input to State arry + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = Data[Row + (4*Collum)]; + } + } + + //Copy key to round key + for(i = 0; i < 16; i++) + { + Round_Key[i] = Key[i]; + } + + //Add round key + AES_Add_Round_Key(Round_Key); + + //Preform 9 full rounds + for(Round = 1; Round < 10; Round++) + { + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Preform Row Shift + AES_Shift_Rows(); + + //Mix Collums + AES_Mix_Collums(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round key + AES_Add_Round_Key(Round_Key); + } + + //Last round whitout mix collums + //Preform Byte substitution with S table + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]); + } + } + + //Shift rows + AES_Shift_Rows(); + + //Calculate new round key + AES_Calculate_Round_Key(Round,Round_Key); + + //Add round Key + AES_Add_Round_Key(Round_Key); + + //Copy the State into the data array + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + Data[Row + (4*Collum)] = State[Row][Collum]; + } + } + +} + +/* +***************************************************************************************** +* Description : Function that add's the round key for the current round +* +* Arguments : *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Add_Round_Key(unsigned char *Round_Key) +{ + unsigned char Row,Collum; + + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)]; + } + } +} + +/* +***************************************************************************************** +* Description : Function that substitutes a byte with a byte from the S_Table +* +* Arguments : Byte The byte that will be substituted +* +* Return : The return is the found byte in the S_Table +***************************************************************************************** +*/ +static unsigned char AES_Sub_Byte(unsigned char Byte) +{ + unsigned char S_Row,S_Collum; + unsigned char S_Byte; + + //Split byte up in Row and Collum + S_Row = ((Byte >> 4) & 0x0F); + S_Collum = (Byte & 0x0F); + + //Find the correct byte in the S_Table + S_Byte = S_Table[S_Row][S_Collum]; + + return S_Byte; +} + +/* +***************************************************************************************** +* Description : Function that preforms the shift row operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Shift_Rows() +{ + unsigned char Buffer; + + //Row 0 doesn't change + + //Shift Row 1 one left + //Store firt byte in buffer + Buffer = State[1][0]; + //Shift all bytes + State[1][0] = State[1][1]; + State[1][1] = State[1][2]; + State[1][2] = State[1][3]; + State[1][3] = Buffer; + + //Shift row 2 two left + Buffer = State[2][0]; + State[2][0] = State[2][2]; + State[2][2] = Buffer; + Buffer = State[2][1]; + State[2][1] = State[2][3]; + State[2][3] = Buffer; + + //Shift row 3 three left + Buffer = State[3][3]; + State[3][3] = State[3][2]; + State[3][2] = State[3][1]; + State[3][1] = State[3][0]; + State[3][0] = Buffer; +} + +/* +***************************************************************************************** +* Description : Function that preforms the Mix Collums operation described in the AES standard +***************************************************************************************** +*/ +static void AES_Mix_Collums() +{ + unsigned char Row,Collum; + unsigned char a[4], b[4]; + for(Collum = 0; Collum < 4; Collum++) + { + for(Row = 0; Row < 4; Row++) + { + a[Row] = State[Row][Collum]; + b[Row] = (State[Row][Collum] << 1); + + if((State[Row][Collum] & 0x80) == 0x80) + { + b[Row] = b[Row] ^ 0x1B; + } + } + State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; + State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; + State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; + State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; + } +} + +/* +***************************************************************************************** +* Description : Function that calculaties the round key for the current round +* +* Arguments : Round Number of current Round +* *Round_Key 16 byte long array holding the Round Key +***************************************************************************************** +*/ +static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key) +{ + unsigned char i,j; + unsigned char b; + unsigned char Temp[4]; + unsigned char Buffer; + unsigned char Rcon; + + //Calculate first Temp + //Copy laste byte from previous key + for(i = 0; i < 4; i++) + { + Temp[i] = Round_Key[i+12]; + } + + //Rotate Temp + Buffer = Temp[0]; + Temp[0] = Temp[1]; + Temp[1] = Temp[2]; + Temp[2] = Temp[3]; + Temp[3] = Buffer; + + //Substitute Temp + for(i = 0; i < 4; i++) + { + Temp[i] = AES_Sub_Byte(Temp[i]); + } + + //Calculate Rcon + Rcon = 0x01; + while(Round != 1) + { + b = Rcon & 0x80; + Rcon = Rcon << 1; + if(b == 0x80) + { + Rcon = Rcon ^ 0x1b; + } + Round--; + } + + //XOR Rcon + Temp[0] = Temp[0] ^ Rcon; + + //Calculate new key + for(i = 0; i < 4; i++) + { + for(j = 0; j < 4; j++) + { + Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j]; + Temp[j] = Round_Key[j + (4*i)]; + } + } +} + +#endif // defined(USE_IDEETRON_AES) diff --git a/lmic/aes.c b/aes/aes-original.c similarity index 99% rename from lmic/aes.c rename to aes/aes-original.c index 84ac7c0..7d7e9a3 100644 --- a/lmic/aes.c +++ b/aes/aes-original.c @@ -4,7 +4,9 @@ // This file is subject to the terms and conditions defined in file 'LICENSE', // which is part of this source code package. -#include "aes.h" +#if defined(USE_ORIGINAL_AES) + +#include "../lmic/aes.h" // global area for passing parameters (aux, key) and for storing round keys @@ -376,3 +378,5 @@ u4_t os_aes (u1_t mode, u1_t* buf, u2_t len) { #pragma GCC diagnostic pop #endif + +#endif // defined(USE_ORIGINAL_AES) diff --git a/projects/ex-join/Makefile b/projects/ex-join/Makefile index 9608bc6..3974ef7 100644 --- a/projects/ex-join/Makefile +++ b/projects/ex-join/Makefile @@ -17,6 +17,7 @@ SVCS += app DEFS += -DDEBUG_RX DEFS += -DDEBUG_TX +DEFS += -DUSE_IDEETRON_AES LMICCFG += eeprom_keys LMICCFG += DEBUG diff --git a/projects/projects.gmk b/projects/projects.gmk index 47d8477..1e0f6e7 100644 --- a/projects/projects.gmk +++ b/projects/projects.gmk @@ -14,6 +14,7 @@ include $(TOPDIR)/projects/platform.mk #TARGET ?= $(error "TARGET not specified (See platform.gmk for possible settings)") LMICDIR := $(TOPDIR)/lmic +AESDIR := $(TOPDIR)/aes TOOLSDIR := $(TOPDIR)/tools COMMONDIR := $(TOPDIR)/common SVCSDIR := $(TOPDIR)/services @@ -46,9 +47,9 @@ else PATCHFLAGS += -v endif -SRCS = $(notdir $(wildcard $(LMICDIR)/*.c $(HALDIR)/*.c *.c $(HALDIR)/*.S)) +SRCS = $(notdir $(wildcard $(LMICDIR)/*.c $(AESDIR)/*.c $(HALDIR)/*.c *.c $(HALDIR)/*.S)) -VPATH += $(LMICDIR) $(HALDIR) +VPATH += $(LMICDIR) $(AESDIR) $(HALDIR) ifneq ($(COMMON),) SRCS += $(COMMON) From 88c1220364ddfc70a5061e1e0cc86a63513b8e2f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 4 Feb 2016 17:53:49 +0100 Subject: [PATCH 011/137] Allow disabling class B suport Disabling this shrinks the code by a fair bit, so this is interesting for low-memory applications that do not need class B. This can also disable processing of some MAC commands. They are completely not recognized anymore, which should be ok since these commands should never be received at all. If they are received, this will break processing of subsequent commands, though. --- lmic/lmic.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- lmic/lmic.h | 14 +++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index c051452..3bf4ec2 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -562,6 +562,7 @@ static ostime_t dr2hsym (dr_t dr, s1_t num) { return us2osticks(us); } +#if !defined(DISABLE_CLASSB) static ostime_t calcRxWindow (u1_t secs, dr_t dr) { ostime_t rxoff, err; @@ -605,6 +606,7 @@ static void calcBcnRxWindowFromMillis (u1_t ms, bit_t ini) { LMIC.bcnRxsyms = wsyms; LMIC.bcnRxtime = LMIC.bcninfo.txtime + BCN_INTV_osticks + cpre - whspan; } +#endif static void iniRxdErr () { // Avg(rxdErrs) == 0 @@ -656,6 +658,7 @@ static void adjustByRxdErr (u1_t rxdelay, u1_t dr) { } +#if !defined(DISABLE_CLASSB) // Setup scheduled RX window (ping/multicast slot) static void rxschedInit (rxsched_t* rxsched) { // Relates to the standard in the following way: @@ -694,6 +697,7 @@ static bit_t rxschedNext (rxsched_t* rxsched, ostime_t cando) { rxsched->rxsyms = LMIC.rxsyms; goto again; } +#endif static ostime_t rndDelay (u1_t secSpan) { @@ -731,6 +735,7 @@ static void setDrTxpow (u1_t reason, dr_t dr, s1_t powadj) { } +#if !defined(DISABLE_CLASSB) void LMIC_stopPingable (void) { LMIC.opmode &= ~(OP_PINGABLE|OP_PINGINI); } @@ -752,6 +757,7 @@ u1_t LMIC_setPingable (u1_t intvExp) { LMIC_enableTracking(3); return 2; } +#endif static u1_t selectRandomChnl (u2_t map, u1_t nbits) { @@ -1029,11 +1035,13 @@ static ostime_t nextTx_dyn (ostime_t now) { return (ostime_t) txavail; } +#if !defined(DISABLE_CLASSB) static void setBcnRxParams_dyn (void) { LMIC.dataLen = 0; LMIC.freq = LMIC.bcnFreq ? LMIC.bcnFreq : REGION.beaconFreq; LMIC.rps = setIh(setNocrc(dndr2rps(REGION.beaconDr), 1), REGION.beaconLen); } +#endif #endif // REG_DYN @@ -1290,6 +1298,7 @@ static void syncDatarate_fix () { KEEP_TXPOWADJ); } +#if !defined(DISABLE_CLASSB) static void setBcnRxParams_fix (void) { LMIC.dataLen = 0; LMIC.rps = setIh(setNocrc(dndr2rps(REGION.beaconDr),1),REGION.beaconLen); @@ -1302,6 +1311,7 @@ static void setBcnRxParams_fix (void) { // US915/AU915 LMIC.freq = (LMIC.bcnFreq ? LMIC.bcnFreq : REGION.baseFreqDn + LMIC.bcnChnl * DNCHSPACING_500kHz); } +#endif static ostime_t nextTx_fix (ostime_t now) { if( LMIC.datarate == REGION.fixDr ) { @@ -1378,11 +1388,13 @@ static void stateJustJoined (void) { LMIC.dn2Freq = REGION.rx2Freq; LMIC.gwmargin = 0; LMIC.gwcnt = 0; +#if !defined(DISABLE_CLASSB) LMIC.bcnfAns = 0; LMIC.bcnChnl = 0; LMIC.bcnFreq = 0; LMIC.ping.freq = REGION.pingFreq; LMIC.ping.dr = REGION.pingDr; +#endif #if defined(CFG_lorawan11) if( (LMIC.opts &= OPT_LORAWAN11) ) { LMIC.opts |= OPT_OPTNEG; @@ -1395,6 +1407,7 @@ static void stateJustJoined (void) { // Decoding frames +#if !defined(DISABLE_CLASSB) // Decode beacon - do not overwrite bcninfo unless we have a match! static int decodeBeacon (void) { u1_t* d = LMIC.frame; @@ -1419,6 +1432,7 @@ static int decodeBeacon (void) { LMIC.bcninfo.flags |= BCN_FULL; return 2; } +#endif static bit_t decodeFrame (void) { @@ -1711,6 +1725,7 @@ static bit_t decodeFrame (void) { oidx += 2; continue; } +#if !defined(DISABLE_CLASSB) case MCMD_PITV_ANS: { if( (LMIC.ping.intvExp & 0x80) ) { LMIC.ping.intvExp &= 0x7F; // clear pending bit @@ -1786,6 +1801,7 @@ static bit_t decodeFrame (void) { oidx += 4; continue; } +#endif case MCMD_ADRP_REQ: { LMIC_setLinkCheck(1 << (opts[oidx+1] >> 4), 1 << (opts[oidx+1] & 0xf)); LMIC.foptsUp[LMIC.foptsUpLen++] = MCMD_ADRP_ANS; @@ -1847,7 +1863,7 @@ static bit_t decodeFrame (void) { return 1; } - +#if !defined(DISABLE_CLASSB) static int decodeMultiCastFrame (void) { u1_t* d = LMIC.frame; u1_t hdr = d[0]; @@ -1925,6 +1941,7 @@ static int decodeMultiCastFrame (void) { } return 1; } +#endif // ================================================================================ @@ -1968,10 +1985,12 @@ static void setupRx1 (osjobcb_t func) { // Called by HAL once TX complete and delivers exact end of TX time stamp in LMIC.rxtime static void txDone (u1_t delay, osjobcb_t func) { +#if !defined(DISABLE_CLASSB) if( (LMIC.opmode & (OP_TRACK|OP_PINGABLE|OP_PINGINI)) == (OP_TRACK|OP_PINGABLE) ) { rxschedInit(&LMIC.ping); // note: reuses LMIC.frame buffer! LMIC.opmode |= OP_PINGINI; } +#endif prepareDn(); LMIC.rps = dndr2rps(LMIC.dndr); @@ -2237,12 +2256,14 @@ static void buildDataFrame (void) { end += n; } } +#if !defined(DISABLE_CLASSB) if( LMIC.ping.intvExp & 0x80 ) { // Announce ping interval - LNS hasn't acked it yet LMIC.frame[end] = MCMD_PITV_REQ; LMIC.frame[end+1] = LMIC.ping.intvExp & 0x7; end += 2; } +#endif if( LMIC.dutyCapAns ) { if( end <= OFF_DAT_OPTS + 15 - 1 ) LMIC.frame[end] = MCMD_DCAP_ANS; @@ -2283,6 +2304,7 @@ static void buildDataFrame (void) { LMIC.devsAns = 0; end += 3; } +#if !defined(DISABLE_CLASSB) if( LMIC.askForTime > 0 ) { LMIC.frame[end] = MCMD_TIME_REQ; end += 1; @@ -2293,6 +2315,7 @@ static void buildDataFrame (void) { LMIC.bcnfAns = 0; end += 2; } +#endif if( LMIC.gwmargin == 255 ) { LMIC.frame[end] = MCMD_LCHK_REQ; end += 1; @@ -2337,6 +2360,7 @@ static void buildDataFrame (void) { } +#if !defined(DISABLE_CLASSB) // Callback from HAL during scan mode or when job timer expires. static void onBcnScanRx (osjob_t* job) { // stop radio and its job @@ -2482,6 +2506,7 @@ int LMIC_track (ostime_t when) { os_setTimedCallback(&LMIC.osjob, LMIC.rxtime - RX_RAMPUP, track_start); return 1; } +#endif // ================================================================================ @@ -2536,6 +2561,7 @@ bit_t LMIC_startJoining (void) { // // ================================================================================ +#if !defined(DISABLE_CLASSB) static void processPingRx (osjob_t* osjob) { if( LMIC.dataLen != 0 ) { LMIC.txrxFlags = TXRX_PING; @@ -2547,6 +2573,7 @@ static void processPingRx (osjob_t* osjob) { // Pick next ping slot engineUpdate(); } +#endif static bit_t processDnData (void) { @@ -2603,6 +2630,7 @@ static bit_t processDnData (void) { LMIC.adrAckReq = 0; reportEvent((LMIC.opmode & OP_LINKDEAD) ? EV_LINK_DEAD : EV_ADR_BACKOFF); } +#if !defined(DISABLE_CLASSB) // If this falls to zero the NWK did not answer our MCMD_TIME_REQ commands - try full scan if( LMIC.askForTime > 0 ) { if( --LMIC.askForTime == 0 ) { @@ -2613,6 +2641,7 @@ static bit_t processDnData (void) { opmodePoll(); } } +#endif txcontinue: LMIC.opmode &= ~OP_TXRXPEND; return 1; @@ -2631,6 +2660,7 @@ static bit_t processDnData (void) { } +#if !defined(DISABLE_CLASSB) static void processBeacon (osjob_t* osjob) { ostime_t lasttx = LMIC.bcninfo.txtime; // save previous - decodeBeacon overwrites u1_t flags = LMIC.bcninfo.flags; @@ -2684,7 +2714,6 @@ static void processBeacon (osjob_t* osjob) { reportEvent(ev); } - static void startRxBcn (osjob_t* osjob) { LMIC.osjob.func = FUNC_ADDR(processBeacon); os_radio(RADIO_RX); @@ -2695,6 +2724,8 @@ static void startRxPing (osjob_t* osjob) { LMIC.osjob.func = FUNC_ADDR(processPingRx); os_radio(RADIO_RX); } +#endif + // Decide what to do next for the MAC layer of a device @@ -2715,6 +2746,7 @@ static void engineUpdate (void) { ostime_t rxtime = 0; ostime_t txbeg = 0; +#if !defined(DISABLE_CLASSB) if( (LMIC.opmode & OP_SCAN) != 0 ) { // Looking for a beacon - LMIC.bcninfo.txtime is timeout for scan // Cancel onging TX/RX transaction @@ -2741,6 +2773,7 @@ static void engineUpdate (void) { ASSERT( (ostime_t)(rxtime-now) >= 0 ); #endif } +#endif if( LMIC.pollcnt ) opmodePoll(); @@ -2758,6 +2791,7 @@ static void engineUpdate (void) { // Delayed TX or waiting for duty cycle? if( (LMIC.globalDutyRate != 0 || (LMIC.opmode & OP_RNDTX) != 0) && (txbeg - LMIC.globalDutyAvail) < 0 ) txbeg = LMIC.globalDutyAvail; +#if !defined(DISABLE_CLASSB) // If we're tracking a beacon... // then make sure TX-RX transaction is complete before beacon if( (LMIC.opmode & OP_TRACK) != 0 && @@ -2768,6 +2802,7 @@ static void engineUpdate (void) { txbeg = 0; goto checkrx; } +#endif // Earliest possible time vs overhead to setup radio if( txbeg - (now + TX_RAMPUP) <= 0 ) { // We could send right now! @@ -2826,6 +2861,7 @@ static void engineUpdate (void) { } } +#if !defined(DISABLE_CLASSB) // Are we pingable? checkrx: if( (LMIC.opmode & OP_PINGINI) != 0 ) { @@ -2858,6 +2894,7 @@ static void engineUpdate (void) { } os_setTimedCallback(&LMIC.osjob, rxtime, FUNC_ADDR(startRxBcn)); return; +#endif txdelay: if( (LMIC.clmode & CLASS_C) ) { @@ -2913,9 +2950,11 @@ void LMIC_reset_ex (u1_t regionCode) { LMIC.dn1Dly = 1; LMIC.dn2Dr = REGION.rx2Dr; // we need this for 2nd DN window of join accept LMIC.dn2Freq = REGION.rx2Freq; // ditto +#if !defined(DISABLE_CLASSB) LMIC.ping.freq = REGION.pingFreq; // defaults for ping LMIC.ping.dr = REGION.pingDr; // ditto LMIC.ping.intvExp = 8; // no ping interval ever sent up +#endif LMIC.refChnl = REG_IS_FIX() ? 0 : os_getRndU1(); // fix uses randomized hoplist starting with block0 LMIC.adrAckLimit = ADR_ACK_LIMIT; LMIC.adrAckDelay = ADR_ACK_DELAY; @@ -3182,7 +3221,9 @@ static const rfuncs_t RFUNCS_DYN = { __dyn(syncDatarate), __dyn(updateTx), __dyn(nextTx), +#if !defined(DISABLE_CLASSB) __dyn(setBcnRxParams), +#endif }; #undef __dyn #endif // REG_DYN @@ -3197,7 +3238,9 @@ static const rfuncs_t RFUNCS_FIX = { __fix(syncDatarate), __fix(updateTx), __fix(nextTx), +#if !defined(DISABLE_CLASSB) __fix(setBcnRxParams), +#endif }; #undef __fix #endif // REG_FIX diff --git a/lmic/lmic.h b/lmic/lmic.h index 3b7bf45..d33e48a 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -161,6 +161,7 @@ enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD }; enum { KEEP_TXPOWADJ = -128 }; +#if !defined(DISABLE_CLASSB) //! \internal typedef struct { u1_t dr; @@ -172,7 +173,6 @@ typedef struct { u4_t freq; } rxsched_t; - //! Parsing and tracking states of beacons. enum { BCN_NONE = 0x00, //!< No beacon received BCN_PARTIAL = 0x01, //!< Only first (common) part could be decoded (info,lat,lon invalid/previous) @@ -191,6 +191,8 @@ typedef struct { s4_t lat; //!< Lat field of last beacon (valid only if BCN_FULL set) s4_t lon; //!< Lon field of last beacon (valid only if BCN_FULL set) } bcninfo_t; +#endif + // purpose of receive window - lmic_t.rxState enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TXCW, RADIO_CCA }; @@ -381,11 +383,13 @@ struct lmic_t { u1_t opts; // negotiated protocol options #endif +#if !defined(DISABLE_CLASSB) // Class B state u1_t missedBcns; // unable to track last N beacons s1_t askForTime; // how often to ask for time //XXX:old: u1_t pingSetAns; // answer set cmd and ACK bits rxsched_t ping; // pingable setup +#endif // Public part of MAC state u1_t txCnt; @@ -394,12 +398,14 @@ struct lmic_t { u1_t dataLen; // 0 no data or zero length data, >0 byte count of data u1_t frame[MAX_LEN_FRAME]; +#if !defined(DISABLE_CLASSB) u1_t bcnfAns; // mcmd beacon freq: bit7:pending, bit0:ACK/NACK u1_t bcnChnl; u4_t bcnFreq; // 0=default, !=0: specific BCN freq/no hopping u1_t bcnRxsyms; // ostime_t bcnRxtime; bcninfo_t bcninfo; // Last received beacon info +#endif u1_t noRXIQinversion; @@ -439,16 +445,22 @@ void LMIC_setTxData (void); int LMIC_setTxData2 (u1_t port, u1_t* data, u1_t dlen, u1_t confirmed); void LMIC_sendAlive (void); +#if !defined(DISABLE_CLASSB) u1_t LMIC_enableTracking (u1_t tryBcnInfo); void LMIC_disableTracking (void); +#endif void LMIC_setClassC (u1_t enabled); +#if !defined(DISABLE_CLASSB) void LMIC_stopPingable (void); u1_t LMIC_setPingable (u1_t intvExp); +#endif void LMIC_tryRejoin (void); +#if !defined(DISABLE_CLASSB) int LMIC_scan (ostime_t timeout); int LMIC_track (ostime_t when); +#endif void LMIC_setMultiCastSession (devaddr_t grpaddr, const u1_t* nwkKeyDn, const u1_t* appKey, u4_t seqnoAdn); void LMIC_setSession (u4_t netid, devaddr_t devaddr, const u1_t* nwkKey, From edd69248a84ab92eda1299b8e2c2f565bab3b689 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 10:56:15 +0100 Subject: [PATCH 012/137] Add extern "C" blocks around header files when using C++ This allows the .h files to be included from C++ code directly, without having to worry about calling conventions and name mangling. --- lmic/aes.h | 8 ++++++++ lmic/hal.h | 8 ++++++++ lmic/lce.h | 8 ++++++++ lmic/lmic.h | 8 ++++++++ lmic/lorabase.h | 7 +++++++ lmic/oslmic.h | 8 ++++++++ 6 files changed, 47 insertions(+) diff --git a/lmic/aes.h b/lmic/aes.h index 131981c..9c19dd0 100644 --- a/lmic/aes.h +++ b/lmic/aes.h @@ -8,6 +8,10 @@ #include "oslmic.h" +#ifdef __cplusplus +extern "C"{ +#endif + // ====================================================================== // AES support // !!Keep in sync with lorabase.hpp!! @@ -28,4 +32,8 @@ extern u1_t* AESaux; u4_t os_aes (u1_t mode, u1_t* buf, u2_t len); #endif +#ifdef __cplusplus +} // extern "C" +#endif + #endif // _aes_h_ diff --git a/lmic/hal.h b/lmic/hal.h index e2f1af5..5a82106 100644 --- a/lmic/hal.h +++ b/lmic/hal.h @@ -7,6 +7,10 @@ #ifndef _hal_hpp_ #define _hal_hpp_ +#ifdef __cplusplus +extern "C"{ +#endif + #ifdef HAL_IMPL_INC #include HAL_IMPL_INC #endif @@ -150,4 +154,8 @@ u4_t hal_dnonce_next (void); void hal_reboot (void); +#ifdef __cplusplus +} // extern "C" +#endif + #endif // _hal_hpp_ diff --git a/lmic/lce.h b/lmic/lce.h index dbb1898..d7257ad 100644 --- a/lmic/lce.h +++ b/lmic/lce.h @@ -8,6 +8,10 @@ #include "oslmic.h" +#ifdef __cplusplus +extern "C"{ +#endif + // Some keyids: #define LCE_APPSKEY (-2) #define LCE_NWKSKEY (-1) @@ -44,4 +48,8 @@ typedef struct lce_ctx { } lce_ctx_t; +#ifdef __cplusplus +} // extern "C" +#endif + #endif // _lce_h_ diff --git a/lmic/lmic.h b/lmic/lmic.h index d33e48a..41f0519 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -15,6 +15,10 @@ #include "lorabase.h" #include "lce.h" +#ifdef __cplusplus +extern "C"{ +#endif + // LMIC version #define LMIC_VERSION_MAJOR 2 #define LMIC_VERSION_MINOR 1 @@ -510,4 +514,8 @@ void LMIC_getRxdErrInfo (s4_t* skew, u4_t* span); #define TRACE_ADDR(a) #endif +#ifdef __cplusplus +} // extern "C" +#endif + #endif // _lmic_h_ diff --git a/lmic/lorabase.h b/lmic/lorabase.h index cca23ce..f8c0520 100644 --- a/lmic/lorabase.h +++ b/lmic/lorabase.h @@ -7,6 +7,10 @@ #ifndef _lorabase_h_ #define _lorabase_h_ +#ifdef __cplusplus +extern "C"{ +#endif + enum _cr_t { CR_4_5=0, CR_4_6, CR_4_7, CR_4_8 }; enum _sf_t { FSK=0, SF7, SF8, SF9, SF10, SF11, SF12, SFrfu }; enum _bw_t { BW125=0, BW250, BW500, BWrfu }; @@ -320,5 +324,8 @@ ostime_t calcAirTime (rps_t rps, u1_t plen); // Sensitivity at given SF/BW int getSensitivity (rps_t rps); +#ifdef __cplusplus +} // extern "C" +#endif #endif // _lorabase_h_ diff --git a/lmic/oslmic.h b/lmic/oslmic.h index 3b74c5e..dddd876 100644 --- a/lmic/oslmic.h +++ b/lmic/oslmic.h @@ -52,6 +52,10 @@ typedef const char* str_t; #define ASSERT(cond) do { } while (0) #endif +#ifdef __cplusplus +extern "C"{ +#endif + #define os_minmax(vmin,v,vmax) ((vmin)>(v)?(vmin):(vmax)<(v)?(vmax):(v)) #define os_clearMem(a,b) memset(a,0,b) #define os_copyMem(a,b,c) memcpy(a,b,c) @@ -332,4 +336,8 @@ void radio_cca (void); #define TX_ERP_ADJ 0 #endif +#ifdef __cplusplus +} // extern "C" +#endif + #endif // _oslmic_h_ From 48388d533484008b6b9f3ff4db22421c427691ac Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 23 May 2019 18:22:09 +0200 Subject: [PATCH 013/137] Fix compilation for SX1272 The `LORA_RXSTART_FIXUP` macro was not defined for this board, breaking compilation. This just defines it to 0, which is probably incorrect, but related values are also 0 and to be determined, so this at least fixes compilation. --- lmic/radio-sx127x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index f28212b..51bbb49 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -286,6 +286,7 @@ static const u2_t LORA_RXDONE_FIXUP_500[] = { #define RegPaDac SX1272_RegPaDac #define RegTcxo SX1272_RegTcxo #define LORA_TXDONE_FIXUP us2osticks(43) // XXX +#define LORA_RXSTART_FIXUP us2osticks(0) // XXX #define FSK_TXDONE_FIXUP us2osticks(0) // XXX #define FSK_RXDONE_FIXUP us2osticks(0) // XXX From aa24fc462b514ee46451ba4d9b00ba56583b30de Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 24 May 2019 12:44:21 +0200 Subject: [PATCH 014/137] Let printf %F accept s4_t instead of int In practice, u4_t and s4_t arguments are always passed to %F (freq mostly). On systems where int is s4_t, this works fine (the signed vs unsigned mismatch works out as long as values are smaller than 2^31). However, on systems where int is s2_t, this causes the printf arguments to run out of sync, producing garbage output and potentially infinite loops. In two cases, %F was used for a s1_t, which must now be ucast to s4_t (it would already be implicitly converted to int, so again this was not needed on systems that have int equal to s4_t). --- lmic/debug.c | 6 +++--- lmic/radio-sx126x.c | 2 +- lmic/radio-sx127x.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lmic/debug.c b/lmic/debug.c index a9f8550..360af16 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -23,10 +23,10 @@ static void btoa (char* buf, unsigned char b) { buf[1] = "0123456789ABCDEF"[b&0xF]; } -static int itoa (char* buf, unsigned int val, int base, int mindigits, int exp, int prec, char sign) { +static int itoa (char* buf, u4_t val, int base, int mindigits, int exp, int prec, char sign) { char num[33], *p = num, *b = buf; if(sign) { - if((int)val < 0) { + if((s4_t)val < 0) { val = -val; *b++ = '-'; } else if(sign != '-') { @@ -166,7 +166,7 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) } case 'F': { // signed integer and exponent as fixed-point decimal char num[33], pad = (zero && left==0) ? '0' : ' '; - int val = va_arg(arg, int); + u4_t val = va_arg(arg, u4_t); int exp = va_arg(arg, int); int len = itoa(num, val, 10, exp+2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-'); dst += strpad(dst, end - dst, num, len, width, left, pad); diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 2777df2..798f117 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -815,7 +815,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { debug_printf("RX[freq=%.1F,sf=%d,bw=%s,rssi=%d,snr=%.2F,len=%d]: %h\r\n", LMIC.freq, 6, getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), - LMIC.rssi - RSSI_OFF, LMIC.snr * 100 / SNR_SCALEUP, 2, + LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 51bbb49..03d83d8 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -1065,7 +1065,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { debug_printf("RX[freq=%.1F,sf=%d,bw=%s,rssi=%d,snr=%.2F,len=%d%s]: %h\r\n", LMIC.freq, 6, getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), - LMIC.rssi - RSSI_OFF, LMIC.snr * 100 / SNR_SCALEUP, 2, + LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, LMIC.dataLen, "", LMIC.frame, LMIC.dataLen); From d07233f6231cd74e848799929964a7709fb43048 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Apr 2020 16:36:22 +0200 Subject: [PATCH 015/137] Let debug_printf accept long modifier (e.g. %ld) This allows printing long values. On ARM, these are identical to int, but on AVR they are different, so this allows printing 32-bit values there. This is not actually used anywhere yet (the only 32-bit values used are timestamps, which are printed using %t), but this prepares for printing raw timestamps in a subsequent commit. --- lmic/debug.c | 12 +++++++++--- lmic/debug.h | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lmic/debug.c b/lmic/debug.c index 360af16..6c3ab25 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -87,14 +87,14 @@ static const char* const evnames[] = { static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) { char c, *dst = str, *end = str + size - 1; - int width, left, base, zero, space, plus, prec, sign; + int width, left, base, zero, space, plus, prec, sign, longint; while( (c = *format++) && dst < end ) { if(c != '%') { *dst++ = c; } else { // flags - width = prec = left = zero = sign = space = plus = 0; + width = prec = left = zero = sign = space = plus = longint = 0; while( (c = *format++) ) { if(c == '-') left = 1; else if(c == ' ') space = 1; @@ -125,6 +125,11 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) } } } + // length + if(c == 'l') { + c = *format++; + longint = 1; + } // conversion specifiers switch(c) { case 'c': // character @@ -160,7 +165,8 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) prec = width - 1; // have itoa() do the leading zero-padding for correct placement of sign pad = '0'; } - int len = itoa(num, va_arg(arg, int), base, prec, 0, 0, sign); + u4_t val = longint ? va_arg(arg, long) : va_arg(arg, int); + int len = itoa(num, val, base, prec, 0, 0, sign); dst += strpad(dst, end - dst, num, len, width, left, pad); break; } diff --git a/lmic/debug.h b/lmic/debug.h index 23ced6c..3ce32e9 100644 --- a/lmic/debug.h +++ b/lmic/debug.h @@ -16,6 +16,21 @@ #else +#include + +// When printing 16-bit values, the code uses %d and friends (which +// accept an `int`-sized argument). This works, because these arguments +// are integer-promoted automatically (provided int is at least 16-bits, +// but the C language requires this +// When printing 32-bit values, the code uses %ld (which accepts a +// `long`-sized argument). This only works when `long` is actually +// 32-bits. This is the case on at least ARM and AVR, but to be sure, +// check at compiletime. Since there is no portable LONG_WIDTH, we use +// LONG_MAX instead. +#if LONG_MAX != ((1 << 31) - 1) +#error "long is not exactly 32 bits, printing will fail" +#endif + // write formatted string to buffer int debug_snprintf (char *str, int size, const char *format, ...); From fde3108978f581da70eda39fa7125b14fc487595 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Apr 2020 16:52:58 +0200 Subject: [PATCH 016/137] Allow printing timestamps in raw ticks This supports a CFG_DEBUG_RAW_TIMESTAMPS define, which changes timestamps from the hh:mm:ss.mmm format to a raw tick value (which might be harder to read, but has more precisions). The default is still the millisecond format. --- lmic/debug.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lmic/debug.c b/lmic/debug.c index 6c3ab25..8b94bb9 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -196,6 +196,14 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) break; case 't': // ostime_t (hh:mm:ss.mmm, no field padding) case 'T': // osxtime_t (ddd.hh:mm:ss, no field padding) + #if defined(CFG_DEBUG_RAW_TIMESTAMPS) + if (c == 't') { + longint = 1; + base = 10; + goto numeric; + } + #endif + if (end - dst >= 12) { uint64_t t = ((c == 'T') ? va_arg(arg, uint64_t) : va_arg(arg, uint32_t)) * 1000 / OSTICKS_PER_SEC; int ms = t % 1000; From 7a60a5d9c8c2e5fd4b36c86904f7b33364a7f6f9 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 27 May 2019 08:45:45 +0200 Subject: [PATCH 017/137] Prepend all debug prints with a timestamp This uses %t for printing ostime_t values. The field width in the format string is not normally used, but added for when the "raw" ticks timestamp format is applied (with CFG_DEBUG_RAW_TIMESTAMP). --- lmic/debug.c | 2 +- lmic/debug.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lmic/debug.c b/lmic/debug.c index 8b94bb9..f62b52f 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -260,7 +260,7 @@ int debug_snprintf (char *str, int size, const char *format, ...) { return length; } -void debug_printf (char const *format, ...) { +void debug_printf_real (char const *format, ...) { char buf[256]; va_list arg; diff --git a/lmic/debug.h b/lmic/debug.h index 3ce32e9..2dc392d 100644 --- a/lmic/debug.h +++ b/lmic/debug.h @@ -35,7 +35,8 @@ int debug_snprintf (char *str, int size, const char *format, ...); // write formatted string to USART -void debug_printf (char const *format, ...); +void debug_printf_real (char const *format, ...); +#define debug_printf(format, ...) debug_printf_real("%10t: " format, os_getTime(), ## __VA_ARGS__) // write nul-terminated string to USART void debug_str (const char* str); From cfee5b8ec8a802bcda2aa407f4a834cdd63630cb Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Apr 2020 17:47:52 +0200 Subject: [PATCH 018/137] Use %ld for printing 32-bit values On e.g. ARM, %d and %ld are equivalent, but on smaller architectures like AVR, they are not, so this makes these prints work there. --- lmic/radio-sx126x.c | 6 +++--- lmic/radio-sx127x.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 798f117..c338fbb 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -640,7 +640,7 @@ static void rxfsk (bool rxcontinuous) { // busy wait until exact rx time ostime_t now = os_getTime(); if (LMIC.rxtime - now < 0) { - debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n", + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", now - LMIC.rxtime, osticks2ms(now - t0), now - t0); } hal_waitUntil(LMIC.rxtime); @@ -688,7 +688,7 @@ static void rxlora (bool rxcontinuous) { // busy wait until exact rx time ostime_t now = os_getTime(); if (LMIC.rxtime - now < 0) { - debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n", + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", now - LMIC.rxtime, osticks2ms(now - t0), now - t0); } hal_waitUntil(LMIC.rxtime); @@ -782,7 +782,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { LMIC.dataLen = 0; } else { // unexpected irq - debug_printf("UNEXPECTED RADIO IRQ %04x (after %d ticks, %.1Fms)\r\n", irqflags, irqtime - LMIC.rxtime, osticks2us(irqtime - LMIC.rxtime), 3); + debug_printf("UNEXPECTED RADIO IRQ %04x (after %ld ticks, %.1Fms)\r\n", irqflags, irqtime - LMIC.rxtime, osticks2us(irqtime - LMIC.rxtime), 3); TRACE_VAL(irqflags); ASSERT(0); } diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 03d83d8..cf64810 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -768,7 +768,7 @@ static void rxlora (bool rxcontinuous) { ostime_t rxtime = LMIC.rxtime - LORA_RXSTART_FIXUP; ostime_t now = os_getTime(); if( rxtime - now < 0 ) { - debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n", + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", now - LMIC.rxtime, osticks2ms(now - t0), now - t0); } hal_waitUntil(rxtime); @@ -855,7 +855,7 @@ static void rxfsk (bool rxcontinuous) { // busy wait until exact rx time ostime_t now = os_getTime(); if (LMIC.rxtime - now < 0) { - debug_printf("WARNING: rxtime is %d ticks in the past! (ramp-up time %d ms / %d ticks)\r\n", + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", now - LMIC.rxtime, osticks2ms(now - t0), now - t0); } hal_waitUntil(LMIC.rxtime); From e8348c9a07c354934a41f24191d395e3174ef982 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 8 Aug 2016 12:16:41 +0200 Subject: [PATCH 019/137] Add more debug output --- lmic/lmic.c | 5 +++++ lmic/radio.c | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/lmic/lmic.c b/lmic/lmic.c index 3bf4ec2..269d227 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -1440,11 +1440,13 @@ static bit_t decodeFrame (void) { u1_t hdr = d[0]; u1_t ftype = hdr & HDR_FTYPE; int dlen = LMIC.dataLen; + const char *window = (LMIC.txrxFlags & TXRX_DNW1) ? "RX1" : ((LMIC.txrxFlags & TXRX_DNW2) ? "RX2" : "Other"); if( dlen < OFF_DAT_OPTS+4 || (hdr & HDR_MAJOR) != HDR_MAJOR_V1 || (ftype != HDR_FTYPE_DADN && ftype != HDR_FTYPE_DCDN) ) { // Basic sanity checks failed norx: + debug_printf("Invalid downlink[window=%s]\r\n", window); LMIC.dataLen = 0; return 0; } @@ -1860,6 +1862,7 @@ static bit_t decodeFrame (void) { LMIC.dataBeg = poff; LMIC.dataLen = pend-poff; } + debug_printf("Received downlink[window=%s,port=%d,ack=%d]\r\n", window, port, ackup); return 1; } @@ -2089,6 +2092,7 @@ static bit_t processJoinAccept (void) { s4_t freq = rdFreq(&LMIC.frame[dlen]); if( freq > 0 ) { setupChannel_dyn(chidx, freq, 0); + debug_printf("Setup channel[idx=%d,freq=%.1F]\r\n", chidx, freq, 6); } } #endif // REG_DYN @@ -2730,6 +2734,7 @@ static void startRxPing (osjob_t* osjob) { // Decide what to do next for the MAC layer of a device static void engineUpdate (void) { + debug_printf("engineUpdate[opmode=0x%x]\r\n", LMIC.opmode); // Check for ongoing state: scan or TX/RX transaction if( (LMIC.opmode & (OP_NOENGINE|OP_TXRXPEND|OP_SHUTDOWN)) != 0 ) { return; diff --git a/lmic/radio.c b/lmic/radio.c index 6435c10..97df94b 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -119,6 +119,12 @@ void os_radio (u1_t mode) { case RADIO_RX: radio_stop(); +#ifdef DEBUG_RX + debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%s,rxtime=%.0F]\r\n", + LMIC.freq, 6, + getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), + LMIC.rxtime, 0); +#endif // set timeout for rx operation (should not happen, might be updated by radio driver) radio_set_irq_timeout(LMIC.rxtime + ms2osticks(5) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100); // receive frame at rxtime/now (wait for completion interrupt) @@ -127,6 +133,11 @@ void os_radio (u1_t mode) { case RADIO_RXON: radio_stop(); +#ifdef DEBUG_RX + debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%s]\r\n", + LMIC.freq, 6, + getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps))); +#endif // start scanning for frame now (wait for completion interrupt) radio_startrx(1); break; From 8402e340dd1998b25e5073a50ddf74dff7b52b0b Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 27 Jun 2017 15:34:00 +0200 Subject: [PATCH 020/137] Add more verbose debug output This is triggered by a CFG_DEBUG_VERBOSE define and gives more spammy debug output. This also adds some sleeping-related debug output, but this is disabled for now since the Arduino port does not do sleeping yet. The osxtime_t values (which are 64-bit) are printed as ostime_t (which are 32-bit). Printing them as 64-bit would make it harder to compare them with ostime_t values. Things might get weird or incorrect after an overflow, though. --- lmic/debug.h | 11 ++++++++++ lmic/lmic.c | 32 +++++++++++++++++++++++++++++- lmic/oslmic.c | 10 ++++++++++ target/arduino/hal/target-config.h | 2 ++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lmic/debug.h b/lmic/debug.h index 2dc392d..4bcdd5b 100644 --- a/lmic/debug.h +++ b/lmic/debug.h @@ -13,6 +13,11 @@ #define debug_printf(f,...) do { } while (0) #define debug_str(s) do { } while (0) #define debug_led(val) do { } while (0) +#define debug_verbose_printf(f,...) do { } while (0) + +#ifdef CFG_DEBUG_VERBOSE +#error CFG_DEBUG_VERBOSE requires CFG_DEBUG +#endif #else @@ -44,6 +49,12 @@ void debug_str (const char* str); // set LED state void debug_led (int val); +#ifndef CFG_DEBUG_VERBOSE +#define debug_verbose_printf(f,...) do { } while (0) +#else +#define debug_verbose_printf debug_printf +#endif + #endif #endif diff --git a/lmic/lmic.c b/lmic/lmic.c index 269d227..088a0ff 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -944,6 +944,9 @@ static void updateTx_dyn (ostime_t txbeg) { if (LMIC.globalDutyRate != 0) { LMIC.globalDutyAvail = txbeg + (airtime << LMIC.globalDutyRate); } + debug_verbose_printf("Updating info for TX at %t, airtime will be %t, frequency %.2F. Setting available time for band %u to %u\r\n", txbeg, airtime, LMIC.freq, 6, b, LMIC.dyn.bandAvail[b]); + if( LMIC.globalDutyRate != 0 ) + debug_verbose_printf("Updating global duty avail to %t\r\n", LMIC.globalDutyAvail); } // select channel, perform LBT if required @@ -968,6 +971,7 @@ static ostime_t nextTx_dyn (ostime_t now) { osxtime_t avail = getAvail(LMIC.dyn.chAvail[chnl]); // check band DC availability osxtime_t bavail = getAvail(LMIC.dyn.bandAvail[LMIC.dyn.chUpFreq[chnl] & BAND_MASK]); + debug_verbose_printf("Considering channel %u, available at %t (in band %u available at %t)\r\n", chnl, (ostime_t)avail, LMIC.dyn.chUpFreq[chnl] & BAND_MASK, (ostime_t)bavail); if( LMIC.noDC ) goto addch; if (REGION.flags & REG_PSA ) { @@ -994,6 +998,7 @@ static ostime_t nextTx_dyn (ostime_t now) { } } if (txavail == OSXTIME_MAX) { + debug_verbose_printf("No suitable channel found, trying different datarate\r\n"); // No suitable channel found - maybe there's no channel which includes current datarate syncDatarate(); drmap_t drbit2 = 1 << LMIC.datarate; @@ -1003,6 +1008,7 @@ static ostime_t nextTx_dyn (ostime_t now) { } if (cccnt) { + debug_verbose_printf("%u channels are available now\r\n", cccnt); while( cccnt ) { u1_t chnl = selectRandomChnl(ccmap, cccnt); @@ -1015,12 +1021,14 @@ static ostime_t nextTx_dyn (ostime_t now) { LMIC.rssi = REGION.ccaThreshold; os_radio(RADIO_CCA); if (LMIC.rssi >= REGION.ccaThreshold) { // channel is not available + debug_verbose_printf("Channel %u not available due to CCA\r\n", chnl); goto unavailable; } } else { // channel decision is stable (i.e. won't change even if not used immediately) LMIC.opmode &= ~OP_NEXTCHNL; // XXX - not sure if that's necessary, since we only consider channels that can be used NOW } + debug_verbose_printf("Selected channel %u (%.2F)\r\n", chnl, LMIC.dyn.chUpFreq[chnl] & ~BAND_MASK, 6); // good to go! LMIC.txChnl = chnl; return now; @@ -1032,6 +1040,7 @@ static ostime_t nextTx_dyn (ostime_t now) { txavail = os_getXTime() + ms2osticks(100); } // Earliest duty cycle expiry or earliest time a channel might be tested again + debug_verbose_printf("Channel(s) will become available at %t\r\n", (ostime_t)txavail); return (ostime_t) txavail; } @@ -1092,6 +1101,10 @@ static ostime_t nextJoinState (void) { // SF12:255, SF11:127, .., SF7:8secs : DNW2_SAFETY_ZONE+rndDelay(255>>LMIC.datarate)); #endif + if (failed) + debug_verbose_printf("Join failed\r\n"); + else + debug_verbose_printf("Scheduling next join at %t\r\n", LMIC.txend); // 1 - triggers EV_JOIN_FAILED event return failed; } @@ -1278,6 +1291,10 @@ static void updateTx_fix (ostime_t txbeg) { //XXX:BUG: this is US915/AU915 cent if( LMIC.globalDutyRate != 0 ) { LMIC.globalDutyAvail = txbeg + (airtime< 0 ) { + + debug_verbose_printf("Awaiting beacon before uplink\r\n"); + // Not enough time to complete TX-RX before beacon - postpone after beacon. // In order to avoid clustering of postponed TX right after beacon randomize start! txDelay(rxtime + BCN_RESERVE_osticks, 16); @@ -2810,6 +2838,7 @@ static void engineUpdate (void) { #endif // Earliest possible time vs overhead to setup radio if( txbeg - (now + TX_RAMPUP) <= 0 ) { + debug_verbose_printf("Ready for uplink\r\n"); // We could send right now! txbeg = now; dr_t txdr = LMIC.datarate; @@ -2850,6 +2879,7 @@ static void engineUpdate (void) { os_radio(RADIO_TX); return; } + debug_verbose_printf("Uplink delayed until %t\r\n", txbeg); // Cannot yet TX if( (LMIC.opmode & OP_TRACK) == 0 ) goto txdelay; // We don't track the beacon - nothing else to do - so wait for the time to TX diff --git a/lmic/oslmic.c b/lmic/oslmic.c index e61f15d..06c5d22 100644 --- a/lmic/oslmic.c +++ b/lmic/oslmic.c @@ -110,6 +110,7 @@ void os_setExtendedTimedCallback (osxjob_t* xjob, osxtime_t xtime, osjobcb_t cb) xjob->deadline = xtime; extendedjobcb(xjob); hal_enableIRQs(); + debug_verbose_printf("Scheduled job %u, cb %u at %t\r\n", (unsigned)xjob, (unsigned)cb, xtime); } // clear scheduled job, return 1 if job was removed @@ -117,6 +118,8 @@ int os_clearCallback (osjob_t* job) { hal_disableIRQs(); int r = unlinkjob(&OS.scheduledjobs, job); hal_enableIRQs(); + if (r) + debug_verbose_printf("Cleared job %u\r\n", (unsigned)job); return r; } @@ -147,6 +150,10 @@ void os_setTimedCallbackEx (osjob_t* job, ostime_t time, osjobcb_t cb, unsigned } *pnext = job; hal_enableIRQs(); + if (flags & OSJOB_FLAG_NOW) + debug_verbose_printf("Scheduled job %u, cb %u ASAP\r\n", (unsigned)job, (unsigned)cb); + else + debug_verbose_printf("Scheduled job %u, cb %u%s at %s%t\r\n", (unsigned)job, (unsigned)cb, flags & OSJOB_FLAG_IRQDISABLED ? " (irq disabled)" : "", flags & OSJOB_FLAG_APPROX ? "approx " : "", time); } // execute 1 job from timer or run queue, or sleep if nothing is pending @@ -155,6 +162,7 @@ void os_runstep (void) { hal_disableIRQs(); // check for runnable jobs if (OS.scheduledjobs) { + //debug_verbose_printf("Sleeping until job %u, cb %u, deadline %t\r\n", (unsigned)OS.scheduledjobs, (unsigned)OS.scheduledjobs->func, (ostime_t)OS.scheduledjobs->deadline); if (hal_sleep(OS.exact ? HAL_SLEEP_EXACT : HAL_SLEEP_APPROX, OS.scheduledjobs->deadline) == 0) { j = OS.scheduledjobs; OS.scheduledjobs = j->next; @@ -163,12 +171,14 @@ void os_runstep (void) { } } } else { // nothing pending + //debug_verbose_printf("Sleeping forever\r\n"); hal_sleep(HAL_SLEEP_FOREVER, 0); } if( j == NULL || (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) { hal_enableIRQs(); } if (j) { // run job callback + debug_verbose_printf("Running job %u, cb %u, deadline %t\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline); hal_watchcount(30); // max 60 sec j->func(j); hal_watchcount(0); diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index d1a4f8c..2c9645f 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -24,6 +24,8 @@ // printf implementation). // Without this, assertion failures are *not* printed! #define CFG_DEBUG +// When this is defined, additional debug output is printed. +//#define CFG_DEBUG_VERBOSE // Debug output (and assertion failures) are printed to this Stream #define CFG_DEBUG_STREAM Serial // Define these to add some TX or RX specific debug output From 8230d358d2b701811d26fea95ebae7e2b1ad5e87 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 27 May 2019 08:18:14 +0200 Subject: [PATCH 021/137] Support boards without reset connected This allows the HAL to indicate reset is not connected, which causes the reset to be skipped entirely. This will often work as expected, but (especially when the MCU is reset while the transceiver is working) might cause problems, of course. --- lmic/hal.h | 3 ++- lmic/radio-sx126x.c | 6 +++++- lmic/radio-sx127x.c | 12 ++++++++---- stm32/hal.c | 7 ++++++- target/arduino/hal/hal.cpp | 5 +++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lmic/hal.h b/lmic/hal.h index 5a82106..c4e47ff 100644 --- a/lmic/hal.h +++ b/lmic/hal.h @@ -38,8 +38,9 @@ bool hal_pin_tcxo (u1_t val); /* * control radio RST pin (0=low, 1=high, 2=floating) + * (return true if reset pin is connected) */ -void hal_pin_rst (u1_t val); +bool hal_pin_rst (u1_t val); /* * wait until radio BUSY pin is low diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index c338fbb..e9c0084 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -715,7 +715,11 @@ void radio_startrx (bool rxcontinuous) { // reset radio void radio_reset (void) { // drive RST pin low - hal_pin_rst(0); + bool has_reset = hal_pin_rst(0); + + // If reset is not connected, just continue and hope for the best + if (!has_reset) + return; // wait > 100us hal_waitUntil(os_getTime() + ms2osticks(1)); diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index cf64810..252b800 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -914,7 +914,14 @@ void radio_reset (void) { power_tcxo(); // drive RST pin - hal_pin_rst(RST_PIN_RESET_STATE); + bool has_reset = hal_pin_rst(RST_PIN_RESET_STATE); + + // power-down TCXO + hal_pin_tcxo(0); + + // If reset is not connected, just continue and hope for the best + if (!has_reset) + return; // wait > 100us hal_waitUntil(os_getTime() + ms2osticks(1)); @@ -925,9 +932,6 @@ void radio_reset (void) { // wait > 5ms hal_waitUntil(os_getTime() + ms2osticks(10)); - // power-down TCXO - hal_pin_tcxo(0); - // check opmode ASSERT( readReg(RegOpMode) == OPMODE_FSK_STANDBY ); } diff --git a/stm32/hal.c b/stm32/hal.c index c81a59e..9943342 100644 --- a/stm32/hal.c +++ b/stm32/hal.c @@ -770,13 +770,18 @@ void hal_pin_rxtx (s1_t val) { } // set radio RST pin to given value (or keep floating!) -void hal_pin_rst (u1_t val) { +bool hal_pin_rst (u1_t val) { + #ifdef GPIO_RST if(val == 0 || val == 1) { // drive pin SET_PIN(GPIO_RST, val); CFG_PIN(GPIO_RST, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); } else { // keep pin floating CFG_PIN(GPIO_RST, GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE); } + return true; + #else + return false; + #endif } void hal_pin_busy_wait (void) { diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index fb65405..004138c 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -43,9 +43,9 @@ void hal_pin_rxtx (s1_t val) { } // set radio RST pin to given value (or keep floating!) -void hal_pin_rst (u1_t val) { +bool hal_pin_rst (u1_t val) { if (lmic_pins.rst == LMIC_UNUSED_PIN) - return; + return false; if(val == 0 || val == 1) { // drive pin pinMode(lmic_pins.rst, OUTPUT); @@ -53,6 +53,7 @@ void hal_pin_rst (u1_t val) { } else { // keep pin floating pinMode(lmic_pins.rst, INPUT); } + return true; } void hal_irqmask_set (int mask) { From 703cdd4a896b36772c1ddfea044631d23ae61829 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 12:04:34 +0100 Subject: [PATCH 022/137] Arduino: Wait for serial on startup This allows seeing startup messages and errors on native USB boards. --- target/arduino/examples/ttn-abp/ttn-abp.ino | 7 +++++++ target/arduino/examples/ttn-otaa/ttn-otaa.ino | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/ttn-abp/ttn-abp.ino index 9a3f582..66ee519 100644 --- a/target/arduino/examples/ttn-abp/ttn-abp.ino +++ b/target/arduino/examples/ttn-abp/ttn-abp.ino @@ -150,6 +150,13 @@ void onLmicEvent (ev_t ev) { void setup() { Serial.begin(115200); + + // Wait up to 5 seconds for serial to be opened, to allow catching + // startup messages on native USB boards (that do not reset when + // serial is opened). + unsigned long start = millis(); + while (millis() - start < 5000 && !Serial); + Serial.println(F("Starting")); // LMIC init diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/ttn-otaa/ttn-otaa.ino index 8d990c2..2bdf06b 100644 --- a/target/arduino/examples/ttn-otaa/ttn-otaa.ino +++ b/target/arduino/examples/ttn-otaa/ttn-otaa.ino @@ -156,6 +156,13 @@ void onLmicEvent (ev_t ev) { void setup() { Serial.begin(9600); + + // Wait up to 5 seconds for serial to be opened, to allow catching + // startup messages on native USB boards (that do not reset when + // serial is opened). + unsigned long start = millis(); + while (millis() - start < 5000 && !Serial); + Serial.println(F("Starting")); // LMIC init From 2570d6bb509d95e3bb39a4ebacf93f684f2967ef Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 12:05:16 +0100 Subject: [PATCH 023/137] Arduino: Use 115200 for serial in otaa example This was already used for the abp example, now they are consistent again. --- target/arduino/examples/ttn-otaa/ttn-otaa.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/ttn-otaa/ttn-otaa.ino index 2bdf06b..6526904 100644 --- a/target/arduino/examples/ttn-otaa/ttn-otaa.ino +++ b/target/arduino/examples/ttn-otaa/ttn-otaa.ino @@ -155,7 +155,7 @@ void onLmicEvent (ev_t ev) { } void setup() { - Serial.begin(9600); + Serial.begin(115200); // Wait up to 5 seconds for serial to be opened, to allow catching // startup messages on native USB boards (that do not reset when From 5dacd66949a801250b54f189d23eaadd10a9155f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 31 Oct 2019 11:47:21 +0100 Subject: [PATCH 024/137] Sx126x: Enable DIO3 als TCXO control This should probably be made configurable, but for now just always enable it (since typically DIO3 is not used as anything else anyway). The voltage and timeout values were taken from other example code and should probably also be configurable. Also, the timout adds TX/RX delay, so should probably be accounted for. --- lmic/radio-sx126x.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index e9c0084..f3fb07c 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -129,6 +129,22 @@ #define IRQ_TIMEOUT (1 << 9) #define IRQ_ALL 0x3FF +// TCXO voltages (limited to VDD - 200mV) +#define TCXO_VOLTAGE1_6V 0x00 +#define TCXO_VOLTAGE1_7V 0x01 +#define TCXO_VOLTAGE1_8V 0x02 +#define TCXO_VOLTAGE2_2V 0x03 +#define TCXO_VOLTAGE2_4V 0x04 +#define TCXO_VOLTAGE2_7V 0x05 +#define TCXO_VOLTAGE3_0V 0x06 +#define TCXO_VOLTAGE3_3V 0x07 + +// XXX: These should probably be configurable +// XXX: The startup time delays TX/RX by 320*15.625=5ms, maybe switch on +// TCXO early? +#define TCXO_VOLTAGE TCXO_VOLTAGE1_7V +#define TCXO_STARTUP_TIME 320 // In multiples of 15.625μs + #define LORA_TXDONE_FIXUP us2osticks(269) // determined by lwtestapp using device pin wired to sx1301 pps... #define FSK_TXDONE_FIXUP us2osticks(0) // XXX #define FSK_RXDONE_FIXUP us2osticks(0) // XXX @@ -269,6 +285,15 @@ static void SetDIO2AsRfSwitchCtrl (uint8_t enable) { writecmd(CMD_SETDIO2ASRFSWITCHCTRL, &enable, 1); } +// use DIO3 to drive crystal enable switch +static void SetDIO3AsTcxoCtrl () { + uint32_t timeout = TCXO_STARTUP_TIME; + uint8_t voltage = TCXO_VOLTAGE1_7V; + uint8_t data[] = {voltage, (timeout >> 16) & 0xff, (timeout >> 8) & 0xff, timeout & 0xff }; + + writecmd(CMD_SETDIO3ASTCXOCTRL, data, sizeof(data)); +} + // write payload to fifo buffer at offset 0 static void WriteFifo (uint8_t *buf, uint8_t len) { static const uint8_t txrxbase[] = { 0, 0 }; @@ -524,6 +549,7 @@ void radio_sleep (void) { static void txlora (void) { SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); + SetDIO3AsTcxoCtrl(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_LORA); SetRfFrequency(LMIC.freq); @@ -549,6 +575,7 @@ static void txlora (void) { static void txfsk (void) { SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); + SetDIO3AsTcxoCtrl(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_FSK); SetRfFrequency(LMIC.freq); @@ -576,6 +603,7 @@ static void txfsk (void) { static void txcw (void) { SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); + SetDIO3AsTcxoCtrl(); SetStandby(STDBY_RC); SetRfFrequency(LMIC.freq); SetTxPower(LMIC.txpow + LMIC.txPowAdj + TX_ERP_ADJ); // bandpow + MACadj/APIadj + ERPadj @@ -608,6 +636,7 @@ static void rxfsk (bool rxcontinuous) { ostime_t t0 = os_getTime(); SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); + SetDIO3AsTcxoCtrl(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_FSK); SetRfFrequency(LMIC.freq); @@ -657,6 +686,7 @@ static void rxlora (bool rxcontinuous) { ostime_t t0 = os_getTime(); SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); + SetDIO3AsTcxoCtrl(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_LORA); SetRfFrequency(LMIC.freq); From 935d4601703ff3009fe9fe699226bc967cae994e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 31 Oct 2019 11:50:52 +0100 Subject: [PATCH 025/137] Sx126x: Remove unneeded cast This actually breaks the build in recent gcc versions. --- lmic/radio-sx126x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index f3fb07c..5cdbbe7 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -351,11 +351,11 @@ static void CalibrateImage (uint32_t freq) { uint32_t max; uint8_t freq[2]; } bands[] = { - { 430000000, 440000000, (uint8_t[]) { 0x6B, 0x6F } }, - { 470000000, 510000000, (uint8_t[]) { 0x75, 0x81 } }, - { 779000000, 787000000, (uint8_t[]) { 0xC1, 0xC5 } }, - { 863000000, 870000000, (uint8_t[]) { 0xD7, 0xDB } }, - { 902000000, 928000000, (uint8_t[]) { 0xE1, 0xE9 } }, + { 430000000, 440000000, { 0x6B, 0x6F } }, + { 470000000, 510000000, { 0x75, 0x81 } }, + { 779000000, 787000000, { 0xC1, 0xC5 } }, + { 863000000, 870000000, { 0xD7, 0xDB } }, + { 902000000, 928000000, { 0xE1, 0xE9 } }, }; for (int i = 0; i < sizeof(bands) / sizeof(bands[0]); i++) { if (freq >= bands[i].min && freq <= bands[i].max) { From 7f0239765448c7e332316d0bf141954561b2398b Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 31 Oct 2019 11:52:22 +0100 Subject: [PATCH 026/137] Arduino: Support SX126x pin configuration The SX126x chips can map all interrupts to DIO1, so no need to connect anything else. --- target/arduino/hal/hal.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index 004138c..a727a56 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -18,10 +18,21 @@ // I/O static void hal_io_init () { - // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK + uint8_t i; ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); + +#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) + //DIO0 is required, DIO1 is required for LoRa, DIO2 for FSK ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN || lmic_pins.dio[2] != LMIC_UNUSED_PIN); +#elif defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + // Only DIO1 should be specified + ASSERT(lmic_pins.dio[0] == LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[2] == LMIC_UNUSED_PIN); +#else + #error "Unknown radio type?" +#endif pinMode(lmic_pins.nss, OUTPUT); if (lmic_pins.rxtx != LMIC_UNUSED_PIN) @@ -29,11 +40,10 @@ static void hal_io_init () { if (lmic_pins.rst != LMIC_UNUSED_PIN) pinMode(lmic_pins.rst, OUTPUT); - pinMode(lmic_pins.dio[0], INPUT); - if (lmic_pins.dio[1] != LMIC_UNUSED_PIN) - pinMode(lmic_pins.dio[1], INPUT); - if (lmic_pins.dio[2] != LMIC_UNUSED_PIN) - pinMode(lmic_pins.dio[2], INPUT); + for (i = 0; i < NUM_DIO; ++i) { + if (lmic_pins.dio[i] != LMIC_UNUSED_PIN) + pinMode(lmic_pins.dio[i], INPUT); + } } // val == 1 => tx 1 From 20afaf14eb2217580f25d9c83689c6d3ba00be02 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 31 Oct 2019 11:52:53 +0100 Subject: [PATCH 027/137] Arduino: Support BUSY pin for SX126x --- target/arduino/hal/hal.cpp | 26 +++++++++++++++++++++++++- target/arduino/hal/hal.h | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index a727a56..d38795d 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -14,6 +14,12 @@ #include "hal.h" #include +// Datasheet defins typical times until busy goes low. Most are < 200us, +// except when waking up from sleep, which typically takes 3500us. Since +// we cannot know here if we are in sleep, we'll have to assume we are. +// Since 3500 is typical, not maximum, wait a bit more than that. +static unsigned long MAX_BUSY_TIME = 5000; + // ----------------------------------------------------------------------------- // I/O @@ -34,11 +40,17 @@ static void hal_io_init () { #error "Unknown radio type?" #endif +#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + ASSERT(lmic_pins.busy != LMIC_UNUSED_PIN); +#endif + pinMode(lmic_pins.nss, OUTPUT); if (lmic_pins.rxtx != LMIC_UNUSED_PIN) pinMode(lmic_pins.rxtx, OUTPUT); if (lmic_pins.rst != LMIC_UNUSED_PIN) pinMode(lmic_pins.rst, OUTPUT); + if (lmic_pins.busy != LMIC_UNUSED_PIN) + pinMode(lmic_pins.busy, INPUT); for (i = 0; i < NUM_DIO; ++i) { if (lmic_pins.dio[i] != LMIC_UNUSED_PIN) @@ -92,7 +104,19 @@ bool hal_pin_tcxo (u1_t val) { } void hal_pin_busy_wait (void) { - // Not implemented + if (lmic_pins.busy == LMIC_UNUSED_PIN) { + // TODO: We could probably keep some state so we know the chip + // is in sleep, since otherwise the delay can be much shorter. + // Also, all delays after commands (rather than waking up from + // sleep) are measured from the *end* of the previous SPI + // transaction, so we could wait shorter if we remember when + // that was. + delayMicroseconds(MAX_BUSY_TIME); + } else { + unsigned long start = micros(); + + while((micros() - start) < MAX_BUSY_TIME && digitalRead(lmic_pins.busy)) /* wait */; + } } // ----------------------------------------------------------------------------- diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h index e096569..8cbef54 100644 --- a/target/arduino/hal/hal.h +++ b/target/arduino/hal/hal.h @@ -17,6 +17,7 @@ struct lmic_pinmap { u1_t rxtx; u1_t rst; u1_t dio[NUM_DIO]; + u1_t busy; }; // Use this for any unused pins. From d27018c269a8ce1b08dd60cfbc32965899d72da3 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 31 Oct 2019 11:53:50 +0100 Subject: [PATCH 028/137] Arduino: Do not disable IRQs on hal_failed This prevents USB operation on native USB boards, which complicates subsequent uploads. --- target/arduino/hal/hal.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index d38795d..2a05dc8 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -343,7 +343,8 @@ void hal_failed () { #ifdef CFG_DEBUG CFG_DEBUG_STREAM.flush(); #endif - hal_disableIRQs(); + // keep IRQs enabled, to allow e.g. USB to continue to run and allow + // firmware uploads on boards with native USB. while(1); } From 5011e02d311921b052ea2e67e201a720dac61a9a Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 31 Oct 2019 11:55:23 +0100 Subject: [PATCH 029/137] Arduino: Use random nonces (HACK) We should really use them incrementally and save them, but for now the LoRaWAN 1.0 behaviour of random nonces should work. --- target/arduino/hal/hal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index 2a05dc8..a208d4c 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -399,5 +399,5 @@ u4_t hal_unique (void) { } u4_t hal_dnonce_next (void) { - return 0; + return os_getRndU2(); } From dc7428ce506eac7b79d5c0a1a66b07600dfa80ee Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jan 2020 14:48:58 +0100 Subject: [PATCH 030/137] Sx126x: Print warnings before disabling IRQs On some Arduino cores, printing with IRQs disabled is problematic (see e.g. https://github.com/arduino/ArduinoCore-samd/issues/472). In general, disabling interrupts for shorter amounts of time is a good idea anyway. --- lmic/radio-sx126x.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 5cdbbe7..bbd6090 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -656,6 +656,12 @@ static void rxfsk (bool rxcontinuous) { // enable IRQs in HAL hal_irqmask_set(HAL_IRQMASK_DIO1); + ostime_t now = os_getTime(); + if (!rxcontinuous && LMIC.rxtime - now < 0) { + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", + now - LMIC.rxtime, osticks2ms(now - t0), now - t0); + } + // now receive (lock interrupts only for final fine tuned rx timing...) hal_disableIRQs(); if (rxcontinuous) { // continous rx @@ -667,11 +673,6 @@ static void rxfsk (bool rxcontinuous) { } else { // single rx BACKTRACE(); // busy wait until exact rx time - ostime_t now = os_getTime(); - if (LMIC.rxtime - now < 0) { - debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", - now - LMIC.rxtime, osticks2ms(now - t0), now - t0); - } hal_waitUntil(LMIC.rxtime); // enable antenna switch for RX (and account power consumption) hal_pin_rxtx(0); @@ -705,6 +706,14 @@ static void rxlora (bool rxcontinuous) { // enable IRQs in HAL hal_irqmask_set(HAL_IRQMASK_DIO1); + ostime_t now = os_getTime(); + if (!rxcontinuous && LMIC.rxtime - now < 0) { + // Print before disabling IRQs, to work around deadlock on some + // Arduino cores that doe not really support printing without IRQs + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", + now - LMIC.rxtime, osticks2ms(now - t0), now - t0); + } + // now receive (lock interrupts only for final fine tuned rx timing...) hal_disableIRQs(); if (rxcontinuous) { // continous rx @@ -716,11 +725,6 @@ static void rxlora (bool rxcontinuous) { } else { // single rx BACKTRACE(); // busy wait until exact rx time - ostime_t now = os_getTime(); - if (LMIC.rxtime - now < 0) { - debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", - now - LMIC.rxtime, osticks2ms(now - t0), now - t0); - } hal_waitUntil(LMIC.rxtime); // enable antenna switch for RX (and account power consumption) hal_pin_rxtx(0); From bd39b0a45a8cd0415aa005f708f0b47e570ca125 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jan 2020 14:45:27 +0100 Subject: [PATCH 031/137] Scheduling: Do not print with interrupts disabled Serial printing with IRQs disabled is problematic on some Arduino cores, see e.g. https://github.com/arduino/ArduinoCore-samd/issues/472 --- lmic/oslmic.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lmic/oslmic.c b/lmic/oslmic.c index 06c5d22..0509f03 100644 --- a/lmic/oslmic.c +++ b/lmic/oslmic.c @@ -178,10 +178,18 @@ void os_runstep (void) { hal_enableIRQs(); } if (j) { // run job callback - debug_verbose_printf("Running job %u, cb %u, deadline %t\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline); + // Only print when interrupts are enabled, some Arduino cores do + // not handle printing with IRQs disabled + if( (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) { + debug_verbose_printf("Running job %u, cb %u, deadline %t\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline); + } hal_watchcount(30); // max 60 sec j->func(j); hal_watchcount(0); + // If we could not print before, at least print after + if( (j->flags & OSJOB_FLAG_IRQDISABLED) != 0) { + debug_verbose_printf("Ran job %u, cb %u, deadline %F\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline, 0); + } } } From e1c2bc54ab088906416f4bb68eb5d5a0f51ca0ce Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jan 2020 15:29:25 +0100 Subject: [PATCH 032/137] Arduino: Make libc printf work on non-AVR too BasicMAC (unlike LMIC) has its own debug_printf functions, so this is probably less necessary, but add it just in case. --- target/arduino/hal/hal.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index a208d4c..f895127 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -8,11 +8,14 @@ * This the HAL to run LMIC on top of the Arduino environment. *******************************************************************************/ +#define _GNU_SOURCE 1 // For fopencookie +// Must be first, otherwise it might have already been included without _GNU_SOURCE +#include +#undef _GNU_SOURCE #include #include #include "../lmic.h" #include "hal.h" -#include // Datasheet defins typical times until busy goes low. Most are < 200us, // except when waking up from sleep, which typically takes 3500us. Since @@ -304,6 +307,8 @@ void hal_debug_led (int val) { // ----------------------------------------------------------------------------- #if defined(LMIC_PRINTF_TO) +#if defined(__AVR__) +// On AVR, use the AVR-specific fdev_setup_stream to redirect stdout static int uart_putchar (char c, FILE *) { LMIC_PRINTF_TO.write(c) ; @@ -321,6 +326,31 @@ void hal_printf_init() { // The uart is the standard output device STDOUT. stdout = &uartout ; } +#else +// On non-AVR platforms, use the somewhat more complex "cookie"-based +// approach to custom streams. This is a GNU-specific extension to libc. +static ssize_t uart_putchar (void *, const char *buf, size_t len) { + auto res = LMIC_PRINTF_TO.write(buf, len); + // Since the C interface has no meaningful way to flush (fflush() is a + // no-op on AVR since stdio does not introduce any buffering), just flush + // every byte. + LMIC_PRINTF_TO.flush(); + return res; +} + +static cookie_io_functions_t functions = { + .read = NULL, + .write = uart_putchar, + .seek = NULL, + .close = NULL +}; + +void hal_printf_init() { + stdout = fopencookie(NULL, "w", functions); + // Disable buffering, so the callbacks get called right away + setbuf(stdout, nullptr); +} +#endif // !defined(__AVR__) #endif // defined(LMIC_PRINTF_TO) void hal_init (void *bootarg) { From 1aa3edb2d046c19b47e2e773c5fa672abc8ec961 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jan 2020 16:08:33 +0100 Subject: [PATCH 033/137] Schedule RX/TX timeout after starting RX/TX Previously, these timeouts were started before starting RX or TX. This worked normally, but when a HAL did not have access to the BUSY pin and instead resorted to conservative delays between SPI transactions, starting RX or TX would take so long that the timeout would effectively end up too short. Note that there is no HAL committed yet that supports working without a BUSY pin, but this came up in some as of yet uncommitted work on the Arduino port. Even if running without a BUSY pin will never be supported, setting the timeout like this should be fine, since in the worst case it only effectively extends the timeout by a few ms. Since the timeout should normally occur, this is fine. --- lmic/radio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lmic/radio.c b/lmic/radio.c index 97df94b..987a743 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -111,10 +111,10 @@ void os_radio (u1_t mode) { (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", LMIC.frame, LMIC.dataLen); #endif - // set timeout for tx operation (should not happen) - radio_set_irq_timeout(os_getTime() + ms2osticks(20) + LMIC_calcAirTime(LMIC.rps, LMIC.dataLen) * 110 / 100); // transmit frame now (wait for completion interrupt) radio_starttx(0); + // set timeout for tx operation (should not happen) + radio_set_irq_timeout(os_getTime() + ms2osticks(20) + LMIC_calcAirTime(LMIC.rps, LMIC.dataLen) * 110 / 100); break; case RADIO_RX: @@ -125,10 +125,10 @@ void os_radio (u1_t mode) { getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), LMIC.rxtime, 0); #endif - // set timeout for rx operation (should not happen, might be updated by radio driver) - radio_set_irq_timeout(LMIC.rxtime + ms2osticks(5) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100); // receive frame at rxtime/now (wait for completion interrupt) radio_startrx(0); + // set timeout for rx operation (should not happen, might be updated by radio driver) + radio_set_irq_timeout(LMIC.rxtime + ms2osticks(5) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100); break; case RADIO_RXON: From 931d301ae1d4234651cb842a69ce17b7c46acddf Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 10:35:56 +0100 Subject: [PATCH 034/137] Arduino: Support separate RX and TX enable pins Some radio modules have separate pins for this, which allows switching the antenna switch off entirely to save power. On boards with just a single RXTX pin, this pin can be set as TX pin, to fall back to RX mode when idle. This commit intentionally breaks compatibility with the pinmap, to force sketches to update their pinmap (also for the busy pin, since omitting a field at the end of a struct initializes it to 0, which could be a valid pin number). --- target/arduino/hal/hal.cpp | 14 +++++++++----- target/arduino/hal/hal.h | 8 +++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index f895127..ee3e753 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -48,8 +48,10 @@ static void hal_io_init () { #endif pinMode(lmic_pins.nss, OUTPUT); - if (lmic_pins.rxtx != LMIC_UNUSED_PIN) - pinMode(lmic_pins.rxtx, OUTPUT); + if (lmic_pins.tx != LMIC_UNUSED_PIN) + pinMode(lmic_pins.tx, OUTPUT); + if (lmic_pins.rx != LMIC_UNUSED_PIN) + pinMode(lmic_pins.rx, OUTPUT); if (lmic_pins.rst != LMIC_UNUSED_PIN) pinMode(lmic_pins.rst, OUTPUT); if (lmic_pins.busy != LMIC_UNUSED_PIN) @@ -61,10 +63,12 @@ static void hal_io_init () { } } -// val == 1 => tx 1 +// rx = 0, tx = 1, off = -1 void hal_pin_rxtx (s1_t val) { - if (lmic_pins.rxtx != LMIC_UNUSED_PIN) - digitalWrite(lmic_pins.rxtx, val); + if (lmic_pins.tx != LMIC_UNUSED_PIN) + digitalWrite(lmic_pins.tx, val == 1); + if (lmic_pins.rx != LMIC_UNUSED_PIN) + digitalWrite(lmic_pins.rx, val == 0); } // set radio RST pin to given value (or keep floating!) diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h index 8cbef54..9d89916 100644 --- a/target/arduino/hal/hal.h +++ b/target/arduino/hal/hal.h @@ -14,7 +14,13 @@ static const int NUM_DIO = 3; struct lmic_pinmap { u1_t nss; - u1_t rxtx; + // Written HIGH in TX mode, LOW otherwise. + // Typically used with a single RXTX switch pin. + u1_t tx; + // Written HIGH in RX mode, LOW otherwise. + // Typicaly used with separate RX/TX pins, to allow switching off + // the antenna switch completely. + u1_t rx; u1_t rst; u1_t dio[NUM_DIO]; u1_t busy; From 32114b3a9d5d2410386e8630e1413bc242db694f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 10:41:32 +0100 Subject: [PATCH 035/137] Enable interrupts on assertion failure This prevents printing with interrupts disabled, which is problematic on some Arduino cores, see e.g. https://github.com/arduino/ArduinoCore-samd/issues/472 --- lmic/oslmic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/oslmic.h b/lmic/oslmic.h index dddd876..72d2f7e 100644 --- a/lmic/oslmic.h +++ b/lmic/oslmic.h @@ -44,7 +44,7 @@ typedef const char* str_t; if(!(cond)) { fprintf(stderr, "ASSERTION FAILED: %s at %s:%d\n", \ #cond, __FILE__, __LINE__); hal_failed(); } } while (0) #elif defined(CFG_DEBUG) -#define ASSERT(cond) do { if(!(cond)) { debug_printf("%s:%d: assertion failed\r\n", __FILE__, __LINE__); hal_failed(); } } while (0) +#define ASSERT(cond) do { if(!(cond)) { hal_enableIRQs(); debug_printf("%s:%d: assertion failed\r\n", __FILE__, __LINE__); hal_failed(); } } while (0) #else #define ASSERT(cond) do { if(!(cond)) hal_failed(); } while (0) #endif From f8bee922cc10184ff17301b6c2d17e4d7d0d2d54 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 14:07:22 +0100 Subject: [PATCH 036/137] Arduino: Support controlling a TCXO pin --- target/arduino/hal/hal.cpp | 9 +++++++-- target/arduino/hal/hal.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index ee3e753..103cee4 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -56,6 +56,8 @@ static void hal_io_init () { pinMode(lmic_pins.rst, OUTPUT); if (lmic_pins.busy != LMIC_UNUSED_PIN) pinMode(lmic_pins.busy, INPUT); + if (lmic_pins.tcxo != LMIC_UNUSED_PIN) + pinMode(lmic_pins.tcxo, OUTPUT); for (i = 0; i < NUM_DIO; ++i) { if (lmic_pins.dio[i] != LMIC_UNUSED_PIN) @@ -106,8 +108,11 @@ static void hal_io_check() { } bool hal_pin_tcxo (u1_t val) { - // Not implemented - return false; + if (lmic_pins.tcxo == LMIC_UNUSED_PIN) + return false; + + digitalWrite(lmic_pins.tcxo, val); + return true; } void hal_pin_busy_wait (void) { diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h index 9d89916..8d04213 100644 --- a/target/arduino/hal/hal.h +++ b/target/arduino/hal/hal.h @@ -24,6 +24,7 @@ struct lmic_pinmap { u1_t rst; u1_t dio[NUM_DIO]; u1_t busy; + u1_t tcxo; }; // Use this for any unused pins. From 9a323b6cfe41edd2867580f3a02593a96ac39779 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 14:59:09 +0100 Subject: [PATCH 037/137] Arduino: Add pinout for some standard boards These are boards that include a LoRa module, so have a well-known pinout. These are added to a separate file, which is copied into each example directory on export, which makes it easier to change these pinmaps in a single place. This commit also adds some comments about how to add your own pinmap. --- .../standard-pinmaps.ino | 80 +++++++++++++++++++ target/arduino/examples/ttn-abp/ttn-abp.ino | 37 +++++++-- target/arduino/examples/ttn-otaa/ttn-otaa.ino | 37 +++++++-- target/arduino/export.sh | 6 ++ 4 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 target/arduino/examples-common-files/standard-pinmaps.ino diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino new file mode 100644 index 0000000..64bdc09 --- /dev/null +++ b/target/arduino/examples-common-files/standard-pinmaps.ino @@ -0,0 +1,80 @@ +// This file defines pinmaps, which tell basicmac what pins to use to +// talk to the radio. +// +// Below, a lot of different pinmaps for various standard boards are +// defined. These are used when USE_STANDARD_PINMAP is defined, +// otherwise the main sketch file should define its own pinmap. +// +// These pinmaps live in their own file, to make it easier to share them +// between example sketches. + +#if defined(USE_STANDARD_PINMAP) + +#if defined(ARDUINO_AVR_MINI) +#if !defined(BRD_sx1276_radio) +#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" +#endif +// Assume this is a Nexus board +const lmic_pinmap lmic_pins = { + .nss = 10, + // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + // RST is hardwarid to MCU reset + .rst = LMIC_UNUSED_PIN, + .dio = {4, 5, 7}, + .busy = LMIC_UNUSED_PIN, + .tcxo = LMIC_UNUSED_PIN, +}; +#elif defined(ARDUINO_MJS_V1) +#if !defined(BRD_sx1276_radio) +#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" +#endif +// https://github.com/meetjestad/mjs_pcb +const lmic_pinmap lmic_pins = { + .nss = 10, + // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + .rst = 9, + .dio = {2, 3, 4}, + .busy = LMIC_UNUSED_PIN, + .tcxo = LMIC_UNUSED_PIN, +}; +#elif defined(ADAFRUIT_FEATHER_M0) +#if !defined(BRD_sx1276_radio) +#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" +#endif +// Assume this a Feather M0 LoRa +const lmic_pinmap lmic_pins = { + .nss = 8, + // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + .rst = 4, + .dio = {3, 5, 6}, + .busy = LMIC_UNUSED_PIN, + .tcxo = LMIC_UNUSED_PIN, +}; +#elif defined(ARDUINO_STM32L4_LS200) +#if !defined(BRD_sx1262_radio) +#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" +#endif +// LacunaSpace LS200 development board +// Uses SPI bus at PC10/11/12 +// This uses E22_* constants from the board variant file +const lmic_pinmap lmic_pins = { + .nss = E22_NSS, // PD2 + // TXEN is controlled through DIO2 by the SX1262 directly + .tx = LMIC_UNUSED_PIN, + .rx = E22_RXEN, // PC4 + .rst = E22_NRST, // PA4 + .dio = {LMIC_UNUSED_PIN, E22_DIO1 /* PC7 */, LMIC_UNUSED_PIN}, + .busy = E22_BUSY, // PB12 + // TCXO is controlled through DIO3 by the SX1262 directly + .tcxo = LMIC_UNUSED_PIN, +}; +#else +#error "Unknown board, no standard pimap available. Define your own in the main sketch file." +#endif +#endif // defined(USE_STANDARD_PINMAP) diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/ttn-abp/ttn-abp.ino index 66ee519..8d5aaa7 100644 --- a/target/arduino/examples/ttn-abp/ttn-abp.ino +++ b/target/arduino/examples/ttn-abp/ttn-abp.ino @@ -58,13 +58,40 @@ u1_t os_getRegion (void) { return REGCODE_EU868; } // cycle limitations). const unsigned TX_INTERVAL = 60000; -// Pin mapping +// When this is defined, a standard pinmap from standard-pinmaps.ino +// will be used. If you need to use a custom pinmap, comment this line +// and enter the pin numbers in the lmic_pins variable below. +#define USE_STANDARD_PINMAP + +#if !defined(USE_STANDARD_PINMAP) +// All pin assignments use Arduino pin numbers (e.g. what you would pass +// to digitalWrite), or LMIC_UNUSED_PIN when a pin is not connected. const lmic_pinmap lmic_pins = { - .nss = 6, - .rxtx = LMIC_UNUSED_PIN, - .rst = 5, - .dio = {2, 3, 4}, + // NSS input pin for SPI communication (required) + .nss = 0, + // If needed, these pins control the RX/TX antenna switch (active + // high outputs). When you have both, the antenna switch can + // powerdown when unused. If you just have a RXTX pin it should + // usually be set to .tx, reverting to RX mode when idle). + // Often, the antenna switch is controlled directly by the radio + // chip, through is RXTX (SX127x) or DIO2 (SX126x) output pins. + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + // Radio reset output pin (active high for SX1276, active low for + // others). When omitted, reset is skipped which might cause problems. + .rst = 1, + // DIO input pins. + // For SX127x, LoRa needs DIO0 and DIO1, FSK needs DIO0, DIO1 and DIO2 + // For SX126x, Only DIO1 is needed (so leave DIO0 and DIO2 as LMIC_UNUSED_PIN) + .dio = {/* DIO0 */ 2, /* DIO1 */ 3, /* DIO2 */ 4}, + // Busy input pin (SX126x only). When omitted, a delay is used which might + // cause problems. + .busy = LMIC_UNUSED_PIN, + // TCXO oscillator enable output pin (active high). + // The SX126x can control the TCXO directly through its DIO3 output pin. + .tcxo = LMIC_UNUSED_PIN, }; +#endif // !defined(USE_STANDARD_PINMAP) void onLmicEvent (ev_t ev) { Serial.print(os_getTime()); diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/ttn-otaa/ttn-otaa.ino index 6526904..814b20c 100644 --- a/target/arduino/examples/ttn-otaa/ttn-otaa.ino +++ b/target/arduino/examples/ttn-otaa/ttn-otaa.ino @@ -58,13 +58,40 @@ u1_t os_getRegion (void) { return REGCODE_EU868; } // cycle limitations). const unsigned TX_INTERVAL = 60000; -// Pin mapping +// When this is defined, a standard pinmap from standard-pinmaps.ino +// will be used. If you need to use a custom pinmap, comment this line +// and enter the pin numbers in the lmic_pins variable below. +#define USE_STANDARD_PINMAP + +#if !defined(USE_STANDARD_PINMAP) +// All pin assignments use Arduino pin numbers (e.g. what you would pass +// to digitalWrite), or LMIC_UNUSED_PIN when a pin is not connected. const lmic_pinmap lmic_pins = { - .nss = 6, - .rxtx = LMIC_UNUSED_PIN, - .rst = 5, - .dio = {2, 3, 4}, + // NSS input pin for SPI communication (required) + .nss = 0, + // If needed, these pins control the RX/TX antenna switch (active + // high outputs). When you have both, the antenna switch can + // powerdown when unused. If you just have a RXTX pin it should + // usually be set to .tx, reverting to RX mode when idle). + // Often, the antenna switch is controlled directly by the radio + // chip, through is RXTX (SX127x) or DIO2 (SX126x) output pins. + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + // Radio reset output pin (active high for SX1276, active low for + // others). When omitted, reset is skipped which might cause problems. + .rst = 1, + // DIO input pins. + // For SX127x, LoRa needs DIO0 and DIO1, FSK needs DIO0, DIO1 and DIO2 + // For SX126x, Only DIO1 is needed (so leave DIO0 and DIO2 as LMIC_UNUSED_PIN) + .dio = {/* DIO0 */ 2, /* DIO1 */ 3, /* DIO2 */ 4}, + // Busy input pin (SX126x only). When omitted, a delay is used which might + // cause problems. + .busy = LMIC_UNUSED_PIN, + // TCXO oscillator enable output pin (active high). + // The SX126x can control the TCXO directly through its DIO3 output pin. + .tcxo = LMIC_UNUSED_PIN, }; +#endif // !defined(USE_STANDARD_PINMAP) void onLmicEvent (ev_t ev) { Serial.print(os_getTime()); diff --git a/target/arduino/export.sh b/target/arduino/export.sh index 6eb16a3..ea5a283 100755 --- a/target/arduino/export.sh +++ b/target/arduino/export.sh @@ -68,3 +68,9 @@ $CMD "$SRC"/../../aes "$TARGET"/src $CMD "$SRC"/examples "$TARGET" $CMD "$SRC"/board.h "$TARGET"/src/lmic $CMD "$SRC"/hw.h "$TARGET"/src/lmic + +# Then copy/link the common files (e.g. pinmap) into each example +# directory +for E in "$TARGET"/examples/*; do + $CMD "$SRC"/examples-common-files/* "$E" +done From 781f2f5d73e16b3936ecd1ef39f04ebb84214369 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 15:45:30 +0100 Subject: [PATCH 038/137] SX127x: Remove useless part from debug print This just inserted an empty string, which does not really make sense. --- lmic/radio-sx127x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 252b800..6494f20 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -1066,12 +1066,11 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { // read FIFO radio_readBuf(RegFifo, LMIC.frame, LMIC.dataLen); #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%s,rssi=%d,snr=%.2F,len=%d%s]: %h\r\n", + debug_printf("RX[freq=%.1F,sf=%d,bw=%s,rssi=%d,snr=%.2F,len=%d]: %h\r\n", LMIC.freq, 6, getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, LMIC.dataLen, - "", LMIC.frame, LMIC.dataLen); #endif } else if (irqflags & IRQ_LORA_RXTOUT_MASK) { // RXTOUT From 02d4a319ef67f0fed84c07753bed0f7716c7fc2a Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 16:52:07 +0100 Subject: [PATCH 039/137] Arduino: Fix NSS initialization Previously, the NSS pin would only be set to OUTPUT mode, causing it to become OUTPUT LOW and starting an SPI transaction already. In some cases (possibly when the chip was subsequently reset), the first SPI transaction would then fail (readReg would return 0), causing an assertion failure during initialization. This is fixed by writing the NSS pin HIGH in addition to changing to OUTPUT mode. To prevent a low pulse on the line, it is written HIGH before setting OUTPUT, but it seems not all cores support this (e.g. the arduino-STM32L4 reset to LOW when changing to OUTPUT), so write HIGH again after setting OUTPUT to be sure. --- target/arduino/hal/hal.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index 103cee4..b6e51c0 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -47,7 +47,14 @@ static void hal_io_init () { ASSERT(lmic_pins.busy != LMIC_UNUSED_PIN); #endif + // Write HIGH to deselect (NSS is active low). Do this before + // setting output, to prevent a moment of OUTPUT LOW on e.g. AVR. + digitalWrite(lmic_pins.nss, HIGH); pinMode(lmic_pins.nss, OUTPUT); + // Write HIGH again after setting output, for architectures that + // reset to LOW when setting OUTPUT (e.g. arduino-STM32L4). + digitalWrite(lmic_pins.nss, HIGH); + if (lmic_pins.tx != LMIC_UNUSED_PIN) pinMode(lmic_pins.tx, OUTPUT); if (lmic_pins.rx != LMIC_UNUSED_PIN) From f3841c0dfc939d760216688934eff44ffec780cb Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 17:19:37 +0100 Subject: [PATCH 040/137] Arduino: Remove raw.ino example This example was not updated to BasicMAC yet, and already for LMIC it was not really using the library properly. Since it is likely only confusing for people, better remove it. --- target/arduino/examples/raw/raw.ino | 162 ---------------------------- 1 file changed, 162 deletions(-) delete mode 100644 target/arduino/examples/raw/raw.ino diff --git a/target/arduino/examples/raw/raw.ino b/target/arduino/examples/raw/raw.ino deleted file mode 100644 index 1e0382f..0000000 --- a/target/arduino/examples/raw/raw.ino +++ /dev/null @@ -1,162 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Matthijs Kooijman - * - * Permission is hereby granted, free of charge, to anyone - * obtaining a copy of this document and accompanying files, - * to do whatever they want with them without any restriction, - * including, but not limited to, copying, modification and redistribution. - * NO WARRANTY OF ANY KIND IS PROVIDED. - * - * This example transmits data on hardcoded channel and receives data - * when not transmitting. Running this sketch on two nodes should allow - * them to communicate. - *******************************************************************************/ - -#include -#include -#include - -#if !defined(DISABLE_INVERT_IQ_ON_RX) -#error This example requires DISABLE_INVERT_IQ_ON_RX to be set. Update \ - config.h in the lmic library to set it. -#endif - -// How often to send a packet. Note that this sketch bypasses the normal -// LMIC duty cycle limiting, so when you change anything in this sketch -// (payload length, frequency, spreading factor), be sure to check if -// this interval should not also be increased. -// See this spreadsheet for an easy airtime and duty cycle calculator: -// https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc -#define TX_INTERVAL 2000 - -// Pin mapping -const lmic_pinmap lmic_pins = { - .nss = 6, - .rxtx = LMIC_UNUSED_PIN, - .rst = 5, - .dio = {2, 3, 4}, -}; - - -// These callbacks are only used in over-the-air activation, so they are -// left empty here (we cannot leave them out completely unless -// DISABLE_JOIN is set in config.h, otherwise the linker will complain). -void os_getArtEui (u1_t* buf) { } -void os_getDevEui (u1_t* buf) { } -void os_getDevKey (u1_t* buf) { } - -void onEvent (ev_t ev) { -} - -osjob_t txjob; -osjob_t timeoutjob; -static void tx_func (osjob_t* job); - -// Transmit the given string and call the given function afterwards -void tx(const char *str, osjobcb_t func) { - os_radio(RADIO_RST); // Stop RX first - delay(1); // Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet - LMIC.dataLen = 0; - while (*str) - LMIC.frame[LMIC.dataLen++] = *str++; - LMIC.osjob.func = func; - os_radio(RADIO_TX); - Serial.println("TX"); -} - -// Enable rx mode and call func when a packet is received -void rx(osjobcb_t func) { - LMIC.osjob.func = func; - LMIC.rxtime = os_getTime(); // RX _now_ - // Enable "continuous" RX (e.g. without a timeout, still stops after - // receiving a packet) - os_radio(RADIO_RXON); - Serial.println("RX"); -} - -static void rxtimeout_func(osjob_t *job) { - digitalWrite(LED_BUILTIN, LOW); // off -} - -static void rx_func (osjob_t* job) { - // Blink once to confirm reception and then keep the led on - digitalWrite(LED_BUILTIN, LOW); // off - delay(10); - digitalWrite(LED_BUILTIN, HIGH); // on - - // Timeout RX (i.e. update led status) after 3 periods without RX - os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*TX_INTERVAL), rxtimeout_func); - - // Reschedule TX so that it should not collide with the other side's - // next TX - os_setTimedCallback(&txjob, os_getTime() + ms2osticks(TX_INTERVAL/2), tx_func); - - Serial.print("Got "); - Serial.print(LMIC.dataLen); - Serial.println(" bytes"); - Serial.write(LMIC.frame, LMIC.dataLen); - Serial.println(); - - // Restart RX - rx(rx_func); -} - -static void txdone_func (osjob_t* job) { - rx(rx_func); -} - -// log text to USART and toggle LED -static void tx_func (osjob_t* job) { - // say hello - tx("Hello, world!", txdone_func); - // reschedule job every TX_INTERVAL (plus a bit of random to prevent - // systematic collisions), unless packets are received, then rx_func - // will reschedule at half this time. - os_setTimedCallback(job, os_getTime() + ms2osticks(TX_INTERVAL + random(500)), tx_func); -} - -// application entry point -void setup() { - Serial.begin(115200); - Serial.println("Starting"); - #ifdef VCC_ENABLE - // For Pinoccio Scout boards - pinMode(VCC_ENABLE, OUTPUT); - digitalWrite(VCC_ENABLE, HIGH); - delay(1000); - #endif - - pinMode(LED_BUILTIN, OUTPUT); - - // initialize runtime env - os_init(); - - // Set up these settings once, and use them for both TX and RX - -#if defined(CFG_eu868) - // Use a frequency in the g3 which allows 10% duty cycling. - LMIC.freq = 869525000; -#elif defined(CFG_us915) - LMIC.freq = 902300000; -#endif - - // Maximum TX power - LMIC.txpow = 27; - // Use a medium spread factor. This can be increased up to SF12 for - // better range, but then the interval should be (significantly) - // lowered to comply with duty cycle limits as well. - LMIC.datarate = DR_SF9; - // This sets CR 4/5, BW125 (except for DR_SF7B, which uses BW250) - LMIC.rps = updr2rps(LMIC.datarate); - - Serial.println("Started"); - Serial.flush(); - - // setup initial job - os_setCallback(&txjob, tx_func); -} - -void loop() { - // execute scheduled jobs and events - os_runloop_once(); -} From ffec276941d744eb90be78226bb5879b4577f037 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 26 Mar 2020 15:33:37 +0100 Subject: [PATCH 041/137] Fix some signed vs unsigned comparisons --- lmic/debug.c | 2 +- lmic/lmic.h | 2 +- lmic/radio-sx126x.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lmic/debug.c b/lmic/debug.c index f62b52f..190e077 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -179,7 +179,7 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) break; } case 'e': { // LMIC event name - int ev = va_arg(arg, int); + unsigned ev = va_arg(arg, unsigned); const char *evn = (ev < sizeof(evnames)/sizeof(evnames[0]) && evnames[ev]) ? evnames[ev] : "UNKNOWN"; dst += strpad(dst, end - dst, evn, strlen(evn), width, left, ' '); break; diff --git a/lmic/lmic.h b/lmic/lmic.h index 41f0519..8ae41fa 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -150,7 +150,7 @@ enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this #define RXDERR_INI 50 // ppm #endif -#define LINK_CHECK_OFF (0x80000000) +#define LINK_CHECK_OFF ((s4_t)0x80000000) #define LINK_CHECK_INIT (-LMIC.adrAckLimit) #define LINK_CHECK_DEAD (LMIC.adrAckDelay) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index bbd6090..1d3b29a 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -357,7 +357,7 @@ static void CalibrateImage (uint32_t freq) { { 863000000, 870000000, { 0xD7, 0xDB } }, { 902000000, 928000000, { 0xE1, 0xE9 } }, }; - for (int i = 0; i < sizeof(bands) / sizeof(bands[0]); i++) { + for (size_t i = 0; i < sizeof(bands) / sizeof(bands[0]); i++) { if (freq >= bands[i].min && freq <= bands[i].max) { writecmd(CMD_CALIBRATEIMAGE, bands[i].freq, 2); } From 245c43e4e16faf67459c6633a06d9e5dc2d9a5a4 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 26 Mar 2020 15:34:17 +0100 Subject: [PATCH 042/137] Mark unused functions unused This prevents a compiler warning. If these are used later, the attribute can stay without problems (it just says "It is ok if these are unused"). --- lmic/radio-sx126x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 1d3b29a..89631f4 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -203,6 +203,7 @@ static void WriteRegs (uint16_t addr, const uint8_t* data, uint8_t len) { hal_spi_select(0); } +static void WriteReg (uint16_t addr, uint8_t val) __attribute__((__unused__)); // Ok if this is unused static void WriteReg (uint16_t addr, uint8_t val) { WriteRegs(addr, &val, 1); } @@ -525,6 +526,7 @@ static void SetCrc16 (uint16_t seed, uint16_t polynomial) { WriteRegs(REG_CRCPOLYVALMSB, buf, 2); } +static uint32_t GetRandom (void) __attribute__((__unused__)); // Ok if unused static uint32_t GetRandom (void) { uint8_t buf[4]; // continuous rx From f74ec707533accd77dd649be143859c952650e78 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 26 Mar 2020 15:40:07 +0100 Subject: [PATCH 043/137] Fix undefined casting in GetRandom In C, it is not allowed to reference variables using pointers to an unrelated type, except when the pointer is uint8_t. So dereferencing a uint8_t[] through uint32_t* is not allowed (and gives a warning about type-punning), but dereferencing uint32_t through a uint8_t* is allowed. This rewrites GetRandom slightly to fix this warning. --- lmic/radio-sx126x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 89631f4..ae34582 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -528,16 +528,16 @@ static void SetCrc16 (uint16_t seed, uint16_t polynomial) { static uint32_t GetRandom (void) __attribute__((__unused__)); // Ok if unused static uint32_t GetRandom (void) { - uint8_t buf[4]; + uint32_t value; // continuous rx SetRx(0xFFFFFF); // wait 1ms hal_waitUntil(os_getTime() + ms2osticks(1)); // read random register - ReadRegs(REG_RANDOMNUMBERGEN0, buf, 4); + ReadRegs(REG_RANDOMNUMBERGEN0, (uint8_t*)value, 4); // standby SetStandby(STDBY_RC); - return *((uint32_t*) buf); + return value; } void radio_sleep (void) { From 52b5642923f3288089499572e6dc26ef5e4de8d5 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 26 Mar 2020 15:43:10 +0100 Subject: [PATCH 044/137] Arduino: Fix unused parameter warnings This just removes the parameter names to indicate they are unused. --- target/arduino/examples/ttn-abp/ttn-abp.ino | 6 +++--- target/arduino/hal/hal.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/ttn-abp/ttn-abp.ino index 8d5aaa7..b96a4aa 100644 --- a/target/arduino/examples/ttn-abp/ttn-abp.ino +++ b/target/arduino/examples/ttn-abp/ttn-abp.ino @@ -49,9 +49,9 @@ static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every no // These callbacks are only used in over-the-air activation, so they are // left empty here (we cannot leave them out completely unless // DISABLE_JOIN is set in config.h, otherwise the linker will complain). -void os_getJoinEui (u1_t* buf) { } -void os_getDevEui (u1_t* buf) { } -void os_getNwkKey (u1_t* buf) { } +void os_getJoinEui (u1_t* /* buf */) { } +void os_getDevEui (u1_t* /* buf */) { } +void os_getNwkKey (u1_t* /* buf */) { } u1_t os_getRegion (void) { return REGCODE_EU868; } // Schedule TX every this many milliseconds (might become longer due to duty diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index b6e51c0..5fefb62 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -94,7 +94,7 @@ bool hal_pin_rst (u1_t val) { return true; } -void hal_irqmask_set (int mask) { +void hal_irqmask_set (int /* mask */) { // Not implemented } @@ -291,7 +291,7 @@ u1_t hal_sleep (u1_t type, u4_t targettime) { return delta_time(targettime) < 10 ? 0 : 1; } -void hal_watchcount (int cnt) { +void hal_watchcount (int /* cnt */) { // Not implemented } @@ -369,7 +369,7 @@ void hal_printf_init() { #endif // !defined(__AVR__) #endif // defined(LMIC_PRINTF_TO) -void hal_init (void *bootarg) { +void hal_init (void * /* bootarg */) { // configure radio I/O and interrupt handler hal_io_init(); // configure radio SPI @@ -404,11 +404,11 @@ u1_t hal_getBattLevel (void) { return 0; } -void hal_setBattLevel (u1_t level) { +void hal_setBattLevel (u1_t /* level */) { // Not implemented } -void hal_fwinfo (hal_fwi* fwi) { +void hal_fwinfo (hal_fwi* /* fwi */) { // Not implemented } From d03014f073350303c8e839fef36d852c4e5ee0bf Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 26 Mar 2020 15:43:35 +0100 Subject: [PATCH 045/137] Fix unused parameter warnings In C, we cannot omit parameter names (like in C++), so use a void-cast to reference the parameters without really using them. This is typically done with a macro, but doing it explicitly like this prevents further polluting the macro namespace with potential conflicting names. --- lmic/lmic.c | 6 ++++++ lmic/oslmic.c | 1 + lmic/radio-sx126x.c | 2 ++ lmic/radio.c | 2 ++ 4 files changed, 11 insertions(+) diff --git a/lmic/lmic.c b/lmic/lmic.c index 088a0ff..d240f30 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -654,6 +654,8 @@ static void adjustByRxdErr (u1_t rxdelay, u1_t dr) { span /= dr2hsym(dr,1); // additional half symbols LMIC.rxsyms += (span + 1) >> 1; LMIC.rxtime -= span*hsym; +#else // CFG_testpin + (void)rxdelay; (void)dr; // unused #endif // CFG_testpin } @@ -721,11 +723,13 @@ static void txDelay (ostime_t reftime, u1_t secSpan) { static void setDrJoin (u1_t reason, dr_t dr) { + (void)reason; // unused LMIC.datarate = dr; } static void setDrTxpow (u1_t reason, dr_t dr, s1_t powadj) { + (void)reason; // unused if( powadj != KEEP_TXPOWADJ ) LMIC.txPowAdj = powadj; if( LMIC.datarate != dr ) { @@ -1364,6 +1368,7 @@ static ostime_t nextTx_fix (ostime_t now) { static void runEngineUpdate (osjob_t* osjob) { + (void)osjob; // unused engineUpdate(); } @@ -1383,6 +1388,7 @@ static void reportEvent (ev_t ev) { static void runReset (osjob_t* osjob) { + (void)osjob; // unused // Disable session LMIC_reset(); LMIC_startJoining(); diff --git a/lmic/oslmic.c b/lmic/oslmic.c index 0509f03..6160292 100644 --- a/lmic/oslmic.c +++ b/lmic/oslmic.c @@ -55,6 +55,7 @@ u1_t os_getRndU1 (void) { } bit_t os_cca (u2_t rps, u4_t freq) { //XXX:this belongs into os_radio module + (void) rps; (void)freq; // unused return 0; // never grant access } diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index ae34582..d2fbc73 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -790,6 +790,8 @@ void radio_init (void) { // (run by irqjob) bool radio_irq_process (ostime_t irqtime, u1_t diomask) { + (void)diomask; // unused + uint16_t irqflags = GetIrqStatus(); // dispatch modem diff --git a/lmic/radio.c b/lmic/radio.c index 987a743..e4a9d83 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -40,6 +40,7 @@ static void radio_stop (void) { // protected job - runs with irqs disabled! static void radio_irq_timeout (osjob_t* j) { BACKTRACE(); + (void)j; // unused // stop everything (antenna switch, hal irqs, sleep, irq job) radio_stop(); @@ -63,6 +64,7 @@ void radio_set_irq_timeout (ostime_t timeout) { // (run by irqjob) static void radio_irq_func (osjob_t* j) { + (void)j; // unused // call radio-specific processing function if( radio_irq_process(state.irqtime, state.diomask) ) { // current radio operation has completed From 75f527cf449f57f7d365d2154e509c690e729744 Mon Sep 17 00:00:00 2001 From: Linar Yusupov Date: Mon, 16 Mar 2020 10:52:18 +0300 Subject: [PATCH 046/137] Fix for aes_encrypt() conflict with built-in function on ESP8266 and ESP32 --- aes/aes-common.c | 14 +++++++------- aes/aes-ideetron.c | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/aes/aes-common.c b/aes/aes-common.c index 020a7b6..2238fd6 100644 --- a/aes/aes-common.c +++ b/aes/aes-common.c @@ -21,7 +21,7 @@ * implementation. This file assumes that there is an encryption * function available with this signature: * - * extern "C" void aes_encrypt(u1_t *data, u1_t *key); + * extern "C" void lmic_aes_encrypt(u1_t *data, u1_t *key); * * That takes a single 16-byte buffer and encrypts it wit the given * 16-byte key. @@ -32,7 +32,7 @@ #if !defined(USE_ORIGINAL_AES) // This should be defined elsewhere -void aes_encrypt(u1_t *data, u1_t *key); +void lmic_aes_encrypt(u1_t *data, u1_t *key); // global area for passing parameters (aux, key) and for storing round keys u4_t AESAUX[16/sizeof(u4_t)]; @@ -55,7 +55,7 @@ static void shift_left(u1_t *buf, u1_t len) { // in any case. The CMAC result is returned in AESAUX as well. static void os_aes_cmac(u1_t *buf, u2_t len, u1_t prepend_aux) { if (prepend_aux) - aes_encrypt(AESaux, AESkey); + lmic_aes_encrypt(AESaux, AESkey); else memset (AESaux, 0, 16); @@ -79,7 +79,7 @@ static void os_aes_cmac(u1_t *buf, u2_t len, u1_t prepend_aux) { // shifts and xor on that. u1_t final_key[16]; memset(final_key, 0, sizeof(final_key)); - aes_encrypt(final_key, AESkey); + lmic_aes_encrypt(final_key, AESkey); // Calculate K1 u1_t msb = final_key[0] & 0x80; @@ -100,7 +100,7 @@ static void os_aes_cmac(u1_t *buf, u2_t len, u1_t prepend_aux) { AESaux[i] ^= final_key[i]; } - aes_encrypt(AESaux, AESkey); + lmic_aes_encrypt(AESaux, AESkey); } } @@ -112,7 +112,7 @@ static void os_aes_ctr (u1_t *buf, u2_t len) { while (len) { // Encrypt the counter block with the selected key memcpy(ctr, AESaux, sizeof(ctr)); - aes_encrypt(ctr, AESkey); + lmic_aes_encrypt(ctr, AESkey); // Xor the payload with the resulting ciphertext for (u1_t i = 0; i < 16 && len > 0; i++, len--, buf++) @@ -132,7 +132,7 @@ u4_t os_aes (u1_t mode, u1_t *buf, u2_t len) { case AES_ENC: // TODO: Check / handle when len is not a multiple of 16 for (u1_t i = 0; i < len; i += 16) - aes_encrypt(buf+i, AESkey); + lmic_aes_encrypt(buf+i, AESkey); break; case AES_CTR: diff --git a/aes/aes-ideetron.c b/aes/aes-ideetron.c index afa74d0..65b0083 100644 --- a/aes/aes-ideetron.c +++ b/aes/aes-ideetron.c @@ -35,7 +35,7 @@ // This file was taken from // https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for // use with LMIC. It was only cosmetically modified: -// - AES_Encrypt was renamed to aes_encrypt. +// - AES_Encrypt was renamed to lmic_aes_encrypt. // - All other functions and variables were made static // - Tabs were converted to 2 spaces // - An #include and #if guard was added @@ -71,7 +71,7 @@ static unsigned char S_Table[16][16] = { {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16} }; -void aes_encrypt(unsigned char *Data, unsigned char *Key); +void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key); static void AES_Add_Round_Key(unsigned char *Round_Key); static unsigned char AES_Sub_Byte(unsigned char Byte); static void AES_Shift_Rows(); @@ -87,7 +87,7 @@ static void Send_State(); * *Key Key to encrypt data with is a 16 byte long arry ***************************************************************************************** */ -void aes_encrypt(unsigned char *Data, unsigned char *Key) +void lmic_aes_encrypt(unsigned char *Data, unsigned char *Key) { unsigned char i; unsigned char Row,Collum; From eb3621a5268fb97dca9ffc6b116712ae8bbf282d Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 5 Mar 2020 16:33:29 +0100 Subject: [PATCH 047/137] Add some debug prints related to channel config --- lmic/lmic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lmic/lmic.c b/lmic/lmic.c index d240f30..de584e4 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -853,6 +853,7 @@ static bit_t setupChannel_dyn (u1_t chidx, freq_t freq, drmap_t drmap) { goto ok; } } + debug_printf("Channel %d frequency (%F) not within any known band, ignoring\r\n", chidx, freq, 6); return 0; } ok: @@ -923,6 +924,7 @@ static void syncDatarate_dyn (void) { } n += 1; } + debug_printf("Current datarate (%d) not enabled in any channel, using closest instead (%d)\r\n", LMIC.datarate, dr); setDrTxpow(DRCHG_SET, dr, KEEP_TXPOWADJ); } } From f4d97a42e18031b3c682eb25fc1250fd4422f830 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 12 Feb 2020 17:14:14 +0100 Subject: [PATCH 048/137] README: Add some info about current status --- README.md | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 03ec29e..c7767d2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,99 @@ -# Basic MAC +# BasicMAC -Basic MAC is a portable implementation of the LoRa™ Alliance's LoRaWAN™ +BasicMAC is a portable implementation of the LoRa™ Alliance's LoRaWAN™ specification in the C programming language. It is a fork of IBM's LMiC library, and supports multiple regions, which are selectable at compile and/or run time. It can handle Class A, Class B, and Class C devices. -#### Documentation +## This repository +This repository contains an Arduino port of the BasicMAC library. +However, the port is done without changing the structure of the +repository itself, so development can continue on other targets at the +same time. -The full documentation is available at -[https://doc.sm.tc/mac](https://doc.sm.tc/mac). +### Development status + +This master branch should be working, testing and contributions are +welcome. There are still some rough edges, so this should not be +considered a finished version. + +Initially, development of this repository has been quite unstable, with +lots of history editing and rebasing, but the intention is to keep the +master branch stable now (i.e. no force pushes). Rebasing and history +editing might still happen in other branches and is encourage in +pullrequests to keep history clean. + +The API is still likely to change in a breaking way, especially wrt +configuration. + +### Relation to other projects + +BasicMAC is a privately developed fork of LMIC, which was released +publically by Semtech in 2019. This repository borrows heavily from the +Arduino LMIC port that was first published at +https://github.com/matthijskooijman/arduino-lmic/. There are some other +LMIC versions (notably https://github.com/mcci-catena/arduino-lmic) for +Arduino based off matthijskooijman's version and have seen more +development, but no effort has been made to incorporate any changes from +there. + +Ideally, all of these LMIC and BasicMAC-based projects would unify their +efforts in a repository that is not Arduino-specific, but given that +there have been signficant changes in both BasicMAC and the MCCI +versions compared to the original IBM LMIC version, this might be a +challenge (both technically, and project-wise). + +### Using this repository on Arduino + +This repository is not directly usable as an Arduino library, in order +to keep the directory structure more generic. To use it in Arduino, +there is a bash script that generates an Arduino library out of it. To do so, +make a git checkout of this repository and from the root of it, run: + + ./target/arduino/export.sh --link ~/Arduino/libraries/BasicMAC + +The `--link` parameter makes the script generate symlinks into the +original repository, allowing you to make changes to the library there +and have them reflected in the Arduino version directly. If you omit +`--link`, copies will be made instead. + +The directory name passed is created and becomes the library directory, +it should be in your Arduino sketchbook's `libraries` directly (the path +shown should work with the default Arduino IDE configuration). + +This script was tested on Linux, should work on OSX as well, might work +on Windows (if you have bash). + +### Using this repository without Arduino + +To build and upload without Arduino, a makefile-based build system is +present in the `projects` subdirectory. It currently only supports +STM32, on some specific boards. + +To use it, go into the example project directory (`projects/ex-join`) +and run `make`. + +Some effort has been made to keep these builds working, but testing of +the code in this repostiory has only been done under Arduino, so the +Makefile-based builds might very well be broken. + +### Hardware support + +This port is intended to work on any Arduino board, regardless of +architecture. It was tested on AVR, SAMD and STM32 boards, and with +SX1272, SX1276 and SX1262 radios. + +Unfortunately, BasicMAC is quite a bit bigger than LMIC, so it seems it +does not fit in an atmega328p anymore. PROGMEM optimizations have not +been applied yet, but those would only free up RAM, not flash (and now +the ttn-otaa example compiles down to 35k of flash, with only 32k +available). + +### Converting from LMIC + +BasicMAC is based on LMIC and largely works in the same way. Sketches +can likely be largely reused, though some small changes need to be made. +See for example commit 3ccfb5b for the changes needed to the examples. + +Additionally the pinmap was significantly changed, so look at one of the +current examples to see how that looks now. From cfca8e993ff2d0ddc5d8a9bc16a4e680cab0b389 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 16 Apr 2020 09:39:56 +0200 Subject: [PATCH 049/137] Rename min/max macros to os_min/os_max This prevents conflicting with existing Arduino macros, or user-defined functions with the same name. --- lmic/lmic.c | 2 +- lmic/oslmic.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 69f5b07..84f5d25 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -956,7 +956,7 @@ static void updateTx_dyn (ostime_t txbeg) { u1_t b = freq & BAND_MASK; // set frequency/power LMIC.freq = freq & ~BAND_MASK; - LMIC.txpow = min(LMIC.txPowAdj + REGION.maxEirp, REGION.bands[b].txpow); + LMIC.txpow = os_min(LMIC.txPowAdj + REGION.maxEirp, REGION.bands[b].txpow); // Update band duty cycle stats osxtime_t xnow = os_getXTime(); //XXX:TBD: osxtime_t xtxbeg = os_time2XTime(txbeg, os_getXTime()); diff --git a/lmic/oslmic.h b/lmic/oslmic.h index 71f393a..2a6ba85 100644 --- a/lmic/oslmic.h +++ b/lmic/oslmic.h @@ -55,17 +55,17 @@ typedef const char* str_t; extern "C"{ #endif -#define max(a,b) \ +#define os_max(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a > _b ? _a : _b; }) -#define min(a,b) \ +#define os_min(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; }) -#define os_minmax(vmin,v,vmax) (max(vmin,min(v,vmax))) +#define os_minmax(vmin,v,vmax) (os_max(vmin,os_min(v,vmax))) #define os_clearMem(a,b) memset(a,0,b) #define os_copyMem(a,b,c) memcpy(a,b,c) #define os_moveMem(a,b,c) memmove(a,b,c) From b83bb9491117e75ac93df54020f5d7c39d60a7c4 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 16 Apr 2020 10:14:35 +0200 Subject: [PATCH 050/137] Sx126x: Fix compilation failure due to radio_cw rename In version 2.2, Semtech changed the 127x driver to rename txsw to radio_cw, but this change was not made in the 126x driver, making compilation fail when using it. This renames the function for 126x to at least fix compilation. However, for 126x the continuous mode was split into a TXCW and TXCONT mode, where the former uses the existing radio_cw (formerly txcw) function, but the latter txfsk() or txlora() with some changes for continuous mode. This is not implemented for 126x, so TXCONT is probably broken for 126x now. --- lmic/radio-sx126x.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 3d5eaf1..3996952 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -597,7 +597,7 @@ static void txfsk (void) { SetTx(64000); // timeout 1s (should not happen, TXDONE irq will be raised) } -static void txcw (void) { +void radio_cw (void) { SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); SetDIO3AsTcxoCtrl(); @@ -616,7 +616,12 @@ static void txcw (void) { void radio_starttx (bool txcontinuous) { if (txcontinuous) { - txcw(); + // XXX: This is probably not right. In 2.2, Semtech changed the + // 127x driver to rename txsw to radio_cw, but + // radio_starttx(true) now uses txfsk/txlora in continuous mode + // (which is apparently different from radio_cw), so that needs + // to be impliemented here as well + radio_cw(); } else { if (getSf(LMIC.rps) == FSK) { // FSK modem txfsk(); From 503dd9fb0f6a160a9d36f2308ab96233dd6f2a81 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 16 Apr 2020 10:36:02 +0200 Subject: [PATCH 051/137] Replace all tabs by spaces Previously, most files used mixed tabs and spaces (e.g. 4 spaces indent, each 8 spaces replaced by a tab) which heavily relies on tabs being rendered as 8 spaces (so it does not allow changing the tab size to your personal preference). To prevent issues and conflicts resulting from inconsistent indentation, just switch everything to use spaces exclusively. A big indentation change like this is prone to causing conflicts with existing pullrequests and git clones, but since this repo is still fairly new, now seems like the best time to do this cleanup. This was done using the following command: for f in $(find -name \*.[ch]); do vim -c "set et|retab|wq" $f; done And then commiting everything except basicloader (which is a submodule) and stm32/CMSIS (which is third-party code imported verbatim). --- lmic/debug.c | 342 ++++++++++++++++----------------- lmic/debug.h | 12 +- lmic/hal.h | 6 +- lmic/lce.c | 22 +-- lmic/lmic.c | 76 ++++---- lmic/lorabase.h | 2 +- lmic/oslmic.c | 58 +++--- lmic/oslmic.h | 34 ++-- lmic/radio-sx126x.c | 374 ++++++++++++++++++------------------- lmic/radio-sx127x.c | 370 ++++++++++++++++++------------------ lmic/radio.c | 150 +++++++-------- lmic/region.h | 2 +- services/appstart/main.c | 12 +- services/lwmux/lwmux.c | 60 +++--- services/lwtest/testmode.c | 210 ++++++++++----------- services/pwrman/pwrman.c | 2 +- stm32/board.h | 28 +-- stm32/brd_devboards.h | 108 +++++------ stm32/eeprom.c | 2 +- stm32/gpio.c | 16 +- stm32/hal_stm32.h | 4 +- stm32/hw.h | 90 ++++----- stm32/i2c.c | 74 ++++---- stm32/leds.c | 156 ++++++++-------- stm32/persodata.c | 4 +- stm32/startup.c | 6 +- stm32/usart.c | 126 ++++++------- target/arduino/hw.h | 4 +- unicorn/hal.c | 20 +- unicorn/hw.h | 6 +- unicorn/persodata.c | 4 +- unicorn/startup.c | 4 +- 32 files changed, 1192 insertions(+), 1192 deletions(-) diff --git a/lmic/debug.c b/lmic/debug.c index d6f89bc..bcb61f5 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -21,23 +21,23 @@ void debug_str (const char* str) { static int itoa (char* buf, u4_t val, int base, int mindigits, int exp, int prec, char sign) { char num[33], *p = num, *b = buf; if (sign) { - if ((s4_t) val < 0) { - val = -val; - *b++ = '-'; - } else if (sign != '-') { - *b++ = sign; // space or plus - } + if ((s4_t) val < 0) { + val = -val; + *b++ = '-'; + } else if (sign != '-') { + *b++ = sign; // space or plus + } } if (mindigits > 32) { - mindigits = 32; + mindigits = 32; } do { - int m = val % base; + int m = val % base; *p++ = (m <= 9) ? m + '0' : m - 10 + 'A'; - if (p - num == exp) *p++ = '.'; + if (p - num == exp) *p++ = '.'; } while ( (val /= base) || p - num < mindigits ); do { - *b++ = *--p; + *b++ = *--p; } while (p > num + exp - prec); *b = 0; return b - buf; @@ -45,13 +45,13 @@ static int itoa (char* buf, u4_t val, int base, int mindigits, int exp, int prec static int strpad (char *buf, int size, const char *str, int len, int width, int leftalign, char pad) { if (len > width) { - width = len; + width = len; } if (width > size) { - width = size; + width = size; } for (int i = 0, npad = width - len; i < width; i++) { - buf[i] = (leftalign) ? ((i < len) ? str[i] : pad) : ((i < npad) ? pad : str[i - npad]); + buf[i] = (leftalign) ? ((i < len) ? str[i] : pad) : ((i < npad) ? pad : str[i - npad]); } return width; } @@ -86,164 +86,164 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) int width, left, base, zero, space, plus, prec, sign, longint; while ( (c = *format++) && dst < end ) { - if (c != '%') { - *dst++ = c; - } else { - // flags - width = prec = left = zero = sign = space = plus = longint = 0; - while ( (c = *format++) ) { - if (c == '-') left = 1; - else if (c == ' ') space = 1; - else if (c == '+') plus = 1; - else if (c == '0') zero = 1; - else break; - } - // width - if (c == '*') { - width = va_arg(arg, int); - c = *format++; - } else { - while (c >= '0' && c <= '9') { - width = width * 10 + c - '0'; - c = *format++; - } - } - // precision - if (c == '.') { - c = *format++; - if (c == '*') { - prec = va_arg(arg, int); - c = *format++; - } else { - while (c >= '0' && c <= '9') { - prec = prec * 10 + c - '0'; - c = *format++; - } - } - } - // length - if(c == 'l') { - c = *format++; - longint = 1; - } - // conversion specifiers - switch (c) { - case 'c': // character - c = va_arg(arg, int); - // fallthrough - case '%': // percent literal - *dst++ = c; - break; - case 's': { // nul-terminated string - char *s = va_arg(arg, char *); - int l = strlen(s); - if(prec && l > prec) { - l = prec; - } - dst += strpad(dst, end - dst, s, l, width, left, ' '); - break; - } - case 'd': // signed integer as decimal - sign = (plus) ? '+' : (space) ? ' ' : '-'; - // fallthrough - case 'u': // unsigned integer as decimal - base = 10; - goto numeric; - case 'x': - case 'X': // unsigned integer as hex - base = 16; - goto numeric; - case 'b': // unsigned integer as binary - base = 2; - numeric: { - char num[33], pad = ' '; - if (zero && left == 0 && prec == 0) { - prec = width - 1; // have itoa() do the leading zero-padding for correct placement of sign - pad = '0'; - } - u4_t val = longint ? va_arg(arg, long) : va_arg(arg, int); - int len = itoa(num, val, base, prec, 0, 0, sign); - dst += strpad(dst, end - dst, num, len, width, left, pad); - break; - } - case 'F': { // signed integer and exponent as fixed-point decimal - char num[33], pad = (zero && left == 0) ? '0' : ' '; - u4_t val = va_arg(arg, u4_t); - int exp = va_arg(arg, int); - int len = itoa(num, val, 10, exp + 2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-'); - dst += strpad(dst, end - dst, num, len, width, left, pad); - break; - } - case 'e': { // LMIC event name - unsigned ev = va_arg(arg, unsigned); - const char *evn = (ev < sizeof(evnames) / sizeof(evnames[0]) && evnames[ev]) ? evnames[ev] : "UNKNOWN"; - dst += strpad(dst, end - dst, evn, strlen(evn), width, left, ' '); - break; - } - case 'E': { // EUI64, lsbf (xx-xx-xx-xx-xx-xx-xx-xx) - char buf[23], *p = buf; - unsigned char *eui = va_arg(arg, unsigned char *); - for (int i = 7; i >= 0; i--) { - p += itoa(p, eui[i], 16, 2, 0, 0, 0); - if (i) *p++ = '-'; - } - dst += strpad(dst, end - dst, buf, 23, width, left, ' '); - break; - } - case 't': // ostime_t (hh:mm:ss.mmm) - case 'T': { // osxtime_t (ddd.hh:mm:ss) - #if defined(CFG_DEBUG_RAW_TIMESTAMPS) - if (c == 't') { - longint = 1; - base = 10; - goto numeric; - } - #endif - char buf[12], *p = buf; - uint64_t t = ((c == 'T') ? va_arg(arg, uint64_t) : va_arg(arg, uint32_t)) * 1000 / OSTICKS_PER_SEC; - int ms = t % 1000; - t /= 1000; - int sec = t % 60; - t /= 60; - int min = t % 60; - t /= 60; - int hr = t % 24; - t /= 24; - int day = t; - if (c == 'T') { - p += itoa(p, day, 10, 3, 0, 0, 0); - *p++ = '.'; - } - p += itoa(p, hr, 10, 2, 0, 0, 0); - *p++ = ':'; - p += itoa(p, min, 10, 2, 0, 0, 0); - *p++ = ':'; - p += itoa(p, sec, 10, 2, 0, 0, 0); - if (c == 't') { - *p++ = '.'; - p += itoa(p, ms, 10, 3, 0, 0, 0); - } - dst += strpad(dst, end - dst, buf, 12, width, left, ' '); - break; - } - case 'h': { // buffer+length as hex dump (no field padding, but precision/maxsize truncation) - unsigned char *buf = va_arg(arg, unsigned char *); - int len = va_arg(arg, int); - char *top = (prec == 0 || dst + prec > end) ? end : dst + prec; - while (len--) { - if ((len == 0 && top - dst >= 2) || top - dst >= 2 + space + 2) { - dst += itoa(dst, *buf++, 16, 2, 0, 0, 0); - if(space && len && dst < top) *dst++ = ' '; - } else { - while (dst < top) *dst++ = '.'; - } - } - break; - } - default: // (also catch '\0') - goto stop; - } - } + if (c != '%') { + *dst++ = c; + } else { + // flags + width = prec = left = zero = sign = space = plus = longint = 0; + while ( (c = *format++) ) { + if (c == '-') left = 1; + else if (c == ' ') space = 1; + else if (c == '+') plus = 1; + else if (c == '0') zero = 1; + else break; + } + // width + if (c == '*') { + width = va_arg(arg, int); + c = *format++; + } else { + while (c >= '0' && c <= '9') { + width = width * 10 + c - '0'; + c = *format++; + } + } + // precision + if (c == '.') { + c = *format++; + if (c == '*') { + prec = va_arg(arg, int); + c = *format++; + } else { + while (c >= '0' && c <= '9') { + prec = prec * 10 + c - '0'; + c = *format++; + } + } + } + // length + if(c == 'l') { + c = *format++; + longint = 1; + } + // conversion specifiers + switch (c) { + case 'c': // character + c = va_arg(arg, int); + // fallthrough + case '%': // percent literal + *dst++ = c; + break; + case 's': { // nul-terminated string + char *s = va_arg(arg, char *); + int l = strlen(s); + if(prec && l > prec) { + l = prec; + } + dst += strpad(dst, end - dst, s, l, width, left, ' '); + break; + } + case 'd': // signed integer as decimal + sign = (plus) ? '+' : (space) ? ' ' : '-'; + // fallthrough + case 'u': // unsigned integer as decimal + base = 10; + goto numeric; + case 'x': + case 'X': // unsigned integer as hex + base = 16; + goto numeric; + case 'b': // unsigned integer as binary + base = 2; + numeric: { + char num[33], pad = ' '; + if (zero && left == 0 && prec == 0) { + prec = width - 1; // have itoa() do the leading zero-padding for correct placement of sign + pad = '0'; + } + u4_t val = longint ? va_arg(arg, long) : va_arg(arg, int); + int len = itoa(num, val, base, prec, 0, 0, sign); + dst += strpad(dst, end - dst, num, len, width, left, pad); + break; + } + case 'F': { // signed integer and exponent as fixed-point decimal + char num[33], pad = (zero && left == 0) ? '0' : ' '; + u4_t val = va_arg(arg, u4_t); + int exp = va_arg(arg, int); + int len = itoa(num, val, 10, exp + 2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-'); + dst += strpad(dst, end - dst, num, len, width, left, pad); + break; + } + case 'e': { // LMIC event name + unsigned ev = va_arg(arg, unsigned); + const char *evn = (ev < sizeof(evnames) / sizeof(evnames[0]) && evnames[ev]) ? evnames[ev] : "UNKNOWN"; + dst += strpad(dst, end - dst, evn, strlen(evn), width, left, ' '); + break; + } + case 'E': { // EUI64, lsbf (xx-xx-xx-xx-xx-xx-xx-xx) + char buf[23], *p = buf; + unsigned char *eui = va_arg(arg, unsigned char *); + for (int i = 7; i >= 0; i--) { + p += itoa(p, eui[i], 16, 2, 0, 0, 0); + if (i) *p++ = '-'; + } + dst += strpad(dst, end - dst, buf, 23, width, left, ' '); + break; + } + case 't': // ostime_t (hh:mm:ss.mmm) + case 'T': { // osxtime_t (ddd.hh:mm:ss) + #if defined(CFG_DEBUG_RAW_TIMESTAMPS) + if (c == 't') { + longint = 1; + base = 10; + goto numeric; + } + #endif + char buf[12], *p = buf; + uint64_t t = ((c == 'T') ? va_arg(arg, uint64_t) : va_arg(arg, uint32_t)) * 1000 / OSTICKS_PER_SEC; + int ms = t % 1000; + t /= 1000; + int sec = t % 60; + t /= 60; + int min = t % 60; + t /= 60; + int hr = t % 24; + t /= 24; + int day = t; + if (c == 'T') { + p += itoa(p, day, 10, 3, 0, 0, 0); + *p++ = '.'; + } + p += itoa(p, hr, 10, 2, 0, 0, 0); + *p++ = ':'; + p += itoa(p, min, 10, 2, 0, 0, 0); + *p++ = ':'; + p += itoa(p, sec, 10, 2, 0, 0, 0); + if (c == 't') { + *p++ = '.'; + p += itoa(p, ms, 10, 3, 0, 0, 0); + } + dst += strpad(dst, end - dst, buf, 12, width, left, ' '); + break; + } + case 'h': { // buffer+length as hex dump (no field padding, but precision/maxsize truncation) + unsigned char *buf = va_arg(arg, unsigned char *); + int len = va_arg(arg, int); + char *top = (prec == 0 || dst + prec > end) ? end : dst + prec; + while (len--) { + if ((len == 0 && top - dst >= 2) || top - dst >= 2 + space + 2) { + dst += itoa(dst, *buf++, 16, 2, 0, 0, 0); + if(space && len && dst < top) *dst++ = ' '; + } else { + while (dst < top) *dst++ = '.'; + } + } + break; + } + default: // (also catch '\0') + goto stop; + } + } } stop: *dst++ = 0; diff --git a/lmic/debug.h b/lmic/debug.h index 4bcdd5b..5ea1f5c 100644 --- a/lmic/debug.h +++ b/lmic/debug.h @@ -9,11 +9,11 @@ #ifndef CFG_DEBUG -#define debug_snprintf(s,n,f,...) do { } while (0) -#define debug_printf(f,...) do { } while (0) -#define debug_str(s) do { } while (0) -#define debug_led(val) do { } while (0) -#define debug_verbose_printf(f,...) do { } while (0) +#define debug_snprintf(s,n,f,...) do { } while (0) +#define debug_printf(f,...) do { } while (0) +#define debug_str(s) do { } while (0) +#define debug_led(val) do { } while (0) +#define debug_verbose_printf(f,...) do { } while (0) #ifdef CFG_DEBUG_VERBOSE #error CFG_DEBUG_VERBOSE requires CFG_DEBUG @@ -50,7 +50,7 @@ void debug_str (const char* str); void debug_led (int val); #ifndef CFG_DEBUG_VERBOSE -#define debug_verbose_printf(f,...) do { } while (0) +#define debug_verbose_printf(f,...) do { } while (0) #else #define debug_verbose_printf debug_printf #endif diff --git a/lmic/hal.h b/lmic/hal.h index 05d32e9..e9129ce 100644 --- a/lmic/hal.h +++ b/lmic/hal.h @@ -89,9 +89,9 @@ void hal_enableIRQs (void); * - return 0 if target time is close * - otherwise sleep until target time / interrupt and return non-zero */ -#define HAL_SLEEP_EXACT 0 -#define HAL_SLEEP_APPROX 1 -#define HAL_SLEEP_FOREVER 2 +#define HAL_SLEEP_EXACT 0 +#define HAL_SLEEP_APPROX 1 +#define HAL_SLEEP_FOREVER 2 u1_t hal_sleep (u1_t type, u4_t targettime); /* diff --git a/lmic/lce.c b/lmic/lce.c index 256f9b8..e7f89a3 100644 --- a/lmic/lce.c +++ b/lmic/lce.c @@ -21,17 +21,17 @@ bool lce_processJoinAccept (u1_t* jacc, u1_t jacclen, u2_t devnonce) { #if defined(CFG_lorawan11) u1_t optneg = jacc[OFF_JA_DLSET] & JA_DLS_OPTNEG; if( optneg ) { - os_moveMem(jacc+OFF_JA_JOINNONCE+2, jacc+OFF_JA_JOINNONCE, jacclen-OFF_JA_JOINNONCE); - os_wlsbf2(jacc+OFF_JA_JOINNONCE, devnonce); - jacclen += 2; + os_moveMem(jacc+OFF_JA_JOINNONCE+2, jacc+OFF_JA_JOINNONCE, jacclen-OFF_JA_JOINNONCE); + os_wlsbf2(jacc+OFF_JA_JOINNONCE, devnonce); + jacclen += 2; } #endif os_getNwkKey(AESkey); u4_t mic2 = os_aes(AES_MIC|AES_MICNOAUX, jacc, jacclen); #if defined(CFG_lorawan11) if( optneg ) { // Restore orig frame - jacclen -= 2; - os_moveMem(jacc+OFF_JA_JOINNONCE, jacc+OFF_JA_JOINNONCE+2, jacclen-OFF_JA_JOINNONCE); + jacclen -= 2; + os_moveMem(jacc+OFF_JA_JOINNONCE, jacc+OFF_JA_JOINNONCE+2, jacclen-OFF_JA_JOINNONCE); os_wlsbf4(jacc+jacclen, mic1); } #endif @@ -54,12 +54,12 @@ bool lce_processJoinAccept (u1_t* jacc, u1_t jacclen, u2_t devnonce) { os_aes(AES_ENC, nwkskey, 16); #if defined(CFG_lorawan11) if( optneg ) { - os_getNwkKey(AESkey); - os_aes(AES_ENC, LMIC.lceCtx.nwkSKeyDn, 16); - os_getAppKey(AESkey); + os_getNwkKey(AESkey); + os_aes(AES_ENC, LMIC.lceCtx.nwkSKeyDn, 16); + os_getAppKey(AESkey); } else { - os_copyMem(LMIC.lceCtx.nwkSKeyDn, nwkskey, 16); - os_getNwkKey(AESkey); + os_copyMem(LMIC.lceCtx.nwkSKeyDn, nwkskey, 16); + os_getNwkKey(AESkey); } #else os_getNwkKey(AESkey); @@ -131,7 +131,7 @@ u4_t lce_micKey0 (u4_t devaddr, u4_t seqno, u1_t* pdu, int len) { void lce_cipher (s1_t keyid, u4_t devaddr, u4_t seqno, int cat, u1_t* payload, int len) { if(len <= 0 || (cat==LCE_SCC_UP && (LMIC.opmode & OP_NOCRYPT)) ) { - return; + return; } const u1_t* key; if( keyid == LCE_NWKSKEY ) { diff --git a/lmic/lmic.c b/lmic/lmic.c index 84f5d25..95f50ad 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -203,7 +203,7 @@ static const rfuncs_t RFUNCS_FIX; // fwd decl static const region_t REGIONS[REGIONS_COUNT] = { #ifdef CFG_eu868 [REGION_EU868] = { - .regcode = REGCODE_EU868, + .regcode = REGCODE_EU868, .flags = 0, .minFreq = 863000000, .maxFreq = 870000000, @@ -235,14 +235,14 @@ static const region_t REGIONS[REGIONS_COUNT] = { .rx1DrOff = RX1DR_OFFSETS(0, 0, 1, 2, 3, 4, 5, ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff), .dr2rps = DR2RPS_EU, - .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0}, + .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0}, .rfuncs = &RFUNCS_DYN, }, #endif #ifdef CFG_as923 [REGION_AS923] = { - .regcode = REGCODE_AS923, + .regcode = REGCODE_AS923, .flags = 0, .minFreq = 920000000, .maxFreq = 928000000, @@ -263,13 +263,13 @@ static const region_t REGIONS[REGIONS_COUNT] = { .maxEirp = 16, .rx1DrOff = RX1DR_OFFSETS(0, 0, 1, 2, 3, 4, 5, -1, -2), .dr2rps = DR2RPS_EU, - .dr2maxAppPload = { 0, 0, 11, 53, 125, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0}, + .dr2maxAppPload = { 0, 0, 11, 53, 125, 242, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0}, .rfuncs = &RFUNCS_DYN, }, #endif #ifdef CFG_us915 [REGION_US915] = { - .regcode = REGCODE_US915, + .regcode = REGCODE_US915, .flags = REG_FIXED, .minFreq = 902000000, .maxFreq = 928000000, @@ -291,13 +291,13 @@ static const region_t REGIONS[REGIONS_COUNT] = { ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff), .dr2rps = DR2RPS_US, - .dr2maxAppPload = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0}, + .dr2maxAppPload = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0}, .rfuncs = &RFUNCS_FIX, }, #endif #ifdef CFG_au915 [REGION_AU915] = { - .regcode = REGCODE_AU915, + .regcode = REGCODE_AU915, .flags = REG_FIXED, .minFreq = 915000000, .maxFreq = 928000000, @@ -319,13 +319,13 @@ static const region_t REGIONS[REGIONS_COUNT] = { .rx1DrOff = RX1DR_OFFSETS(8, 0, 1, 2, 3, 4, 5, ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff), .dr2rps = DR2RPS_AU, - .dr2maxAppPload = {0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0}, + .dr2maxAppPload = {0, 0, 11, 53, 125, 242, 242, 0, 53, 129, 242, 242, 242, 242, 0, 0}, .rfuncs = &RFUNCS_FIX, }, #endif #ifdef CFG_cn470 [REGION_CN470] = { - .regcode = REGCODE_CN470, + .regcode = REGCODE_CN470, .flags = REG_FIXED, .minFreq = 470000000, .maxFreq = 510000000, @@ -346,7 +346,7 @@ static const region_t REGIONS[REGIONS_COUNT] = { .rx1DrOff = RX1DR_OFFSETS(0, 0, 1, 2, 3, 4, ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff, ILLEGAL_RX1DRoff), .dr2rps = DR2RPS_125kHz, - .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .dr2maxAppPload = {51, 51, 51, 115, 242, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .rfuncs = &RFUNCS_FIX, }, #endif @@ -1155,7 +1155,7 @@ static ostime_t nextJoinState (void) { LMIC.txChnl = LMIC.fix.hoplist[LMIC.refChnl]; done: LMIC.opmode &= ~OP_NEXTCHNL; - delay = rndDelay(32); + delay = rndDelay(32); #endif } else { #ifdef REG_DYN @@ -1174,7 +1174,7 @@ static ostime_t nextJoinState (void) { setDrJoin(DRCHG_NOJACC, decDR(LMIC.datarate)); } } - delay = rndDelay(255 >> LMIC.datarate); + delay = rndDelay(255 >> LMIC.datarate); #endif } if (failed) @@ -1208,16 +1208,16 @@ u1_t prng_next (u1_t* prngbuf) { // generate pseudo-random permutation from start..end-1 static void perm (unsigned char* p, int start, int end, u1_t* prng) { for (int i = 0; i < (end - start); i++) { - uint32_t j = (prng_next(prng) << 8 ) | prng_next(prng); - j %= (i + 1); // has small bias - p[i] = p[j]; - p[j] = i + start; + uint32_t j = (prng_next(prng) << 8 ) | prng_next(prng); + j %= (i + 1); // has small bias + p[i] = p[j]; + p[j] = i + start; } } // generate a pseudo-random hoplist static void generateHopList (u1_t* hoplist, int nch) { - int nb = nch >> 3; // number of 8-ch blocks + int nb = nch >> 3; // number of 8-ch blocks u1_t prng[16]; // prng state memcpy(prng, "\x10hoplist", 8); @@ -1228,11 +1228,11 @@ static void generateHopList (u1_t* hoplist, int nch) { perm(bp + 1, 1, nb, prng); for (int b = 0; b < nb; b++) { - unsigned char cp[8]; - perm(cp, 0, 8, prng); // channel permutation - for (int c = 0; c < 8; c++) { - hoplist[c*nb+b] = bp[b]*8+cp[c]; - } + unsigned char cp[8]; + perm(cp, 0, 8, prng); // channel permutation + for (int c = 0; c < 8; c++) { + hoplist[c*nb+b] = bp[b]*8+cp[c]; + } } } @@ -2181,15 +2181,15 @@ static bit_t processJoinAccept (void) { } LMIC.opmode &= ~OP_TXRXPEND; ostime_t delay = nextJoinState(); - // update txend - LMIC.txend = os_getTime() + delay; + // update txend + LMIC.txend = os_getTime() + delay; // Build next JOIN REQUEST with next engineUpdate call // Optionally, report join failed. // Both after a random/chosen amount of ticks. os_setApproxTimedCallback(&LMIC.osjob, LMIC.txend, - ((delay&1) != 0) - ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed - : FUNC_ADDR(runEngineUpdate)); // next step to be delayed + ((delay&1) != 0) + ? FUNC_ADDR(onJoinFailed) // one JOIN iteration done and failed + : FUNC_ADDR(runEngineUpdate)); // next step to be delayed return 1; } u1_t hdr = LMIC.frame[0]; @@ -2324,7 +2324,7 @@ static void processRx1DnData (osjob_t* osjob) { static void processRx2ClassC (osjob_t* osjob) { if( LMIC.dataLen != 0 ) { LMIC.txrxFlags = TXRX_DNW2; - if ((LMIC.devaddr == os_rlsbf4(&LMIC.frame[OFF_DAT_ADDR]) && decodeFrame()) || decodeMultiCastFrame() ) { + if ((LMIC.devaddr == os_rlsbf4(&LMIC.frame[OFF_DAT_ADDR]) && decodeFrame()) || decodeMultiCastFrame() ) { reportEvent(EV_RXCOMPLETE); return; } @@ -2419,9 +2419,9 @@ static void buildDataFrame (void) { if( LMIC.dutyCapAns ) { if( end <= OFF_DAT_OPTS + 15 - 1 ) { LMIC.frame[end] = MCMD_DCAP_ANS; - end += 1; - } - LMIC.dutyCapAns = 0; + end += 1; + } + LMIC.dutyCapAns = 0; } if( LMIC.dn2Ans ) { // Note: this is cleared with reception of a frame in a class A RX1/RX2 window @@ -2814,7 +2814,7 @@ static bit_t processDnData (void) { if (LMIC.txPowAdj) { setDrTxpow(DRCHG_NOADRACK, LMIC.datarate, 0); } - if (decDR(LMIC.datarate) != LMIC.datarate) { + if (decDR(LMIC.datarate) != LMIC.datarate) { setDrTxpow(DRCHG_NOADRACK, decDR(LMIC.datarate), KEEP_TXPOWADJ); } else if (REG_IS_FIX() #ifdef REG_FIX @@ -3156,9 +3156,9 @@ int LMIC_regionIdx (u1_t regionCode) { return 0; } for( int idx = 0; idx < REGIONS_COUNT; idx++ ) { - if (REGIONS[idx].regcode == regionCode) { - return idx; - } + if (REGIONS[idx].regcode == regionCode) { + return idx; + } } return -1; } @@ -3330,19 +3330,19 @@ int LMIC_setMultiCastSession (devaddr_t grpaddr, const u1_t* nwkKeyDn, const u1_ session_t* s; for(s = LMIC.sessions; sgrpaddr!=0 && s->grpaddr!=grpaddr; s++); if (s >= LMIC.sessions+MAX_MULTICAST_SESSIONS) - return 0; + return 0; s->grpaddr = grpaddr; s->seqnoADn = seqnoADn; if( nwkKeyDn != (u1_t*)0 ) { os_copyMem(s->nwkKeyDn, nwkKeyDn, 16); - os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].nwkSKeyDn, nwkKeyDn, 16); + os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].nwkSKeyDn, nwkKeyDn, 16); } if( appKey != (u1_t*)0 ) { os_copyMem(s->appKey, appKey, 16); - os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].appSKey, appKey, 16); + os_copyMem(&LMIC.lceCtx.mcgroup[LCE_MCGRP_0 + (s-LMIC.sessions)].appSKey, appKey, 16); } return 1; } diff --git a/lmic/lorabase.h b/lmic/lorabase.h index 4cb9ae1..ac2f90c 100644 --- a/lmic/lorabase.h +++ b/lmic/lorabase.h @@ -200,7 +200,7 @@ enum { MCMD_RKEY_CNF = 0x0B, // - reset confirmation : u1: opt1, [n opts...] MCMD_ADRP_REQ = 0x0C, // - adr params : u1: 7-4: limit_exp, 3-0: delay_exp MCMD_TIME_ANS = 0x0D, // - time answer : u4:epoch_secs, u1:fracs - // Class B - + // Class B - MCMD_PITV_ANS = 0x10, // - ping interval ack : - MCMD_PNGC_REQ = 0x11, // - set ping freq/dr : u3: freq, u1:7-4:RFU/3-0:datarate MCMD_BCNI_ANS = 0x12, // - next beacon start : u2: delay(in TUNIT millis), u1:channel -- DEPRECATED diff --git a/lmic/oslmic.c b/lmic/oslmic.c index e0876fb..dd35a93 100644 --- a/lmic/oslmic.c +++ b/lmic/oslmic.c @@ -80,9 +80,9 @@ static int unlinkjob (osjob_t** pnext, osjob_t* job) { for( ; *pnext; pnext = &((*pnext)->next)) { if(*pnext == job) { // unlink *pnext = job->next; - if ((job->flags & OSJOB_FLAG_APPROX) == 0) { - OS.exact -= 1; - } + if ((job->flags & OSJOB_FLAG_APPROX) == 0) { + OS.exact -= 1; + } return 1; } } @@ -98,11 +98,11 @@ static void extendedjobcb (osxjob_t* xjob) { hal_disableIRQs(); osxtime_t now = os_getXTime(); if (xjob->deadline - now > XJOBTIME_MAX_DIFF) { - // schedule intermediate callback - os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) (now + XJOBTIME_MAX_DIFF), (osjobcb_t) extendedjobcb, OSJOB_FLAG_APPROX); + // schedule intermediate callback + os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) (now + XJOBTIME_MAX_DIFF), (osjobcb_t) extendedjobcb, OSJOB_FLAG_APPROX); } else { - // schedule final callback - os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) xjob->deadline, xjob->func, OSJOB_FLAG_APPROX); + // schedule final callback + os_setTimedCallbackEx((osjob_t*) xjob, (ostime_t) xjob->deadline, xjob->func, OSJOB_FLAG_APPROX); } hal_enableIRQs(); } @@ -139,14 +139,14 @@ void os_setTimedCallbackEx (osjob_t* job, ostime_t time, osjobcb_t cb, unsigned if( flags & OSJOB_FLAG_NOW ) { time = now; } else if ( time - now <= 0 ) { - flags |= OSJOB_FLAG_NOW; + flags |= OSJOB_FLAG_NOW; } job->deadline = time; job->func = cb; job->next = NULL; job->flags = flags; if ((flags & OSJOB_FLAG_APPROX) == 0) { - OS.exact += 1; + OS.exact += 1; } // insert into schedule for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) { @@ -171,16 +171,16 @@ void os_runstep (void) { // check for runnable jobs if (OS.scheduledjobs) { //debug_verbose_printf("Sleeping until job %u, cb %u, deadline %t\r\n", (unsigned)OS.scheduledjobs, (unsigned)OS.scheduledjobs->func, (ostime_t)OS.scheduledjobs->deadline); - if (hal_sleep(OS.exact ? HAL_SLEEP_EXACT : HAL_SLEEP_APPROX, OS.scheduledjobs->deadline) == 0) { - j = OS.scheduledjobs; - OS.scheduledjobs = j->next; - if ((j->flags & OSJOB_FLAG_APPROX) == 0) { - OS.exact -= 1; - } - } + if (hal_sleep(OS.exact ? HAL_SLEEP_EXACT : HAL_SLEEP_APPROX, OS.scheduledjobs->deadline) == 0) { + j = OS.scheduledjobs; + OS.scheduledjobs = j->next; + if ((j->flags & OSJOB_FLAG_APPROX) == 0) { + OS.exact -= 1; + } + } } else { // nothing pending //debug_verbose_printf("Sleeping forever\r\n"); - hal_sleep(HAL_SLEEP_FOREVER, 0); + hal_sleep(HAL_SLEEP_FOREVER, 0); } if( j == NULL || (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) { hal_enableIRQs(); @@ -193,35 +193,35 @@ void os_runstep (void) { delta = os_getTime() - j->deadline; } #endif - // Only print when interrupts are enabled, some Arduino cores do - // not handle printing with IRQs disabled - if( (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) { + // Only print when interrupts are enabled, some Arduino cores do + // not handle printing with IRQs disabled + if( (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) { debug_verbose_printf("Running job %u, cb %u, deadline %t\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline); #ifdef CFG_warnjobs if ( delta > 1 ) { debug_printf("WARNING: job 0x%08x (func 0x%08x) executed %d ticks late\r\n", j, j->func, delta); } #endif - } - hal_watchcount(30); // max 60 sec - j->func(j); - hal_watchcount(0); - // If we could not print before, at least print after - if( (j->flags & OSJOB_FLAG_IRQDISABLED) != 0) { - debug_verbose_printf("Ran job %u, cb %u, deadline %F\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline, 0); + } + hal_watchcount(30); // max 60 sec + j->func(j); + hal_watchcount(0); + // If we could not print before, at least print after + if( (j->flags & OSJOB_FLAG_IRQDISABLED) != 0) { + debug_verbose_printf("Ran job %u, cb %u, deadline %F\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline, 0); #ifdef CFG_warnjobs if ( delta > 1 ) { debug_printf("WARNING: job 0x%08x (func 0x%08x) executed %d ticks late\r\n", j, j->func, delta); } #endif - } + } } } // execute jobs from timer and from run queue void os_runloop (void) { while (1) { - os_runstep(); + os_runstep(); } } diff --git a/lmic/oslmic.h b/lmic/oslmic.h index 2a6ba85..78f2fff 100644 --- a/lmic/oslmic.h +++ b/lmic/oslmic.h @@ -20,17 +20,17 @@ // Target platform as C library #include #include -typedef uint8_t bit_t; -typedef uint8_t u1_t; -typedef int8_t s1_t; -typedef uint16_t u2_t; -typedef int16_t s2_t; -typedef uint32_t u4_t; -typedef int32_t s4_t; -typedef uint64_t u8_t; -typedef int64_t s8_t; -typedef unsigned int uint; -typedef const char* str_t; +typedef uint8_t bit_t; +typedef uint8_t u1_t; +typedef int8_t s1_t; +typedef uint16_t u2_t; +typedef int16_t s2_t; +typedef uint32_t u4_t; +typedef int32_t s4_t; +typedef uint64_t u8_t; +typedef int64_t s8_t; +typedef unsigned int uint; +typedef const char* str_t; #include #if !defined(CFG_simul) @@ -41,7 +41,7 @@ typedef const char* str_t; #include #define ASSERT(cond) do { \ if(!(cond)) { fprintf(stderr, "ASSERTION FAILED: %s at %s:%d\n", \ - #cond, __FILE__, __LINE__); hal_failed(); } } while (0) + #cond, __FILE__, __LINE__); hal_failed(); } } while (0) #elif defined(CFG_DEBUG) #define ASSERT(cond) do { if(!(cond)) { hal_enableIRQs(); debug_printf("%s:%d: assertion failed\r\n", __FILE__, __LINE__); hal_failed(); } } while (0) #else @@ -137,10 +137,10 @@ extern u4_t AESKEY[]; #define APP(t) (APPDATA) #endif -#define LOGCHECK(lvl,block) do { \ - if( lvl <= log_lvl ) { \ - block; \ - } \ +#define LOGCHECK(lvl,block) do { \ + if( lvl <= log_lvl ) { \ + block; \ + } \ } while(0) #if defined(CFG_simul) extern int log_lvl; @@ -188,7 +188,7 @@ u1_t os_getRndU1 (void); typedef s4_t ostime_t; typedef s8_t osxtime_t; -#define OSXTIME_MAX INT64_MAX +#define OSXTIME_MAX INT64_MAX #define OSTIME_MAX_DIFF ((1U << 31) - 1) #if !HAS_ostick_conv diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 3996952..1c85e16 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -12,132 +12,132 @@ // ---------------------------------------- // Commands Selecting the Operating Modes of the Radio -#define CMD_SETSLEEP 0x84 -#define CMD_SETSTANDBY 0x80 -#define CMD_SETFS 0xC1 -#define CMD_SETTX 0x83 -#define CMD_SETRX 0x82 -#define CMD_STOPTIMERONPREAMBLE 0x9F -#define CMD_SETRXDUTYCYCLE 0x94 -#define CMD_SETCAD 0xC5 -#define CMD_SETTXCONTINUOUSWAVE 0xD1 -#define CMD_SETTXINFINITEPREAMBLE 0xD2 -#define CMD_SETREGULATORMODE 0x96 -#define CMD_CALIBRATE 0x89 -#define CMD_CALIBRATEIMAGE 0x98 -#define CMD_SETPACONFIG 0x95 -#define CMD_SETRXTXFALLBACKMODE 0x93 +#define CMD_SETSLEEP 0x84 +#define CMD_SETSTANDBY 0x80 +#define CMD_SETFS 0xC1 +#define CMD_SETTX 0x83 +#define CMD_SETRX 0x82 +#define CMD_STOPTIMERONPREAMBLE 0x9F +#define CMD_SETRXDUTYCYCLE 0x94 +#define CMD_SETCAD 0xC5 +#define CMD_SETTXCONTINUOUSWAVE 0xD1 +#define CMD_SETTXINFINITEPREAMBLE 0xD2 +#define CMD_SETREGULATORMODE 0x96 +#define CMD_CALIBRATE 0x89 +#define CMD_CALIBRATEIMAGE 0x98 +#define CMD_SETPACONFIG 0x95 +#define CMD_SETRXTXFALLBACKMODE 0x93 // Commands to Access the Radio Registers and FIFO Buffer -#define CMD_WRITEREGISTER 0x0D -#define CMD_READREGISTER 0x1D -#define CMD_WRITEBUFFER 0x0E -#define CMD_READBUFFER 0x1E +#define CMD_WRITEREGISTER 0x0D +#define CMD_READREGISTER 0x1D +#define CMD_WRITEBUFFER 0x0E +#define CMD_READBUFFER 0x1E // Commands Controlling the Radio IRQs and DIOs -#define CMD_SETDIOIRQPARAMS 0x08 -#define CMD_GETIRQSTATUS 0x12 -#define CMD_CLEARIRQSTATUS 0x02 -#define CMD_SETDIO2ASRFSWITCHCTRL 0x9D -#define CMD_SETDIO3ASTCXOCTRL 0x97 +#define CMD_SETDIOIRQPARAMS 0x08 +#define CMD_GETIRQSTATUS 0x12 +#define CMD_CLEARIRQSTATUS 0x02 +#define CMD_SETDIO2ASRFSWITCHCTRL 0x9D +#define CMD_SETDIO3ASTCXOCTRL 0x97 // Commands Controlling the RF and Packets Settings -#define CMD_SETRFFREQUENCY 0x86 -#define CMD_SETPACKETTYPE 0x8A -#define CMD_GETPACKETTYPE 0x11 -#define CMD_SETTXPARAMS 0x8E -#define CMD_SETMODULATIONPARAMS 0x8B -#define CMD_SETPACKETPARAMS 0x8C -#define CMD_SETCADPARAMS 0x88 -#define CMD_SETBUFFERBASEADDRESS 0x8F -#define CMD_SETLORASYMBNUMTIMEOUT 0xA0 +#define CMD_SETRFFREQUENCY 0x86 +#define CMD_SETPACKETTYPE 0x8A +#define CMD_GETPACKETTYPE 0x11 +#define CMD_SETTXPARAMS 0x8E +#define CMD_SETMODULATIONPARAMS 0x8B +#define CMD_SETPACKETPARAMS 0x8C +#define CMD_SETCADPARAMS 0x88 +#define CMD_SETBUFFERBASEADDRESS 0x8F +#define CMD_SETLORASYMBNUMTIMEOUT 0xA0 // Commands Returning the Radio Status -#define CMD_GETSTATUS 0xC0 -#define CMD_GETRSSIINST 0x15 -#define CMD_GETRXBUFFERSTATUS 0x13 -#define CMD_GETPACKETSTATUS 0x14 -#define CMD_GETDEVICEERRORS 0x17 -#define CMD_CLEARDEVICEERRORS 0x07 -#define CMD_GETSTATS 0x10 -#define CMD_RESETSTATS 0x00 +#define CMD_GETSTATUS 0xC0 +#define CMD_GETRSSIINST 0x15 +#define CMD_GETRXBUFFERSTATUS 0x13 +#define CMD_GETPACKETSTATUS 0x14 +#define CMD_GETDEVICEERRORS 0x17 +#define CMD_CLEARDEVICEERRORS 0x07 +#define CMD_GETSTATS 0x10 +#define CMD_RESETSTATS 0x00 // ---------------------------------------- // List of Registers -#define REG_WHITENINGMSB 0x06B8 -#define REG_WHITENINGLSB 0x06B9 -#define REG_CRCINITVALMSB 0x06BC -#define REG_CRCINITVALLSB 0x06BD -#define REG_CRCPOLYVALMSB 0x06BE -#define REG_CRCPOLYVALLSB 0x06BF -#define REG_SYNCWORD0 0x06C0 -#define REG_SYNCWORD1 0x06C1 -#define REG_SYNCWORD2 0x06C2 -#define REG_SYNCWORD3 0x06C3 -#define REG_SYNCWORD4 0x06C4 -#define REG_SYNCWORD5 0x06C5 -#define REG_SYNCWORD6 0x06C6 -#define REG_SYNCWORD7 0x06C7 -#define REG_NODEADDRESS 0x06CD -#define REG_BROADCASTADDR 0x06CE -#define REG_LORASYNCWORDMSB 0x0740 -#define REG_LORASYNCWORDLSB 0x0741 -#define REG_RANDOMNUMBERGEN0 0x0819 -#define REG_RANDOMNUMBERGEN1 0x081A -#define REG_RANDOMNUMBERGEN2 0x081B -#define REG_RANDOMNUMBERGEN3 0x081C -#define REG_RXGAIN 0x08AC -#define REG_OCPCONFIG 0x08E7 -#define REG_XTATRIM 0x0911 -#define REG_XTBTRIM 0x0912 +#define REG_WHITENINGMSB 0x06B8 +#define REG_WHITENINGLSB 0x06B9 +#define REG_CRCINITVALMSB 0x06BC +#define REG_CRCINITVALLSB 0x06BD +#define REG_CRCPOLYVALMSB 0x06BE +#define REG_CRCPOLYVALLSB 0x06BF +#define REG_SYNCWORD0 0x06C0 +#define REG_SYNCWORD1 0x06C1 +#define REG_SYNCWORD2 0x06C2 +#define REG_SYNCWORD3 0x06C3 +#define REG_SYNCWORD4 0x06C4 +#define REG_SYNCWORD5 0x06C5 +#define REG_SYNCWORD6 0x06C6 +#define REG_SYNCWORD7 0x06C7 +#define REG_NODEADDRESS 0x06CD +#define REG_BROADCASTADDR 0x06CE +#define REG_LORASYNCWORDMSB 0x0740 +#define REG_LORASYNCWORDLSB 0x0741 +#define REG_RANDOMNUMBERGEN0 0x0819 +#define REG_RANDOMNUMBERGEN1 0x081A +#define REG_RANDOMNUMBERGEN2 0x081B +#define REG_RANDOMNUMBERGEN3 0x081C +#define REG_RXGAIN 0x08AC +#define REG_OCPCONFIG 0x08E7 +#define REG_XTATRIM 0x0911 +#define REG_XTBTRIM 0x0912 // sleep modes -#define SLEEP_COLD 0x00 // (no rtc timeout) -#define SLEEP_WARM 0x04 // (no rtc timeout) +#define SLEEP_COLD 0x00 // (no rtc timeout) +#define SLEEP_WARM 0x04 // (no rtc timeout) // standby modes -#define STDBY_RC 0x00 -#define STDBY_XOSC 0x01 +#define STDBY_RC 0x00 +#define STDBY_XOSC 0x01 // regulator modes -#define REGMODE_LDO 0x00 -#define REGMODE_DCDC 0x01 +#define REGMODE_LDO 0x00 +#define REGMODE_DCDC 0x01 // packet types -#define PACKET_TYPE_FSK 0x00 -#define PACKET_TYPE_LORA 0x01 +#define PACKET_TYPE_FSK 0x00 +#define PACKET_TYPE_LORA 0x01 // crc types -#define CRC_OFF 0x01 -#define CRC_1_BYTE 0x00 -#define CRC_2_BYTE 0x02 -#define CRC_1_BYTE_INV 0x04 -#define CRC_2_BYTE_INV 0x06 +#define CRC_OFF 0x01 +#define CRC_1_BYTE 0x00 +#define CRC_2_BYTE 0x02 +#define CRC_1_BYTE_INV 0x04 +#define CRC_2_BYTE_INV 0x06 // irq types -#define IRQ_TXDONE (1 << 0) -#define IRQ_RXDONE (1 << 1) -#define IRQ_PREAMBLEDETECTED (1 << 2) -#define IRQ_SYNCWORDVALID (1 << 3) -#define IRQ_HEADERVALID (1 << 4) -#define IRQ_HEADERERR (1 << 5) -#define IRQ_CRCERR (1 << 6) -#define IRQ_CADDONE (1 << 7) -#define IRQ_CADDETECTED (1 << 8) -#define IRQ_TIMEOUT (1 << 9) -#define IRQ_ALL 0x3FF +#define IRQ_TXDONE (1 << 0) +#define IRQ_RXDONE (1 << 1) +#define IRQ_PREAMBLEDETECTED (1 << 2) +#define IRQ_SYNCWORDVALID (1 << 3) +#define IRQ_HEADERVALID (1 << 4) +#define IRQ_HEADERERR (1 << 5) +#define IRQ_CRCERR (1 << 6) +#define IRQ_CADDONE (1 << 7) +#define IRQ_CADDETECTED (1 << 8) +#define IRQ_TIMEOUT (1 << 9) +#define IRQ_ALL 0x3FF // TCXO voltages (limited to VDD - 200mV) -#define TCXO_VOLTAGE1_6V 0x00 -#define TCXO_VOLTAGE1_7V 0x01 -#define TCXO_VOLTAGE1_8V 0x02 -#define TCXO_VOLTAGE2_2V 0x03 -#define TCXO_VOLTAGE2_4V 0x04 -#define TCXO_VOLTAGE2_7V 0x05 -#define TCXO_VOLTAGE3_0V 0x06 -#define TCXO_VOLTAGE3_3V 0x07 +#define TCXO_VOLTAGE1_6V 0x00 +#define TCXO_VOLTAGE1_7V 0x01 +#define TCXO_VOLTAGE1_8V 0x02 +#define TCXO_VOLTAGE2_2V 0x03 +#define TCXO_VOLTAGE2_4V 0x04 +#define TCXO_VOLTAGE2_7V 0x05 +#define TCXO_VOLTAGE3_0V 0x06 +#define TCXO_VOLTAGE3_3V 0x07 // XXX: These should probably be configurable // XXX: The startup time delays TX/RX by 320*15.625=5ms, maybe switch on @@ -348,20 +348,20 @@ static void SetPacketType (uint8_t type) { // calibrate the image rejection static void CalibrateImage (uint32_t freq) { static const struct { - uint32_t min; - uint32_t max; - uint8_t freq[2]; + uint32_t min; + uint32_t max; + uint8_t freq[2]; } bands[] = { - { 430000000, 440000000, { 0x6B, 0x6F } }, - { 470000000, 510000000, { 0x75, 0x81 } }, - { 779000000, 787000000, { 0xC1, 0xC5 } }, - { 863000000, 870000000, { 0xD7, 0xDB } }, - { 902000000, 928000000, { 0xE1, 0xE9 } }, + { 430000000, 440000000, { 0x6B, 0x6F } }, + { 470000000, 510000000, { 0x75, 0x81 } }, + { 779000000, 787000000, { 0xC1, 0xC5 } }, + { 863000000, 870000000, { 0xD7, 0xDB } }, + { 902000000, 928000000, { 0xE1, 0xE9 } }, }; for (size_t i = 0; i < sizeof(bands) / sizeof(bands[0]); i++) { - if (freq >= bands[i].min && freq <= bands[i].max) { - writecmd(CMD_CALIBRATEIMAGE, bands[i].freq, 2); - } + if (freq >= bands[i].min && freq <= bands[i].max) { + writecmd(CMD_CALIBRATEIMAGE, bands[i].freq, 2); + } } } @@ -538,8 +538,8 @@ static uint32_t GetRandom (void) { void radio_sleep (void) { // cache sleep state to avoid unneccessary wakeup (waking up from cold sleep takes about 4ms) if (state.sleeping == 0) { - SetSleep(SLEEP_COLD); - state.sleeping = 1; + SetSleep(SLEEP_COLD); + state.sleeping = 1; } } @@ -616,20 +616,20 @@ void radio_cw (void) { void radio_starttx (bool txcontinuous) { if (txcontinuous) { - // XXX: This is probably not right. In 2.2, Semtech changed the - // 127x driver to rename txsw to radio_cw, but - // radio_starttx(true) now uses txfsk/txlora in continuous mode - // (which is apparently different from radio_cw), so that needs - // to be impliemented here as well - radio_cw(); + // XXX: This is probably not right. In 2.2, Semtech changed the + // 127x driver to rename txsw to radio_cw, but + // radio_starttx(true) now uses txfsk/txlora in continuous mode + // (which is apparently different from radio_cw), so that needs + // to be impliemented here as well + radio_cw(); } else { - if (getSf(LMIC.rps) == FSK) { // FSK modem - txfsk(); - } else { // LoRa modem - txlora(); - } - // the radio will go back to STANDBY mode as soon as the TX is finished - // the corresponding IRQ will inform us about completion. + if (getSf(LMIC.rps) == FSK) { // FSK modem + txfsk(); + } else { // LoRa modem + txlora(); + } + // the radio will go back to STANDBY mode as soon as the TX is finished + // the corresponding IRQ will inform us about completion. } } @@ -667,19 +667,19 @@ static void rxfsk (bool rxcontinuous) { // now receive (lock interrupts only for final fine tuned rx timing...) hal_disableIRQs(); if (rxcontinuous) { // continous rx - BACKTRACE(); - // enable antenna switch for RX (and account power consumption) - hal_ant_switch(HAL_ANTSW_RX); - // rx infinitely (no timeout, until rxdone, will be restarted) - SetRx(0); + BACKTRACE(); + // enable antenna switch for RX (and account power consumption) + hal_ant_switch(HAL_ANTSW_RX); + // rx infinitely (no timeout, until rxdone, will be restarted) + SetRx(0); } else { // single rx - BACKTRACE(); - // busy wait until exact rx time + BACKTRACE(); + // busy wait until exact rx time hal_waitUntil(LMIC.rxtime); - // enable antenna switch for RX (and account power consumption) - hal_ant_switch(HAL_ANTSW_RX); - // rx for max LMIC.rxsyms symbols (rxsyms = nbytes for FSK) - SetRx((LMIC.rxsyms << 9) / 50); // nbytes * 8 * 64 * 1000 / 50000 + // enable antenna switch for RX (and account power consumption) + hal_ant_switch(HAL_ANTSW_RX); + // rx for max LMIC.rxsyms symbols (rxsyms = nbytes for FSK) + SetRx((LMIC.rxsyms << 9) / 50); // nbytes * 8 * 64 * 1000 / 50000 } hal_enableIRQs(); } @@ -712,26 +712,26 @@ static void rxlora (bool rxcontinuous) { if (!rxcontinuous && LMIC.rxtime - now < 0) { // Print before disabling IRQs, to work around deadlock on some // Arduino cores that doe not really support printing without IRQs - debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", now - LMIC.rxtime, osticks2ms(now - t0), now - t0); } // now receive (lock interrupts only for final fine tuned rx timing...) hal_disableIRQs(); if (rxcontinuous) { // continous rx - BACKTRACE(); - // enable antenna switch for RX (and account power consumption) - hal_ant_switch(HAL_ANTSW_RX); - // rx infinitely (no timeout, until rxdone, will be restarted) - SetRx(0); + BACKTRACE(); + // enable antenna switch for RX (and account power consumption) + hal_ant_switch(HAL_ANTSW_RX); + // rx infinitely (no timeout, until rxdone, will be restarted) + SetRx(0); } else { // single rx - BACKTRACE(); - // busy wait until exact rx time + BACKTRACE(); + // busy wait until exact rx time hal_waitUntil(LMIC.rxtime); - // enable antenna switch for RX (and account power consumption) - hal_ant_switch(HAL_ANTSW_RX); - // rx for max LMIC.rxsyms symbols - SetRx(0); // (infinite, timeout set via SetLoRaSymbNumTimeout) + // enable antenna switch for RX (and account power consumption) + hal_ant_switch(HAL_ANTSW_RX); + // rx for max LMIC.rxsyms symbols + SetRx(0); // (infinite, timeout set via SetLoRaSymbNumTimeout) } hal_enableIRQs(); } @@ -788,7 +788,7 @@ void radio_init (bool calibrate) { ASSERT( ReadReg(REG_LORASYNCWORDLSB) == 0x24 ); if (calibrate) { - CalibrateImage(LMIC.freq); + CalibrateImage(LMIC.freq); } // go to SLEEP mode @@ -805,56 +805,56 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { // dispatch modem if (getSf(LMIC.rps) == FSK) { // FSK modem - if (irqflags & IRQ_TXDONE) { // TXDONE - BACKTRACE(); + if (irqflags & IRQ_TXDONE) { // TXDONE + BACKTRACE(); // save exact tx time LMIC.txend = irqtime - FSK_TXDONE_FIXUP; } else if (irqflags & IRQ_RXDONE) { // RXDONE - BACKTRACE(); + BACKTRACE(); // read rx quality parameters - LMIC.rssi = GetPacketStatusFsk(); - LMIC.snr = 0; // N/A + LMIC.rssi = GetPacketStatusFsk(); + LMIC.snr = 0; // N/A - // read FIFO - LMIC.dataLen = ReadFifo(LMIC.frame); + // read FIFO + LMIC.dataLen = ReadFifo(LMIC.frame); // save exact rx timestamps LMIC.rxtime = irqtime - FSK_RXDONE_FIXUP; // end of frame timestamp - LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp + LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n", - LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); + debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n", + LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif - } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT - BACKTRACE(); + } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT + BACKTRACE(); // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK]: TIMEOUT\r\n", LMIC.freq, 6); + debug_printf("RX[freq=%.1F,FSK]: TIMEOUT\r\n", LMIC.freq, 6); #endif } else { - // unexpected irq - debug_printf("UNEXPECTED RADIO IRQ %04x (after %ld ticks, %.1Fms)\r\n", irqflags, irqtime - LMIC.rxtime, osticks2us(irqtime - LMIC.rxtime), 3); - TRACE_VAL(irqflags); - ASSERT(0); - } + // unexpected irq + debug_printf("UNEXPECTED RADIO IRQ %04x (after %ld ticks, %.1Fms)\r\n", irqflags, irqtime - LMIC.rxtime, osticks2us(irqtime - LMIC.rxtime), 3); + TRACE_VAL(irqflags); + ASSERT(0); + } } else { // LORA modem - if (irqflags & IRQ_TXDONE) { // TXDONE - BACKTRACE(); + if (irqflags & IRQ_TXDONE) { // TXDONE + BACKTRACE(); // save exact tx time LMIC.txend = irqtime - LORA_TXDONE_FIXUP; } else if (irqflags & IRQ_RXDONE) { // RXDONE - BACKTRACE(); + BACKTRACE(); // read rx quality parameters - GetPacketStatusLora(&LMIC.rssi, &LMIC.snr); + GetPacketStatusLora(&LMIC.rssi, &LMIC.snr); - // read FIFO - LMIC.dataLen = ReadFifo(LMIC.frame); + // read FIFO + LMIC.dataLen = ReadFifo(LMIC.frame); // save exact rx timestamps LMIC.rxtime = irqtime; // end of frame timestamp @@ -864,27 +864,27 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { else if (getBw(LMIC.rps) == BW500) { LMIC.rxtime -= LORA_RXDONE_FIXUP_500[getSf(LMIC.rps)]; } - LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp + LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %h\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), - LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, - LMIC.dataLen, LMIC.frame, LMIC.dataLen); + debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %h\r\n", + LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), + LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, + LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif - } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT - BACKTRACE(); + } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT + BACKTRACE(); // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps)); + debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT\r\n", + LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps)); #endif } else { - // unexpected irq - debug_printf("UNEXPECTED RADIO IRQ %04x\r\n", irqflags); - TRACE_VAL(irqflags); - ASSERT(0); - } + // unexpected irq + debug_printf("UNEXPECTED RADIO IRQ %04x\r\n", irqflags); + TRACE_VAL(irqflags); + ASSERT(0); + } } // mask all IRQs diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 3311a80..bf5b1a4 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -373,35 +373,35 @@ static void setopmode (u1_t opmode) { writeReg(RegOpMode, opmode); ostime_t t0 = os_getTime(); while (readReg(RegOpMode) != opmode) { - if (os_getTime() - t0 > ms2osticks(20)) { - // panic when opmode is not reached within 20ms - debug_printf("FAILED TO SET OPMODE %02x within 20ms\r\n", opmode); - ASSERT(0); - } + if (os_getTime() - t0 > ms2osticks(20)) { + // panic when opmode is not reached within 20ms + debug_printf("FAILED TO SET OPMODE %02x within 20ms\r\n", opmode); + ASSERT(0); + } } } // fill fifo when empty static void LoadFifo (void) { if (state.fifolen > 0) { - int n = (state.fifolen > FIFOTHRESH) ? FIFOTHRESH : state.fifolen; - radio_writeBuf(RegFifo, state.fifoptr, n); - state.fifoptr += n; - state.fifolen -= n; + int n = (state.fifolen > FIFOTHRESH) ? FIFOTHRESH : state.fifolen; + radio_writeBuf(RegFifo, state.fifoptr, n); + state.fifoptr += n; + state.fifolen -= n; } } // read fifo when level or ready static void UnloadFifo (void) { if (state.fifolen < 0) { // first byte - state.fifolen = 0; - radio_readBuf(RegFifo, &LMIC.dataLen, 1); + state.fifolen = 0; + radio_readBuf(RegFifo, &LMIC.dataLen, 1); } int n = (LMIC.dataLen - state.fifolen > (FIFOTHRESH-1)) ? (FIFOTHRESH-1) : (LMIC.dataLen - state.fifolen); // errata: unload one byte less if (n) { - radio_readBuf(RegFifo, state.fifoptr, n); - state.fifoptr += n; - state.fifolen += n; + radio_readBuf(RegFifo, state.fifoptr, n); + state.fifoptr += n; + state.fifolen += n; } } @@ -410,47 +410,47 @@ static void configLoraModem (bool txcont) { #if defined(BRD_sx1276_radio) // set ModemConfig1 'bbbbccch' (bw=xxxx, cr=xxx, implicitheader=x) writeReg(LORARegModemConfig1, - ((getBw(LMIC.rps) + 7) << 4) | // BW125=0 --> 7 - ((getCr(LMIC.rps) + 1) << 1) | // CR4_5=0 --> 1 - (getIh(LMIC.rps) != 0)); // implicit header + ((getBw(LMIC.rps) + 7) << 4) | // BW125=0 --> 7 + ((getCr(LMIC.rps) + 1) << 1) | // CR4_5=0 --> 1 + (getIh(LMIC.rps) != 0)); // implicit header // set ModemConfig2 'sssstcmm' (sf=xxxx, txcont=x, rxpayloadcrc=x, symtimeoutmsb=00) writeReg(LORARegModemConfig2, - ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7 + ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7 (txcont ? 0x08 : 0x00) | // txcont: 0x08 - ((getNocrc(LMIC.rps) == 0) << 2)); // rxcrc + ((getNocrc(LMIC.rps) == 0) << 2)); // rxcrc // set ModemConfig3 'uuuuoarr' (unused=0000, lowdatarateoptimize=x, agcauto=1, reserved=00) writeReg(LORARegModemConfig3, - (enDro(LMIC.rps) << 3) | // symtime >= 16ms - (1 << 2)); // autoagc + (enDro(LMIC.rps) << 3) | // symtime >= 16ms + (1 << 2)); // autoagc // SX1276 Errata: 2.1 Sensitivity Optimization with a 500kHz Bandwith if (getBw(LMIC.rps) == BW500) { - writeReg(0x36, 0x02); - writeReg(0x3A, 0x64); + writeReg(0x36, 0x02); + writeReg(0x3A, 0x64); } else { - writeReg(0x36, 0x03); - // no need to reset register 0x3a + writeReg(0x36, 0x03); + // no need to reset register 0x3a } #elif defined(BRD_sx1272_radio) // set ModemConfig1 'bbccchco' (bw=xx, cr=xxx, implicitheader=x, rxpayloadcrc=x, lowdatarateoptimize=x) writeReg(LORARegModemConfig1, - (getBw(LMIC.rps) << 6) | // BW125=0 --> 0 - ((getCr(LMIC.rps) + 1) << 3) | // CR4_5=0 --> 1 - ((getIh(LMIC.rps) != 0) << 2) | // implicit header - ((getNocrc(LMIC.rps) == 0) << 1) | // rxcrc - enDro(LMIC.rps)); // symtime >= 16ms + (getBw(LMIC.rps) << 6) | // BW125=0 --> 0 + ((getCr(LMIC.rps) + 1) << 3) | // CR4_5=0 --> 1 + ((getIh(LMIC.rps) != 0) << 2) | // implicit header + ((getNocrc(LMIC.rps) == 0) << 1) | // rxcrc + enDro(LMIC.rps)); // symtime >= 16ms // set ModemConfig2 'sssstamm' (sf=xxxx, txcont=x, agcauto=1 symtimeoutmsb=00) writeReg(LORARegModemConfig2, - ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7 + ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7 (txcont ? 0x08 : 0x00) | // txcont: 0x08 - (1 << 2)); // autoagc + (1 << 2)); // autoagc #endif // BRD_sx1272_radio if (getIh(LMIC.rps)) { - writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length + writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length } } @@ -552,47 +552,47 @@ static void configPower (int pw) { // XXX - TODO - externalize this somehow // wailmer/wailord can only use 17dBm at DR4 (US) if (getBw(LMIC.rps) == BW500 && pw > 17) { - pw = 17; + pw = 17; } #endif if (BRD_PABOOSTSEL(LMIC.freq, pw)) { // use PA_BOOST - if (pw > 17) { // use high-power +20dBm option - if (pw > 20) { - pw = 20; - } - writeReg(RegPaDac, 0x87); // high power - writeReg(RegPaConfig, 0x80 | (pw - 5)); // BOOST (5..20dBm) - } else { - if (pw < 2) { - pw = 2; - } - writeReg(RegPaDac, 0x84); // normal power - writeReg(RegPaConfig, 0x80 | (pw - 2)); // BOOST (2..17dBm) - } + if (pw > 17) { // use high-power +20dBm option + if (pw > 20) { + pw = 20; + } + writeReg(RegPaDac, 0x87); // high power + writeReg(RegPaConfig, 0x80 | (pw - 5)); // BOOST (5..20dBm) + } else { + if (pw < 2) { + pw = 2; + } + writeReg(RegPaDac, 0x84); // normal power + writeReg(RegPaConfig, 0x80 | (pw - 2)); // BOOST (2..17dBm) + } setRadioConsumption_ua(true, pw); } else { // use PA_RFO #if defined(BRD_sx1276_radio) - if (pw > 0) { - if (pw > 15) { - pw = 15; - } - writeReg(RegPaConfig, 0x70 | pw); // RFO, maxpower=111 (0..15dBm) - } else { - if (pw < -4) { - pw = -4; - } - writeReg(RegPaConfig, pw + 4); // RFO, maxpower=000 (-4..11dBm) - } - writeReg(RegPaDac, 0x84); // normal power + if (pw > 0) { + if (pw > 15) { + pw = 15; + } + writeReg(RegPaConfig, 0x70 | pw); // RFO, maxpower=111 (0..15dBm) + } else { + if (pw < -4) { + pw = -4; + } + writeReg(RegPaConfig, pw + 4); // RFO, maxpower=000 (-4..11dBm) + } + writeReg(RegPaDac, 0x84); // normal power #elif defined(BRD_sx1272_radio) - if (pw < -1) { - pw = -1; - } else if (pw > 14) { - pw = 14; - } - writeReg(RegPaConfig, pw + 1); // RFO (-1..14dBm) - writeReg(RegPaDac, 0x84); // normal power + if (pw < -1) { + pw = -1; + } else if (pw > 14) { + pw = 14; + } + writeReg(RegPaConfig, pw + 1); // RFO (-1..14dBm) + writeReg(RegPaDac, 0x84); // normal power #endif setRadioConsumption_ua(false, (pw < 0) ? 0 : pw); } @@ -604,9 +604,9 @@ static void configPower (int pw) { static void power_tcxo (void) { // power-up TCXO and set tcxo as input if ( hal_pin_tcxo(1) ) { - writeReg(RegTcxo, 0b00011001); // reserved=000, tcxo=1, reserved=1001 - // delay to allow TCXO to wake up - hal_waitUntil(os_getTime() + ms2osticks(1)); + writeReg(RegTcxo, 0b00011001); // reserved=000, tcxo=1, reserved=1001 + // delay to allow TCXO to wake up + hal_waitUntil(os_getTime() + ms2osticks(1)); } } @@ -694,11 +694,11 @@ static void txfsk (bool txcont) { LoadFifo(); if (!txcont) { - // enable IRQs in HAL - hal_irqmask_set(HAL_IRQMASK_DIO0 | HAL_IRQMASK_DIO1); + // enable IRQs in HAL + hal_irqmask_set(HAL_IRQMASK_DIO0 | HAL_IRQMASK_DIO1); - // set tx timeout - radio_set_irq_timeout(os_getTime() + us2osticks((FIFOTHRESH+10)*8*1000/50)); + // set tx timeout + radio_set_irq_timeout(os_getTime() + us2osticks((FIFOTHRESH+10)*8*1000/50)); } // enable antenna switch for TX @@ -859,8 +859,8 @@ static void rxlorasingle (void) { hal_enableIRQs(); // warn about delayed rx if( rxtime - now < 0 ) { - debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", - now - rxtime, osticks2ms(now - t0), now - t0); + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", + now - rxtime, osticks2ms(now - t0), now - t0); } } @@ -980,22 +980,22 @@ static void rxfsk (bool rxcontinuous) { hal_disableIRQs(); if (rxcontinuous) { - BACKTRACE(); - // XXX not suppported - receiver does not automatically restart - radio_set_irq_timeout(os_getTime() + sec2osticks(5)); // time out after 5 sec + BACKTRACE(); + // XXX not suppported - receiver does not automatically restart + radio_set_irq_timeout(os_getTime() + sec2osticks(5)); // time out after 5 sec } else { - BACKTRACE(); - // set preamble timeout - writeReg(FSKRegRxTimeout2, (LMIC.rxsyms + 1) / 2); // (TimeoutRxPreamble * 16 * Tbit) - // set rx timeout - radio_set_irq_timeout(LMIC.rxtime + us2osticks((2*FIFOTHRESH)*8*1000/50)); - // busy wait until exact rx time - ostime_t now = os_getTime(); - if (LMIC.rxtime - now < 0) { - debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", - now - LMIC.rxtime, osticks2ms(now - t0), now - t0); - } - hal_waitUntil(LMIC.rxtime); + BACKTRACE(); + // set preamble timeout + writeReg(FSKRegRxTimeout2, (LMIC.rxsyms + 1) / 2); // (TimeoutRxPreamble * 16 * Tbit) + // set rx timeout + radio_set_irq_timeout(LMIC.rxtime + us2osticks((2*FIFOTHRESH)*8*1000/50)); + // busy wait until exact rx time + ostime_t now = os_getTime(); + if (LMIC.rxtime - now < 0) { + debug_printf("WARNING: rxtime is %ld ticks in the past! (ramp-up time %ld ms / %ld ticks)\r\n", + now - LMIC.rxtime, osticks2ms(now - t0), now - t0); + } + hal_waitUntil(LMIC.rxtime); } // enable antenna switch for RX (and account power consumption) @@ -1016,10 +1016,10 @@ void radio_startrx (bool rxcontinuous) { rxfsk(rxcontinuous); } else { // LoRa modem if (rxcontinuous) { - rxloracont(); - } else { - rxlorasingle(); - } + rxloracont(); + } else { + rxlorasingle(); + } } } @@ -1059,7 +1059,7 @@ void radio_cca (void) { // set receiver bandwidth (SSB) writeReg(FSKRegRxBw, (getSf(LMIC.rps) == FSK) ? 0x0B /* 50kHz SSB */ : - 3 - getBw(LMIC.rps)); // 62.5/125/250kHz SSB (RxBwMant=0, RxBwExp 3/2/1) + 3 - getBw(LMIC.rps)); // 62.5/125/250kHz SSB (RxBwMant=0, RxBwExp 3/2/1) // set power consumption for statistics LMIC.radioPwr_ua = 11500; @@ -1079,9 +1079,9 @@ void radio_cca (void) { // sample rssi values do { rssi = -readReg(FSKRegRssiValue) / 2 + RSSI_OFF; - if (rssi > rssi_max) { - rssi_max = rssi; - } + if (rssi > rssi_max) { + rssi_max = rssi; + } } while (rssi < rssi_th && os_getTime() - t0 < LMIC.rxtime); // return max observed rssi value @@ -1140,12 +1140,12 @@ void radio_init (bool calibrate) { // optionally perform receiver chain calibration in FSK/STANDBY mode if (calibrate) { - // set band/frequency - configChannel(); + // set band/frequency + configChannel(); - // run receiver chain calibration - writeReg(FSKRegImageCal, RF_IMAGECAL_IMAGECAL_START); // (clear auto-cal) - while ( readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING ); + // run receiver chain calibration + writeReg(FSKRegImageCal, RF_IMAGECAL_IMAGECAL_START); // (clear auto-cal) + while ( readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING ); } // go to SLEEP mode @@ -1161,94 +1161,94 @@ void radio_init (bool calibrate) { bool radio_irq_process (ostime_t irqtime, u1_t diomask) { // dispatch modem if (getSf(LMIC.rps) == FSK) { // FSK modem - u1_t irqflags1 = readReg(FSKRegIrqFlags1); - u1_t irqflags2 = readReg(FSKRegIrqFlags2); + u1_t irqflags1 = readReg(FSKRegIrqFlags1); + u1_t irqflags2 = readReg(FSKRegIrqFlags2); - if (irqflags2 & IRQ_FSK2_PACKETSENT_MASK) { // TXDONE - BACKTRACE(); + if (irqflags2 & IRQ_FSK2_PACKETSENT_MASK) { // TXDONE + BACKTRACE(); // save exact tx time LMIC.txend = irqtime - FSK_TXDONE_FIXUP; - } else if (irqflags2 & IRQ_FSK2_PAYLOADREADY_MASK) { // RXDONE - BACKTRACE(); + } else if (irqflags2 & IRQ_FSK2_PAYLOADREADY_MASK) { // RXDONE + BACKTRACE(); // read rx quality parameters (at end of packet, not optimal since energy might already be gone) - // (unfortunately in SX1272/SX1276 no averaged RSSI available in FSK mode, better in SX1261) - LMIC.rssi = -readReg(FSKRegRssiValue) / 2 + RSSI_OFF; - LMIC.snr = 0; // N/A + // (unfortunately in SX1272/SX1276 no averaged RSSI available in FSK mode, better in SX1261) + LMIC.rssi = -readReg(FSKRegRssiValue) / 2 + RSSI_OFF; + LMIC.snr = 0; // N/A - // read FIFO - UnloadFifo(); + // read FIFO + UnloadFifo(); // save exact rx timestamps LMIC.rxtime = irqtime - FSK_RXDONE_FIXUP; // end of frame timestamp - LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp + LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n", - LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); + debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n", + LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif - } else if (irqflags1 & IRQ_FSK1_TIMEOUT_MASK) { // TIMEOUT - BACKTRACE(); + } else if (irqflags1 & IRQ_FSK1_TIMEOUT_MASK) { // TIMEOUT + BACKTRACE(); // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK]: TIMEOUT (%d us)\r\n", LMIC.freq, 6, osticks2us(irqtime - LMIC.rxtime)); + debug_printf("RX[freq=%.1F,FSK]: TIMEOUT (%d us)\r\n", LMIC.freq, 6, osticks2us(irqtime - LMIC.rxtime)); #endif - } else if( irqflags2 & IRQ_FSK2_FIFOEMPTY_MASK ) { // FIFOEMPTY (TX) - BACKTRACE(); + } else if( irqflags2 & IRQ_FSK2_FIFOEMPTY_MASK ) { // FIFOEMPTY (TX) + BACKTRACE(); - // fill FIFO buffer - LoadFifo(); + // fill FIFO buffer + LoadFifo(); - // update tx timeout - radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50)); + // update tx timeout + radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50)); - // keep waiting for FifoEmpty or PacketSent interrupt - return false; + // keep waiting for FifoEmpty or PacketSent interrupt + return false; - } else if( irqflags2 & IRQ_FSK2_FIFOLEVEL_MASK ) { // FIFOLEVEL (RX) - BACKTRACE(); + } else if( irqflags2 & IRQ_FSK2_FIFOLEVEL_MASK ) { // FIFOLEVEL (RX) + BACKTRACE(); - // read FIFO buffer - UnloadFifo(); + // read FIFO buffer + UnloadFifo(); - // update rx timeout - radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50)); + // update rx timeout + radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50)); - // keep waiting for FifoLevel or PayloadReady interrupt - return false; + // keep waiting for FifoLevel or PayloadReady interrupt + return false; - } else { - // unexpected irq - debug_printf("UNEXPECTED FSK IRQ %02x %02x\r\n", irqflags1, irqflags2); - ASSERT(0); - } + } else { + // unexpected irq + debug_printf("UNEXPECTED FSK IRQ %02x %02x\r\n", irqflags1, irqflags2); + ASSERT(0); + } - // clear FSK IRQ flags - writeReg(FSKRegIrqFlags1, 0xFF); - writeReg(FSKRegIrqFlags2, 0xFF); + // clear FSK IRQ flags + writeReg(FSKRegIrqFlags1, 0xFF); + writeReg(FSKRegIrqFlags2, 0xFF); } else { // LORA modem - u1_t irqflags = readReg(LORARegIrqFlags); + u1_t irqflags = readReg(LORARegIrqFlags); if (irqflags & IRQ_LORA_TXDONE_MASK) { // TXDONE - BACKTRACE(); + BACKTRACE(); // save exact tx time LMIC.txend = irqtime - LORA_TXDONE_FIXUP; } else if (irqflags & IRQ_LORA_RXDONE_MASK) { // RXDONE (rx or scan) - BACKTRACE(); + BACKTRACE(); // read rx quality parameters (averaged over packet) LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4 - LMIC.rssi = readReg(LORARegPktRssiValue); // final values -128..127 correspond to -196...+63 dBm (subtract RSSI_OFF) - if (LMIC.snr < 0) { - LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi + LMIC.snr/4 + RSSI_OFF; - } else { - LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi * 16/15 + RSSI_OFF; - } + LMIC.rssi = readReg(LORARegPktRssiValue); // final values -128..127 correspond to -196...+63 dBm (subtract RSSI_OFF) + if (LMIC.snr < 0) { + LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi + LMIC.snr/4 + RSSI_OFF; + } else { + LMIC.rssi = -RSSI_HF_CONST + LMIC.rssi * 16/15 + RSSI_OFF; + } // get PDU length LMIC.dataLen = readReg(LORARegRxNbBytes); @@ -1261,49 +1261,49 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { else if (getBw(LMIC.rps) == BW500) { LMIC.rxtime -= LORA_RXDONE_FIXUP_500[getSf(LMIC.rps)]; } - LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp + LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp - // set FIFO read address pointer (to address of last packet received) - writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); + // set FIFO read address pointer (to address of last packet received) + writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr)); - // read FIFO - radio_readBuf(RegFifo, LMIC.frame, LMIC.dataLen); + // read FIFO + radio_readBuf(RegFifo, LMIC.frame, LMIC.dataLen); #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %.80h\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), - LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, - LMIC.dataLen, LMIC.frame, LMIC.dataLen); + debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %.80h\r\n", + LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), + LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, + LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif - } else if (irqflags & IRQ_LORA_RXTOUT_MASK) { // RXTOUT - BACKTRACE(); + } else if (irqflags & IRQ_LORA_RXTOUT_MASK) { // RXTOUT + BACKTRACE(); // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT (%d us)\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), osticks2us(irqtime - LMIC.rxtime)); + debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT (%d us)\r\n", + LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), osticks2us(irqtime - LMIC.rxtime)); #endif - } else if (irqflags & IRQ_LORA_CDDONE_MASK) { // CDDONE - BACKTRACE(); - // check if preamble symbol was detected - if (irqflags & IRQ_LORA_CDDETD_MASK) { - // switch to receiving (continuous) - writeReg(RegOpMode, OPMODE_LORA_RX); - // continue waiting - return false; - } else { - // indicate timeout - LMIC.dataLen = 0; - } + } else if (irqflags & IRQ_LORA_CDDONE_MASK) { // CDDONE + BACKTRACE(); + // check if preamble symbol was detected + if (irqflags & IRQ_LORA_CDDETD_MASK) { + // switch to receiving (continuous) + writeReg(RegOpMode, OPMODE_LORA_RX); + // continue waiting + return false; + } else { + // indicate timeout + LMIC.dataLen = 0; + } } else { - // unexpected irq - ASSERT(0); - } + // unexpected irq + ASSERT(0); + } - // mask all LoRa IRQs - writeReg(LORARegIrqFlagsMask, 0xFF); + // mask all LoRa IRQs + writeReg(LORARegIrqFlagsMask, 0xFF); - // clear LoRa IRQ flags - writeReg(LORARegIrqFlags, 0xFF); + // clear LoRa IRQ flags + writeReg(LORARegIrqFlags, 0xFF); } // radio operation completed diff --git a/lmic/radio.c b/lmic/radio.c index 6ce1129..2bee51e 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -48,7 +48,7 @@ static void radio_irq_timeout (osjob_t* j) { // re-initialize radio if tx operation timed out if (state.txmode) { - radio_init(true); + radio_init(true); } // enable IRQs! @@ -73,11 +73,11 @@ static void radio_irq_func (osjob_t* j) { (void)j; // unused // call radio-specific processing function if( radio_irq_process(state.irqtime, state.diomask) ) { - // current radio operation has completed - radio_stop(); // (disable antenna switch and HAL irqs, make radio sleep) + // current radio operation has completed + radio_stop(); // (disable antenna switch and HAL irqs, make radio sleep) - // run LMIC job (use preset func ptr) - os_setCallback(&LMIC.osjob, LMIC.osjob.func); + // run LMIC job (use preset func ptr) + os_setCallback(&LMIC.osjob, LMIC.osjob.func); } // clear irq state (job has been run) @@ -103,82 +103,82 @@ void radio_irq_handler (u1_t diomask, ostime_t ticks) { void os_radio (u1_t mode) { switch (mode) { - case RADIO_STOP: - radio_stop(); - break; + case RADIO_STOP: + radio_stop(); + break; - case RADIO_TX: - radio_stop(); + case RADIO_TX: + radio_stop(); #ifdef DEBUG_TX - debug_printf("TX[fcnt=%ld,freq=%.1F,sf=%d,bw=%d,pow=%d,len=%d%s]: %.80h\r\n", - LMIC.seqnoUp - 1, LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), - LMIC.txpow, LMIC.dataLen, - (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", - LMIC.frame, LMIC.dataLen); + debug_printf("TX[fcnt=%ld,freq=%.1F,sf=%d,bw=%d,pow=%d,len=%d%s]: %.80h\r\n", + LMIC.seqnoUp - 1, LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), + LMIC.txpow, LMIC.dataLen, + (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", + LMIC.frame, LMIC.dataLen); #endif - // transmit frame now (wait for completion interrupt) - radio_starttx(false); - // set timeout for tx operation (should not happen) - state.txmode = 1; - radio_set_irq_timeout(os_getTime() + ms2osticks(20) + LMIC_calcAirTime(LMIC.rps, LMIC.dataLen) * 110 / 100); - break; - - case RADIO_RX: - radio_stop(); + // transmit frame now (wait for completion interrupt) + radio_starttx(false); + // set timeout for tx operation (should not happen) + state.txmode = 1; + radio_set_irq_timeout(os_getTime() + ms2osticks(20) + LMIC_calcAirTime(LMIC.rps, LMIC.dataLen) * 110 / 100); + break; + + case RADIO_RX: + radio_stop(); #ifdef DEBUG_RX - debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%s,rxtime=%.0F]\r\n", - LMIC.freq, 6, - getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), - LMIC.rxtime, 0); + debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%s,rxtime=%.0F]\r\n", + LMIC.freq, 6, + getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), + LMIC.rxtime, 0); #endif - // receive frame at rxtime/now (wait for completion interrupt) - radio_startrx(false); - // set timeout for rx operation (should not happen, might be updated by radio driver) - state.txmode = 0; - radio_set_irq_timeout(LMIC.rxtime + ms2osticks(5) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100); - break; - - case RADIO_RXON: - radio_stop(); + // receive frame at rxtime/now (wait for completion interrupt) + radio_startrx(false); + // set timeout for rx operation (should not happen, might be updated by radio driver) + state.txmode = 0; + radio_set_irq_timeout(LMIC.rxtime + ms2osticks(5) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100); + break; + + case RADIO_RXON: + radio_stop(); #ifdef DEBUG_RX - debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%s]\r\n", - LMIC.freq, 6, - getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps))); + debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%s]\r\n", + LMIC.freq, 6, + getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps))); #endif - // start scanning for frame now (wait for completion interrupt) - state.txmode = 0; - radio_startrx(true); - break; - - case RADIO_TXCW: - radio_stop(); - // transmit continuous wave (until abort) - radio_cw(); - break; - - case RADIO_CCA: - radio_stop(); - // clear channel assessment - radio_cca(); - break; - - case RADIO_INIT: - // reset and calibrate radio (uses LMIC.freq) - radio_init(true); - break; - - case RADIO_TXCONT: - radio_stop(); - radio_starttx(true); - break; - - case RADIO_CAD: - radio_stop(); - // set timeout for cad/rx operation (should not happen, might be updated by radio driver) - state.txmode = 0; - radio_set_irq_timeout(os_getTime() + ms2osticks(10) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100); - // channel activity detection and rx if preamble symbol found - radio_cad(); - break; + // start scanning for frame now (wait for completion interrupt) + state.txmode = 0; + radio_startrx(true); + break; + + case RADIO_TXCW: + radio_stop(); + // transmit continuous wave (until abort) + radio_cw(); + break; + + case RADIO_CCA: + radio_stop(); + // clear channel assessment + radio_cca(); + break; + + case RADIO_INIT: + // reset and calibrate radio (uses LMIC.freq) + radio_init(true); + break; + + case RADIO_TXCONT: + radio_stop(); + radio_starttx(true); + break; + + case RADIO_CAD: + radio_stop(); + // set timeout for cad/rx operation (should not happen, might be updated by radio driver) + state.txmode = 0; + radio_set_irq_timeout(os_getTime() + ms2osticks(10) + LMIC_calcAirTime(LMIC.rps, 255) * 110 / 100); + // channel activity detection and rx if preamble symbol found + radio_cad(); + break; } } diff --git a/lmic/region.h b/lmic/region.h index aa44aff..7a4bd58 100644 --- a/lmic/region.h +++ b/lmic/region.h @@ -140,7 +140,7 @@ enum { #define MAX_FIX_CHNLS_500 0 #endif -#define MAX_FIX_CHNLS (MAX_FIX_CHNLS_125 + MAX_FIX_CHNLS_500) +#define MAX_FIX_CHNLS (MAX_FIX_CHNLS_125 + MAX_FIX_CHNLS_500) #endif diff --git a/services/appstart/main.c b/services/appstart/main.c index 3937962..316081d 100644 --- a/services/appstart/main.c +++ b/services/appstart/main.c @@ -22,14 +22,14 @@ static void initfunc (osjob_t* job) { debug_printf("id: %E | sn: %.16s | hw: 0x%03x | flash: %dK\r\n", eui, hal_serial(), hal_hwid(), fwi.flashsz >> 10); debug_printf("bl: v%d | fw: %s %s 0x%08x 0x%08x | boot: %s\r\n", - fwi.blversion, - PROJECT, VARIANT, fwi.version, fwi.crc, + fwi.blversion, + PROJECT, VARIANT, fwi.version, fwi.crc, #if 0 - (BOOT_DEVINFO->bootmode == TABS_BOOT_UFT) ? "uft" : - (BOOT_DEVINFO->bootmode == TABS_BOOT_SHIPPING) ? "ship" : - (BOOT_DEVINFO->bootmode == TABS_BOOT_FLIGHT) ? "flight" : + (BOOT_DEVINFO->bootmode == TABS_BOOT_UFT) ? "uft" : + (BOOT_DEVINFO->bootmode == TABS_BOOT_SHIPPING) ? "ship" : + (BOOT_DEVINFO->bootmode == TABS_BOOT_FLIGHT) ? "flight" : #endif - "normal"); + "normal"); #endif #ifdef SVC_backtrace diff --git a/services/lwmux/lwmux.c b/services/lwmux/lwmux.c index edc076b..db3925b 100644 --- a/services/lwmux/lwmux.c +++ b/services/lwmux/lwmux.c @@ -9,23 +9,23 @@ DECL_ON_LMIC_EVENT; enum { - FLAG_MODESWITCH = (1 << 0), // mode switch pending - FLAG_BUSY = (1 << 1), // radio is busy - FLAG_JOINING = (1 << 2), // trying to join - FLAG_SHUTDOWN = (1 << 3), // shutdown reqested + FLAG_MODESWITCH = (1 << 0), // mode switch pending + FLAG_BUSY = (1 << 1), // radio is busy + FLAG_JOINING = (1 << 2), // trying to join + FLAG_SHUTDOWN = (1 << 3), // shutdown reqested }; static struct { - unsigned int flags; // flags (FLAG_*) - unsigned int mode; // current mode (LWM_MODE_*) - unsigned int nextmode; // next mode (if FLAG_MODESWITCH is set) + unsigned int flags; // flags (FLAG_*) + unsigned int mode; // current mode (LWM_MODE_*) + unsigned int nextmode; // next mode (if FLAG_MODESWITCH is set) - lwm_job* queue; // job queue head + lwm_job* queue; // job queue head unsigned int runprio; // minimum priority level for runnning jobs - lwm_complete completefunc; // current job completion function - osjob_t job; // tx opportunity job + lwm_complete completefunc; // current job completion function + osjob_t job; // tx opportunity job - unsigned int jcnt; // join attempt counter + unsigned int jcnt; // join attempt counter struct { bool use_profile; // Use ADR profile instead of network-managed @@ -37,21 +37,21 @@ static struct { #ifdef LWM_SLOTTED struct { - ostime_t interval; // beacon interval - u4_t freq; // beacon frequency - dr_t dndr; // beacon datarate + ostime_t interval; // beacon interval + u4_t freq; // beacon frequency + dr_t dndr; // beacon datarate - u1_t slotsz; // slot size (payload length in bytes) - ostime_t t_slots; // time span available for uplink slots - ostime_t off_slots; // offset to first slot relative to beacon start + u1_t slotsz; // slot size (payload length in bytes) + ostime_t t_slots; // time span available for uplink slots + ostime_t off_slots; // offset to first slot relative to beacon start - ostime_t nextrx; // time of next beacon + ostime_t nextrx; // time of next beacon - int missed; // number of missed beacons - int missed_max; // max. number of missed beacons before going back to scanning + int missed; // number of missed beacons + int missed_max; // max. number of missed beacons before going back to scanning - int timeouts; // number of beacon scan timeouts - int timeouts_max; // max. number of beacon scan timeouts before creating an unaligned TX opportunity + int timeouts; // number of beacon scan timeouts + int timeouts_max; // max. number of beacon scan timeouts before creating an unaligned TX opportunity } bcn; #endif } state; @@ -253,13 +253,13 @@ static void reschedule_join (void) { os_setApproxTimedCallback(&state.job, os_getTime() + ( #if defined(CFG_eu868) || defined(CFG_in865) - (state.jcnt < 10) ? sec2osticks(360) : // first hour: every 6 minutes - (state.jcnt < 20) ? sec2osticks(3600) : // next 10 hours: every hour - sec2osticks(3600 * 12) // after: every 12 hours + (state.jcnt < 10) ? sec2osticks(360) : // first hour: every 6 minutes + (state.jcnt < 20) ? sec2osticks(3600) : // next 10 hours: every hour + sec2osticks(3600 * 12) // after: every 12 hours #elif defined(CFG_us915) - (state.jcnt < 6) ? sec2osticks(600) : // first hour: every 10 minutes - (state.jcnt < 12) ? sec2osticks(6000) : // next 10 hours: every 100 minutes - sec2osticks(3600 * 12) // after: every 12 hours + (state.jcnt < 6) ? sec2osticks(600) : // first hour: every 10 minutes + (state.jcnt < 12) ? sec2osticks(6000) : // next 10 hours: every 100 minutes + sec2osticks(3600 * 12) // after: every 12 hours #else #warning "Unsupported region" sec2osticks(3600) @@ -301,7 +301,7 @@ static bool mode_switch (void) { #ifdef LWM_SLOTTED else if (state.nextmode == LWM_MODE_SLOTTED) { debug_printf("slotted\r\n"); - state.bcn.missed = state.bcn.missed_max; // start with scanning + state.bcn.missed = state.bcn.missed_max; // start with scanning state.bcn.timeouts = 0; if (!(state.flags & (FLAG_BUSY | FLAG_JOINING))) { bcn_continue(); @@ -433,7 +433,7 @@ DECL_ON_LMIC_EVENT { if (e == EV_TXCOMPLETE || e == EV_RXCOMPLETE) { if ((LMIC.txrxFlags & TXRX_PORT) && LMIC.frame[LMIC.dataBeg-1]) { SVCHOOK_lwm_downlink(LMIC.frame[LMIC.dataBeg-1], - LMIC.frame + LMIC.dataBeg, LMIC.dataLen, LMIC.txrxFlags & LWM_FLAG_MASK); + LMIC.frame + LMIC.dataBeg, LMIC.dataLen, LMIC.txrxFlags & LWM_FLAG_MASK); } } diff --git a/services/lwtest/testmode.c b/services/lwtest/testmode.c index a0fdb8c..b2677d0 100644 --- a/services/lwtest/testmode.c +++ b/services/lwtest/testmode.c @@ -74,28 +74,28 @@ static void stoptestmode (void) { static bool txfunc (lwm_txinfo* txi) { if (testmode.confirmed && (LMIC.txrxFlags & TXRX_NACK) && testmode.retrans < 8) { - // no ACK received - retransmit last uplink data in LMIC.pendTxData with same seqnoUp - txi->dlen = LMIC.pendTxLen; - LMIC.seqnoUp -= 1; - testmode.retrans += 1; + // no ACK received - retransmit last uplink data in LMIC.pendTxData with same seqnoUp + txi->dlen = LMIC.pendTxLen; + LMIC.seqnoUp -= 1; + testmode.retrans += 1; } else if (LMIC.frame[LMIC.dataBeg] == TESTCMD_ECHO) { txi->data[0] = TESTCMD_ECHO; for( int i = 1; i < LMIC.dataLen; i++ ) { txi->data[i] = LMIC.frame[LMIC.dataBeg + i] + 1; } - txi->dlen = LMIC.dataLen; - testmode.retrans = 0; + txi->dlen = LMIC.dataLen; + testmode.retrans = 0; } else { - txi->data[0] = testmode.dncnt >> 8; // fill in downlink_counter - txi->data[1] = testmode.dncnt; // (2 bytes, big endian) - txi->dlen = 2; - testmode.retrans = 0; + txi->data[0] = testmode.dncnt >> 8; // fill in downlink_counter + txi->data[1] = testmode.dncnt; // (2 bytes, big endian) + txi->dlen = 2; + testmode.retrans = 0; } txi->port = TESTMODE_PORT; txi->confirmed = testmode.confirmed; LMIC_setAdrMode(1); debug_printf("TESTMODE UPLINK #%d (%sconfirmed, seq=%d, len=%d): %h\r\n", - testmode.uptotal++, (testmode.confirmed) ? "" : "un", LMIC.seqnoUp, txi->dlen, txi->data, txi->dlen); + testmode.uptotal++, (testmode.confirmed) ? "" : "un", LMIC.seqnoUp, txi->dlen, txi->data, txi->dlen); return true; } @@ -113,104 +113,104 @@ static void stopcw (osjob_t* job) { // referenced by tabs / rm_event() void testmode_handleEvent (ev_t ev) { switch (ev) { - case EV_TXCOMPLETE: { - // check for downlink - if (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2)) { - unsigned char *buf = LMIC.frame + LMIC.dataBeg; - int port = -1; - if (LMIC.txrxFlags & TXRX_PORT) { - port = LMIC.frame[LMIC.dataBeg - 1]; - } - - // save timestamp of last downlink - testmode.dntime = os_getTime(); - - // reset uplink-without-downlink counter - testmode.upcnt = 0; - - if (testmode.active) { - debug_printf("TESTMODE DOWNLINK (seq=%d, port=%d, len=%d%s%s%s%s): %h\r\n", - LMIC.seqnoDn, port, LMIC.dataLen, - (LMIC.txrxFlags & TXRX_DNW1) ? ", RX1" : "", - (LMIC.txrxFlags & TXRX_DNW2) ? ", RX2" : "", - (LMIC.txrxFlags & TXRX_ACK) ? ", ACK" : "", - (LMIC.txrxFlags & TXRX_NACK) ? ", NACK" : "", - buf, LMIC.dataLen); - - // update downlink counter + case EV_TXCOMPLETE: { + // check for downlink + if (LMIC.txrxFlags & (TXRX_DNW1|TXRX_DNW2)) { + unsigned char *buf = LMIC.frame + LMIC.dataBeg; + int port = -1; + if (LMIC.txrxFlags & TXRX_PORT) { + port = LMIC.frame[LMIC.dataBeg - 1]; + } + + // save timestamp of last downlink + testmode.dntime = os_getTime(); + + // reset uplink-without-downlink counter + testmode.upcnt = 0; + + if (testmode.active) { + debug_printf("TESTMODE DOWNLINK (seq=%d, port=%d, len=%d%s%s%s%s): %h\r\n", + LMIC.seqnoDn, port, LMIC.dataLen, + (LMIC.txrxFlags & TXRX_DNW1) ? ", RX1" : "", + (LMIC.txrxFlags & TXRX_DNW2) ? ", RX2" : "", + (LMIC.txrxFlags & TXRX_ACK) ? ", ACK" : "", + (LMIC.txrxFlags & TXRX_NACK) ? ", NACK" : "", + buf, LMIC.dataLen); + + // update downlink counter if( testmode.lastdf != LMIC.seqnoDn) { testmode.lastdf = LMIC.seqnoDn; testmode.dncnt += 1; } - if (port == TESTMODE_PORT && LMIC.dataLen > 0) { - - // dispatch test commands - switch (buf[0]) { - - case TESTCMD_STOP: // deactivate test mode - stoptestmode(); - break; - - case TESTCMD_CONFIRMED: // activate confirmations - testmode.confirmed = 1; - break; - - case TESTCMD_UNCONFIRMED: // deactivate confirmations - testmode.confirmed = 0; - break; - - case TESTCMD_LINKCHECK: // XXX undocumented?!? - // XXX - break; - - case TESTCMD_JOIN: // trigger join request - stoptestmode(); // (activation command will be resent after join) - lwm_setmode(LWM_MODE_SHUTDOWN); - lwm_setmode(LWM_MODE_NORMAL); - break; - - case TESTCMD_ECHO: // modify and echo frame - LMIC.pendTxData[0] = buf[0]; - for (int i = 1; i < LMIC.dataLen; i++) { - LMIC.pendTxData[i] = buf[i] + 1; - } - LMIC.pendTxLen = LMIC.dataLen; - break; - - case TESTCMD_CW: // continous wave - // set timeout and parameters - os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks((buf[1] << 8) | buf[2]), stopcw); // duration [s] - LMIC.freq = ((buf[3] << 16) | (buf[4] << 8) | buf[5]) * 100; // [Hz] - LMIC.txpow = buf[6]; // dBm - // start continuous wave - os_radio(RADIO_TXCW); - return; // no uplink now - } - } - } else { // test mode not active - if (port == TESTMODE_PORT && LMIC.dataLen == 4 && os_rlsbf4(buf) == 0x01010101) { - // activate test mode - starttestmode(); - } - } - } else { // no downlink - if (testmode.active && - (++testmode.upcnt > TESTMODE_MAXCNT || - (os_getTime() - testmode.dntime) > sec2osticks(TESTMODE_TIMEOUT))) { - // test mode timed out - debug_printf("TEST MODE TIMEOUT\r\n"); - stoptestmode(); - } - } - - if (testmode.active) { - // schedule next uplink - os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks(TESTMODE_INTERVAL), uplink); - } - } - - default: // ignore other events - break; + if (port == TESTMODE_PORT && LMIC.dataLen > 0) { + + // dispatch test commands + switch (buf[0]) { + + case TESTCMD_STOP: // deactivate test mode + stoptestmode(); + break; + + case TESTCMD_CONFIRMED: // activate confirmations + testmode.confirmed = 1; + break; + + case TESTCMD_UNCONFIRMED: // deactivate confirmations + testmode.confirmed = 0; + break; + + case TESTCMD_LINKCHECK: // XXX undocumented?!? + // XXX + break; + + case TESTCMD_JOIN: // trigger join request + stoptestmode(); // (activation command will be resent after join) + lwm_setmode(LWM_MODE_SHUTDOWN); + lwm_setmode(LWM_MODE_NORMAL); + break; + + case TESTCMD_ECHO: // modify and echo frame + LMIC.pendTxData[0] = buf[0]; + for (int i = 1; i < LMIC.dataLen; i++) { + LMIC.pendTxData[i] = buf[i] + 1; + } + LMIC.pendTxLen = LMIC.dataLen; + break; + + case TESTCMD_CW: // continous wave + // set timeout and parameters + os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks((buf[1] << 8) | buf[2]), stopcw); // duration [s] + LMIC.freq = ((buf[3] << 16) | (buf[4] << 8) | buf[5]) * 100; // [Hz] + LMIC.txpow = buf[6]; // dBm + // start continuous wave + os_radio(RADIO_TXCW); + return; // no uplink now + } + } + } else { // test mode not active + if (port == TESTMODE_PORT && LMIC.dataLen == 4 && os_rlsbf4(buf) == 0x01010101) { + // activate test mode + starttestmode(); + } + } + } else { // no downlink + if (testmode.active && + (++testmode.upcnt > TESTMODE_MAXCNT || + (os_getTime() - testmode.dntime) > sec2osticks(TESTMODE_TIMEOUT))) { + // test mode timed out + debug_printf("TEST MODE TIMEOUT\r\n"); + stoptestmode(); + } + } + + if (testmode.active) { + // schedule next uplink + os_setApproxTimedCallback(&testmode.timer, os_getTime() + sec2osticks(TESTMODE_INTERVAL), uplink); + } + } + + default: // ignore other events + break; } } diff --git a/services/pwrman/pwrman.c b/services/pwrman/pwrman.c index b6ab0e0..921a8f2 100644 --- a/services/pwrman/pwrman.c +++ b/services/pwrman/pwrman.c @@ -48,7 +48,7 @@ static void add (ptime* ppt, uint64_t uaticks) { uint32_t uah = uaticks / (OSTICKS_PER_SEC * 60 * 60); if( uah != 0 ) { ppt->hr += uah; - uaticks -= ((int64_t) uah * (OSTICKS_PER_SEC * 60 * 60)); + uaticks -= ((int64_t) uah * (OSTICKS_PER_SEC * 60 * 60)); } ppt->ticks = uaticks; } diff --git a/stm32/board.h b/stm32/board.h index 635d9df..a5dccd6 100644 --- a/stm32/board.h +++ b/stm32/board.h @@ -11,29 +11,29 @@ // 10987654 32109876 54321098 76543210 // ________ _____fff ccccaaaa PPPPpppp -#define BRD_GPIO(port,pin) (((port) << 4) | (pin)) -#define BRD_GPIO_EX(port,pin,ex) (((port) << 4) | (pin) | (ex)) -#define BRD_GPIO_AF(port,pin,af) (((af) << 8) | ((port) << 4) | (pin)) -#define BRD_GPIO_AF_EX(port,pin,af,ex) (((af) << 8) | ((port) << 4) | (pin) | (ex)) -#define BRD_PIN(gpio) ((gpio) & 0x0f) -#define BRD_PORT(gpio) (((gpio) >> 4) & 0x0f) -#define BRD_AF(gpio) (((gpio) >> 8) & 0x0f) +#define BRD_GPIO(port,pin) (((port) << 4) | (pin)) +#define BRD_GPIO_EX(port,pin,ex) (((port) << 4) | (pin) | (ex)) +#define BRD_GPIO_AF(port,pin,af) (((af) << 8) | ((port) << 4) | (pin)) +#define BRD_GPIO_AF_EX(port,pin,af,ex) (((af) << 8) | ((port) << 4) | (pin) | (ex)) +#define BRD_PIN(gpio) ((gpio) & 0x0f) +#define BRD_PORT(gpio) (((gpio) >> 4) & 0x0f) +#define BRD_AF(gpio) (((gpio) >> 8) & 0x0f) // alternate function configuratons (c) #define BRD_GPIO_CHAN(ch) ((ch) << 12) #define BRD_GPIO_GET_CHAN(gpio) (((gpio) >> 12) & 0x07) // flags (f) -#define BRD_GPIO_EXT_PULLUP (1 << 16) -#define BRD_GPIO_EXT_PULLDN (1 << 17) -#define BRD_GPIO_ACTIVE_LOW (1 << 18) +#define BRD_GPIO_EXT_PULLUP (1 << 16) +#define BRD_GPIO_EXT_PULLDN (1 << 17) +#define BRD_GPIO_ACTIVE_LOW (1 << 18) // special values for low-power UART -#define BRD_LPUART(x) ((x) | (1 << 8)) +#define BRD_LPUART(x) ((x) | (1 << 8)) -#define PORT_A 0 -#define PORT_B 1 -#define PORT_C 2 +#define PORT_A 0 +#define PORT_B 1 +#define PORT_C 2 #ifdef BRD_IMPL_INC diff --git a/stm32/brd_devboards.h b/stm32/brd_devboards.h index bfebd27..bcd63f2 100644 --- a/stm32/brd_devboards.h +++ b/stm32/brd_devboards.h @@ -8,22 +8,22 @@ // ------------------------------------------- #if defined(CFG_nucleo_board) -#define GPIO_RST BRD_GPIO(PORT_A, 0) +#define GPIO_RST BRD_GPIO(PORT_A, 0) #if defined(CFG_sx1272mbed) #define BRD_sx1272_radio -#define GPIO_DIO0 BRD_GPIO(PORT_A, 10) -#define GPIO_DIO1 BRD_GPIO(PORT_B, 3) -#define GPIO_DIO2 BRD_GPIO(PORT_B, 5) -#define GPIO_NSS BRD_GPIO(PORT_B, 6) +#define GPIO_DIO0 BRD_GPIO(PORT_A, 10) +#define GPIO_DIO1 BRD_GPIO(PORT_B, 3) +#define GPIO_DIO2 BRD_GPIO(PORT_B, 5) +#define GPIO_NSS BRD_GPIO(PORT_B, 6) #elif defined(CFG_sx1276mb1mas) || defined(CFG_sx1276mb1las) -#define GPIO_DIO0 BRD_GPIO(PORT_A, 10) -#define GPIO_DIO1 BRD_GPIO(PORT_B, 3) -#define GPIO_DIO2 BRD_GPIO(PORT_B, 5) -#define GPIO_NSS BRD_GPIO(PORT_B, 6) -#define GPIO_TX BRD_GPIO(PORT_C, 1) +#define GPIO_DIO0 BRD_GPIO(PORT_A, 10) +#define GPIO_DIO1 BRD_GPIO(PORT_B, 3) +#define GPIO_DIO2 BRD_GPIO(PORT_B, 5) +#define GPIO_NSS BRD_GPIO(PORT_B, 6) +#define GPIO_TX BRD_GPIO(PORT_C, 1) #define BRD_sx1276_radio #if defined(CFG_sx1276mb1las) @@ -39,33 +39,33 @@ #elif defined(CFG_sx1262mbed) #define BRD_sx1262_radio #endif -#define GPIO_DIO1 BRD_GPIO_AF_EX(PORT_B, 4, 4, BRD_GPIO_CHAN(1)) -#define GPIO_BUSY BRD_GPIO(PORT_B, 3) -#define GPIO_NSS BRD_GPIO(PORT_A, 8) -#define GPIO_TXRX_EN BRD_GPIO(PORT_A, 9) +#define GPIO_DIO1 BRD_GPIO_AF_EX(PORT_B, 4, 4, BRD_GPIO_CHAN(1)) +#define GPIO_BUSY BRD_GPIO(PORT_B, 3) +#define GPIO_NSS BRD_GPIO(PORT_A, 8) +#define GPIO_TXRX_EN BRD_GPIO(PORT_A, 9) #else #error "Missing radio configuration" #endif -#define BRD_RADIO_SPI 1 -#define GPIO_SCK BRD_GPIO_AF(PORT_A, 5, 0) -#define GPIO_MISO BRD_GPIO_AF(PORT_A, 6, 0) -#define GPIO_MOSI BRD_GPIO_AF(PORT_A, 7, 0) +#define BRD_RADIO_SPI 1 +#define GPIO_SCK BRD_GPIO_AF(PORT_A, 5, 0) +#define GPIO_MISO BRD_GPIO_AF(PORT_A, 6, 0) +#define GPIO_MOSI BRD_GPIO_AF(PORT_A, 7, 0) -#define GPIO_BOOT_LED BRD_GPIO(PORT_A, 5) // -- LED is shared with SCK!! -//#define GPIO_DBG_LED BRD_GPIO(PORT_A, 5) // -- LED is shared with SCK!! -#define GPIO_DBG_TX BRD_GPIO_AF(PORT_A, 2, 4) +#define GPIO_BOOT_LED BRD_GPIO(PORT_A, 5) // -- LED is shared with SCK!! +//#define GPIO_DBG_LED BRD_GPIO(PORT_A, 5) // -- LED is shared with SCK!! +#define GPIO_DBG_TX BRD_GPIO_AF(PORT_A, 2, 4) #define GPIO_DBG_RX BRD_GPIO_AF(PORT_A, 3, 4) -#define BRD_DBG_UART 2 +#define BRD_DBG_UART 2 -#define GPIO_PERSO_TX BRD_GPIO_AF(PORT_A, 2, 4) -#define GPIO_PERSO_RX BRD_GPIO_AF(PORT_A, 3, 4) -#define BRD_PERSO_UART 2 +#define GPIO_PERSO_TX BRD_GPIO_AF(PORT_A, 2, 4) +#define GPIO_PERSO_RX BRD_GPIO_AF(PORT_A, 3, 4) +#define BRD_PERSO_UART 2 -#define BRD_USART BRD_LPUART(1) -#define GPIO_USART_TX BRD_GPIO_AF(PORT_C, 4, 2) -#define GPIO_USART_RX BRD_GPIO_AF(PORT_C, 5, 2) +#define BRD_USART BRD_LPUART(1) +#define GPIO_USART_TX BRD_GPIO_AF(PORT_C, 4, 2) +#define GPIO_USART_RX BRD_GPIO_AF(PORT_C, 5, 2) // power consumption @@ -89,23 +89,23 @@ // ------------------------------------------- #elif defined(CFG_b_l072Z_lrwan1_board) -#define GPIO_RST BRD_GPIO(PORT_C, 0) -#define GPIO_DIO0 BRD_GPIO_AF_EX(PORT_B, 4, 4, BRD_GPIO_CHAN(1)) -#define GPIO_DIO1 BRD_GPIO(PORT_B, 1) -#define GPIO_DIO2 BRD_GPIO(PORT_B, 0) -#define GPIO_DIO3 BRD_GPIO(PORT_C, 13) -#define GPIO_DIO4 BRD_GPIO(PORT_A, 5) -#define GPIO_DIO5 BRD_GPIO(PORT_A, 4) +#define GPIO_RST BRD_GPIO(PORT_C, 0) +#define GPIO_DIO0 BRD_GPIO_AF_EX(PORT_B, 4, 4, BRD_GPIO_CHAN(1)) +#define GPIO_DIO1 BRD_GPIO(PORT_B, 1) +#define GPIO_DIO2 BRD_GPIO(PORT_B, 0) +#define GPIO_DIO3 BRD_GPIO(PORT_C, 13) +#define GPIO_DIO4 BRD_GPIO(PORT_A, 5) +#define GPIO_DIO5 BRD_GPIO(PORT_A, 4) -#define GPIO_TCXO_PWR BRD_GPIO(PORT_A, 12) -#define GPIO_RX BRD_GPIO(PORT_A, 1) // PA_RFI -#define GPIO_TX BRD_GPIO(PORT_C, 1) // PA_BOOST -#define GPIO_TX2 BRD_GPIO(PORT_C, 2) // PA_RFO +#define GPIO_TCXO_PWR BRD_GPIO(PORT_A, 12) +#define GPIO_RX BRD_GPIO(PORT_A, 1) // PA_RFI +#define GPIO_TX BRD_GPIO(PORT_C, 1) // PA_BOOST +#define GPIO_TX2 BRD_GPIO(PORT_C, 2) // PA_RFO -#define GPIO_LED1 BRD_GPIO(PORT_B, 5) // grn -#define GPIO_LED2 BRD_GPIO(PORT_A, 5) // red -- used by bootloader -#define GPIO_LED3 BRD_GPIO(PORT_B, 6) // blu -#define GPIO_LED4 BRD_GPIO(PORT_B, 7) // red +#define GPIO_LED1 BRD_GPIO(PORT_B, 5) // grn +#define GPIO_LED2 BRD_GPIO(PORT_A, 5) // red -- used by bootloader +#define GPIO_LED3 BRD_GPIO(PORT_B, 6) // blu +#define GPIO_LED4 BRD_GPIO(PORT_B, 7) // red // button PB2 @@ -113,20 +113,20 @@ #define BRD_PABOOSTSEL(f,p) ((p) > 15) #define BRD_TXANTSWSEL(f,p) ((BRD_PABOOSTSEL(f,p)) ? HAL_ANTSW_TX : HAL_ANTSW_TX2) -#define BRD_RADIO_SPI 1 -#define GPIO_NSS BRD_GPIO(PORT_A, 15) -#define GPIO_SCK BRD_GPIO_AF(PORT_B, 3, 0) -#define GPIO_MISO BRD_GPIO_AF(PORT_A, 6, 0) -#define GPIO_MOSI BRD_GPIO_AF(PORT_A, 7, 0) +#define BRD_RADIO_SPI 1 +#define GPIO_NSS BRD_GPIO(PORT_A, 15) +#define GPIO_SCK BRD_GPIO_AF(PORT_B, 3, 0) +#define GPIO_MISO BRD_GPIO_AF(PORT_A, 6, 0) +#define GPIO_MOSI BRD_GPIO_AF(PORT_A, 7, 0) -#define GPIO_DBG_LED GPIO_LED4 -#define GPIO_DBG_TX BRD_GPIO_AF(PORT_A, 2, 4) +#define GPIO_DBG_LED GPIO_LED4 +#define GPIO_DBG_TX BRD_GPIO_AF(PORT_A, 2, 4) #define GPIO_DBG_RX BRD_GPIO_AF(PORT_A, 3, 4) -#define BRD_DBG_UART 2 +#define BRD_DBG_UART 2 -#define BRD_USART 1 -#define GPIO_USART_TX BRD_GPIO_AF(PORT_A, 9, 4) -#define GPIO_USART_RX BRD_GPIO_AF(PORT_A, 10, 4) +#define BRD_USART 1 +#define GPIO_USART_TX BRD_GPIO_AF(PORT_A, 9, 4) +#define GPIO_USART_RX BRD_GPIO_AF(PORT_A, 10, 4) // power consumption diff --git a/stm32/eeprom.c b/stm32/eeprom.c index 9b14aec..badbcde 100644 --- a/stm32/eeprom.c +++ b/stm32/eeprom.c @@ -32,7 +32,7 @@ void eeprom_write (void* dest, unsigned int val) { FLASH->PECR |= FLASH_PECR_PELOCK; // verify value - ASSERT( *((volatile u4_t*) addr) == val ); + ASSERT( *((volatile u4_t*) addr) == val ); } } diff --git a/stm32/gpio.c b/stm32/gpio.c index bbfbe60..71927c1 100644 --- a/stm32/gpio.c +++ b/stm32/gpio.c @@ -8,9 +8,9 @@ static inline void gpio_begin (int port) { unsigned int enr = GPIO_RCC_ENR; if ((enr & GPIO_EN(port)) == 0) { - GPIO_RCC_ENR = enr | GPIO_EN(port); - // dummy read as per errata - (void) GPIOx(port)->IDR; + GPIO_RCC_ENR = enr | GPIO_EN(port); + // dummy read as per errata + (void) GPIOx(port)->IDR; } } @@ -74,10 +74,10 @@ void gpio_cfg_extirq_ex (int port, int pin, bool rising, bool falling) { EXTI->RTSR &= ~mask; // clear trigger EXTI->FTSR &= ~mask; // clear trigger if( rising ) { - EXTI->RTSR |= mask; + EXTI->RTSR |= mask; } if( falling ) { - EXTI->FTSR |= mask; + EXTI->FTSR |= mask; } // configure the NVIC @@ -99,10 +99,10 @@ void gpio_cfg_extirq (int port, int pin, int irqcfg) { void gpio_set_extirq (int pin, int on) { if (on) { - EXTI->PR = (1 << pin); - EXTI->IMR |= (1 << pin); + EXTI->PR = (1 << pin); + EXTI->IMR |= (1 << pin); } else { - EXTI->IMR &= ~(1 << pin); + EXTI->IMR &= ~(1 << pin); } } diff --git a/stm32/hal_stm32.h b/stm32/hal_stm32.h index 9ec95c5..86d5f37 100644 --- a/stm32/hal_stm32.h +++ b/stm32/hal_stm32.h @@ -43,8 +43,8 @@ void hal_rtstats_collect (hal_rtstats* stats); // NVIC interrupt definition typedef struct { - uint32_t num; // NVIC interrupt number - void* handler; // Pointer to handler function + uint32_t num; // NVIC interrupt number + void* handler; // Pointer to handler function } irqdef; extern const irqdef HAL_irqdefs[]; diff --git a/stm32/hw.h b/stm32/hw.h index 4595c29..feb10bf 100644 --- a/stm32/hw.h +++ b/stm32/hw.h @@ -79,63 +79,63 @@ enum { GPIO_LOHI = 0, GPIO_HILO = 1 }; // generate a transition, then switch to HiZ int gpio_transition (int port, int pin, int type, int duration, unsigned int config); -#define GPIO_DEFAULT_CFG (GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE) -#define SET_PIN(gpio, val) gpio_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (val)) -#define GET_PIN(gpio) gpio_get_pin(BRD_PORT(gpio), BRD_PIN(gpio)) -#define CFG_PIN(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), (opts)) -#define CFG_PIN_VAL(gpio, o, s) gpio_cfg_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (o), (s)) -#define CFG_PIN_AF(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIOCFG_MODE_ALT | BRD_AF(gpio) | (opts)) -#define CFG_PIN_DEFAULT(gpio) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIO_DEFAULT_CFG) -#define IRQ_PIN(gpio, opts) gpio_cfg_extirq(BRD_PORT(gpio), BRD_PIN(gpio), (opts)) -#define IRQ_PIN_SET(gpio, on) gpio_set_extirq(BRD_PIN(gpio), (on)) -#define TXN_PIN(gpio, t, d, c) gpio_transition(BRD_PORT(gpio), BRD_PIN(gpio), (t), (d), (c)) +#define GPIO_DEFAULT_CFG (GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE) +#define SET_PIN(gpio, val) gpio_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (val)) +#define GET_PIN(gpio) gpio_get_pin(BRD_PORT(gpio), BRD_PIN(gpio)) +#define CFG_PIN(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), (opts)) +#define CFG_PIN_VAL(gpio, o, s) gpio_cfg_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (o), (s)) +#define CFG_PIN_AF(gpio, opts) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIOCFG_MODE_ALT | BRD_AF(gpio) | (opts)) +#define CFG_PIN_DEFAULT(gpio) gpio_cfg_pin(BRD_PORT(gpio), BRD_PIN(gpio), GPIO_DEFAULT_CFG) +#define IRQ_PIN(gpio, opts) gpio_cfg_extirq(BRD_PORT(gpio), BRD_PIN(gpio), (opts)) +#define IRQ_PIN_SET(gpio, on) gpio_set_extirq(BRD_PIN(gpio), (on)) +#define TXN_PIN(gpio, t, d, c) gpio_transition(BRD_PORT(gpio), BRD_PIN(gpio), (t), (d), (c)) #define SET_PIN_ONOFF(gpio, on) gpio_set_pin(BRD_PORT(gpio), BRD_PIN(gpio), (((gpio) & BRD_GPIO_ACTIVE_LOW) ? 1 : 0) ^ ((on) ? 1 : 0)) // Convenience macros to set GPIO configuration registers -#define GPIO_AF_BITS 4 // width of bit field -#define GPIO_AF_MASK 0x0F // mask in AFR[0/1] -#define GPIO_AFRLR(i) ((i) >> 3) -#define GPIO_AF_PINi(i,af) ((af) << (((i) & 7) * GPIO_AF_BITS)) -#define GPIO_AF_set(p,i,af) do { \ +#define GPIO_AF_BITS 4 // width of bit field +#define GPIO_AF_MASK 0x0F // mask in AFR[0/1] +#define GPIO_AFRLR(i) ((i) >> 3) +#define GPIO_AF_PINi(i,af) ((af) << (((i) & 7) * GPIO_AF_BITS)) +#define GPIO_AF_set(p,i,af) do { \ (p)->AFR[GPIO_AFRLR(i)] = ((p)->AFR[GPIO_AFRLR(i)] \ - & ~GPIO_AF_PINi(i, GPIO_AF_MASK)) | GPIO_AF_PINi(i, af); \ + & ~GPIO_AF_PINi(i, GPIO_AF_MASK)) | GPIO_AF_PINi(i, af); \ } while (0) -#define HW_CFG_PIN(p,i,cfg) do { \ +#define HW_CFG_PIN(p,i,cfg) do { \ if (((cfg) & GPIOCFG_MODE_MASK) == GPIOCFG_MODE_ALT) { \ - GPIO_AF_set(p, i, (cfg) & GPIOCFG_AF_MASK); \ - (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \ + GPIO_AF_set(p, i, (cfg) & GPIOCFG_AF_MASK); \ + (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \ } \ (p)->OSPEEDR = ((p)->OSPEEDR & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_OSPEED_SHIFT) & 3) << (2*(i))); \ (p)->OTYPER = ((p)->OTYPER & ~(1 << (1*(i)))) | ((((cfg) >> GPIOCFG_OTYPE_SHIFT ) & 1) << (1*(i))); \ (p)->PUPDR = ((p)->PUPDR & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_PUPD_SHIFT ) & 3) << (2*(i))); \ if (((cfg) & GPIOCFG_MODE_MASK) != GPIOCFG_MODE_ALT) { \ - (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \ + (p)->MODER = ((p)->MODER & ~(3 << (2*(i)))) | ((((cfg) >> GPIOCFG_MODE_SHIFT ) & 3) << (2*(i))); \ } \ } while (0) -#define HW_SET_PIN(p,i,state) do { \ +#define HW_SET_PIN(p,i,state) do { \ (p)->BSRR |= (1 << (i + ((state) ? 0 : 16))); \ } while (0) -#define HW_GET_PIN(p,i) ((p)->IDR & (1 << (i))) +#define HW_GET_PIN(p,i) ((p)->IDR & (1 << (i))) // Theses macros manipulate the GPIO registers directly, without generating a function call -#define SET_PIN_DIRECT(gpio, val) HW_SET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (val)) -#define GET_PIN_DIRECT(gpio) HW_GET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio)) -#define CFG_PIN_DIRECT(gpio, opts) HW_CFG_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (opts)) +#define SET_PIN_DIRECT(gpio, val) HW_SET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (val)) +#define GET_PIN_DIRECT(gpio) HW_GET_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio)) +#define CFG_PIN_DIRECT(gpio, opts) HW_CFG_PIN(GPIOx(BRD_PORT(gpio)), BRD_PIN(gpio), (opts)) // Determine RCC enable bit for GPIO port #if defined(STM32L0) -#define GPIO_RCC_ENR (RCC->IOPENR) -#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_IOPENR_GPIOAEN \ - : ((gpio) == PORT_B) ? RCC_IOPENR_GPIOBEN \ - : ((gpio) == PORT_C) ? RCC_IOPENR_GPIOCEN \ - : 0) +#define GPIO_RCC_ENR (RCC->IOPENR) +#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_IOPENR_GPIOAEN \ + : ((gpio) == PORT_B) ? RCC_IOPENR_GPIOBEN \ + : ((gpio) == PORT_C) ? RCC_IOPENR_GPIOCEN \ + : 0) #elif defined(STM32L1) -#define GPIO_RCC_ENR (RCC->AHBENR) -#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_AHBENR_GPIOAEN \ - : ((gpio) == PORT_B) ? RCC_AHBENR_GPIOBEN \ - : ((gpio) == PORT_C) ? RCC_AHBENR_GPIOCEN \ - : 0) +#define GPIO_RCC_ENR (RCC->AHBENR) +#define GPIO_EN(gpio) (((gpio) == PORT_A) ? RCC_AHBENR_GPIOAEN \ + : ((gpio) == PORT_B) ? RCC_AHBENR_GPIOBEN \ + : ((gpio) == PORT_C) ? RCC_AHBENR_GPIOCEN \ + : 0) #endif @@ -154,13 +154,13 @@ int gpio_transition (int port, int pin, int type, int duration, unsigned int con #define UNIQUE_ID2 (UNIQUE_ID_BASE+0x14) // EEPROM (these macros are addresses, type uint32_t) -#define EEPROM_BASE DATA_EEPROM_BASE +#define EEPROM_BASE DATA_EEPROM_BASE #ifdef DATA_EEPROM_BANK2_END -#define EEPROM_END (DATA_EEPROM_BANK2_END + 1) +#define EEPROM_END (DATA_EEPROM_BANK2_END + 1) #else -#define EEPROM_END DATA_EEPROM_END +#define EEPROM_END DATA_EEPROM_END #endif -#define EEPROM_SZ (EEPROM_END - EEPROM_BASE) +#define EEPROM_SZ (EEPROM_END - EEPROM_BASE) // EEPROM layout for STM32 @@ -218,11 +218,11 @@ int gpio_transition (int port, int pin, int type, int duration, unsigned int con #define PERIPH_USART #if (BRD_USART & BRD_LPUART(0)) == 0 -#define USART_BR_9600 0xd05 -#define USART_BR_115200 0x116 +#define USART_BR_9600 0xd05 +#define USART_BR_115200 0x116 #else -#define USART_BR_9600 0xd0555 -#define USART_BR_115200 0x115c7 +#define USART_BR_9600 0xd0555 +#define USART_BR_115200 0x115c7 #endif @@ -246,8 +246,8 @@ u1_t spi_xfer (u1_t out); #define PERIPH_FLASH -#define FLASH_PAGE_SZ 128 -#define FLASH_PAGE_NW (FLASH_PAGE_SZ >> 2) +#define FLASH_PAGE_SZ 128 +#define FLASH_PAGE_NW (FLASH_PAGE_SZ >> 2) #ifdef FLASH_END #undef FLASH_END // already defined by some STM32L0XXXxx header files diff --git a/stm32/i2c.c b/stm32/i2c.c index 077a3df..2b7319c 100644 --- a/stm32/i2c.c +++ b/stm32/i2c.c @@ -8,10 +8,10 @@ #ifdef BRD_I2C #if BRD_I2C == 1 -#define I2Cx I2C1 -#define I2Cx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; } while (0) -#define I2Cx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN; } while (0) -#define I2Cx_IRQn I2C1_IRQn +#define I2Cx I2C1 +#define I2Cx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; } while (0) +#define I2Cx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_I2C1EN; } while (0) +#define I2Cx_IRQn I2C1_IRQn #else #error "Unsupported I2C peripheral" #endif @@ -47,9 +47,9 @@ static void i2c_stop (int status) { // schedule callback *(xfr.pstatus) = status; if (xfr.job != NULL) { - os_setCallback(xfr.job, xfr.cb); + os_setCallback(xfr.job, xfr.cb); } else { - xfr.cb(NULL); + xfr.cb(NULL); } // re-enable sleep hal_clearMaxSleep(HAL_SLEEP_S0); @@ -75,47 +75,47 @@ static void i2c_start (int addr) { static void i2c_cont (void) { if (xfr.wlen) { - // calculate length; TODO: handle >255 - int n = xfr.wlen & 0xff; - xfr.wlen -= n; - // set direction & number of bytes - I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | (n << 16); - // enable interrupts - I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_TXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE; - // start TX - I2Cx->CR2 |= I2C_CR2_START; + // calculate length; TODO: handle >255 + int n = xfr.wlen & 0xff; + xfr.wlen -= n; + // set direction & number of bytes + I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | (n << 16); + // enable interrupts + I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_TXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE; + // start TX + I2Cx->CR2 |= I2C_CR2_START; } else if (xfr.rlen) { - // calculate length; TODO: handle >255 - int n = xfr.rlen & 0xff; - xfr.rlen -= n; - // set direction & number of bytes - I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | I2C_CR2_RD_WRN | (n << 16); - // enable interrupts - I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_RXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE; - // start RX - I2Cx->CR2 |= I2C_CR2_START; + // calculate length; TODO: handle >255 + int n = xfr.rlen & 0xff; + xfr.rlen -= n; + // set direction & number of bytes + I2Cx->CR2 = (I2Cx->CR2 & ~(I2C_CR2_RD_WRN | I2C_CR2_NBYTES)) | I2C_CR2_RD_WRN | (n << 16); + // enable interrupts + I2Cx->CR1 = (I2Cx->CR1 & ~0xfe) | I2C_CR1_RXIE | I2C_CR1_TCIE | I2C_CR1_NACKIE | I2C_CR1_ERRIE; + // start RX + I2Cx->CR2 |= I2C_CR2_START; } else { - // done - i2c_stop(I2C_OK); + // done + i2c_stop(I2C_OK); } } void i2c_irq (void) { unsigned int isr = I2Cx->ISR; if (isr & I2C_ISR_NACKF) { - // NACK detected, transfer failed! - i2c_stop(I2C_NAK); + // NACK detected, transfer failed! + i2c_stop(I2C_NAK); } else if (isr & I2C_ISR_TC) { - // transfer complete, move on - i2c_cont(); + // transfer complete, move on + i2c_cont(); } else if (isr & I2C_ISR_TXIS) { - // write next byte - I2Cx->TXDR = *xfr.wptr++; + // write next byte + I2Cx->TXDR = *xfr.wptr++; } else if (isr & I2C_ISR_RXNE) { - // next byte received - *xfr.rptr++ = I2Cx->RXDR; + // next byte received + *xfr.rptr++ = I2Cx->RXDR; } else { - hal_failed(); // XXX + hal_failed(); // XXX } } @@ -124,7 +124,7 @@ static void i2c_timeout (osjob_t* job) { } void i2c_xfer_ex (unsigned int addr, unsigned char* buf, unsigned int wlen, unsigned int rlen, ostime_t timeout, - osjob_t* job, osjobcb_t cb, int* pstatus) { + osjob_t* job, osjobcb_t cb, int* pstatus) { // setup xfr structure xfr.wlen = wlen; xfr.rlen = rlen; @@ -135,7 +135,7 @@ void i2c_xfer_ex (unsigned int addr, unsigned char* buf, unsigned int wlen, unsi *xfr.pstatus = I2C_BUSY; // set timeout if (timeout) { - os_setTimedCallback(job, os_getTime() + timeout, i2c_timeout); + os_setTimedCallback(job, os_getTime() + timeout, i2c_timeout); } // prepare peripheral i2c_start(addr); diff --git a/stm32/leds.c b/stm32/leds.c index 2c9b43e..b3f8e6d 100644 --- a/stm32/leds.c +++ b/stm32/leds.c @@ -8,10 +8,10 @@ #if defined(BRD_LED_TIM) #if BRD_LED_TIM == 2 -#define TIMx TIM2 -#define TIMx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } while (0) -#define TIMx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN; } while (0) -#define TIMx_IRQn TIM2_IRQn +#define TIMx TIM2 +#define TIMx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; } while (0) +#define TIMx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN; } while (0) +#define TIMx_IRQn TIM2_IRQn #else #error "Unsupported timer" #endif @@ -19,11 +19,11 @@ static struct { unsigned int state; struct { - int step; - unsigned int n; - unsigned int delay; - unsigned int min; - unsigned int max; + int step; + unsigned int n; + unsigned int delay; + unsigned int min; + unsigned int max; } pulse[4]; } pwm; @@ -35,11 +35,11 @@ void leds_init (void) { TIMx->PSC = 4; TIMx->ARR = 0xffff; TIMx->CCMR1 = - TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE | - TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE; + TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE | + TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE; TIMx->CCMR2 = - TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE | - TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE; + TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE | + TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE; TIMx_disable(); #endif } @@ -53,56 +53,56 @@ static void pwm_set_gpio (unsigned int gpio, bool enable, bool pulse, unsigned i unsigned int state1 = state0; if (enable) { - state1 |= (0x01 << ch); - if (pulse) { - state1 |= (0x10 << ch); - } + state1 |= (0x01 << ch); + if (pulse) { + state1 |= (0x10 << ch); + } } else { - state1 &= ~(0x11 << ch); + state1 &= ~(0x11 << ch); } if (state0 == state1) { - return; + return; } hal_disableIRQs(); if (state1) { - if (state0 == 0) { - TIMx_enable(); // enable peripheral clock + if (state0 == 0) { + TIMx_enable(); // enable peripheral clock hal_setMaxSleep(HAL_SLEEP_S0); // disable sleep (keep clock at full speed) - TIMx->CR1 |= TIM_CR1_CEN; // enable timer peripheral - TIMx->EGR |= TIM_EGR_UG; // start pwm - } - if (state1 & 0xf0) { - if ((state0 & 0xf0) == 0) { - TIMx->DIER |= TIM_DIER_UIE; // enable update interrupt - NVIC_EnableIRQ(TIMx_IRQn); // enable interrupt in NVIC - } - } else { - if ((state0 & 0xf0) == 0) { - TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt - NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC - } - } + TIMx->CR1 |= TIM_CR1_CEN; // enable timer peripheral + TIMx->EGR |= TIM_EGR_UG; // start pwm + } + if (state1 & 0xf0) { + if ((state0 & 0xf0) == 0) { + TIMx->DIER |= TIM_DIER_UIE; // enable update interrupt + NVIC_EnableIRQ(TIMx_IRQn); // enable interrupt in NVIC + } + } else { + if ((state0 & 0xf0) == 0) { + TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt + NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC + } + } } else if (state0) { - TIMx->CR1 &= ~TIM_CR1_CEN; // disable timer - TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt - TIMx_disable(); // disable peripheral clock + TIMx->CR1 &= ~TIM_CR1_CEN; // disable timer + TIMx->DIER &= ~TIM_DIER_UIE; // disable update interrupt + TIMx_disable(); // disable peripheral clock hal_clearMaxSleep(HAL_SLEEP_S0); // re-enable sleep - NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC + NVIC_DisableIRQ(TIMx_IRQn); // disable interrupt in NVIC } if (enable) { - *((&(TIMx->CCR1)) + ch) = ccr; // set initial CCR value - if ((state0 & (1 << ch)) == 0) { - unsigned int ccer = TIM_CCER_CC1E; - if (gpio & BRD_GPIO_ACTIVE_LOW) { - ccer |= TIM_CCER_CC1P; - } - TIMx->CCER |= (ccer << (4 * ch)); // enable channel - CFG_PIN_AF(gpio, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); - } + *((&(TIMx->CCR1)) + ch) = ccr; // set initial CCR value + if ((state0 & (1 << ch)) == 0) { + unsigned int ccer = TIM_CCER_CC1E; + if (gpio & BRD_GPIO_ACTIVE_LOW) { + ccer |= TIM_CCER_CC1P; + } + TIMx->CCER |= (ccer << (4 * ch)); // enable channel + CFG_PIN_AF(gpio, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); + } } pwm.state = state1; @@ -131,14 +131,14 @@ void leds_pulse (unsigned int gpio, unsigned int min, unsigned int max, int step void leds_set (unsigned int gpio, int state) { if (state) { - if (gpio & BRD_GPIO_ACTIVE_LOW) { - SET_PIN(gpio, 0); - } else { - SET_PIN(gpio, 1); - } - CFG_PIN(gpio, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); + if (gpio & BRD_GPIO_ACTIVE_LOW) { + SET_PIN(gpio, 0); + } else { + SET_PIN(gpio, 1); + } + CFG_PIN(gpio, GPIOCFG_MODE_OUT | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); } else { - CFG_PIN(gpio, GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE); + CFG_PIN(gpio, GPIOCFG_MODE_ANA | GPIOCFG_OSPEED_400kHz | GPIOCFG_OTYPE_OPEN | GPIOCFG_PUPD_NONE); } #if defined(BRD_LED_TIM) pwm_set_gpio(gpio, false, false, 0); @@ -148,29 +148,29 @@ void leds_set (unsigned int gpio, int state) { #if defined(BRD_LED_TIM) void leds_pwm_irq (void) { if (TIMx->SR & TIM_SR_UIF) { // update event - TIMx->SR = ~TIM_SR_UIF; // clear flag - unsigned int ps = pwm.state & 0x0f; - while (ps) { - unsigned int ch = __builtin_ctz(ps); - if (pwm.pulse[ch].step) { - if (pwm.pulse[ch].n < pwm.pulse[ch].delay) { - pwm.pulse[ch].n += 1; - } else { - pwm.pulse[ch].n = 0; - int ccr = *((&(TIMx->CCR1)) + ch); - ccr += pwm.pulse[ch].step; - if (ccr <= pwm.pulse[ch].min) { - ccr = pwm.pulse[ch].min; - pwm.pulse[ch].step = -pwm.pulse[ch].step; - } else if (ccr >= pwm.pulse[ch].max) { - ccr = pwm.pulse[ch].max; - pwm.pulse[ch].step = -pwm.pulse[ch].step; - } - *((&(TIMx->CCR1)) + ch) = ccr; - } - } - ps &= ~(1 << ch); - } + TIMx->SR = ~TIM_SR_UIF; // clear flag + unsigned int ps = pwm.state & 0x0f; + while (ps) { + unsigned int ch = __builtin_ctz(ps); + if (pwm.pulse[ch].step) { + if (pwm.pulse[ch].n < pwm.pulse[ch].delay) { + pwm.pulse[ch].n += 1; + } else { + pwm.pulse[ch].n = 0; + int ccr = *((&(TIMx->CCR1)) + ch); + ccr += pwm.pulse[ch].step; + if (ccr <= pwm.pulse[ch].min) { + ccr = pwm.pulse[ch].min; + pwm.pulse[ch].step = -pwm.pulse[ch].step; + } else if (ccr >= pwm.pulse[ch].max) { + ccr = pwm.pulse[ch].max; + pwm.pulse[ch].step = -pwm.pulse[ch].step; + } + *((&(TIMx->CCR1)) + ch) = ccr; + } + } + ps &= ~(1 << ch); + } } } #endif diff --git a/stm32/persodata.c b/stm32/persodata.c index 711d15d..1452954 100644 --- a/stm32/persodata.c +++ b/stm32/persodata.c @@ -28,7 +28,7 @@ persodata_v1 pd; static persodata_v1* pd_check_v1 (void* ptr) { persodata_v1* ppd = ptr; if( ppd->magic == PERSODATA_MAGIC_V1 ) { - uint32_t hash[8]; + uint32_t hash[8]; sha256(hash, ptr, sizeof(persodata_v1) - 32); if( memcmp(hash, ppd->hash, 32) != 0 ) { return NULL; @@ -59,7 +59,7 @@ void pd_init (void) { bool pd_verify (void) { persodata_v1* ppd = (void*) PERSODATA_BASE; if( ppd->magic == PERSODATA_MAGIC_V1 ) { - uint32_t hash[8]; + uint32_t hash[8]; sha256(hash, (uint8_t*) ppd, sizeof(persodata_v1) - 32); if( memcmp(hash, ppd->hash, 32) == 0 ) { return true; diff --git a/stm32/startup.c b/stm32/startup.c index f54a51c..49b777f 100644 --- a/stm32/startup.c +++ b/stm32/startup.c @@ -25,20 +25,20 @@ void _start (boot_boottab* boottab) { uint32_t* src = &_sidata; uint32_t* dst = &_sdata; while( dst < &_edata ) { - *dst++ = *src++; + *dst++ = *src++; } // initialize bss dst = &_sbss; while( dst < &_ebss ) { - *dst++ = 0; + *dst++ = 0; } // copy current Cortex M IRQ + NVIC vector to RAM src = (uint32_t*) 0; dst = irqvector; for( int i = 0; i < (16 + MAX_IRQn); i++ ) { - *dst++ = *src++; + *dst++ = *src++; } // fix-up NVIC vector with handlers from firmware for( const irqdef* id = HAL_irqdefs; id->handler; id++ ) { diff --git a/stm32/usart.c b/stm32/usart.c index bc86d83..3bb2298 100644 --- a/stm32/usart.c +++ b/stm32/usart.c @@ -8,20 +8,20 @@ #ifdef BRD_USART #if BRD_USART == 1 -#define USARTx USART1 -#define USARTx_enable() do { RCC->APB2ENR |= RCC_APB2ENR_USART1EN; } while (0) -#define USARTx_disable() do { RCC->APB2ENR &= ~RCC_APB2ENR_USART1EN; } while (0) -#define USARTx_IRQn USART1_IRQn +#define USARTx USART1 +#define USARTx_enable() do { RCC->APB2ENR |= RCC_APB2ENR_USART1EN; } while (0) +#define USARTx_disable() do { RCC->APB2ENR &= ~RCC_APB2ENR_USART1EN; } while (0) +#define USARTx_IRQn USART1_IRQn #elif BRD_USART == BRD_LPUART(1) -#define USARTx LPUART1 -#define USARTx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_LPUART1EN; } while (0) -#define USARTx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_LPUART1EN; } while (0) -#define USARTx_IRQn LPUART1_IRQn +#define USARTx LPUART1 +#define USARTx_enable() do { RCC->APB1ENR |= RCC_APB1ENR_LPUART1EN; } while (0) +#define USARTx_disable() do { RCC->APB1ENR &= ~RCC_APB1ENR_LPUART1EN; } while (0) +#define USARTx_IRQn LPUART1_IRQn #endif enum { - RX_ON = (1 << 0), - TX_ON = (1 << 1), + RX_ON = (1 << 0), + TX_ON = (1 << 1), }; static struct { @@ -39,16 +39,16 @@ static struct { static void usart_on (unsigned int flag) { hal_disableIRQs(); if (usart.on == 0) { - // disable sleep (keep clock at full speed during transfer + // disable sleep (keep clock at full speed during transfer hal_setMaxSleep(HAL_SLEEP_S0); - // enable peripheral clock - USARTx_enable(); - // set baudrate - USARTx->BRR = usart.br; - // usart enable - USARTx->CR1 = USART_CR1_UE; - // enable interrupts in NVIC - NVIC_EnableIRQ(USARTx_IRQn); + // enable peripheral clock + USARTx_enable(); + // set baudrate + USARTx->BRR = usart.br; + // usart enable + USARTx->CR1 = USART_CR1_UE; + // enable interrupts in NVIC + NVIC_EnableIRQ(USARTx_IRQn); } usart.on |= flag; hal_enableIRQs(); @@ -58,13 +58,13 @@ static void usart_off (unsigned int flag) { hal_disableIRQs(); usart.on &= ~flag; if (usart.on == 0) { - // disable USART - USARTx->CR1 = 0; - // disable peripheral clock - USARTx_disable(); - // disable interrupts in NVIC - NVIC_DisableIRQ(USARTx_IRQn); - // re-enable sleep + // disable USART + USARTx->CR1 = 0; + // disable peripheral clock + USARTx_disable(); + // disable interrupts in NVIC + NVIC_DisableIRQ(USARTx_IRQn); + // re-enable sleep hal_clearMaxSleep(HAL_SLEEP_S0); } hal_enableIRQs(); @@ -78,10 +78,10 @@ static void rx_on (unsigned int noirq) { // setup I/O line CFG_PIN_AF(GPIO_USART_RX, GPIOCFG_OSPEED_40MHz | GPIOCFG_OTYPE_PUPD | GPIOCFG_PUPD_NONE); if (noirq == 0) { - // flush data, clear ORE and enable receive interrupt - USARTx->RQR |= USART_RQR_RXFRQ; - USARTx->ICR |= USART_ISR_ORE; - USARTx->CR1 |= USART_CR1_RXNEIE; + // flush data, clear ORE and enable receive interrupt + USARTx->RQR |= USART_RQR_RXFRQ; + USARTx->ICR |= USART_ISR_ORE; + USARTx->CR1 |= USART_CR1_RXNEIE; } } @@ -133,8 +133,8 @@ void usart_recv (usart_rx_func rx, void* arg) { void usart_abort_recv (void) { hal_disableIRQs(); if (usart.on & RX_ON) { - rx_off(); - usart.rx(USART_ERROR, usart.rxarg); + rx_off(); + usart.rx(USART_ERROR, usart.rxarg); } hal_enableIRQs(); } @@ -149,30 +149,30 @@ void usart_irq (void) { unsigned int isr = USARTx->ISR; unsigned int cr1 = USARTx->CR1; if (cr1 & USART_CR1_RXNEIE) { - if (isr & USART_ISR_ORE) { - USARTx->ICR |= USART_ISR_ORE; - rx_off(); - usart.rx(USART_ERROR, usart.rxarg); - } else if (isr & USART_ISR_RXNE) { - if (usart.rx(USARTx->RDR, usart.rxarg) != USART_CONTINUE) { // done - rx_off(); - } - } + if (isr & USART_ISR_ORE) { + USARTx->ICR |= USART_ISR_ORE; + rx_off(); + usart.rx(USART_ERROR, usart.rxarg); + } else if (isr & USART_ISR_RXNE) { + if (usart.rx(USARTx->RDR, usart.rxarg) != USART_CONTINUE) { // done + rx_off(); + } + } } if ((cr1 & USART_CR1_TXEIE) && (isr & USART_ISR_TXE)) { - int ch; - if ((ch = usart.tx(USART_CONTINUE, usart.txarg)) < 0) { // done - unsigned int cr1 = USARTx->CR1; - cr1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE; - USARTx->CR1 = cr1; - } else { - USARTx->TDR = ch; - } + int ch; + if ((ch = usart.tx(USART_CONTINUE, usart.txarg)) < 0) { // done + unsigned int cr1 = USARTx->CR1; + cr1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE; + USARTx->CR1 = cr1; + } else { + USARTx->TDR = ch; + } } if ((cr1 & USART_CR1_TCIE) && (isr & USART_ISR_TC)) { - USARTx->CR1 &= ~USART_CR1_TCIE; - tx_off(); - usart.tx(USART_DONE, usart.txarg); + USARTx->CR1 &= ~USART_CR1_TCIE; + tx_off(); + usart.tx(USART_DONE, usart.txarg); } } @@ -182,17 +182,17 @@ int usart_wait_silence (int silence_ticks, int timeout_ticks) { int retval = 0; rx_on(1); while (1) { - if (USARTx->ISR & USART_ISR_BUSY) { - threshold = os_getTime() + silence_ticks; - } - if ((deadline - os_getTime()) < 0) { - retval = -1; - break; - } - if ((threshold - os_getTime()) < 0) { - retval = 0; - break; - } + if (USARTx->ISR & USART_ISR_BUSY) { + threshold = os_getTime() + silence_ticks; + } + if ((deadline - os_getTime()) < 0) { + retval = -1; + break; + } + if ((threshold - os_getTime()) < 0) { + retval = 0; + break; + } } rx_off(); return retval; diff --git a/target/arduino/hw.h b/target/arduino/hw.h index efdffe1..29090e7 100644 --- a/target/arduino/hw.h +++ b/target/arduino/hw.h @@ -33,8 +33,8 @@ #define PERIPH_USART -#define USART_BR_9600 9600 -#define USART_BR_115200 115200 +#define USART_BR_9600 9600 +#define USART_BR_115200 115200 #define PERIPH_PIO #define PIO_IRQ_LINE(gpio) (gpio) diff --git a/unicorn/hal.c b/unicorn/hal.c index f9db212..61fdaa2 100644 --- a/unicorn/hal.c +++ b/unicorn/hal.c @@ -202,10 +202,10 @@ void radio_init (bool calibrate) { static ostime_t syms2ticks (rps_t rps, int n) { if( getSf(rps) == FSK ) { - // rough estimate of FSK @ 50kBit/s + // rough estimate of FSK @ 50kBit/s int extra = 5+3+1+2; // preamble, syncword, len, crc - double us = ((n+extra) * 8e6 / 50e3); // bits * (us/sec) / Bit/s - return (ostime_t)(us * OSTICKS_PER_SEC / 1e6); + double us = ((n+extra) * 8e6 / 50e3); // bits * (us/sec) / Bit/s + return (ostime_t)(us * OSTICKS_PER_SEC / 1e6); } double Rs = (double) ((1<txframe.xbeg), pctx->txframe.xbeg, ticks2time(pctx->txframe.xend), pctx->txframe.xend, enow, now); pctx->txfunc(pctx, pctx->txframe.frame, pctx->txframe.flen, pctx->txframe.freq, pctx->txframe.rps, - ticks2time(pctx->txframe.xbeg), ticks2time(pctx->txframe.xend)); + ticks2time(pctx->txframe.xbeg), ticks2time(pctx->txframe.xend)); #endif svc(SVC_TX, (uint32_t) &sim.tx, 0, 0); os_setTimedCallback(&LMIC.osjob, LMIC.txend + us2osticks(43), LMIC.osjob.func); @@ -330,14 +330,14 @@ void os_radio (u1_t mode) { } #endif switch (mode) { - case RADIO_STOP: + case RADIO_STOP: os_clearCallback(&sim.rjob); break; - case RADIO_TX: + case RADIO_TX: tx(); break; - case RADIO_RX: + case RADIO_RX: rx(); break; @@ -345,14 +345,14 @@ void os_radio (u1_t mode) { LMIC.rssi = -127; break; - case RADIO_RXON: + case RADIO_RXON: rxon(&sim.rjob); break; - case RADIO_INIT: + case RADIO_INIT: break; - case RADIO_CAD: + case RADIO_CAD: // Turn on CAD and optionally RX a frame // - if no activity or RX frame failed set LMIC.dataLen = 0 // - if activity detected start RX of a frame diff --git a/unicorn/hw.h b/unicorn/hw.h index 11f9f24..1e0d792 100644 --- a/unicorn/hw.h +++ b/unicorn/hw.h @@ -30,11 +30,11 @@ #define FLASH_SZ (128 * 1024) #define FLASH_END (FLASH_BASE + FLASH_SZ) #define FLASH_PAGE_SZ 128 -#define FLASH_PAGE_NW (FLASH_PAGE_SZ >> 2) +#define FLASH_PAGE_NW (FLASH_PAGE_SZ >> 2) #define PERIPH_USART -#define USART_BR_9600 9600 -#define USART_BR_115200 115200 +#define USART_BR_9600 9600 +#define USART_BR_115200 115200 #define PERIPH_PIO #define PIO_IRQ_LINE(gpio) (gpio) diff --git a/unicorn/persodata.c b/unicorn/persodata.c index 711d15d..1452954 100644 --- a/unicorn/persodata.c +++ b/unicorn/persodata.c @@ -28,7 +28,7 @@ persodata_v1 pd; static persodata_v1* pd_check_v1 (void* ptr) { persodata_v1* ppd = ptr; if( ppd->magic == PERSODATA_MAGIC_V1 ) { - uint32_t hash[8]; + uint32_t hash[8]; sha256(hash, ptr, sizeof(persodata_v1) - 32); if( memcmp(hash, ppd->hash, 32) != 0 ) { return NULL; @@ -59,7 +59,7 @@ void pd_init (void) { bool pd_verify (void) { persodata_v1* ppd = (void*) PERSODATA_BASE; if( ppd->magic == PERSODATA_MAGIC_V1 ) { - uint32_t hash[8]; + uint32_t hash[8]; sha256(hash, (uint8_t*) ppd, sizeof(persodata_v1) - 32); if( memcmp(hash, ppd->hash, 32) == 0 ) { return true; diff --git a/unicorn/startup.c b/unicorn/startup.c index b463837..691761a 100644 --- a/unicorn/startup.c +++ b/unicorn/startup.c @@ -15,13 +15,13 @@ void _start (boot_boottab* boottab) { uint32_t* src = &_sidata; uint32_t* dst = &_sdata; while( dst < &_edata ) { - *dst++ = *src++; + *dst++ = *src++; } // initialize bss dst = &_sbss; while( dst < &_ebss ) { - *dst++ = 0; + *dst++ = 0; } // call main function From 42efdda59f42ba3ebfe9adda11c1e820c0b51af0 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 16 Apr 2020 12:34:00 +0200 Subject: [PATCH 052/137] Arduino: Update library.properties This still had some values from LMIC in there. Also update the project URL now Semtech officially stopped developing this. --- target/arduino/library.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arduino/library.properties b/target/arduino/library.properties index 9ee51d9..cac2521 100644 --- a/target/arduino/library.properties +++ b/target/arduino/library.properties @@ -2,8 +2,8 @@ name=Basicmac LoRaWAN stack version=2.1.0 author=Various maintainer=Matthijs Kooijman -sentence=Arduino port of the LMIC (LoraWAN-in-C, formerly LoraMAC-in-C) framework provided by IBM. -paragraph=Supports SX1272/SX1276 and HopeRF RFM92/RFM95 tranceivers +sentence=LoRaWAN framework originally released by Semtech and based on the LMIC library by IBM. +paragraph=Supports SX1272/SX1276/SX1261/SX1262 LoRa tranceivers category=Communication -url=https://github.com/lorabasics/basicmac +url=https://github.com/lacunaspace/basicmac architectures=* From cedc92570933bdd1e514327ff2e5fea3a6493cf8 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 16 Apr 2020 12:34:49 +0200 Subject: [PATCH 053/137] Bump version to 2.2.1 This helps distinguish this version from the 2.2 version released by Semtech. Assigning a version number is not any particular indication of stability, though. --- target/arduino/library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arduino/library.properties b/target/arduino/library.properties index cac2521..822b6e0 100644 --- a/target/arduino/library.properties +++ b/target/arduino/library.properties @@ -1,5 +1,5 @@ name=Basicmac LoRaWAN stack -version=2.1.0 +version=2.2.1 author=Various maintainer=Matthijs Kooijman sentence=LoRaWAN framework originally released by Semtech and based on the LMIC library by IBM. From 4c399a2ca0f07bdc8d1e68145980ee494eb92e60 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 16 Apr 2020 12:39:56 +0200 Subject: [PATCH 054/137] README: Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce3aeb3..fb14019 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ configuration. BasicMAC is a privately developed fork of LMIC, which was released publically by Semtech in 2019. In 2020, Semtech has announced it no longer intends to develop or support BasicMAC, so this repository is -intended to become the primary repository for BasicMAC> +intended to become the primary repository for BasicMAC. This repository borrows heavily from the Arduino LMIC port that was first published at https://github.com/matthijskooijman/arduino-lmic/. From d8c82e8fe23b24669cbdd7ec2417ed4dc24633e6 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:02:04 +0200 Subject: [PATCH 055/137] Arduino: Show how to enable sx126x radio support This leaves the default at sx1276, but adds a commented line for 126x in target-config.h. --- target/arduino/hal/target-config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index 2c9645f..75968d3 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -12,6 +12,8 @@ // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on // the HopeRF RFM95 boards. #define BRD_sx1276_radio 1 +// This is the newer SX1261/SX1261 radio. +// #define BRD_sx1262_radio 1 // 16 μs per tick // LMIC requires ticks to be 15.5μs - 100 μs long From 63023a054c18ebe5586518a8725550607aeff830 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 4 May 2020 19:45:51 +0200 Subject: [PATCH 056/137] Arduino: Rename top-level include to basicmac.h This is the first header from this library that an Arduino sketch should include, allowing the IDE to detect the sketch needs this library. Renaming this from the lmic.h that LMIC uses, allows LMIC and BasicMAC to co-exist in your libraries folder, with sketches selecting the right library based on this include. Note that other header files still use 'lmic' in their name (and contents). Renaming all these would be too invasive, but also not needed (once the top-level include selected one library, all other includes will be resolved using that library if possible). This is a breaking change, which breaks any existing sketches. Fixing such sketches is a matter of replacing `#include ` with `#include `. --- target/arduino/{lmic.h => basicmac.h} | 0 target/arduino/examples/ttn-abp/ttn-abp.ino | 2 +- target/arduino/examples/ttn-otaa/ttn-otaa.ino | 2 +- target/arduino/export.sh | 2 +- target/arduino/hal/hal.cpp | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename target/arduino/{lmic.h => basicmac.h} (100%) diff --git a/target/arduino/lmic.h b/target/arduino/basicmac.h similarity index 100% rename from target/arduino/lmic.h rename to target/arduino/basicmac.h diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/ttn-abp/ttn-abp.ino index b96a4aa..6fbaafb 100644 --- a/target/arduino/examples/ttn-abp/ttn-abp.ino +++ b/target/arduino/examples/ttn-abp/ttn-abp.ino @@ -29,7 +29,7 @@ * *******************************************************************************/ -#include +#include #include #include diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/ttn-otaa/ttn-otaa.ino index 814b20c..8ef5492 100644 --- a/target/arduino/examples/ttn-otaa/ttn-otaa.ino +++ b/target/arduino/examples/ttn-otaa/ttn-otaa.ino @@ -29,7 +29,7 @@ * *******************************************************************************/ -#include +#include #include #include diff --git a/target/arduino/export.sh b/target/arduino/export.sh index ea5a283..433034e 100755 --- a/target/arduino/export.sh +++ b/target/arduino/export.sh @@ -61,7 +61,7 @@ mkdir -p "$TARGET"/src # directories, the contained files are copied or linked, so that when # linking relative includes still work as expected $CMD "$SRC"/library.properties "$TARGET" -$CMD "$SRC"/lmic.h "$TARGET"/src +$CMD "$SRC"/basicmac.h "$TARGET"/src $CMD "$SRC"/../../lmic "$TARGET"/src $CMD "$SRC"/hal "$TARGET"/src $CMD "$SRC"/../../aes "$TARGET"/src diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index 22f12bc..11293f4 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -14,7 +14,7 @@ #undef _GNU_SOURCE #include #include -#include "../lmic.h" +#include "../basicmac.h" #include "hal.h" // Datasheet defins typical times until busy goes low. Most are < 200us, From 3305d8e9a1e9ee23fa7e84528ed8682ee7fc0cd2 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 4 May 2020 19:52:52 +0200 Subject: [PATCH 057/137] Arduino: Rename examples to basicmac-abp/otaa These examples were not really TTN-specific, but could just as easily be used for other networks (maybe needing some settings changes), so reflect that in the name. This adds "basicmac" to the name, to make it easier to distinguish them from the lmic examples too, when working with both libraries. --- .../{ttn-abp/ttn-abp.ino => basicmac-abp/basicmac-abp.ino} | 0 .../{ttn-otaa/ttn-otaa.ino => basicmac-otaa/basicmac-otaa.ino} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename target/arduino/examples/{ttn-abp/ttn-abp.ino => basicmac-abp/basicmac-abp.ino} (100%) rename target/arduino/examples/{ttn-otaa/ttn-otaa.ino => basicmac-otaa/basicmac-otaa.ino} (100%) diff --git a/target/arduino/examples/ttn-abp/ttn-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino similarity index 100% rename from target/arduino/examples/ttn-abp/ttn-abp.ino rename to target/arduino/examples/basicmac-abp/basicmac-abp.ino diff --git a/target/arduino/examples/ttn-otaa/ttn-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino similarity index 100% rename from target/arduino/examples/ttn-otaa/ttn-otaa.ino rename to target/arduino/examples/basicmac-otaa/basicmac-otaa.ino From e2841f7fae4d2ae03b8a9a8f0b4403fd25e7bd69 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 10:58:07 +0200 Subject: [PATCH 058/137] Arduino: Fix FSK channel in basicmac-abp example This defined a channel for FSK, but specified the DR using the `FSK` constant, which is for specifying SF, not DR. This extends the list of DR constants (which are not currently defined in BasicMAC itself) in the example to include all EU868-defined datarates. --- .../arduino/examples/basicmac-abp/basicmac-abp.ino | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index 6fbaafb..442f868 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -209,7 +209,16 @@ void setup() { #if defined(CFG_eu868) // These are defined by the LoRaWAN specification - const uint8_t EU_DR_SF12 = 0, EU_DR_SF9 = 3, EU_DR_SF7 = 5, EU_DR_SF7_BW250 = 6; + enum { + EU_DR_SF12 = 0, + EU_DR_SF11 = 1, + EU_DR_SF10 = 2, + EU_DR_SF9 = 3, + EU_DR_SF8 = 4, + EU_DR_SF7 = 5, + EU_DR_SF7_BW250 = 6, + EU_DR_FSK = 7, + }; // Set up the channels used by the Things Network, which corresponds // to the defaults of most gateways. Without this, only three base @@ -227,7 +236,7 @@ void setup() { LMIC_setupChannel(5, 867500000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band LMIC_setupChannel(6, 867700000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band LMIC_setupChannel(7, 867900000, DR_RANGE_MAP(EU_DR_SF12, EU_DR_SF7)); // g-band - LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(FSK, FSK)); // g2-band + LMIC_setupChannel(8, 868800000, DR_RANGE_MAP(EU_DR_FSK, EU_DR_FSK)); // g2-band // TTN uses SF9 at 869.525Mhz for its RX2 window (frequency is // default LoRaWAN, SF is different, but set them both to be From 2fe87158ebf746644eb738388413db317409e6f6 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 22 May 2020 10:37:55 +0200 Subject: [PATCH 059/137] Arduino: Remove extra comma from debug output --- target/arduino/examples/basicmac-abp/basicmac-abp.ino | 2 +- target/arduino/examples/basicmac-otaa/basicmac-otaa.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index 442f868..668dd8f 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -154,7 +154,7 @@ void onLmicEvent (ev_t ev) { Serial.println(F("EV_SCAN_FOUND")); break; case EV_TXSTART: - Serial.println(F("EV_TXSTART,")); + Serial.println(F("EV_TXSTART")); break; case EV_TXDONE: Serial.println(F("EV_TXDONE")); diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino index 8ef5492..439130b 100644 --- a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino +++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino @@ -159,7 +159,7 @@ void onLmicEvent (ev_t ev) { Serial.println(F("EV_SCAN_FOUND")); break; case EV_TXSTART: - Serial.println(F("EV_TXSTART,")); + Serial.println(F("EV_TXSTART")); break; case EV_TXDONE: Serial.println(F("EV_TXDONE")); From cf7bf9c549928c0e79dabe9d6296fe06c7f2d84c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 26 May 2020 18:01:13 +0200 Subject: [PATCH 060/137] Arduino: Enable DEBUG_TX/RX by default These contain detailed, but concise info about TX and RX and are generally useful without cluttering too much, so better enable them by default. --- target/arduino/hal/target-config.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index 75968d3..ea8444a 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -30,9 +30,10 @@ //#define CFG_DEBUG_VERBOSE // Debug output (and assertion failures) are printed to this Stream #define CFG_DEBUG_STREAM Serial -// Define these to add some TX or RX specific debug output -//#define DEBUG_TX -//#define DEBUG_RX +// Define these to add some TX or RX specific debug output (needs +// CFG_DEBUG) +#define DEBUG_TX +#define DEBUG_RX // Uncomment to display timestamps in ticks rather than milliseconds //#define CFG_DEBUG_RAW_TIMESTAMPS From b789d1d94f1f9a323e7f6772a46672e91a053405 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 22 May 2020 11:19:23 +0200 Subject: [PATCH 061/137] Arduino: Wait for join to complete in basicmac-otaa example This is not strictly needed, but starting the join explicitly and waiting for it to complete before scheduling the first packet typically makes things a little more predictable (and would prevent sending old data when the join takes long in a real sensor application). --- .../examples/basicmac-otaa/basicmac-otaa.ino | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino index 439130b..29e2232 100644 --- a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino +++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino @@ -58,6 +58,9 @@ u1_t os_getRegion (void) { return REGCODE_EU868; } // cycle limitations). const unsigned TX_INTERVAL = 60000; +// Timestamp of last packet sent +uint32_t last_packet = 0; + // When this is defined, a standard pinmap from standard-pinmaps.ino // will be used. If you need to use a custom pinmap, comment this line // and enter the pin numbers in the lmic_pins variable below. @@ -201,11 +204,17 @@ void setup() { // error, a lower value will likely be more appropriate. //LMIC_setClockError(MAX_CLOCK_ERROR * 10 / 100); - // Queue first packet (sending automatically starts OTAA too) - send_packet(); -} + // Start join + LMIC_startJoining(); -uint32_t last_packet = 0; + // Make sure the first packet is scheduled ASAP after join completes + last_packet = millis() - TX_INTERVAL; + + // Optionally wait for join to complete (uncomment this is you want + // to run the loop while joining). + while ((LMIC.opmode & (OP_JOINING))) + os_runstep(); +} void loop() { // Let LMIC handle background tasks From fa4ef16c82eb2eded08f3b31ac7f9877932fc6e3 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 26 May 2020 15:59:22 +0200 Subject: [PATCH 062/137] Arduino: Fix printing of downlink data size This was accidentally spread over three lines, now it is again just one line. --- target/arduino/examples/basicmac-abp/basicmac-abp.ino | 4 ++-- target/arduino/examples/basicmac-otaa/basicmac-otaa.ino | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index 668dd8f..b7afbc1 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -129,8 +129,8 @@ void onLmicEvent (ev_t ev) { if (LMIC.txrxFlags & TXRX_ACK) Serial.println(F("Received ack")); if (LMIC.dataLen) { - Serial.println(F("Received ")); - Serial.println(LMIC.dataLen); + Serial.print(F("Received ")); + Serial.print(LMIC.dataLen); Serial.println(F(" bytes of payload")); } break; diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino index 29e2232..9e682d8 100644 --- a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino +++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino @@ -137,8 +137,8 @@ void onLmicEvent (ev_t ev) { if (LMIC.txrxFlags & TXRX_ACK) Serial.println(F("Received ack")); if (LMIC.dataLen) { - Serial.println(F("Received ")); - Serial.println(LMIC.dataLen); + Serial.print(F("Received ")); + Serial.print(LMIC.dataLen); Serial.println(F(" bytes of payload")); } break; From b9e74a6c5835ca1f61b445e47eeeadfd7630a0e7 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 26 May 2020 17:28:33 +0200 Subject: [PATCH 063/137] Arduino: Add empty lines around "Starting" message This makes it a bit easier to find restarts in the serial log. --- target/arduino/examples/basicmac-abp/basicmac-abp.ino | 3 +++ target/arduino/examples/basicmac-otaa/basicmac-otaa.ino | 3 +++ 2 files changed, 6 insertions(+) diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index b7afbc1..702acfb 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -184,7 +184,10 @@ void setup() { unsigned long start = millis(); while (millis() - start < 5000 && !Serial); + Serial.println(); + Serial.println(); Serial.println(F("Starting")); + Serial.println(); // LMIC init os_init(nullptr); diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino index 9e682d8..4f8121f 100644 --- a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino +++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino @@ -193,7 +193,10 @@ void setup() { unsigned long start = millis(); while (millis() - start < 5000 && !Serial); + Serial.println(); + Serial.println(); Serial.println(F("Starting")); + Serial.println(); // LMIC init os_init(nullptr); From c4977f2d38ac41220cb17fa810408702e683f70d Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 17:25:56 +0200 Subject: [PATCH 064/137] Introduce isFsk and isLora helper functions These replace explicit checks against SF, making the code easier to read and prepares for adding additional modulations later. --- lmic/lmic.c | 14 ++++++++------ lmic/lorabase.h | 2 ++ lmic/radio-sx126x.c | 6 +++--- lmic/radio-sx127x.c | 8 ++++---- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 95f50ad..11e6cbe 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -505,12 +505,12 @@ int getSensitivity (rps_t rps) { } ostime_t calcAirTime (rps_t rps, u1_t plen) { - u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz - u1_t sf = getSf(rps); // 0=FSK, 1..6 = SF7..12 - if( sf == FSK ) { + if( isFsk(rps) ) { return (plen+/*preamble*/5+/*syncword*/3+/*len*/1+/*crc*/2) * /*bits/byte*/8 * (s4_t)OSTICKS_PER_SEC / /*kbit/s*/50000; } + u1_t bw = getBw(rps); // 0,1,2 = 125,250,500kHz + u1_t sf = getSf(rps); u1_t sfx = 4*(sf+(7-SF7)); u1_t q = sfx - 8*enDro(rps); int tmp = 8*plen - sfx + 28 + (getNocrc(rps)?0:16) - (getIh(rps)?20:0); @@ -554,6 +554,8 @@ extern inline int getIh (rps_t params); extern inline rps_t setIh (rps_t params, int ih); extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc); extern inline int sameSfBw (rps_t r1, rps_t r2); +extern inline sf_t isLora (rps_t params); +extern inline sf_t isFsk (rps_t params); extern inline int enDro (rps_t params); @@ -847,7 +849,7 @@ static drmap_t all125up () { drmap_t map = 0; for( u1_t dr=0; dr < 16; dr++ ) { rps_t rps = REGION.dr2rps[dr]; - if( rps != ILLEGAL_RPS && getSf(rps) != FSK + if( rps != ILLEGAL_RPS && isLora(rps) && getBw(rps) == BW125 && !getNocrc(rps) ) // not DN only DR map |= 1<= SF7 && getSf(params) <= SF12; } +inline sf_t isFsk (rps_t params) { return getSf(params) == FSK; } // return 1 for low data rate optimize should be enabled (symbol time equal or above 16.384 ms) else 0 // Must be enabled for: SF11/BW125, SF12/BW125, SF12/BW250 diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 1c85e16..33f3258 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -623,7 +623,7 @@ void radio_starttx (bool txcontinuous) { // to be impliemented here as well radio_cw(); } else { - if (getSf(LMIC.rps) == FSK) { // FSK modem + if (isFsk(LMIC.rps)) { // FSK modem txfsk(); } else { // LoRa modem txlora(); @@ -746,7 +746,7 @@ void radio_cad (void) { } void radio_startrx (bool rxcontinuous) { - if (getSf(LMIC.rps) == FSK) { // FSK modem + if (isFsk(LMIC.rps)) { // FSK modem rxfsk(rxcontinuous); } else { // LoRa modem rxlora(rxcontinuous); @@ -804,7 +804,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { uint16_t irqflags = GetIrqStatus(); // dispatch modem - if (getSf(LMIC.rps) == FSK) { // FSK modem + if (isFsk(LMIC.rps)) { // FSK modem if (irqflags & IRQ_TXDONE) { // TXDONE BACKTRACE(); // save exact tx time diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index bf5b1a4..4fe02e3 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -1012,7 +1012,7 @@ void radio_startrx (bool rxcontinuous) { // set power consumption for statistics LMIC.radioPwr_ua = 11500; - if (getSf(LMIC.rps) == FSK) { // FSK modem + if (isFsk(LMIC.rps)) { // FSK modem rxfsk(rxcontinuous); } else { // LoRa modem if (rxcontinuous) { @@ -1031,7 +1031,7 @@ void radio_cad (void) { void radio_starttx (bool txcontinuous) { ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP ); - if (getSf(LMIC.rps) == FSK) { // FSK modem + if (isFsk(LMIC.rps)) { // FSK modem txfsk(txcontinuous); } else { // LoRa modem txlora(txcontinuous); @@ -1058,7 +1058,7 @@ void radio_cca (void) { writeReg(RegLna, 0b00100011); // highest gain, boost enable // set receiver bandwidth (SSB) - writeReg(FSKRegRxBw, (getSf(LMIC.rps) == FSK) ? 0x0B /* 50kHz SSB */ : + writeReg(FSKRegRxBw, (isFsk(LMIC.rps)) ? 0x0B /* 50kHz SSB */ : 3 - getBw(LMIC.rps)); // 62.5/125/250kHz SSB (RxBwMant=0, RxBwExp 3/2/1) // set power consumption for statistics @@ -1160,7 +1160,7 @@ void radio_init (bool calibrate) { // (run by irqjob) bool radio_irq_process (ostime_t irqtime, u1_t diomask) { // dispatch modem - if (getSf(LMIC.rps) == FSK) { // FSK modem + if (isFsk(LMIC.rps)) { // FSK modem u1_t irqflags1 = readReg(FSKRegIrqFlags1); u1_t irqflags2 = readReg(FSKRegIrqFlags2); From e3284687f3491fe2c30c1859d6fa54d089724f47 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 16:28:32 +0200 Subject: [PATCH 065/137] Remove some assumptions on constant values This makes it easier to change these constants if needed. --- lmic/radio-sx126x.c | 6 +++--- lmic/radio-sx127x.c | 12 ++++++------ lmic/radio.c | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 33f3258..d49ba58 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -376,9 +376,9 @@ static void SetRfFrequency (uint32_t freq) { // configure modulation parameters for LoRa static void SetModulationParamsLora (u2_t rps) { uint8_t param[4]; - param[0] = getSf(rps) + 6; // SF (sf7=1) - param[1] = getBw(rps) + 4; // BW (bw125=0) - param[2] = getCr(rps) + 1; // CR (cr45=0) + param[0] = getSf(rps) - SF7 + 7; // SF (sf7 -> 7) + param[1] = getBw(rps) - BW125 + 4; // BW (bw125 -> 4) + param[2] = getCr(rps) - CR_4_5 + 1; // CR (cr45 -> 1) param[3] = enDro(rps); // low-data-rate-opt (symbol time equal or above 16.38 ms) writecmd(CMD_SETMODULATIONPARAMS, param, 4); } diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 4fe02e3..3a14832 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -410,13 +410,13 @@ static void configLoraModem (bool txcont) { #if defined(BRD_sx1276_radio) // set ModemConfig1 'bbbbccch' (bw=xxxx, cr=xxx, implicitheader=x) writeReg(LORARegModemConfig1, - ((getBw(LMIC.rps) + 7) << 4) | // BW125=0 --> 7 - ((getCr(LMIC.rps) + 1) << 1) | // CR4_5=0 --> 1 + ((getBw(LMIC.rps) - BW125 + 7) << 4) | // BW125 --> 7 + ((getCr(LMIC.rps) - CR4_5 + 1) << 1) | // CR_4_5 --> 1 (getIh(LMIC.rps) != 0)); // implicit header // set ModemConfig2 'sssstcmm' (sf=xxxx, txcont=x, rxpayloadcrc=x, symtimeoutmsb=00) writeReg(LORARegModemConfig2, - ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7 + ((getSf(LMIC.rps)-SF7+7) << 4) | // SF7 --> 7 (txcont ? 0x08 : 0x00) | // txcont: 0x08 ((getNocrc(LMIC.rps) == 0) << 2)); // rxcrc @@ -436,15 +436,15 @@ static void configLoraModem (bool txcont) { #elif defined(BRD_sx1272_radio) // set ModemConfig1 'bbccchco' (bw=xx, cr=xxx, implicitheader=x, rxpayloadcrc=x, lowdatarateoptimize=x) writeReg(LORARegModemConfig1, - (getBw(LMIC.rps) << 6) | // BW125=0 --> 0 - ((getCr(LMIC.rps) + 1) << 3) | // CR4_5=0 --> 1 + ((getBw(LMIC.rps) - BW125) << 6) | // BW125 --> 0 + ((getCr(LMIC.rps) - CR_4_5 + 1) << 3) | // CR_4_5 --> 1 ((getIh(LMIC.rps) != 0) << 2) | // implicit header ((getNocrc(LMIC.rps) == 0) << 1) | // rxcrc enDro(LMIC.rps)); // symtime >= 16ms // set ModemConfig2 'sssstamm' (sf=xxxx, txcont=x, agcauto=1 symtimeoutmsb=00) writeReg(LORARegModemConfig2, - ((getSf(LMIC.rps)-1+7) << 4) | // SF7=1 --> 7 + ((getSf(LMIC.rps)-SF7+7) << 4) | // SF7 --> 7 (txcont ? 0x08 : 0x00) | // txcont: 0x08 (1 << 2)); // autoagc #endif // BRD_sx1272_radio diff --git a/lmic/radio.c b/lmic/radio.c index 2bee51e..803d21d 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -111,7 +111,7 @@ void os_radio (u1_t mode) { radio_stop(); #ifdef DEBUG_TX debug_printf("TX[fcnt=%ld,freq=%.1F,sf=%d,bw=%d,pow=%d,len=%d%s]: %.80h\r\n", - LMIC.seqnoUp - 1, LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), + LMIC.seqnoUp - 1, LMIC.freq, 6, getSf(LMIC.rps) - SF7 + 7, 125 << getBw(LMIC.rps), LMIC.txpow, LMIC.dataLen, (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", LMIC.frame, LMIC.dataLen); @@ -128,7 +128,7 @@ void os_radio (u1_t mode) { #ifdef DEBUG_RX debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%s,rxtime=%.0F]\r\n", LMIC.freq, 6, - getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), + getSf(LMIC.rps) - SF7 + 7, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), LMIC.rxtime, 0); #endif // receive frame at rxtime/now (wait for completion interrupt) @@ -143,7 +143,7 @@ void os_radio (u1_t mode) { #ifdef DEBUG_RX debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%s]\r\n", LMIC.freq, 6, - getSf(LMIC.rps) + 6, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps))); + getSf(LMIC.rps) - SF7 + 7, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps))); #endif // start scanning for frame now (wait for completion interrupt) state.txmode = 0; From 897766d2c5e1078e6fb1154e2947c1ce80ed10e1 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 16 Apr 2020 22:11:55 +0200 Subject: [PATCH 066/137] debug: Add some prints for exceptional TX situations --- lmic/lmic.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 11e6cbe..fa22ab6 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -2479,12 +2479,15 @@ static void buildDataFrame (void) { int foptslen = end - OFF_DAT_OPTS; if( foptslen && txdata && LMIC.pendTxPort == 0 ) { + debug_printf("Payload and MAC commands on port 0, sending only MAC commands\n"); // app layer wants to transmit on port 0, but we have fopts txdata = 0; LMIC.txrxFlags |= TXRX_NOTX; } - if( foptslen > 15 ) { + int foptslen_max = 15; + if( foptslen > foptslen_max ) { + debug_printf("MAC commands large (%u > %u), sending only MAC commands\n", foptslen, foptslen_max); // too big for FOpts, send as MAC frame with port=0 (cancels application payload) memcpy(LMIC.pendTxData, LMIC.frame+OFF_DAT_OPTS, foptslen); dlen = foptslen; @@ -2506,11 +2509,13 @@ static void buildDataFrame (void) { if( txdata ) { if( LMIC.pendTxPort ) { if( foptslen ) { + debug_printf("Frame too large (%u > %u), sending only MAC commands\n", flen, flen_max); // cancel application payload txdata = 0; LMIC.txrxFlags |= TXRX_NOTX; goto again; } else { + debug_printf("Frame too large (%u > %u), not sending\n", flen, flen_max); // cancel transmission completely LMIC.dataLen = 0; return; @@ -3048,11 +3053,13 @@ static void engineUpdate (void) { // Imminent roll over - proactively reset MAC // Device has to react! NWK will not roll over and just stop sending. // Thus, we have N frames to detect a possible lock up. + debug_printf("Down FCNT about to rollover (0x%lx), resetting session\n", LMIC.seqnoDn); reset: os_setCallback(&LMIC.osjob, FUNC_ADDR(runReset)); return; } if( (LMIC.txCnt==0 && LMIC.seqnoUp == 0xFFFFFFFF) ) { + debug_printf("Up FCNT about to rollover (0x%lx), resetting session\n", LMIC.seqnoUp); // Roll over of up seq counter // Do not run RESET event callback from here! // App code might do some stuff after send unaware of RESET. @@ -3061,6 +3068,7 @@ static void engineUpdate (void) { LMIC.txrxFlags = 0; buildDataFrame(); if( LMIC.dataLen == 0 ) { + debug_printf("Zero data length, not sending\n"); txError(); return; } From c7a5db2653fb592731ae11138fdc999abe46aa88 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:05:18 +0200 Subject: [PATCH 067/137] debug: Remove duplicate entry for EV_ADR_BACKOFF This fixes a compiler warning but should not otherwise change anything. --- lmic/debug.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lmic/debug.c b/lmic/debug.c index bcb61f5..ae1845d 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -70,7 +70,6 @@ static const char* const evnames[] = { [EV_LOST_TSYNC] = "LOST_TSYNC", [EV_RESET] = "RESET", [EV_RXCOMPLETE] = "RXCOMPLETE", - [EV_ADR_BACKOFF] = "ADR_BACKOFF", [EV_LINK_DEAD] = "LINK_DEAD", [EV_LINK_ALIVE] = "LINK_ALIVE", [EV_SCAN_FOUND] = "SCAN_FOUND", From 599b61e6b5e5ae6c94dbeb39a10970b55ee2855d Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 15:37:49 +0200 Subject: [PATCH 068/137] debug: Simplify RX debug messages There is no need to repeat all modulation parameters on successful RX or RX timeout, since those have already been printed when RX started with the RXMODE message. --- lmic/radio-sx126x.c | 12 +++++------- lmic/radio-sx127x.c | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index d49ba58..2280840 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -824,15 +824,15 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { LMIC.rxtime = irqtime - FSK_RXDONE_FIXUP; // end of frame timestamp LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n", - LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); + debug_printf("RX[rssi=%d,len=%d]: %h\r\n", + LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif } else if (irqflags & IRQ_TIMEOUT) { // TIMEOUT BACKTRACE(); // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK]: TIMEOUT\r\n", LMIC.freq, 6); + debug_printf("RX: TIMEOUT\r\n"); #endif } else { // unexpected irq @@ -866,8 +866,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { } LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %h\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), + debug_printf("RX[rssi=%d,snr=%.2F,len=%d]: %h\r\n", LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif @@ -876,8 +875,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps)); + debug_printf("RX: TIMEOUT\r\n"); #endif } else { // unexpected irq diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 3a14832..9fbe6b2 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -1185,15 +1185,15 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { LMIC.rxtime = irqtime - FSK_RXDONE_FIXUP; // end of frame timestamp LMIC.rxtime0 = LMIC.rxtime - calcAirTime(LMIC.rps, LMIC.dataLen); // beginning of frame timestamp #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK,rssi=%d,len=%d]: %h\r\n", - LMIC.freq, 6, LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); + debug_printf("RX[rssi=%d,len=%d]: %h\r\n", + LMIC.rssi - RSSI_OFF, LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif } else if (irqflags1 & IRQ_FSK1_TIMEOUT_MASK) { // TIMEOUT BACKTRACE(); // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,FSK]: TIMEOUT (%d us)\r\n", LMIC.freq, 6, osticks2us(irqtime - LMIC.rxtime)); + debug_printf("RX: TIMEOUT (%d us)\r\n", osticks2us(irqtime - LMIC.rxtime)); #endif } else if( irqflags2 & IRQ_FSK2_FIFOEMPTY_MASK ) { // FIFOEMPTY (TX) BACKTRACE(); @@ -1269,8 +1269,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { // read FIFO radio_readBuf(RegFifo, LMIC.frame, LMIC.dataLen); #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d,rssi=%d,snr=%.2F,len=%d]: %.80h\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), + debug_printf("RX[rssi=%d,snr=%.2F,len=%d]: %.80h\r\n", LMIC.rssi - RSSI_OFF, (s4_t)(LMIC.snr * 100 / SNR_SCALEUP), 2, LMIC.dataLen, LMIC.frame, LMIC.dataLen); #endif @@ -1279,8 +1278,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { // indicate timeout LMIC.dataLen = 0; #ifdef DEBUG_RX - debug_printf("RX[freq=%.1F,sf=%d,bw=%d]: TIMEOUT (%d us)\r\n", - LMIC.freq, 6, getSf(LMIC.rps) + 6, 125 << getBw(LMIC.rps), osticks2us(irqtime - LMIC.rxtime)); + debug_printf("RX: TIMEOUT (%d us)\r\n"); #endif } else if (irqflags & IRQ_LORA_CDDONE_MASK) { // CDDONE BACKTRACE(); From 5335f998ebdac2b13b50d700ff0d6a2d83e43128 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 15:43:18 +0200 Subject: [PATCH 069/137] debug: Fix message about next join This would print a timestamp in the past, rather than the actual time of the next join attempt. --- lmic/lmic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index fa22ab6..6af8984 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -1182,7 +1182,7 @@ static ostime_t nextJoinState (void) { if (failed) debug_verbose_printf("Join failed\r\n"); else - debug_verbose_printf("Scheduling next join at %t\r\n", LMIC.txend); + debug_verbose_printf("Scheduling next join at %t\r\n", os_getTime() + delay); // 1 - triggers EV_JOIN_FAILED event return (delay & ~1) | failed; From a60e70183f37b66203744f622df94da0994a990c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 15:50:50 +0200 Subject: [PATCH 070/137] debug: Simplify bandwidth in RX prints Rather than indexing a string, just calculate the bandwidth value. This will produce "1000" instead of "rfu" for bw=3, but that should not be used anyway. This calculation was already used for TX, now also for RX. --- lmic/radio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lmic/radio.c b/lmic/radio.c index 803d21d..a4c276a 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -126,9 +126,9 @@ void os_radio (u1_t mode) { case RADIO_RX: radio_stop(); #ifdef DEBUG_RX - debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%s,rxtime=%.0F]\r\n", + debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%d,rxtime=%.0F]\r\n", LMIC.freq, 6, - getSf(LMIC.rps) - SF7 + 7, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps)), + getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), LMIC.rxtime, 0); #endif // receive frame at rxtime/now (wait for completion interrupt) @@ -141,9 +141,9 @@ void os_radio (u1_t mode) { case RADIO_RXON: radio_stop(); #ifdef DEBUG_RX - debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%s]\r\n", + debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%d]\r\n", LMIC.freq, 6, - getSf(LMIC.rps) - SF7 + 7, ("125\0" "250\0" "500\0" "rfu") + (4 * getBw(LMIC.rps))); + getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), #endif // start scanning for frame now (wait for completion interrupt) state.txmode = 0; From eaabb27cd0a2a51ceeffd6f00f359ee957f31d9a Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 15:53:46 +0200 Subject: [PATCH 071/137] debug: Include CR, nocrc and IH in TX/RX_MODE prints --- lmic/radio.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lmic/radio.c b/lmic/radio.c index a4c276a..ee8dffe 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -110,8 +110,9 @@ void os_radio (u1_t mode) { case RADIO_TX: radio_stop(); #ifdef DEBUG_TX - debug_printf("TX[fcnt=%ld,freq=%.1F,sf=%d,bw=%d,pow=%d,len=%d%s]: %.80h\r\n", + debug_printf("TX[fcnt=%ld,freq=%.1F,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d,pow=%d,len=%d%s]: %.80h\r\n", LMIC.seqnoUp - 1, LMIC.freq, 6, getSf(LMIC.rps) - SF7 + 7, 125 << getBw(LMIC.rps), + getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps), LMIC.txpow, LMIC.dataLen, (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", LMIC.frame, LMIC.dataLen); @@ -126,9 +127,10 @@ void os_radio (u1_t mode) { case RADIO_RX: radio_stop(); #ifdef DEBUG_RX - debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%d,rxtime=%.0F]\r\n", + debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d,rxtime=%.0F]\r\n", LMIC.freq, 6, getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), + getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps), LMIC.rxtime, 0); #endif // receive frame at rxtime/now (wait for completion interrupt) @@ -141,9 +143,10 @@ void os_radio (u1_t mode) { case RADIO_RXON: radio_stop(); #ifdef DEBUG_RX - debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%d]\r\n", + debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d]\r\n", LMIC.freq, 6, getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), + getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); #endif // start scanning for frame now (wait for completion interrupt) state.txmode = 0; From c7f4ff0f5477da28d1086294d31c312c480377ed Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 17:28:12 +0200 Subject: [PATCH 072/137] debug: Split TX/RX prints by modulation Previously, sf=6 would indicate FSK and all modulation parameters were shown for FSK, even when most were unused. This makes makes the modulation used more explicit, and shows only relevant parameters in the debug output. --- lmic/radio.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lmic/radio.c b/lmic/radio.c index ee8dffe..c90e613 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -110,9 +110,16 @@ void os_radio (u1_t mode) { case RADIO_TX: radio_stop(); #ifdef DEBUG_TX - debug_printf("TX[fcnt=%ld,freq=%.1F,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d,pow=%d,len=%d%s]: %.80h\r\n", - LMIC.seqnoUp - 1, LMIC.freq, 6, getSf(LMIC.rps) - SF7 + 7, 125 << getBw(LMIC.rps), - getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps), + if( isFsk(LMIC.rps) ) { + debug_printf("TX[mod=FSK,nocrc=%d", getNocrc(LMIC.rps)); + } else { + assert(isLora(LMIC.rps)); + debug_printf("TX[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d", + getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), + getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); + } + debug_printf_continue(",fcnt=%ld,freq=%.1F,pow=%d,len=%d%s]: %.80h\r\n", + LMIC.seqnoUp - 1, LMIC.freq, 6, LMIC.txpow, LMIC.dataLen, (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", LMIC.frame, LMIC.dataLen); @@ -127,10 +134,16 @@ void os_radio (u1_t mode) { case RADIO_RX: radio_stop(); #ifdef DEBUG_RX - debug_printf("RX_MODE[freq=%.1F,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d,rxtime=%.0F]\r\n", + if( isFsk(LMIC.rps) ) { + debug_printf("RX_MODE[mod=FSK,nocrc=%d", getNocrc(LMIC.rps)); + } else { + assert(isLora(LMIC.rps)); + debug_printf("RX_MODE[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d", + getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), + getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); + } + debug_printf_continue(",freq=%.1F,rxtime=%.0F]\r\n", LMIC.freq, 6, - getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), - getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps), LMIC.rxtime, 0); #endif // receive frame at rxtime/now (wait for completion interrupt) @@ -143,10 +156,15 @@ void os_radio (u1_t mode) { case RADIO_RXON: radio_stop(); #ifdef DEBUG_RX - debug_printf("RXON_MODE[freq=%.1F,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d]\r\n", - LMIC.freq, 6, - getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), - getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); + if( isFsk(LMIC.rps) ) { + debug_printf("RXON_MODE[mod=FSK,nocrc=%d", getNocrc(LMIC.rps)); + } else { + assert(isLora(LMIC.rps)); + debug_printf("RXON_MODE[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d", + getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), + getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); + } + debug_printf_continue(",freq=%.1F]\r\n", LMIC.freq, 6); #endif // start scanning for frame now (wait for completion interrupt) state.txmode = 0; From 77307ef9c361901d5f03b0aef39268cf2bcfa256 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 18:04:47 +0200 Subject: [PATCH 073/137] debug: add [verbose_]debug_printf_continue macros This can be used to continue a previously started line, since this version does not include a timestamp. --- lmic/debug.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lmic/debug.h b/lmic/debug.h index 5ea1f5c..205064e 100644 --- a/lmic/debug.h +++ b/lmic/debug.h @@ -11,6 +11,7 @@ #define debug_snprintf(s,n,f,...) do { } while (0) #define debug_printf(f,...) do { } while (0) +#define debug_printf_continue(f,...) do { } while (0) #define debug_str(s) do { } while (0) #define debug_led(val) do { } while (0) #define debug_verbose_printf(f,...) do { } while (0) @@ -42,6 +43,8 @@ int debug_snprintf (char *str, int size, const char *format, ...); // write formatted string to USART void debug_printf_real (char const *format, ...); #define debug_printf(format, ...) debug_printf_real("%10t: " format, os_getTime(), ## __VA_ARGS__) +// To continue a line, omit the timestamp +#define debug_printf_continue(format, ...) debug_printf_real(format, ## __VA_ARGS__) // write nul-terminated string to USART void debug_str (const char* str); @@ -51,8 +54,10 @@ void debug_led (int val); #ifndef CFG_DEBUG_VERBOSE #define debug_verbose_printf(f,...) do { } while (0) +#define debug_verbose_printf_continue(f,...) do { } while (0) #else #define debug_verbose_printf debug_printf +#define debug_verbose_printf_continue debug_printf_continue #endif #endif From b71a8dda7007bc338209d210c9a666c27cb15c05 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 18:08:05 +0200 Subject: [PATCH 074/137] debug: Print frame counter as unsigned --- lmic/radio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/radio.c b/lmic/radio.c index c90e613..14999fe 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -118,7 +118,7 @@ void os_radio (u1_t mode) { getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); } - debug_printf_continue(",fcnt=%ld,freq=%.1F,pow=%d,len=%d%s]: %.80h\r\n", + debug_printf_continue(",fcnt=%lu,freq=%.1F,pow=%d,len=%d%s]: %.80h\r\n", LMIC.seqnoUp - 1, LMIC.freq, 6, LMIC.txpow, LMIC.dataLen, (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", From 389cbd31c742eb117532f4a67d30bfea26359fa9 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 18:16:57 +0200 Subject: [PATCH 075/137] debug: Handle a zero framecounter properly The `LMIC.seqnoUp` is the frame counter to be used for the *next* uplink packet. Therefore, one is subtracted before printing, to get the counter of the current framecounter. However, when no data uplinks have been sent yet (e.g. when sending a join request), `LMIC.seqnoUp` would be 0 and the subtraction would underflow. Earlier, this would print -1, but now that the counter is properly printed as unsigned, this would print some large value. With this commit, simply print 0 when no frames have been sent yet. --- lmic/radio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lmic/radio.c b/lmic/radio.c index 14999fe..352d4ab 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -119,7 +119,8 @@ void os_radio (u1_t mode) { getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); } debug_printf_continue(",fcnt=%lu,freq=%.1F,pow=%d,len=%d%s]: %.80h\r\n", - LMIC.seqnoUp - 1, LMIC.freq, 6, + (LMIC.seqnoUp ? LMIC.seqnoUp - 1 : 0), + LMIC.freq, 6, LMIC.txpow, LMIC.dataLen, (LMIC.pendTxPort != 0 && (LMIC.frame[OFF_DAT_FCT] & FCT_ADRARQ)) ? ",ADRARQ" : "", LMIC.frame, LMIC.dataLen); From b9d13d834e6717d8d58492e3b9b2c9e613ec768e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 22 May 2020 10:21:07 +0200 Subject: [PATCH 076/137] debug: Add DEBUG_JOBS macro This can be used to independently toggle debug output about job scheduling. This is debug output is not usually useful, so it is disabled by default, making the verbose debug output a lot easier to read. --- lmic/oslmic.c | 10 ++++++++++ target/arduino/hal/target-config.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/lmic/oslmic.c b/lmic/oslmic.c index dd35a93..e637403 100644 --- a/lmic/oslmic.c +++ b/lmic/oslmic.c @@ -115,7 +115,9 @@ void os_setExtendedTimedCallback (osxjob_t* xjob, osxtime_t xtime, osjobcb_t cb) xjob->deadline = xtime; extendedjobcb(xjob); hal_enableIRQs(); +#ifdef DEBUG_JOBS debug_verbose_printf("Scheduled job %u, cb %u at %t\r\n", (unsigned)xjob, (unsigned)cb, xtime); +#endif // DEBUG_JOBS } // clear scheduled job, return 1 if job was removed @@ -123,8 +125,10 @@ int os_clearCallback (osjob_t* job) { hal_disableIRQs(); int r = unlinkjob(&OS.scheduledjobs, job); hal_enableIRQs(); +#ifdef DEBUG_JOBS if (r) debug_verbose_printf("Cleared job %u\r\n", (unsigned)job); +#endif // DEBUG_JOBS return r; } @@ -158,10 +162,12 @@ void os_setTimedCallbackEx (osjob_t* job, ostime_t time, osjobcb_t cb, unsigned } *pnext = job; hal_enableIRQs(); +#ifdef DEBUG_JOBS if (flags & OSJOB_FLAG_NOW) debug_verbose_printf("Scheduled job %u, cb %u ASAP\r\n", (unsigned)job, (unsigned)cb); else debug_verbose_printf("Scheduled job %u, cb %u%s at %s%t\r\n", (unsigned)job, (unsigned)cb, flags & OSJOB_FLAG_IRQDISABLED ? " (irq disabled)" : "", flags & OSJOB_FLAG_APPROX ? "approx " : "", time); +#endif // DEBUG_JOBS } // execute 1 job from timer or run queue, or sleep if nothing is pending @@ -196,7 +202,9 @@ void os_runstep (void) { // Only print when interrupts are enabled, some Arduino cores do // not handle printing with IRQs disabled if( (j->flags & OSJOB_FLAG_IRQDISABLED) == 0) { +#ifdef DEBUG_JOBS debug_verbose_printf("Running job %u, cb %u, deadline %t\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline); +#endif // DEBUG_JOBS #ifdef CFG_warnjobs if ( delta > 1 ) { debug_printf("WARNING: job 0x%08x (func 0x%08x) executed %d ticks late\r\n", j, j->func, delta); @@ -208,7 +216,9 @@ void os_runstep (void) { hal_watchcount(0); // If we could not print before, at least print after if( (j->flags & OSJOB_FLAG_IRQDISABLED) != 0) { +#ifdef DEBUG_JOBS debug_verbose_printf("Ran job %u, cb %u, deadline %F\r\n", (unsigned)j, (unsigned)j->func, (ostime_t)j->deadline, 0); +#endif // DEBUG_JOBS #ifdef CFG_warnjobs if ( delta > 1 ) { debug_printf("WARNING: job 0x%08x (func 0x%08x) executed %d ticks late\r\n", j, j->func, delta); diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index ea8444a..c5d909b 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -34,6 +34,9 @@ // CFG_DEBUG) #define DEBUG_TX #define DEBUG_RX +// Define these to add some job scheduling specific debug output (needs +// CFG_DEBUG_VERBOSE) +//#define DEBUG_JOBS // Uncomment to display timestamps in ticks rather than milliseconds //#define CFG_DEBUG_RAW_TIMESTAMPS From a509eba24f575e4e743153f004e801ad17e69b2e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 10:34:37 +0200 Subject: [PATCH 077/137] Expose LMIC_disableDC always Previously, this was only exposed when CFG_extapi was set. However, disabling DC limitations can sometimes be useful for testing. Use with caution! --- lmic/lmic.c | 10 +++++----- lmic/lmic.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 6af8984..0a65429 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -3433,6 +3433,11 @@ ostime_t LMIC_nextTx (ostime_t now) { return nextTx(now); } +// Remove duty cycle limitations +void LMIC_disableDC (void) { + LMIC.noDC = 1; +} + #if defined(CFG_simul) #include "addr2func.h" #include "arr2len.h" @@ -3447,11 +3452,6 @@ void LMIC_enableFastJoin (void) { // XXX deprecated } -// Remove duty cycle limitations -void LMIC_disableDC (void) { - LMIC.noDC = 1; -} - /// Used for regression testing ostime_t LMIC_dr2hsym (dr_t dr, s1_t num) { return dr2hsym(dr,num); diff --git a/lmic/lmic.h b/lmic/lmic.h index b6cb2d8..0eef2a7 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -500,6 +500,7 @@ rps_t LMIC_dndr2rps (u1_t dr); ostime_t LMIC_calcAirTime (rps_t rps, u1_t plen); u1_t LMIC_maxAppPayload(); ostime_t LMIC_nextTx (ostime_t now); +void LMIC_disableDC (void); // Simulation only APIs #if defined(CFG_simul) @@ -515,7 +516,6 @@ DECL_ON_LMIC_EVENT; // !!!See implementation for caveats!!! #if defined(CFG_extapi) void LMIC_enableFastJoin (void); -void LMIC_disableDC (void); ostime_t LMIC_dr2hsym (dr_t dr, s1_t num); void LMIC_updateTx (ostime_t now); void LMIC_getRxdErrInfo (s4_t* skew, u4_t* span); From 0f545464e8a513d0b89df9516a319b0117ffc2b6 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:06:03 +0200 Subject: [PATCH 078/137] Remove EV_SHUTDOWN constant This event was never fired, so better just remove it. --- lmic/lmic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/lmic.h b/lmic/lmic.h index 0eef2a7..d0c2153 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -241,7 +241,7 @@ enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND, EV_JOINED, EV_RFU1, EV_JOIN_FAILED, EV_REJOIN_FAILED, EV_TXCOMPLETE, EV_LOST_TSYNC, EV_RESET, EV_RXCOMPLETE, EV_LINK_DEAD, EV_LINK_ALIVE, EV_SCAN_FOUND, EV_TXSTART, - EV_TXDONE, EV_DATARATE, EV_START_SCAN, EV_ADR_BACKOFF, EV_SHUTDOWN }; + EV_TXDONE, EV_DATARATE, EV_START_SCAN, EV_ADR_BACKOFF }; typedef enum _ev_t ev_t; From 3ecfc590d7bb87de2171b81c03e482d6633b41b4 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:10:29 +0200 Subject: [PATCH 079/137] Fix unused osjob parameter warnings --- lmic/lmic.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lmic/lmic.c b/lmic/lmic.c index 0a65429..8cef5f2 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -2160,6 +2160,7 @@ static void txDone (u1_t delay, osjobcb_t func) { static void onJoinFailed (osjob_t* osjob) { + (void)osjob; // unused // Notify app - must call LMIC_reset() to stop joining // otherwise join procedure continues. reportEvent(EV_JOIN_FAILED); @@ -2260,6 +2261,7 @@ static bit_t processJoinAccept (void) { static void processRx2Jacc (osjob_t* osjob) { + (void)osjob; // unused if( LMIC.dataLen == 0 ) LMIC.txrxFlags = 0; // nothing in 1st/2nd DN slot processJoinAccept(); @@ -2267,23 +2269,27 @@ static void processRx2Jacc (osjob_t* osjob) { static void setupRx2Jacc (osjob_t* osjob) { + (void)osjob; // unused LMIC.osjob.func = FUNC_ADDR(processRx2Jacc); setupRx2(); } static void processRx1Jacc (osjob_t* osjob) { + (void)osjob; // unused if( LMIC.dataLen == 0 || !processJoinAccept() ) schedRx2(DELAY_JACC2, FUNC_ADDR(setupRx2Jacc)); } static void setupRx1Jacc (osjob_t* osjob) { + (void)osjob; // unused setupRx1(FUNC_ADDR(processRx1Jacc)); } static void jreqDone (osjob_t* osjob) { + (void)osjob; // unused txDone(DELAY_JACC1, FUNC_ADDR(setupRx1Jacc)); reportEvent(EV_TXDONE); } @@ -2294,10 +2300,12 @@ static void jreqDone (osjob_t* osjob) { static bit_t processDnData(void); static void processRx2DnDataDelay (osjob_t* osjob) { + (void)osjob; // unused processDnData(); } static void processRx2DnData (osjob_t* osjob) { + (void)osjob; // unused if( LMIC.dataLen == 0 ) { LMIC.txrxFlags = (LMIC.txrxFlags & TXRX_NOTX) | 0; // nothing in 1st/2nd DN slot // Delay callback processing to avoid up TX while gateway is txing our missed frame! @@ -2312,18 +2320,21 @@ static void processRx2DnData (osjob_t* osjob) { static void setupRx2DnData (osjob_t* osjob) { + (void)osjob; // unused LMIC.osjob.func = FUNC_ADDR(processRx2DnData); setupRx2(); } static void processRx1DnData (osjob_t* osjob) { + (void)osjob; // unused if( LMIC.dataLen == 0 || !processDnData() ) { schedRx2(LMIC.dn1Dly+DELAY_EXTDNW2, FUNC_ADDR(setupRx2DnData)); } } static void processRx2ClassC (osjob_t* osjob) { + (void)osjob; // unused if( LMIC.dataLen != 0 ) { LMIC.txrxFlags = TXRX_DNW2; if ((LMIC.devaddr == os_rlsbf4(&LMIC.frame[OFF_DAT_ADDR]) && decodeFrame()) || decodeMultiCastFrame() ) { @@ -2344,16 +2355,19 @@ static void setupRx2ClassC () { } static void processRx1ClassC (osjob_t* osjob) { + (void)osjob; // unused if( !processDnData() ) { setupRx2ClassC(); } } static void setupRx1DnData (osjob_t* osjob) { + (void)osjob; // unused setupRx1(FUNC_ADDR(processRx1DnData)); } static void setupRx1ClassC (osjob_t* osjob) { + (void)osjob; // unused setupRx1(FUNC_ADDR(processRx1ClassC)); } @@ -2368,6 +2382,7 @@ static void txError (void) { static void updataDone (osjob_t* osjob) { + (void)osjob; // unused reportEvent(EV_TXDONE); // check if rx window is to be scheduled // (dataLen is reset by radio if tx didn't complete regularly and txend is unknown) @@ -2728,6 +2743,7 @@ static void buildJoinRequest (u1_t ftype) { } static void startJoining (osjob_t* osjob) { + (void)osjob; // unused // reset and calibrate radio LMIC.freq = (REGION.maxFreq - REGION.minFreq) / 2; os_radio(RADIO_INIT); @@ -2766,6 +2782,7 @@ bit_t LMIC_startJoining (void) { #if !defined(DISABLE_CLASSB) static void processPingRx (osjob_t* osjob) { + (void)osjob; // unused if( LMIC.dataLen != 0 ) { LMIC.txrxFlags = TXRX_PING; if( decodeFrame() ) { @@ -2877,6 +2894,7 @@ static bit_t processDnData (void) { #if !defined(DISABLE_CLASSB) static void processBeacon (osjob_t* osjob) { + (void)osjob; // unused ostime_t lasttx = LMIC.bcninfo.txtime; // save previous - decodeBeacon overwrites u1_t flags = LMIC.bcninfo.flags; ev_t ev; @@ -2930,12 +2948,14 @@ static void processBeacon (osjob_t* osjob) { } static void startRxBcn (osjob_t* osjob) { + (void)osjob; // unused LMIC.osjob.func = FUNC_ADDR(processBeacon); os_radio(RADIO_RX); } static void startRxPing (osjob_t* osjob) { + (void)osjob; // unused LMIC.osjob.func = FUNC_ADDR(processPingRx); os_radio(RADIO_RX); } From f9d00f404fc65cd22d7098f05c0d112c79d9ad37 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:14:34 +0200 Subject: [PATCH 080/137] Remove forward declaration of startScan This was not actually used before its definition, so this declaration is pointless. Also, when DISABLE_CLASS_B was defined, it would never be defined, resulting in: warning: 'startScan' declared 'static' but never defined [-Wunused-function] --- lmic/lmic.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 8cef5f2..6445a5c 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -44,7 +44,6 @@ DEFINE_LMIC; // Fwd decls. static void engineUpdate(void); -static void startScan (void); // ================================================================================ From 8c2316c534273fa3ccf4a4dbffc1128d859e171e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:23:41 +0200 Subject: [PATCH 081/137] Make rx1DrOff signed This array stores offsets between up and own datarates. Some of these values are negative, but were now stored as unsigned numbers. In practice, this worked out ok because unsigned and signed addition are the same, and the result of such addition (in prepareDnDr) was cast to s1_t (so any upper bits incorrect due to lack of sign extension were discarded anyway). However, in a few cases, comparisons with ILLEGAL_RX1DRoff (which is defined as -128) would *not* work. The compiler also warned about this: warning: comparison is always false due to limited range of data type [-Wtype-limits] if( REGION.rx1DrOff[LMIC.dn1DrOffIdx] == ILLEGAL_RX1DRoff ) ^~ And then the optimizer would remove the if, assuming it is always false. In practice, this only meant that any invalid offsets in a join accept packet or MCMD_DN2P_SET MAC command would not be rejected. --- lmic/lmic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/lmic.h b/lmic/lmic.h index d0c2153..6e0050d 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -133,7 +133,7 @@ typedef struct { u1_t beaconLen; // beacon length ostime_t beaconAirtime; // beacon air time eirp_t maxEirp; // max. EIRP (initial value) - u1_t rx1DrOff[8]; // RX1 data rate offsets + s1_t rx1DrOff[8]; // RX1 data rate offsets u1_t dr2maxAppPload[16]; // max application payload (assuming no repeater and no fopts) u1_t regcode; // external region code From 3d7a5e71d1a0f192d684ff1c5f333605742fc256 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:38:00 +0200 Subject: [PATCH 082/137] Fix potential infinite loop in rps2dndr If this function would be passed rps that matches none of the defined DR's, this loop would loop infinitely. This happened because dr_t is signed, so `dr--` would loop from 0 to 255 rather than to -1, and `dr >= 0` would never be false. The compiler warned about this: warning: comparison is always true due to limited range of data type [-Wtype-limits] for( dr_t dr = 15; dr >= 0; dr-- ) { ^~ In practice, this never happened because there would always be a matching dr to break the loop, but fix this anyway. --- lmic/lmic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 6445a5c..5d9908a 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -471,7 +471,7 @@ static u1_t numBcnChannels() { } static dr_t rps2dndr (rps_t rps) { - for( dr_t dr = 15; dr >= 0; dr-- ) { + for( dr_t dr = 15; dr != (dr_t)-1; dr-- ) { if( setNocrc(REGION.dr2rps[dr], 1) == rps ) { return dr; } From bb2462d61f67b69f8f8d4c550176f9eac0c31440 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 11:43:35 +0200 Subject: [PATCH 083/137] Fix some more warnings This fixes some signed vs unsigned mismatches, and some functions and variables that were only used inside preprocessor guards. --- lmic/lmic.c | 7 ++++++- lmic/lmic.h | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 5d9908a..d0c60df 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -466,9 +466,11 @@ static rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr), 1); } +#if !defined(DISABLE_CLASSB) static u1_t numBcnChannels() { return REG_IS_FIX() ? 8 : 1; } +#endif // !defined(DISABLE_CLASSB) static dr_t rps2dndr (rps_t rps) { for( dr_t dr = 15; dr != (dr_t)-1; dr-- ) { @@ -657,6 +659,7 @@ static void addRxdErr (u1_t rxdelay) { LMIC.rxdErrIdx = (LMIC.rxdErrIdx + 1) % RXDERR_NUM; } +#if defined(CFG_testpin) || defined(CFG_extapi) static s4_t evalRxdErr (u4_t* span) { s4_t min = 0x7FFFFFFF, min2=0x7FFFFFFF; s4_t max = 0x80000000, max2=0x80000000; @@ -674,6 +677,7 @@ static s4_t evalRxdErr (u4_t* span) { *span = max2-min2; return (sum - max - min + ((RXDERR_NUM-2)/2)) / (RXDERR_NUM-2); } +#endif static void adjustByRxdErr (u1_t rxdelay, u1_t dr) { #ifdef CFG_testpin @@ -2978,10 +2982,11 @@ static void engineUpdate (void) { #endif // CFG_autojoin ostime_t now = os_getTime(); - ostime_t rxtime = 0; ostime_t txbeg = 0; #if !defined(DISABLE_CLASSB) + ostime_t rxtime = 0; + if( (LMIC.opmode & OP_SCAN) != 0 ) { // Looking for a beacon - LMIC.bcninfo.txtime is timeout for scan // Cancel onging TX/RX transaction diff --git a/lmic/lmic.h b/lmic/lmic.h index 6e0050d..df03f17 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -155,7 +155,7 @@ enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this #endif #define LINK_CHECK_OFF ((s4_t)0x80000000) -#define LINK_CHECK_INIT (-LMIC.adrAckLimit) +#define LINK_CHECK_INIT ((s4_t)(-LMIC.adrAckLimit)) #define LINK_CHECK_DEAD (LMIC.adrAckDelay) enum { TIME_RESYNC = 6*128 }; // secs @@ -205,7 +205,7 @@ typedef struct { // purpose of receive window - lmic_t.rxState enum { RADIO_STOP=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3, RADIO_TXCW, RADIO_CCA, RADIO_INIT, RADIO_CAD, RADIO_TXCONT }; // Netid values / lmic_t.netid -enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF }; +enum { NETID_NONE=~0U, NETID_MASK=0xFFFFFF }; // MAC operation modes (lmic_t.opmode). enum { OP_NONE = 0x0000, OP_SCAN = 0x0001, // radio scan to find a beacon From 9632795ea070f40b2b64864bd13f1373d81e69ab Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 14:49:33 +0200 Subject: [PATCH 084/137] Change LMIC.errcr to cr_t This used to be u1_t, but cr_t is more appropriate, also saving a cast. Since cr_t is just a typdef for u1_t, this should not change the resulting output. --- lmic/lmic.c | 2 +- lmic/lmic.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index d0c60df..f153133 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -3098,7 +3098,7 @@ static void engineUpdate (void) { } LMIC.osjob.func = FUNC_ADDR(updataDone); } - LMIC.rps = setCr(updr2rps(txdr), (cr_t)LMIC.errcr); + LMIC.rps = setCr(updr2rps(txdr), LMIC.errcr); LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1 LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL; updateTx(txbeg); diff --git a/lmic/lmic.h b/lmic/lmic.h index df03f17..0ea57df 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -342,7 +342,7 @@ struct lmic_t { s1_t txPowAdj; // adjustment for txpow (ADR controlled) s1_t brdTxPowOff; // board-specific power adjustment offset dr_t datarate; // current data rate - u1_t errcr; // error coding rate (used for TX only) + cr_t errcr; // error coding rate (used for TX only) u1_t rejoinCnt; // adjustment for rejoin datarate s2_t drift; // last measured drift s2_t lastDriftDiff; From f24e39788384a91cfeb6285c48fa850a15872525 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 17:45:35 +0200 Subject: [PATCH 085/137] Add LMIC_STATIC_ASSERT macro This uses the C11/C++11 static_assert feature, to be able to check certain conditions at compiletime. Unlike `#if`, this can make assertions involving C-level constants, instead of just preprocessor macros. --- lmic/oslmic.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lmic/oslmic.h b/lmic/oslmic.h index 78f2fff..66d8012 100644 --- a/lmic/oslmic.h +++ b/lmic/oslmic.h @@ -51,6 +51,13 @@ typedef const char* str_t; #define ASSERT(cond) do { } while (0) #endif +#include +#if __cplusplus >= 201103L || defined(static_assert) +#define LMIC_STATIC_ASSERT static_assert +#else +#define LMIC_STATIC_ASSERT(expr, msg) +#endif + #ifdef __cplusplus extern "C"{ #endif From 2cc01a365b988f8cddcb9d0d4b2895c25053cc6c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 17:32:39 +0200 Subject: [PATCH 086/137] Fix warning in assert This assert would result in: warning: comparison is always true due to limited range of data type [-Wtype-limits] ASSERT(sf >= SF7 && sf <=SF12 && bw >= BW125 && bw <= BW500); Because it checked `bw >= BW125`, but `BW125` is 0. This means this part of the check can be removed, but replace it with a static assertion that `BW125` is indeed 0, just in case this code is every changed. --- lmic/lmic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index f153133..ad44e97 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -590,7 +590,8 @@ static ostime_t dr2hsym (dr_t dr, s1_t num) { rps_t rps = REGION.dr2rps[dr]; u1_t sf = getSf(rps); u1_t bw = getBw(rps); - ASSERT(sf >= SF7 && sf <=SF12 && bw >= BW125 && bw <= BW500); + LMIC_STATIC_ASSERT(BW125 == 0, "BW125 assumed to be 0"); + ASSERT(sf >= SF7 && sf <= SF12 && bw <= BW500); s4_t us = num << (9 + sf - SF7 - bw); // 10 => 9 half symbol time return us2osticks(us); } From 420e9150d1e84b97149881b836238d3174444326 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 16:33:37 +0200 Subject: [PATCH 087/137] Remove sameSfBw function It was unused. --- lmic/lmic.c | 1 - lmic/lorabase.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index ad44e97..0cc4101 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -554,7 +554,6 @@ extern inline rps_t setNocrc (rps_t params, int nocrc); extern inline int getIh (rps_t params); extern inline rps_t setIh (rps_t params, int ih); extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc); -extern inline int sameSfBw (rps_t r1, rps_t r2); extern inline sf_t isLora (rps_t params); extern inline sf_t isFsk (rps_t params); extern inline int enDro (rps_t params); diff --git a/lmic/lorabase.h b/lmic/lorabase.h index 7ecf414..bf917d8 100644 --- a/lmic/lorabase.h +++ b/lmic/lorabase.h @@ -306,8 +306,6 @@ inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { #define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) #define LWUPDR(sf,bw) ((u1_t)MAKERPS((sf),(bw),CR_4_5,0,0)) #define LWDNDR(sf,bw) ((u1_t)MAKERPS((sf),(bw),CR_4_5,0,1)) -// Two frames with params r1/r2 would interfere on air: same SFx + BWx -inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } inline sf_t isLora (rps_t params) { return getSf(params) >= SF7 && getSf(params) <= SF12; } inline sf_t isFsk (rps_t params) { return getSf(params) == FSK; } From d189edc844b1b2d44d03b51c17360a01d0a34a51 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 5 May 2020 15:43:40 +0200 Subject: [PATCH 088/137] Make RPS-building more clear This renames the LWUPDR/LWDNDR macros to LORA_UP_RPS and LORA_DN_RPS to make it more clear that these parameters are LoRa-specific and build an RPS rather than an DR-number. MAKERPS is also renamed to MAKE_LORA_RPS and makeRps to makeLoraRps. For FSK, FSK_UP_RPS, MAKE_FSK_RPS and makeFskRps are introduced which accept only the parameters needed for FSK. --- lmic/lmic.c | 47 ++++++++++++++++++++++++++--------------------- lmic/lorabase.h | 12 ++++++------ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 0cc4101..fa8d91c 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -144,45 +144,49 @@ u2_t os_crc16 (u1_t* data, uint len) { // ================================================================================ // BEG NEW REGION STUFF +#define LORA_UP_RPS(sf,bw) (MAKE_LORA_RPS((sf),(bw),CR_4_5,0,0)) +#define LORA_DN_RPS(sf,bw) (MAKE_LORA_RPS((sf),(bw),CR_4_5,0,1)) +#define FSK_UP_RPS() (MAKE_FSK_RPS(0)) + // data rate tables #ifdef REG_DRTABLE_EU static const u1_t DR2RPS_EU[16] = { - LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125), - LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), LWUPDR(SF7, BW250), LWUPDR(FSK, BW125), - ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, - ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, + LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125), + LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF7, BW250), FSK_UP_RPS(), + ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, + ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, }; #endif #ifdef REG_DRTABLE_125kHz static const u1_t DR2RPS_125kHz[16] = { - LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125), - LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), LWUPDR(SF7, BW250), ILLEGAL_RPS, - ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, - ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, + LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125), + LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF7, BW250), ILLEGAL_RPS, + ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, + ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, }; #endif #ifdef REG_DRTABLE_US static const u1_t DR2RPS_US[16] = { - LWUPDR(SF10, BW125), LWUPDR(SF9, BW125), LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), - LWUPDR(SF8, BW500), ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, - LWDNDR(SF12, BW500), LWDNDR(SF11, BW500), LWDNDR(SF10, BW500), LWDNDR(SF9, BW500), - LWDNDR(SF8, BW500), LWDNDR(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS, + LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125), LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), + LORA_UP_RPS(SF8, BW500), ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, + LORA_DN_RPS(SF12, BW500), LORA_DN_RPS(SF11, BW500), LORA_DN_RPS(SF10, BW500), LORA_DN_RPS(SF9, BW500), + LORA_DN_RPS(SF8, BW500), LORA_DN_RPS(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS, }; #endif #ifdef REG_DRTABLE_AU static const u1_t DR2RPS_AU[16] = { - LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125), - LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), LWUPDR(SF7, BW500), ILLEGAL_RPS, - LWDNDR(SF12, BW500), LWDNDR(SF11, BW500), LWDNDR(SF10, BW500), LWDNDR(SF9, BW500), - LWDNDR(SF8, BW500), LWDNDR(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS, + LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125), + LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF7, BW500), ILLEGAL_RPS, + LORA_DN_RPS(SF12, BW500), LORA_DN_RPS(SF11, BW500), LORA_DN_RPS(SF10, BW500), LORA_DN_RPS(SF9, BW500), + LORA_DN_RPS(SF8, BW500), LORA_DN_RPS(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS, }; #endif #ifdef REG_DRTABLE_IN static const u1_t DR2RPS_IN[16] = { - LWUPDR(SF12, BW125), LWUPDR(SF11, BW125), LWUPDR(SF10, BW125), LWUPDR(SF9, BW125), - LWUPDR(SF8, BW125), LWUPDR(SF7, BW125), ILLEGAL_RPS, LWUPDR(FSK, BW125), - ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, - ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, + LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125), + LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), ILLEGAL_RPS, FSK_UP_RPS(), + ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, + ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, ILLEGAL_RPS, }; #endif #ifdef REG_DYN @@ -543,6 +547,8 @@ ostime_t calcAirTime (rps_t rps, u1_t plen) { return (((ostime_t)tmp << sfx) * OSTICKS_PER_SEC + div/2) / div; } +extern inline rps_t makeLoraRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc); +extern inline rps_t makeFskRps (int nocrc); extern inline sf_t getSf (rps_t params); extern inline rps_t setSf (rps_t params, sf_t sf); extern inline bw_t getBw (rps_t params); @@ -553,7 +559,6 @@ extern inline int getNocrc (rps_t params); extern inline rps_t setNocrc (rps_t params, int nocrc); extern inline int getIh (rps_t params); extern inline rps_t setIh (rps_t params, int ih); -extern inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc); extern inline sf_t isLora (rps_t params); extern inline sf_t isFsk (rps_t params); extern inline int enDro (rps_t params); diff --git a/lmic/lorabase.h b/lmic/lorabase.h index bf917d8..d66b1c2 100644 --- a/lmic/lorabase.h +++ b/lmic/lorabase.h @@ -290,6 +290,12 @@ typedef u4_t devaddr_t; // RX quality (device) enum { RSSI_OFF=64, SNR_SCALEUP=4 }; +#define MAKE_LORA_RPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) +// FSK uses a subset of LORA fields, so just use MAKE_LORA_RPS here +#define MAKE_FSK_RPS(nocrc) (MAKE_LORA_RPS(FSK, 0, 0, 0, nocrc)) + +inline rps_t makeLoraRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { return MAKE_LORA_RPS(sf, bw, cr, ih, nocrc); } +inline rps_t makeFskRps (int nocrc) { return MAKE_FSK_RPS(nocrc); } inline sf_t getSf (rps_t params) { return (sf_t)(params & 0x7); } inline rps_t setSf (rps_t params, sf_t sf) { return (rps_t)((params & ~0x7) | sf); } inline bw_t getBw (rps_t params) { return (bw_t)((params >> 3) & 0x3); } @@ -300,12 +306,6 @@ inline int getNocrc(rps_t params) { return ((params >> 7) & inline rps_t setNocrc(rps_t params, int nocrc) { return (rps_t)((params & ~0x80) | (nocrc<<7)); } inline int getIh (rps_t params) { return ((params >> 8) & 0xFF); } inline rps_t setIh (rps_t params, int ih) { return (rps_t)((params & ~0xFF00) | (ih<<8)); } -inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { - return sf | (bw<<3) | (cr<<5) | (nocrc?(1<<7):0) | ((ih&0xFF)<<8); -} -#define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) -#define LWUPDR(sf,bw) ((u1_t)MAKERPS((sf),(bw),CR_4_5,0,0)) -#define LWDNDR(sf,bw) ((u1_t)MAKERPS((sf),(bw),CR_4_5,0,1)) inline sf_t isLora (rps_t params) { return getSf(params) >= SF7 && getSf(params) <= SF12; } inline sf_t isFsk (rps_t params) { return getSf(params) == FSK; } From 0a09a6f0ceeb3286626ef13feff893a56b0ab63b Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 20 May 2020 12:04:55 +0200 Subject: [PATCH 089/137] Replace decDR with lowerDR The `decDR` function would decrement a DR by one, while `lowerDR` would decrement it by a given amount. To make the code a bit more uniform and simple, just use ` lowerDR` for both, passing an explicit `1` for decrementing by one. --- lmic/lmic.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index fa8d91c..cb8822f 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -440,10 +440,6 @@ static void setAvail (avail_t* pavail, osxtime_t t) { *pavail = v; } -static dr_t decDR (dr_t dr) { // move to slower DR - return dr == 0 ? 0 : dr-1; -} - static dr_t lowerDR (dr_t dr, u1_t n) { return dr <= n ? 0 : dr-n; } @@ -1181,7 +1177,7 @@ static ostime_t nextJoinState (void) { failed = 1; // we have tried all DR - signal EV_JOIN_FAILED } else { - setDrJoin(DRCHG_NOJACC, decDR(LMIC.datarate)); + setDrJoin(DRCHG_NOJACC, lowerDR(LMIC.datarate, 1)); } } delay = rndDelay(255 >> LMIC.datarate); @@ -2846,8 +2842,8 @@ static bit_t processDnData (void) { if (LMIC.txPowAdj) { setDrTxpow(DRCHG_NOADRACK, LMIC.datarate, 0); } - if (decDR(LMIC.datarate) != LMIC.datarate) { - setDrTxpow(DRCHG_NOADRACK, decDR(LMIC.datarate), KEEP_TXPOWADJ); + if (lowerDR(LMIC.datarate, 1) != LMIC.datarate) { + setDrTxpow(DRCHG_NOADRACK, lowerDR(LMIC.datarate, 1), KEEP_TXPOWADJ); } else if (REG_IS_FIX() #ifdef REG_FIX && enableAllChannels_fix() From 68b82060c40418c841ae384e7db1c2d9a6eda47c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 10:34:08 +0200 Subject: [PATCH 090/137] dr2hsym: Use updr2rps instead of accessing REGION directly This is currently equivalent, but this prepares for adding a custom DR exception in updr2rps next. --- lmic/lmic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index cb8822f..4f2dccf 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -587,7 +587,7 @@ static void decPollcnt (void) { // 500kHz | 8 9 10 11 12 13 // static ostime_t dr2hsym (dr_t dr, s1_t num) { - rps_t rps = REGION.dr2rps[dr]; + rps_t rps = updr2rps(dr); u1_t sf = getSf(rps); u1_t bw = getBw(rps); LMIC_STATIC_ASSERT(BW125 == 0, "BW125 assumed to be 0"); From 9ca1ec456c5043c3617c1eefa4f276e905ecda37 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 11:58:56 +0200 Subject: [PATCH 091/137] Calculate LMIC.dndr earlier Previously, LMIC.dndr was set to the TX datarate on TX, and then the corresponding RX1 datarate was derived in txDone. Since nothing happens in between that could influence the RX1 datarate, it is easier to just derive the RX1 datarate on TX already. This removes the confusion where `LMIC.dndr` would actually contain the TX datarate. It also prepares for introducing a custom datarate. --- lmic/lmic.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 4f2dccf..3c1f8f6 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -828,7 +828,6 @@ static dr_t prepareDnDr (dr_t updr) { #ifdef REG_DYN static void prepareDn_dyn () { - LMIC.dndr = prepareDnDr(LMIC.dndr); // Check reconfigured DN link freq freq_t dnfreq = LMIC.dyn.chDnFreq[LMIC.txChnl]; if( dnfreq ) { @@ -1297,7 +1296,6 @@ static void initDefaultChannels_fix (void) { } static void prepareDn_fix () { - LMIC.dndr = prepareDnDr(LMIC.dndr); LMIC.freq = REGION.baseFreqDn + (LMIC.txChnl % (REGION.numChDnBlocks*8)) * (REGION.baseFreqFix ? DNCHSPACING_500kHz : DNCHSPACING_125kHz); @@ -3100,7 +3098,8 @@ static void engineUpdate (void) { LMIC.osjob.func = FUNC_ADDR(updataDone); } LMIC.rps = setCr(updr2rps(txdr), LMIC.errcr); - LMIC.dndr = txdr; // carry TX datarate (can be != LMIC.datarate) over to txDone/setupRx1 + // Calculate dndr to use based on txdr (can be != LMIC.datarate for joins) + LMIC.dndr = prepareDnDr(txdr); LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL; updateTx(txbeg); reportEvent(EV_TXSTART); From 3010a4e7114fd8a695012f9a0df9400e8aa97e2d Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 22 May 2020 10:44:43 +0200 Subject: [PATCH 092/137] Report EV_TXDONE slightly later for data packets This makes sure that the `txDone()` function is ran before `EV_TXDONE`, which makes this consistent with join request packets, and allows to customize the radio settings for the RX windows without `txDone()` overwriting them again. --- lmic/lmic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 3c1f8f6..62eb268 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -2385,7 +2385,6 @@ static void txError (void) { static void updataDone (osjob_t* osjob) { (void)osjob; // unused - reportEvent(EV_TXDONE); // check if rx window is to be scheduled // (dataLen is reset by radio if tx didn't complete regularly and txend is unknown) if( LMIC.pendTxNoRx || LMIC.dataLen == 0 ) { @@ -2398,6 +2397,7 @@ static void updataDone (osjob_t* osjob) { ? FUNC_ADDR(setupRx1DnData) : FUNC_ADDR(setupRx1ClassC)); } + reportEvent(EV_TXDONE); } // ======================================== From 42456b1a7063fee6b832eaea25a494812f614c31 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 21 May 2020 15:41:20 +0200 Subject: [PATCH 093/137] Support custom datarates This adds the LMIC_setCustomDr() function which can be used to enable a custom datarate with arbitrary settings (e.g different CR, bandwidth, etc.), which is used until explicitly changed back to a standard datarate. See comments in lmic.h for detailed usage info. --- lmic/lmic.c | 44 +++++++++++++++++++++++++++------- lmic/lmic.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ lmic/lorabase.h | 5 ++++ 3 files changed, 104 insertions(+), 8 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 62eb268..6ced6e2 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -441,6 +441,8 @@ static void setAvail (avail_t* pavail, osxtime_t t) { } static dr_t lowerDR (dr_t dr, u1_t n) { + if (dr == CUSTOM_DR) + return dr; return dr <= n ? 0 : dr-n; } @@ -459,6 +461,8 @@ static inline bit_t validDR (dr_t dr) { } static rps_t updr2rps (dr_t dr) { + if (dr == CUSTOM_DR) + return LMIC.custom_rps; return REGION.dr2rps[dr]; } @@ -810,6 +814,8 @@ static freq_t rdFreq (u1_t* p) { // Shard between REG_DYN/REG_FIX static dr_t prepareDnDr (dr_t updr) { + ASSERT(updr != CUSTOM_DR); + s1_t dndr = (s1_t) updr + REGION.rx1DrOff[LMIC.dn1DrOffIdx]; dr_t dr8 = REGION.dr2rps[8]; s1_t mindr = (dr8 != ILLEGAL_RPS && getNocrc(dr8)) ? 8 : 0; @@ -1734,10 +1740,14 @@ static bit_t decodeFrame (void) { debug_printf("ADR: p1=%02x,dr=%d,powadj=%d,chmap=%04x,chpage=%d,nbtrans=%d\r\n", p1, dr, powadj, chmap, chpage, nbtrans); #endif - if( dr == 15 ) { + if( LMIC.datarate == CUSTOM_DR ) { + // Reject modifying a custom DR. The network probably + // does not really know how to handle this, but at least + // they know we did not change the DR. + ans &= ~MCMD_LADR_ANS_DRACK; + } else if( dr == 15 ) { dr = LMIC.datarate; // Do not change DR - } - if( !validDR(dr) || ( REG_IS_FIX() + } else if( !validDR(dr) || ( REG_IS_FIX() #ifdef REG_FIX && !checkChannel_fix(dmap, dr) #endif @@ -1784,7 +1794,7 @@ static bit_t decodeFrame (void) { freq_t freq = rdFreq(&opts[oidx+2]); oidx += 5; u1_t ans = 0; - if( validDR(dr) ) //XXX:BUG validDNDR + if( validDR(dr) && LMIC.dn2Dr != CUSTOM_DR) //XXX:BUG validDNDR ans |= MCMD_DN2P_ANS_DRACK; if( freq > 0 ) ans |= MCMD_DN2P_ANS_CHACK; @@ -2518,7 +2528,9 @@ static void buildDataFrame (void) { end = OFF_DAT_OPTS; } - int flen, flen_max = LMIC_maxAppPayload() + 13; + int flen, flen_max = MAX_LEN_FRAME; + if (LMIC.datarate != CUSTOM_DR) + flen_max = LMIC_maxAppPayload() + 13; again: flen = end + (txdata ? 5+dlen : 4); if( flen > flen_max ) { @@ -3097,9 +3109,14 @@ static void engineUpdate (void) { } LMIC.osjob.func = FUNC_ADDR(updataDone); } - LMIC.rps = setCr(updr2rps(txdr), LMIC.errcr); - // Calculate dndr to use based on txdr (can be != LMIC.datarate for joins) - LMIC.dndr = prepareDnDr(txdr); + LMIC.rps = updr2rps(txdr); + // For CUSTOM_DR, assume that rps already has the right CR + // and that dndr is also preset to the right DR. + if (txdr != CUSTOM_DR) { + LMIC.rps = setCr(LMIC.rps, LMIC.errcr); + // Calculate dndr to use based on txdr (can be != LMIC.datarate for joins) + LMIC.dndr = prepareDnDr(txdr); + } LMIC.opmode = (LMIC.opmode & ~(OP_POLL|OP_RNDTX)) | OP_TXRXPEND | OP_NEXTCHNL; updateTx(txbeg); reportEvent(EV_TXSTART); @@ -3173,10 +3190,21 @@ void LMIC_setAdrMode (bit_t enabled) { // Should we have/need an ext. API like this? void LMIC_setDrTxpow (dr_t dr, s1_t txpowadj) { + ASSERT(dr != CUSTOM_DR); // Use setCustomDr() for that setDrTxpow(DRCHG_SET, dr, txpowadj); syncDatarate(); + // Make sure dndr is not CUSTOM_DR anymore (actual value does not + // matter, it will be set later *if* not CUSTOM_DR). + LMIC.dndr = dr; } +dr_t LMIC_setCustomDr (rps_t custom_rps, dr_t dndr) { + dr_t old_dr = LMIC.datarate; + setDrTxpow(DRCHG_SET, CUSTOM_DR, KEEP_TXPOWADJ); + LMIC.dndr = dndr; + LMIC.custom_rps = custom_rps; + return old_dr; +} void LMIC_shutdown (void) { os_clearCallback(&LMIC.osjob); diff --git a/lmic/lmic.h b/lmic/lmic.h index 0ea57df..13fbf97 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -296,6 +296,7 @@ struct lmic_t { s1_t rssi; s1_t snr; rps_t rps; + rps_t custom_rps; // Used for CUSTOM_DR u1_t rxsyms; u1_t dndr; s1_t txpow; // dBm -- needs to be combined with brdTxPowOff @@ -451,6 +452,68 @@ DECLARE_LMIC; //!< \internal bit_t LMIC_setupChannel (u1_t channel, freq_t freq, u2_t drmap); void LMIC_disableChannel (u1_t channel); +// Use a custom datrate and rps value. +// +// This causes the uplink to use the radio settings described by the +// given rps value, which can be any valid rps setting (even when the +// region does not normally enable it). The rps setting is used +// unmodified for uplink, and will have nocrc set for downlink. +// +// While the custom datarate is active, it will not be modified +// automatically (e.g. LinkADRReq is rejected and lowring DR for ADR is +// suspended), except when it is not enabled for any channel (in dynamic +// regions). +// +// However, if you call this function again to change the rps value for +// RX1 or RX2 (see below), it will also apply to subsequent uplinks, so +// you might need to set a new rps or standard datarate before the next +// uplink. +// +// This returns the old uplink DR, which can be later be passed to +// LMIC_setDrTxpow() to disable the custom datarate again, if needed. +// +// RX1 +// +// Normally, the RX1 datarate is derived from the uplink datarate. When +// using a custom datarate, it must be set explicitly using the dndr +// parameter to this function. This can be either a standard datarate +// value, or CUSTOM_DR to use the same custom rps value as the uplink. +// +// To use a custom rps for RX1 that is different from the uplink (or +// use a custom rps just for RX1), call this function (again) after the +// EV_TXSTART event (but before EV_TXDONE). +// +// +// RX2 +// +// To also use a custom datarate for the RX2 window, call this function +// and set `LMIC.dn2Dr` to CUSTOM_DR. This also causes RXParamSetupReq +// to be rejected, keeping dn2Dr unmodified. +// +// To use a custom rps for RX2 that is different from the uplink and/or +// RX1 (or use a custom rps just for RX2), call this function (again) +// after the EV_TXDONE event (but before RX2 starts). +// +// +// Channel selection as normal +// +// For fixed regions, any enabled (125kHz) channel will be used. +// +// For dynamic regions, any channel that supports CUSTOM_DR will be +// considered. LMIC_setupChannel() can be used normally, to create one +// or more channels enabled for CUSTOM_DR. Since the network can +// potentially disable or reconfigure channels, it is recommended to set +// up these channels again before every transmission. +// +// Disabling CUSTOM_DR +// +// To revert uplink and RX1 back to a normal datarate and allow ADR to +// work again (if enabled), call LMIC_setDrTxpow as normal, passing the +// DR to use. +// +// To revert RX2 back to a normal datarate, just set LMIC.dn2Dr to the +// appropriate datarate directly. +dr_t LMIC_setCustomDr (rps_t custom_rps, dr_t dndr); void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) bit_t LMIC_startJoining (void); diff --git a/lmic/lorabase.h b/lmic/lorabase.h index d66b1c2..0bdad15 100644 --- a/lmic/lorabase.h +++ b/lmic/lorabase.h @@ -27,6 +27,11 @@ typedef s1_t eirp_t; enum { ILLEGAL_DR = 0xFF }; enum { ILLEGAL_RPS = 0xFF }; enum { ILLEGAL_MAS = 0x00 }; +// DR 15 is used in the LinkADRReq to indicate "no DR changes", so it +// will never be allocated as a standard DR. So it is used here for a +// custom DR. By using 15, rather than 0xFF or similar, it can be used +// in per-channel drmap as normal. +enum { CUSTOM_DR = 0xF }; // Global maximum frame length enum { BCN_PREAMBLE_LEN = 10 }; // length in symbols - actual time depends on DR From a4fd7d0029a5aa9a24e4f5eed69996f66d248e02 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 25 May 2020 19:27:08 +0200 Subject: [PATCH 094/137] Add LMIC_selectChannel to manually choose channel --- lmic/lmic.c | 7 +++++++ lmic/lmic.h | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/lmic/lmic.c b/lmic/lmic.c index 6ced6e2..3401b04 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -3206,6 +3206,13 @@ dr_t LMIC_setCustomDr (rps_t custom_rps, dr_t dndr) { return old_dr; } +void LMIC_selectChannel(u1_t channel) { + LMIC.opmode &= ~OP_NEXTCHNL; + LMIC.txChnl = channel; + LMIC.txend = os_getTime(); +} + + void LMIC_shutdown (void) { os_clearCallback(&LMIC.osjob); os_radio(RADIO_STOP); diff --git a/lmic/lmic.h b/lmic/lmic.h index 13fbf97..7fb8542 100644 --- a/lmic/lmic.h +++ b/lmic/lmic.h @@ -452,6 +452,18 @@ DECLARE_LMIC; //!< \internal bit_t LMIC_setupChannel (u1_t channel, freq_t freq, u2_t drmap); void LMIC_disableChannel (u1_t channel); +// Manually select the next channel for the next transmission. +// +// Warning: This does not do any checking. In particular, this bypasses +// duty cycle limits, allows selecting a channel that is not configured +// for the current datarate, and breaks when you select an invalid or +// disabled channel. +// +// The selected channel applies only to the next transmission. Call this +// *after* setting the datarate (if needed) with LMIC_setDrTxpow(), +// since that forces a new channel to be selected automatically. +void LMIC_selectChannel(u1_t channel); + // Use a custom datrate and rps value. // // This causes the uplink to use the radio settings described by the From 4edad96c1a20ffdd0d6d2ffcd2ee2c9198a6b533 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 26 May 2020 16:52:28 +0200 Subject: [PATCH 095/137] Include TX_RAMPUP in channel selection nextTx is called to select any channel that is available now. It then returns the timestamp when a channel is first available. If that timestamp is <= now, the channel is selected, if the timestamp > now, the nextTx should be called again after the returned timestamp. However, to decide whether a channel is available, nextTx looked at `now`, but to decide whether TX should then start, engineUpdate looked at `now + TX_RAMPUP`. This meant that if `nextTx` was called within `TX_RAMPUP` of airtime becoming available, `nextTx` could decide that no airtime was available yet, *not* selecting a channel. Then engineUpdate decides that airtime *is* available and continues with the transmission, with the same channel as the previous transmission. This fixes this by passing the actual threshold (`now + TX_RAMPUP`) to `nextTx`, so both decisions end up the same. Additionally, `nextTx_dyn` would actually call `os_getXTime()` rather than use the `now` argument passed. It now expands the given timestamp to an `osxtime_t` instead, so the added `TX_RAMPUP` is actually used. --- lmic/lmic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 3401b04..6ef508d 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -1013,7 +1013,7 @@ static u1_t selectRandomChnl (u2_t map, u1_t nbits) { // will block while doing LBT static ostime_t nextTx_dyn (ostime_t now) { drmap_t drbit = 1 << LMIC.datarate; - osxtime_t xnow = os_getXTime(); + osxtime_t xnow = os_time2XTime(now, os_getXTime()); osxtime_t txavail = OSXTIME_MAX; u1_t cccnt = 0; // number of candidate channels u2_t ccmap = 0; // candidate channel mask @@ -3039,7 +3039,7 @@ static void engineUpdate (void) { debug_verbose_printf("Uplink data pending\r\n", os_getTime()); // Find next suitable channel and return availability time if( (LMIC.opmode & OP_NEXTCHNL) != 0 ) { - txbeg = LMIC.txend = nextTx(now); + txbeg = LMIC.txend = nextTx(now+TX_RAMPUP); debug_verbose_printf("Airtime available at %t (channel duty limit)\r\n", txbeg); } else { txbeg = LMIC.txend; From 7caf7dc80d545fe2a8f8e4591231208c99fd8cc3 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 26 May 2020 15:56:56 +0200 Subject: [PATCH 096/137] sx126x: Fix random write in GetRandom In commit f74ec70 (Fix undefined casting in GetRandom), an addressof operator for `value` was forgotten, so instead of casting the pointer to `value`, this would cast `value` itself to a pointer. Combined with a lack of initialization on `value`, this would end up writing the random value to a random place in memory, and not actually returning it. While we're here, also use sizeof instead of hardcoding the number of bytes to read. Note that none of this actually seems to make GetRandom work, the hardware still returns only zeroes from the random register somehow... --- lmic/radio-sx126x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 2280840..3fb8dee 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -529,7 +529,7 @@ static uint32_t GetRandom (void) { // wait 1ms hal_waitUntil(os_getTime() + ms2osticks(1)); // read random register - ReadRegs(REG_RANDOMNUMBERGEN0, (uint8_t*)value, 4); + ReadRegs(REG_RANDOMNUMBERGEN0, (uint8_t*)&value, sizeof(value)); // standby SetStandby(STDBY_RC); return value; From a493acb7b5238ab563508ced59e9e39a3596bf07 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 12 Jun 2020 17:18:34 +0200 Subject: [PATCH 097/137] Arduino: Fix comment This referred to the DISABLE_PING that LMIC had, but this was merged with DISABLE_CLASSB in Basicmac. --- target/arduino/hal/target-config.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index c5d909b..f758b73 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -45,8 +45,7 @@ // instead, though. //#define LMIC_PRINTF_FO -// Uncomment this to disable all code related to beacon tracking. -// Requires ping to be disabled too +// Remove/comment this to enable code related to beacon tracking. #define DISABLE_CLASSB // This allows choosing between multiple included AES implementations. From cc25499096fb034016fdb43c9599e680f68010ad Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 17 Jun 2020 17:28:32 +0200 Subject: [PATCH 098/137] sx126x: Factor out regulator and DIO setup This code would be duplicated four times for different tx and rx mode, so better make it a function (this also prepares for more conditional and complicated setup later). --- lmic/radio-sx126x.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 3fb8dee..9dd622c 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -543,10 +543,15 @@ void radio_sleep (void) { } } -static void txlora (void) { +// Do config common to all RF modes +static void CommonSetup (void) { SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); SetDIO3AsTcxoCtrl(); +} + +static void txlora (void) { + CommonSetup(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_LORA); SetRfFrequency(LMIC.freq); @@ -570,9 +575,7 @@ static void txlora (void) { } static void txfsk (void) { - SetRegulatorMode(REGMODE_DCDC); - SetDIO2AsRfSwitchCtrl(1); - SetDIO3AsTcxoCtrl(); + CommonSetup(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_FSK); SetRfFrequency(LMIC.freq); @@ -598,9 +601,7 @@ static void txfsk (void) { } void radio_cw (void) { - SetRegulatorMode(REGMODE_DCDC); - SetDIO2AsRfSwitchCtrl(1); - SetDIO3AsTcxoCtrl(); + CommonSetup(); SetStandby(STDBY_RC); SetRfFrequency(LMIC.freq); SetTxPower(LMIC.txpow + LMIC.brdTxPowOff); @@ -636,9 +637,7 @@ void radio_starttx (bool txcontinuous) { static void rxfsk (bool rxcontinuous) { // configure radio (needs rampup time) ostime_t t0 = os_getTime(); - SetRegulatorMode(REGMODE_DCDC); - SetDIO2AsRfSwitchCtrl(1); - SetDIO3AsTcxoCtrl(); + CommonSetup(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_FSK); SetRfFrequency(LMIC.freq); @@ -687,9 +686,7 @@ static void rxfsk (bool rxcontinuous) { static void rxlora (bool rxcontinuous) { // configure radio (needs rampup time) ostime_t t0 = os_getTime(); - SetRegulatorMode(REGMODE_DCDC); - SetDIO2AsRfSwitchCtrl(1); - SetDIO3AsTcxoCtrl(); + CommonSetup(); SetStandby(STDBY_RC); SetPacketType(PACKET_TYPE_LORA); SetRfFrequency(LMIC.freq); From a42d65c0b9533852e22f8440f69ee297038fb0f3 Mon Sep 17 00:00:00 2001 From: Jason Reiss Date: Sat, 4 May 2019 08:30:48 -0500 Subject: [PATCH 099/137] AU915: DR6 = SF8BW500 Lw 1.0.2 and Lw 1.1 define DR6 for AU915 to be SF8BW500. --- lmic/lmic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 6ef508d..64df12a 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -176,7 +176,7 @@ static const u1_t DR2RPS_US[16] = { #ifdef REG_DRTABLE_AU static const u1_t DR2RPS_AU[16] = { LORA_UP_RPS(SF12, BW125), LORA_UP_RPS(SF11, BW125), LORA_UP_RPS(SF10, BW125), LORA_UP_RPS(SF9, BW125), - LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF7, BW500), ILLEGAL_RPS, + LORA_UP_RPS(SF8, BW125), LORA_UP_RPS(SF7, BW125), LORA_UP_RPS(SF8, BW500), ILLEGAL_RPS, LORA_DN_RPS(SF12, BW500), LORA_DN_RPS(SF11, BW500), LORA_DN_RPS(SF10, BW500), LORA_DN_RPS(SF9, BW500), LORA_DN_RPS(SF8, BW500), LORA_DN_RPS(SF7, BW500), ILLEGAL_RPS, ILLEGAL_RPS, }; From 20b58e21b620011e4992643a7627af5ad669e388 Mon Sep 17 00:00:00 2001 From: Remko <11438883+pe1mew@users.noreply.github.com> Date: Mon, 6 Jul 2020 00:04:29 +0200 Subject: [PATCH 100/137] Fix typo in constant name (#14) In commit e328468 (Remove some assumptions on constant values), a typo was introduced in this constant. This was only problematic for the SX1276, which was probably not tested after that commit. This fixes #13. --- lmic/radio-sx127x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 9fbe6b2..de0df68 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -411,7 +411,7 @@ static void configLoraModem (bool txcont) { // set ModemConfig1 'bbbbccch' (bw=xxxx, cr=xxx, implicitheader=x) writeReg(LORARegModemConfig1, ((getBw(LMIC.rps) - BW125 + 7) << 4) | // BW125 --> 7 - ((getCr(LMIC.rps) - CR4_5 + 1) << 1) | // CR_4_5 --> 1 + ((getCr(LMIC.rps) - CR_4_5 + 1) << 1) | // CR_4_5 --> 1 (getIh(LMIC.rps) != 0)); // implicit header // set ModemConfig2 'sssstcmm' (sf=xxxx, txcont=x, rxpayloadcrc=x, symtimeoutmsb=00) From 2f72bc9583e40993c5693dbd737c2694e3976b39 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 3 Jul 2020 15:40:35 +0200 Subject: [PATCH 101/137] debug: Rename itoa to prevent conflicts On some platforms (e.g. AVR), this produces conflicts with the builtin itoa function. It is not entirely clear why this was not problematic before, but now it prevents compilation. Renaming to debug_itoa fixes this. --- lmic/debug.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lmic/debug.c b/lmic/debug.c index ae1845d..dd432a7 100644 --- a/lmic/debug.c +++ b/lmic/debug.c @@ -18,7 +18,7 @@ void debug_str (const char* str) { hal_debug_str(str); } -static int itoa (char* buf, u4_t val, int base, int mindigits, int exp, int prec, char sign) { +static int debug_itoa (char* buf, u4_t val, int base, int mindigits, int exp, int prec, char sign) { char num[33], *p = num, *b = buf; if (sign) { if ((s4_t) val < 0) { @@ -157,11 +157,11 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) numeric: { char num[33], pad = ' '; if (zero && left == 0 && prec == 0) { - prec = width - 1; // have itoa() do the leading zero-padding for correct placement of sign + prec = width - 1; // have debug_itoa() do the leading zero-padding for correct placement of sign pad = '0'; } u4_t val = longint ? va_arg(arg, long) : va_arg(arg, int); - int len = itoa(num, val, base, prec, 0, 0, sign); + int len = debug_itoa(num, val, base, prec, 0, 0, sign); dst += strpad(dst, end - dst, num, len, width, left, pad); break; } @@ -169,7 +169,7 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) char num[33], pad = (zero && left == 0) ? '0' : ' '; u4_t val = va_arg(arg, u4_t); int exp = va_arg(arg, int); - int len = itoa(num, val, 10, exp + 2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-'); + int len = debug_itoa(num, val, 10, exp + 2, exp, (prec) ? prec : exp, (plus) ? '+' : (space) ? ' ' : '-'); dst += strpad(dst, end - dst, num, len, width, left, pad); break; } @@ -183,7 +183,7 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) char buf[23], *p = buf; unsigned char *eui = va_arg(arg, unsigned char *); for (int i = 7; i >= 0; i--) { - p += itoa(p, eui[i], 16, 2, 0, 0, 0); + p += debug_itoa(p, eui[i], 16, 2, 0, 0, 0); if (i) *p++ = '-'; } dst += strpad(dst, end - dst, buf, 23, width, left, ' '); @@ -210,17 +210,17 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) t /= 24; int day = t; if (c == 'T') { - p += itoa(p, day, 10, 3, 0, 0, 0); + p += debug_itoa(p, day, 10, 3, 0, 0, 0); *p++ = '.'; } - p += itoa(p, hr, 10, 2, 0, 0, 0); + p += debug_itoa(p, hr, 10, 2, 0, 0, 0); *p++ = ':'; - p += itoa(p, min, 10, 2, 0, 0, 0); + p += debug_itoa(p, min, 10, 2, 0, 0, 0); *p++ = ':'; - p += itoa(p, sec, 10, 2, 0, 0, 0); + p += debug_itoa(p, sec, 10, 2, 0, 0, 0); if (c == 't') { *p++ = '.'; - p += itoa(p, ms, 10, 3, 0, 0, 0); + p += debug_itoa(p, ms, 10, 3, 0, 0, 0); } dst += strpad(dst, end - dst, buf, 12, width, left, ' '); break; @@ -231,7 +231,7 @@ static int debug_vsnprintf(char *str, int size, const char *format, va_list arg) char *top = (prec == 0 || dst + prec > end) ? end : dst + prec; while (len--) { if ((len == 0 && top - dst >= 2) || top - dst >= 2 + space + 2) { - dst += itoa(dst, *buf++, 16, 2, 0, 0, 0); + dst += debug_itoa(dst, *buf++, 16, 2, 0, 0, 0); if(space && len && dst < top) *dst++ = ' '; } else { while (dst < top) *dst++ = '.'; From 934a8ada3c26f53e835effad792298b00cd9310f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sat, 4 Jul 2020 19:15:00 +0200 Subject: [PATCH 102/137] arduino: Make basicmac-abp example compile for US915 This removes the LMIC_selectSubBand function call, which no longer exists. This does not actually make the example work, just makes it compile. --- target/arduino/examples/basicmac-abp/basicmac-abp.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index 702acfb..b4148dd 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -254,7 +254,8 @@ void setup() { // but only one group of 8 should (a subband) should be active // TTN recommends the second sub band, 1 in a zero based count. // https://github.com/TheThingsNetwork/gateway-conf/blob/master/US-global_conf.json - LMIC_selectSubBand(1); + // TODO: How to configure these channels? LMIC had LMIC_selectSubBand, + // but it seems BasicMac only has LMIC_disableChannel. #endif // Disable link check validation From 8d73349110795825cde70f99b643bfb038600a00 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 3 Jul 2020 14:15:05 +0200 Subject: [PATCH 103/137] github: Autocompile Arduino examples --- .../workflows/compile-arduino-examples.yml | 61 +++++++++++++++++++ .../standard-pinmaps.ino | 15 ++++- target/arduino/hal/target-config.h | 4 ++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/compile-arduino-examples.yml diff --git a/.github/workflows/compile-arduino-examples.yml b/.github/workflows/compile-arduino-examples.yml new file mode 100644 index 0000000..2061cd5 --- /dev/null +++ b/.github/workflows/compile-arduino-examples.yml @@ -0,0 +1,61 @@ +name: Compile Arduino examples +on: [push, pull_request] +jobs: + build: + name: ${{ matrix.board }} / ${{ matrix.region }} / ${{ matrix.transceiver }} + runs-on: ubuntu-latest + env: + PLATFORM_DEFAULT_URL: https://arduino.esp8266.com/stable/package_esp8266com_index.json,https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json,https://lacunaspace.github.io/arduino-STM32L4-Lacuna/package_STM32L4_Lacuna_boards_index.json + strategy: + matrix: + board: + - arduino:avr:mega + - arduino:samd:arduino_zero_edbg + - esp8266:esp8266:generic + - lacunaspace:stm32l4:Lacuna-LS200 + # Just a random STM32 board + - STM32:stm32:Nucleo_64:pnum=NUCLEO_F401RE + region: + - CFG_eu868 + - CFG_us915 + transceiver: + - BRD_sx1272_radio + - BRD_sx1276_radio + - BRD_sx1262_radio + + # Run the entire matrix, even if one failed + fail-fast: false + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Generate Arduino library + # Generate twice, once to be moved into the Arduino directory and + # once to compile the examples from + run: | + ./target/arduino/export.sh BasicMacCustom + ./target/arduino/export.sh BasicMacArduino + + - name: Compile all examples + # This uses a fork that also works for .c files and runs outside + # of docker (for performance, but also inside docker the lacuna + # build somehow fails). + # TODO: Convert back to the original once merged: + # https://github.com/ArminJo/arduino-test-compile/pull/14 + # https://github.com/ArminJo/arduino-test-compile/pull/16 + uses: matthijskooijman/arduino-test-compile@extra-flags-for-c-and-S-and-no-docker + with: + arduino-board-fqbn: ${{ matrix.board }} + platform-default-url: ${{ env.PLATFORM_DEFAULT_URL }} + sketch-names-find-start: ./BasicMacArduino/examples/ + # Normally, all .ino files are treated as separate sketches, but + # this is just a secondary .ino file that does not need to be + # separately compiled. + examples-exclude: standard-pinmaps + examples-build-properties: > + {"All": " + -DBASICMAC_DUMMY_PINMAP + -D${{matrix.region}} + -D${{matrix.transceiver}} + "} diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino index 64bdc09..27f9ca1 100644 --- a/target/arduino/examples-common-files/standard-pinmaps.ino +++ b/target/arduino/examples-common-files/standard-pinmaps.ino @@ -10,7 +10,20 @@ #if defined(USE_STANDARD_PINMAP) -#if defined(ARDUINO_AVR_MINI) +#if defined(BASICMAC_DUMMY_PINMAP) +// Dummy minimal pinmap, just used for CI compile-testing. +const lmic_pinmap lmic_pins = { + .nss = 10, + // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + // RST is hardwarid to MCU reset + .rst = LMIC_UNUSED_PIN, + .dio = {1, 2, 3}, + .busy = LMIC_UNUSED_PIN, + .tcxo = LMIC_UNUSED_PIN, +}; +#elif defined(ARDUINO_AVR_MINI) #if !defined(BRD_sx1276_radio) #error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" #endif diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index f758b73..842c678 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -1,11 +1,14 @@ #ifndef _lmic_arduino_hal_config_h_ #define _lmic_arduino_hal_config_h_ +#if !defined(CFG_eu868) && !defined(CFG_us915) #define CFG_eu868 1 //#define CFG_us915 1 +#endif // !defined(CFG_eu868) && !defined(CFG_us915) #define CFG_autojoin +#if !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1262_radio) // This is the SX1272/SX1273 radio, which is also used on the HopeRF // RFM92 boards. //#define BRD_sx1272_radio 1 @@ -14,6 +17,7 @@ #define BRD_sx1276_radio 1 // This is the newer SX1261/SX1261 radio. // #define BRD_sx1262_radio 1 +#endif // !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1262_radio) // 16 μs per tick // LMIC requires ticks to be 15.5μs - 100 μs long From 3b5bcdda69d192c677ca4b22f42285a48060a8fe Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 5 Jul 2020 00:19:23 +0200 Subject: [PATCH 104/137] Fix warnings on 16-bit architectures There were some warnings caused by assuming int is 32-bit, which is not true on e.g. AVR. All of these probably resulted in actual problems at runtime, i.e.: - Problems with very high "available" times (e.g. long airtime combined with low duty cycle limits). - Problems parsing frequencies in network commands. - Extended callback scheduling not working (not used by BasicMAC itself). - Problems with the FSK TX timeout job. --- lmic/lmic.c | 4 ++-- lmic/oslmic.h | 2 +- lmic/radio-sx127x.c | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 64df12a..423945a 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -415,7 +415,7 @@ static void adjAvail (avail_t* pavail, osxtime_t base) { static void setAvail (avail_t* pavail, osxtime_t t) { osxtime_t base = LMIC.baseAvail; - int v; + u4_t v; if( base > t ) { t = base; // make sure t is not in the past } @@ -805,7 +805,7 @@ u1_t LMIC_setPingable (u1_t intvExp) { static freq_t rdFreq (u1_t* p) { - freq_t freq = ((p[2] << 16) | (p[1] << 8) | p[0]) * 100; + freq_t freq = (((freq_t)p[2] << 16) | ((freq_t)p[1] << 8) | (freq_t)p[0]) * 100; if( freq != 0 && (freq < REGION.minFreq || freq > REGION.maxFreq) ) return -1; return freq; diff --git a/lmic/oslmic.h b/lmic/oslmic.h index 66d8012..701d331 100644 --- a/lmic/oslmic.h +++ b/lmic/oslmic.h @@ -196,7 +196,7 @@ typedef s4_t ostime_t; typedef s8_t osxtime_t; #define OSXTIME_MAX INT64_MAX -#define OSTIME_MAX_DIFF ((1U << 31) - 1) +#define OSTIME_MAX_DIFF INT32_MAX #if !HAS_ostick_conv #define us2osticks(us) ((ostime_t)( ((s8_t)(us) * OSTICKS_PER_SEC) / 1000000)) diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index de0df68..9cbc6ee 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -698,7 +698,7 @@ static void txfsk (bool txcont) { hal_irqmask_set(HAL_IRQMASK_DIO0 | HAL_IRQMASK_DIO1); // set tx timeout - radio_set_irq_timeout(os_getTime() + us2osticks((FIFOTHRESH+10)*8*1000/50)); + radio_set_irq_timeout(os_getTime() + us2osticks((u4_t)(FIFOTHRESH+10)*8*1000/50)); } // enable antenna switch for TX @@ -988,7 +988,7 @@ static void rxfsk (bool rxcontinuous) { // set preamble timeout writeReg(FSKRegRxTimeout2, (LMIC.rxsyms + 1) / 2); // (TimeoutRxPreamble * 16 * Tbit) // set rx timeout - radio_set_irq_timeout(LMIC.rxtime + us2osticks((2*FIFOTHRESH)*8*1000/50)); + radio_set_irq_timeout(LMIC.rxtime + us2osticks((u4_t)(2*FIFOTHRESH)*8*1000/50)); // busy wait until exact rx time ostime_t now = os_getTime(); if (LMIC.rxtime - now < 0) { @@ -1202,7 +1202,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { LoadFifo(); // update tx timeout - radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50)); + radio_set_irq_timeout(irqtime + us2osticks((u4_t)(FIFOTHRESH+10)*8*1000/50)); // keep waiting for FifoEmpty or PacketSent interrupt return false; @@ -1214,7 +1214,7 @@ bool radio_irq_process (ostime_t irqtime, u1_t diomask) { UnloadFifo(); // update rx timeout - radio_set_irq_timeout(irqtime + us2osticks((FIFOTHRESH+10)*8*1000/50)); + radio_set_irq_timeout(irqtime + us2osticks((u4_t)(FIFOTHRESH+10)*8*1000/50)); // keep waiting for FifoLevel or PayloadReady interrupt return false; From 277571d97c066fe8922b0c0c1dff91d9dba80542 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 5 Jul 2020 00:35:13 +0200 Subject: [PATCH 105/137] Fix warnings --- lmic/lmic.c | 8 +++++--- lmic/radio-sx127x.c | 3 +++ target/arduino/hal/hal.cpp | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lmic/lmic.c b/lmic/lmic.c index 423945a..ffd56de 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -1264,8 +1264,9 @@ static u1_t enableAllChannels_fix (void) { } } if (nch & 0xf) { - if (LMIC.fix.channelMap[nch >> 4] != (1 << (nch & 0xf)) - 1) { - LMIC.fix.channelMap[nch >> 4] = (1 << (nch & 0xf)) - 1; + u1_t newval = (1 << (nch & 0xf)) - 1; + if (LMIC.fix.channelMap[nch >> 4] != newval) { + LMIC.fix.channelMap[nch >> 4] = newval; rv = 1; } } @@ -1355,7 +1356,7 @@ static u1_t applyChannelMap_fix (u1_t chpage, u2_t chmap, u2_t* dest) { return 0; } if ((nch & 15) && chpage == (REGION.numChBlocks >> 1)) { // partial map in last 16bit word - chmap &= ~(~0 << (nch & 15)); + chmap &= ~(0xffff << (nch & 15)); } dest[chpage] = chmap; } @@ -3436,6 +3437,7 @@ void LMIC_setLinkCheck (u4_t limit, u4_t delay) { bit_t LMIC_setupChannel (u1_t chidx, freq_t freq, u2_t drmap) { if (REG_IS_FIX()) { + (void)chidx; (void)freq; (void)drmap; // unused return 0; } else { #ifdef REG_DYN diff --git a/lmic/radio-sx127x.c b/lmic/radio-sx127x.c index 9cbc6ee..c300942 100644 --- a/lmic/radio-sx127x.c +++ b/lmic/radio-sx127x.c @@ -514,6 +514,7 @@ static void setRadioConsumption_ua (bool boost, u1_t pow) { ua = RFOPOW[pow]; } #else + (void)boost; (void)pow; // unused ua = 120000; // something better than nothing? #endif LMIC.radioPwr_ua = ua; @@ -1159,6 +1160,8 @@ void radio_init (bool calibrate) { // (run by irqjob) bool radio_irq_process (ostime_t irqtime, u1_t diomask) { + (void)diomask; //unused + // dispatch modem if (isFsk(LMIC.rps)) { // FSK modem u1_t irqflags1 = readReg(FSKRegIrqFlags1); diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index 11293f4..c8ba4b6 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -12,6 +12,8 @@ // Must be first, otherwise it might have already been included without _GNU_SOURCE #include #undef _GNU_SOURCE +// Prevent warning on samd where samd21g18a.h from CMSIS defines this +#undef LITTLE_ENDIAN #include #include #include "../basicmac.h" From 12e6dd5d0081413a6c3249b1a7a5e0837fad4dbe Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 5 Jul 2020 01:37:54 +0200 Subject: [PATCH 106/137] Work around double definition of REGION This prevents a warning about this. --- lmic/lmic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lmic/lmic.c b/lmic/lmic.c index ffd56de..3a05a35 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -381,6 +381,8 @@ static const region_t REGIONS[REGIONS_COUNT] = { #endif }; +// Workaround for Lacuna LS200 core defining this on the gcc commandline +#undef REGION #define REGION (*LMIC.region) #define isREGION(reg) (®ION == ®IONS[REGION_##reg]) From 3e21aa33c73e2f4a1823f3099e4998ecaea24ac4 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 5 Jul 2020 00:00:11 +0200 Subject: [PATCH 107/137] aes-ideetron: Remove unused declaration --- aes/aes-ideetron.c | 1 - 1 file changed, 1 deletion(-) diff --git a/aes/aes-ideetron.c b/aes/aes-ideetron.c index 65b0083..1f51a60 100644 --- a/aes/aes-ideetron.c +++ b/aes/aes-ideetron.c @@ -77,7 +77,6 @@ static unsigned char AES_Sub_Byte(unsigned char Byte); static void AES_Shift_Rows(); static void AES_Mix_Collums(); static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key); -static void Send_State(); /* ***************************************************************************************** From d49f97c1c0b297e96a6035bc91be3345523425fa Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 5 Jul 2020 00:41:30 +0200 Subject: [PATCH 108/137] github: Compile examples with -Werror This should help accidentally (re)introducing warnings. --- .github/workflows/compile-arduino-examples.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/compile-arduino-examples.yml b/.github/workflows/compile-arduino-examples.yml index 2061cd5..d052813 100644 --- a/.github/workflows/compile-arduino-examples.yml +++ b/.github/workflows/compile-arduino-examples.yml @@ -22,6 +22,21 @@ jobs: - BRD_sx1272_radio - BRD_sx1276_radio - BRD_sx1262_radio + include: + # Older compilers (used by STM32 and ESP) seem to get confused + # by the REGION initializers and produce (seemingly unfixable) + # missing-field-initializers warnings. So just ignore these + # for these boards. + - board: esp8266:esp8266:generic + extra_flags: -Wno-error=missing-field-initializers + # The STM32 cores have unused-parameter warnings in the core, + # so these (this also ignores the same warnings in the sketch + # and library, which is not ideal, but this seems to be the + # only way to fix this). + - board: lacunaspace:stm32l4:Lacuna-LS200 + extra_flags: -Wno-error=unused-parameter -Wno-error=missing-field-initializers + - board: STM32:stm32:Nucleo_64:pnum=NUCLEO_F401RE + extra_flags: -Wno-error=unused-parameter -Wno-error=missing-field-initializers # Run the entire matrix, even if one failed fail-fast: false @@ -58,4 +73,6 @@ jobs: -DBASICMAC_DUMMY_PINMAP -D${{matrix.region}} -D${{matrix.transceiver}} + -Werror + ${{matrix.extra_flags}} "} From f6757c13dd1d62720e2aa5a6935222377eec19dd Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 6 Jul 2020 01:09:54 +0200 Subject: [PATCH 109/137] radio: Use ASSERT, not assert In commit 2fae1e8 (debug: Split TX/RX prints by modulation), three new assertions were introduced that accidentally used the system `assert` rather than BasicMAC's own ASSERT. It seems this worked ok for the Arduino build, but broke the regular Makefile build with a linker error. --- lmic/radio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lmic/radio.c b/lmic/radio.c index 352d4ab..e26ccbc 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -113,7 +113,7 @@ void os_radio (u1_t mode) { if( isFsk(LMIC.rps) ) { debug_printf("TX[mod=FSK,nocrc=%d", getNocrc(LMIC.rps)); } else { - assert(isLora(LMIC.rps)); + ASSERT(isLora(LMIC.rps)); debug_printf("TX[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d", getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); @@ -138,7 +138,7 @@ void os_radio (u1_t mode) { if( isFsk(LMIC.rps) ) { debug_printf("RX_MODE[mod=FSK,nocrc=%d", getNocrc(LMIC.rps)); } else { - assert(isLora(LMIC.rps)); + ASSERT(isLora(LMIC.rps)); debug_printf("RX_MODE[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d", getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); @@ -160,7 +160,7 @@ void os_radio (u1_t mode) { if( isFsk(LMIC.rps) ) { debug_printf("RXON_MODE[mod=FSK,nocrc=%d", getNocrc(LMIC.rps)); } else { - assert(isLora(LMIC.rps)); + ASSERT(isLora(LMIC.rps)); debug_printf("RXON_MODE[mod=LoRa,sf=%d,bw=%d,cr=4/%d,nocrc=%d,ih=%d", getSf(LMIC.rps) - SF7 + 7, 125 << (getBw(LMIC.rps) - BW125), getCr(LMIC.rps) - CR_4_5 + 5, getNocrc(LMIC.rps), getIh(LMIC.rps)); From 52c849c427254fe9f780df3b57bf631964e44f63 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 6 Jul 2020 00:13:45 +0200 Subject: [PATCH 110/137] basicloader: Update submodule URL This used a relative URL, but the basicmac repository was moved, so the relative url no longer works. At some point, this basicloader reference might be removed, but until then, at least make it valid. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 35b88b8..89ed92b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "basicloader"] path = basicloader - url = ../basicloader.git + url = https://github.com/lorabasics/basicloader.git [submodule "services/fuota/micro-ecc"] path = services/fuota/micro-ecc url = https://github.com/kmackay/micro-ecc.git From 1c826fa00d882098517ab41ea8f23bf8642e2d93 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 6 Jul 2020 00:36:34 +0200 Subject: [PATCH 111/137] basicloader: Update to correct version In BasicMac v2.2, the basicloader submodule was updated. However, when merging that release into this branch, it seems the submodule update was accidentally reverted, so this still had the version used with v2.1 which prevented compilation from working. --- basicloader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basicloader b/basicloader index f5ca35b..a1e6086 160000 --- a/basicloader +++ b/basicloader @@ -1 +1 @@ -Subproject commit f5ca35b2fb5ca10e2a20f1607ea46ece9dfd7c25 +Subproject commit a1e6086dd9b106d9a563427bea9da14f614edb7e From aa4e3dc0c71eb3ae0f3e81687590ea4eaadd0855 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 6 Jul 2020 02:01:24 +0200 Subject: [PATCH 112/137] stm32: Fix const-related warning The const was in the wrong place, making this an array of pointers to const-functions, rather than a const-array of pointers to functions. In certain environments (probably newer gcc) this produced a warning. Moving the const fixes this. --- stm32/hal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32/hal.c b/stm32/hal.c index 082687b..eb59d80 100644 --- a/stm32/hal.c +++ b/stm32/hal.c @@ -576,7 +576,7 @@ static u4_t sleep0 (u4_t hticks, u4_t htt, u4_t ltt) { } static void sleep (int stype, u4_t htt, u4_t ltt) { - static const u4_t(*sleepfuncs[])(u4_t,u4_t,u4_t) = { + static u4_t(* const sleepfuncs[])(u4_t,u4_t,u4_t) = { sleep0, sleep1, sleep2, From 6e529253f3385a3a3d9d0ef872d0923556a558d5 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 6 Jul 2020 01:33:31 +0200 Subject: [PATCH 113/137] projects: Compile with -Werror The code is currently warning-free, so try to keep it that way. --- projects/projects.gmk | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/projects.gmk b/projects/projects.gmk index 1f6cb0e..7c994ae 100644 --- a/projects/projects.gmk +++ b/projects/projects.gmk @@ -37,6 +37,7 @@ ASDEFS += $(PDEFS) CFLAGS += -I$(LMICDIR) -I$(HALDIR) CFLAGS += -I$(COMMONDIR) +CFLAGS += -Werror BUILDTIME := $(shell date -u +'%FT%TZ') From a374aaadb30b974fb35de00f1e424a089694b73c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 6 Jul 2020 01:37:26 +0200 Subject: [PATCH 114/137] github: Autocompile projects --- .github/workflows/compile-projects.yml | 42 ++++++++++++++++++++++++++ README.md | 5 +-- 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/compile-projects.yml diff --git a/.github/workflows/compile-projects.yml b/.github/workflows/compile-projects.yml new file mode 100644 index 0000000..df4f0d7 --- /dev/null +++ b/.github/workflows/compile-projects.yml @@ -0,0 +1,42 @@ +name: Compile projects +on: [push, pull_request] +jobs: + build: + name: ${{matrix.target}} + runs-on: ubuntu-latest + strategy: + matrix: + target: + # List taken from ex-join project, there might be more valid + # combinations. + - b_l072z_lrwan1 + - nucleo_l073rz-sx1276mb1las + - nucleo_l073rz-sx1272mbed + - nucleo_l073rz-sx1276mb1mas + - nucleo_l053r8-sx1276mb1las + - nucleo_l053r8-sx1261mbed + - nucleo_l053r8-sx1262mbed + + # Run the entire matrix, even if one failed + fail-fast: false + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: true + + - name: Install compiler + run: sudo apt install gcc-arm-none-eabi + + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: '3.x' # Version range or exact version of a Python version to use, using SemVer's version range syntax + + - name: Install python packages + run: python -m pip install lz4 pycryptodome click intelhex pyyaml + + - name: Compile project + run: | + make -C projects/ex-join TARGET="${{matrix.target}}" || exit 1 diff --git a/README.md b/README.md index fb14019..f00eee0 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,9 @@ STM32, on some specific boards. To use it, go into the example project directory (`projects/ex-join`) and run `make`. -Some effort has been made to keep these builds working, but testing of -the code in this repostiory has only been done under Arduino, so the +Some effort has been made to keep these builds working, but actual +testing of the code in this repository has only been done under Arduino +(Makefile-based builds are only automatically compile-tested), so the Makefile-based builds might very well be broken. ### Hardware support From 15fc2d11465109925aafb518eac0c4713846325a Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 14 Jul 2020 17:52:06 +0200 Subject: [PATCH 115/137] arduino: Default to using the SX1262 This transceiver is getting more common and one of the primary reasons for using basicmac, so make it the default. --- README.md | 9 +++++++++ target/arduino/hal/target-config.h | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f00eee0..e10f2aa 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,15 @@ pullrequests to keep history clean. The API is still likely to change in a breaking way, especially wrt configuration. +### Notable and breaking changes + +This section lists notable changes, in particular those that can break +existing sketches. For full details, see the git changelog. + + - 2020-07-14: On Arduino, the default config is changed to SX1262. If + your board uses SX1276 (the old default), you need to modify + `target-config.h` in the library. + ### Relation to other projects BasicMAC is a privately developed fork of LMIC, which was released diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index 842c678..7354715 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -14,9 +14,9 @@ //#define BRD_sx1272_radio 1 // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on // the HopeRF RFM95 boards. -#define BRD_sx1276_radio 1 +//#define BRD_sx1276_radio 1 // This is the newer SX1261/SX1261 radio. -// #define BRD_sx1262_radio 1 +#define BRD_sx1262_radio 1 #endif // !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1262_radio) // 16 μs per tick From 419798260d7eb8780fbdce2046718814b7988d4e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 14 Jul 2020 17:19:24 +0200 Subject: [PATCH 116/137] hal: Define hal_pin_tcxo only for sx127x Configuring the TCXO is not possible using a MCU-controlled GPIO on the SX126x, so this function makes no sense there. This prevents accidentally calling it anyway. --- lmic/hal.h | 2 ++ lmic/radio.c | 3 +++ stm32/hal.c | 2 ++ target/arduino/hal/hal.cpp | 6 ++++++ 4 files changed, 13 insertions(+) diff --git a/lmic/hal.h b/lmic/hal.h index e9129ce..fe1988d 100644 --- a/lmic/hal.h +++ b/lmic/hal.h @@ -34,11 +34,13 @@ void hal_watchcount (int cnt); #define HAL_ANTSW_TX2 3 void hal_ant_switch (u1_t val); +#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) /* * control radio TCXO power (0=off, 1=on) * (return if TCXO is present and in use) */ bool hal_pin_tcxo (u1_t val); +#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) /* * control radio RST pin (0=low, 1=high, 2=floating) diff --git a/lmic/radio.c b/lmic/radio.c index e26ccbc..6bab834 100644 --- a/lmic/radio.c +++ b/lmic/radio.c @@ -26,8 +26,11 @@ static void radio_stop (void) { radio_sleep(); // disable antenna switch hal_ant_switch(HAL_ANTSW_OFF); +#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) // power-down TCXO hal_pin_tcxo(0); +#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) + // disable antenna switch // disable IRQs in HAL hal_irqmask_set(0); // cancel radio job diff --git a/stm32/hal.c b/stm32/hal.c index eb59d80..0222eea 100644 --- a/stm32/hal.c +++ b/stm32/hal.c @@ -738,6 +738,7 @@ static void hal_io_init () { #endif } +#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) bool hal_pin_tcxo (u1_t val) { #if defined(GPIO_TCXO_PWR) if (val != 0) { @@ -752,6 +753,7 @@ bool hal_pin_tcxo (u1_t val) { return false; #endif } +#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) void hal_ant_switch (u1_t val) { #ifdef SVC_pwrman diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index c8ba4b6..f5c87e6 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -49,6 +49,10 @@ static void hal_io_init () { ASSERT(lmic_pins.busy != LMIC_UNUSED_PIN); #endif +#if !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) + ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN); +#endif + // Write HIGH to deselect (NSS is active low). Do this before // setting output, to prevent a moment of OUTPUT LOW on e.g. AVR. digitalWrite(lmic_pins.nss, HIGH); @@ -117,6 +121,7 @@ static void hal_io_check() { } } +#if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) bool hal_pin_tcxo (u1_t val) { if (lmic_pins.tcxo == LMIC_UNUSED_PIN) return false; @@ -124,6 +129,7 @@ bool hal_pin_tcxo (u1_t val) { digitalWrite(lmic_pins.tcxo, val); return true; } +#endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) void hal_pin_busy_wait (void) { if (lmic_pins.busy == LMIC_UNUSED_PIN) { From 587776f85cd151f55f57f38c180a0584105e9722 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 14 Jul 2020 17:41:54 +0200 Subject: [PATCH 117/137] sx126x: No longer use DIO3 as TCXO control by default Enabling this unconditionally originally seemed harmless, since the pin was otherwise unused, but this also switches the transceiver into TCXO mode, breaking operation with a crystal. Instead of enabling this unconditionally, it is now disabled by default and can be enabled explicitly. On Arduino, set the `tcxo` field of the pin map to `LMIC_CONTROLLED_BY_DIO3`. With Makefile-based stm32, define `LMIC_DIO3_CONTROLS_TCXO` on the compiler commandline. The API between the radio driver and the HAL is not ideal and might need to be reconsidered later, but since the change on the Arduino side is breaking, better to get that out of the way soon (and the HAL side can be improved later). This fixes #9 --- README.md | 6 ++++++ lmic/hal.h | 9 +++++++++ lmic/radio-sx126x.c | 3 ++- stm32/hal.c | 10 ++++++++++ .../arduino/examples-common-files/standard-pinmaps.ino | 3 +-- target/arduino/examples/basicmac-abp/basicmac-abp.ino | 10 +++++++++- .../arduino/examples/basicmac-otaa/basicmac-otaa.ino | 10 +++++++++- target/arduino/hal/hal.cpp | 8 ++++++++ target/arduino/hal/hal.h | 7 ++++++- 9 files changed, 60 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e10f2aa..a4ebb25 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,12 @@ existing sketches. For full details, see the git changelog. your board uses SX1276 (the old default), you need to modify `target-config.h` in the library. + - 2020-07-14: On SX126x, DIO3 is no longer configured to control a TCXO + by default. If your board needs this, it must be explicitly enabled. + On Arduino, set the `tcxo` field of the pin map to + `LMIC_CONTROLLED_BY_DIO3`. With Makefile-based stm32, define + `LMIC_DIO3_CONTROLS_TCXO` on the compiler commandline. + ### Relation to other projects BasicMAC is a privately developed fork of LMIC, which was released diff --git a/lmic/hal.h b/lmic/hal.h index fe1988d..5bd091c 100644 --- a/lmic/hal.h +++ b/lmic/hal.h @@ -62,6 +62,15 @@ void hal_pin_busy_wait (void); #define HAL_IRQMASK_DIO3 (1<<3) void hal_irqmask_set (int mask); +#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) +/* + * Returns true if DIO3 should control the TCXO. + * TODO: Reconsider this HAL function, maybe integrate with hal_pin_tcxo + * somehow? + */ +bool hal_dio3_controls_tcxo (void); +#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + /* * drive radio NSS pin (on=low, off=high). */ diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 9dd622c..b5fe975 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -547,7 +547,8 @@ void radio_sleep (void) { static void CommonSetup (void) { SetRegulatorMode(REGMODE_DCDC); SetDIO2AsRfSwitchCtrl(1); - SetDIO3AsTcxoCtrl(); + if (hal_dio3_controls_tcxo()) + SetDIO3AsTcxoCtrl(); } static void txlora (void) { diff --git a/stm32/hal.c b/stm32/hal.c index 0222eea..0f2fa7f 100644 --- a/stm32/hal.c +++ b/stm32/hal.c @@ -816,6 +816,16 @@ void hal_pin_busy_wait (void) { #endif } +#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) +bool hal_dio3_controls_tcxo (void) { + #if defined(LMIC_DIO3_CONTROLS_TCXO) + return true; + #else + return false; + #endif +} +#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + #define DIO_UPDATE(dio,mask,time) do { \ if( (EXTI->PR & (1 << BRD_PIN(GPIO_DIO ## dio))) ) { \ EXTI->PR = (1 << BRD_PIN(GPIO_DIO ## dio)); \ diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino index 27f9ca1..5a950a5 100644 --- a/target/arduino/examples-common-files/standard-pinmaps.ino +++ b/target/arduino/examples-common-files/standard-pinmaps.ino @@ -84,8 +84,7 @@ const lmic_pinmap lmic_pins = { .rst = E22_NRST, // PA4 .dio = {LMIC_UNUSED_PIN, E22_DIO1 /* PC7 */, LMIC_UNUSED_PIN}, .busy = E22_BUSY, // PB12 - // TCXO is controlled through DIO3 by the SX1262 directly - .tcxo = LMIC_UNUSED_PIN, + .tcxo = LMIC_CONTROLLED_BY_DIO3, }; #else #error "Unknown board, no standard pimap available. Define your own in the main sketch file." diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index b4148dd..1dceeec 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -88,7 +88,15 @@ const lmic_pinmap lmic_pins = { // cause problems. .busy = LMIC_UNUSED_PIN, // TCXO oscillator enable output pin (active high). - // The SX126x can control the TCXO directly through its DIO3 output pin. + // + // For SX127x this should be an I/O pin that controls the TCXO, or + // LMIC_UNUSED_PIN when a crystal is used instead of a TCXO. + // + // For SX126x this should be LMIC_CONTROLLED_BY_DIO3 when a TCXO is + // directly connected to the transceiver DIO3 to let the transceiver + // start and stop the TCXO, or LMIC_UNUSED_PIN when a crystal is + // used instead of a TCXO. Controlling the TCXO from the MCU is not + // supported. .tcxo = LMIC_UNUSED_PIN, }; #endif // !defined(USE_STANDARD_PINMAP) diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino index 4f8121f..e417060 100644 --- a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino +++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino @@ -91,7 +91,15 @@ const lmic_pinmap lmic_pins = { // cause problems. .busy = LMIC_UNUSED_PIN, // TCXO oscillator enable output pin (active high). - // The SX126x can control the TCXO directly through its DIO3 output pin. + // + // For SX127x this should be an I/O pin that controls the TCXO, or + // LMIC_UNUSED_PIN when a crystal is used instead of a TCXO. + // + // For SX126x this should be LMIC_CONTROLLED_BY_DIO3 when a TCXO is + // directly connected to the transceiver DIO3 to let the transceiver + // start and stop the TCXO, or LMIC_UNUSED_PIN when a crystal is + // used instead of a TCXO. Controlling the TCXO from the MCU is not + // supported. .tcxo = LMIC_UNUSED_PIN, }; #endif // !defined(USE_STANDARD_PINMAP) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index f5c87e6..e4cc386 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -51,6 +51,8 @@ static void hal_io_init () { #if !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN); +#else + ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN || lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3); #endif // Write HIGH to deselect (NSS is active low). Do this before @@ -147,6 +149,12 @@ void hal_pin_busy_wait (void) { } } +#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) +bool hal_dio3_controls_tcxo (void) { + return lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3; +} +#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + // ----------------------------------------------------------------------------- // SPI diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h index 8d04213..b16967f 100644 --- a/target/arduino/hal/hal.h +++ b/target/arduino/hal/hal.h @@ -28,7 +28,12 @@ struct lmic_pinmap { }; // Use this for any unused pins. -const u1_t LMIC_UNUSED_PIN = 0xff; +const u1_t LMIC_UNUSED_PIN = 0xfe; + +#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) +// Used for lmic_pinmap.tcxo only +const u1_t LMIC_CONTROLLED_BY_DIO3 = 0xff; +#endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) // Declared here, to be defined an initialized by the application extern const lmic_pinmap lmic_pins; From abd286aaec0957892f303ad039820740bec8f646 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 09:29:13 +0200 Subject: [PATCH 118/137] github: Compile examples for SX1261 too --- .github/workflows/compile-arduino-examples.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/compile-arduino-examples.yml b/.github/workflows/compile-arduino-examples.yml index d052813..9a1117e 100644 --- a/.github/workflows/compile-arduino-examples.yml +++ b/.github/workflows/compile-arduino-examples.yml @@ -21,6 +21,7 @@ jobs: transceiver: - BRD_sx1272_radio - BRD_sx1276_radio + - BRD_sx1261_radio - BRD_sx1262_radio include: # Older compilers (used by STM32 and ESP) seem to get confused From 7bd00cafdad7bd5095cc4d3168b6d3de68030086 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 08:22:49 +0200 Subject: [PATCH 119/137] arduino: Improve pin checks With the added `LMIC_CONTROLLED_BY_DIO3` pin value, the pin value asserts need to be slightly changed (and some asserts added for optional pins) to make sure that this new value is not considered a valid value for all pins. This also moves some asserts around to form a single block per transceiver type, rather than being spread around in multiple `#if` blocks. --- target/arduino/hal/hal.cpp | 56 ++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index e4cc386..1d79679 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -30,29 +30,33 @@ static unsigned long MAX_BUSY_TIME = 5000; static void hal_io_init () { uint8_t i; - ASSERT(lmic_pins.nss != LMIC_UNUSED_PIN); + // Checks below assume that all special pin values are >= LMIC_UNUSED_PIN, so check that. + #if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + LMIC_STATIC_ASSERT(LMIC_UNUSED_PIN < LMIC_CONTROLLED_BY_DIO3, "Unexpected constant values"); + #endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + + ASSERT(lmic_pins.nss < LMIC_UNUSED_PIN); + ASSERT(lmic_pins.rst <= LMIC_UNUSED_PIN); + ASSERT(lmic_pins.rx <= LMIC_UNUSED_PIN); + ASSERT(lmic_pins.tx <= LMIC_UNUSED_PIN); #if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) //DIO0 is required, DIO1 is required for LoRa, DIO2 for FSK - ASSERT(lmic_pins.dio[0] != LMIC_UNUSED_PIN); - ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN || lmic_pins.dio[2] != LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[0] < LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[1] < LMIC_UNUSED_PIN || lmic_pins.dio[2] < LMIC_UNUSED_PIN); + + ASSERT(lmic_pins.busy == LMIC_UNUSED_PIN); + ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN); #elif defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) // Only DIO1 should be specified ASSERT(lmic_pins.dio[0] == LMIC_UNUSED_PIN); - ASSERT(lmic_pins.dio[1] != LMIC_UNUSED_PIN); + ASSERT(lmic_pins.dio[1] < LMIC_UNUSED_PIN); ASSERT(lmic_pins.dio[2] == LMIC_UNUSED_PIN); -#else - #error "Unknown radio type?" -#endif -#if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) - ASSERT(lmic_pins.busy != LMIC_UNUSED_PIN); -#endif - -#if !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) - ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN); -#else + ASSERT(lmic_pins.busy < LMIC_UNUSED_PIN); ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN || lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3); +#else + #error "Unknown radio type?" #endif // Write HIGH to deselect (NSS is active low). Do this before @@ -63,19 +67,19 @@ static void hal_io_init () { // reset to LOW when setting OUTPUT (e.g. arduino-STM32L4). digitalWrite(lmic_pins.nss, HIGH); - if (lmic_pins.tx != LMIC_UNUSED_PIN) + if (lmic_pins.tx < LMIC_UNUSED_PIN) pinMode(lmic_pins.tx, OUTPUT); - if (lmic_pins.rx != LMIC_UNUSED_PIN) + if (lmic_pins.rx < LMIC_UNUSED_PIN) pinMode(lmic_pins.rx, OUTPUT); - if (lmic_pins.rst != LMIC_UNUSED_PIN) + if (lmic_pins.rst < LMIC_UNUSED_PIN) pinMode(lmic_pins.rst, OUTPUT); - if (lmic_pins.busy != LMIC_UNUSED_PIN) + if (lmic_pins.busy < LMIC_UNUSED_PIN) pinMode(lmic_pins.busy, INPUT); - if (lmic_pins.tcxo != LMIC_UNUSED_PIN) + if (lmic_pins.tcxo < LMIC_UNUSED_PIN) pinMode(lmic_pins.tcxo, OUTPUT); for (i = 0; i < NUM_DIO; ++i) { - if (lmic_pins.dio[i] != LMIC_UNUSED_PIN) + if (lmic_pins.dio[i] < LMIC_UNUSED_PIN) pinMode(lmic_pins.dio[i], INPUT); } } @@ -83,15 +87,15 @@ static void hal_io_init () { // rx = 0, tx = 1, off = -1 void hal_ant_switch (u1_t val) { // TODO: Support separate pin for TX2 (PA_BOOST output) - if (lmic_pins.tx != LMIC_UNUSED_PIN) + if (lmic_pins.tx < LMIC_UNUSED_PIN) digitalWrite(lmic_pins.tx, val == HAL_ANTSW_TX || val == HAL_ANTSW_TX2); - if (lmic_pins.rx != LMIC_UNUSED_PIN) + if (lmic_pins.rx < LMIC_UNUSED_PIN) digitalWrite(lmic_pins.rx, val == HAL_ANTSW_RX); } // set radio RST pin to given value (or keep floating!) bool hal_pin_rst (u1_t val) { - if (lmic_pins.rst == LMIC_UNUSED_PIN) + if (lmic_pins.rst >= LMIC_UNUSED_PIN) return false; if(val == 0 || val == 1) { // drive pin @@ -112,7 +116,7 @@ static bool dio_states[NUM_DIO] = {0}; static void hal_io_check() { uint8_t i; for (i = 0; i < NUM_DIO; ++i) { - if (lmic_pins.dio[i] == LMIC_UNUSED_PIN) + if (lmic_pins.dio[i] >= LMIC_UNUSED_PIN) continue; if (dio_states[i] != digitalRead(lmic_pins.dio[i])) { @@ -125,7 +129,7 @@ static void hal_io_check() { #if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) bool hal_pin_tcxo (u1_t val) { - if (lmic_pins.tcxo == LMIC_UNUSED_PIN) + if (lmic_pins.tcxo >= LMIC_UNUSED_PIN) return false; digitalWrite(lmic_pins.tcxo, val); @@ -134,7 +138,7 @@ bool hal_pin_tcxo (u1_t val) { #endif // defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) void hal_pin_busy_wait (void) { - if (lmic_pins.busy == LMIC_UNUSED_PIN) { + if (lmic_pins.busy >= LMIC_UNUSED_PIN) { // TODO: We could probably keep some state so we know the chip // is in sleep, since otherwise the delay can be much shorter. // Also, all delays after commands (rather than waking up from From c94c2cc21ee55f66a2ba42d65feb6bd7c0a78b12 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 08:31:31 +0200 Subject: [PATCH 120/137] arduino: Fix running with busy = LMIC_UNUSED_PIN There was code to run without a busy pin, but the initialization asserts would forbid setting it to LMIC_UNUSED_PIN. This should make that work again. --- target/arduino/hal/hal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index 1d79679..cef5f02 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -53,7 +53,7 @@ static void hal_io_init () { ASSERT(lmic_pins.dio[1] < LMIC_UNUSED_PIN); ASSERT(lmic_pins.dio[2] == LMIC_UNUSED_PIN); - ASSERT(lmic_pins.busy < LMIC_UNUSED_PIN); + ASSERT(lmic_pins.busy <= LMIC_UNUSED_PIN); ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN || lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3); #else #error "Unknown radio type?" From 3e09a9723799aa3e4d65729b8cb3fd9a9ba3b4cd Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 08:44:30 +0200 Subject: [PATCH 121/137] hal: No longer use DIO2 to control RXTX by default Enabling this unconditionally originally seemed harmless, since the pin was otherwise unused, but there is a (tiny) chance that boards have unused DIO pins grounded, which could lead to short-circuits. Also, it is probably good to make the DIO2-controls-TXRX situation explicit in the pinmap and be consistent with DIO3-controls-TCXO. Instead of enabling this unconditionally, it is now disabled by default and can be enabled explicitly. On Arduino, set the `tx` field of the pin map to `LMIC_CONTROLLED_BY_DIO2`. With Makefile-based stm32, define `LMIC_DIO2_CONTROLS_RXTX` on the compiler commandline. The API between the radio driver and the HAL is not ideal and might need to be reconsidered later, but since the change on the Arduino side is breaking, better to get that out of the way soon (and the HAL side can be improved later). --- README.md | 7 +++++++ lmic/hal.h | 7 +++++++ lmic/radio-sx126x.c | 3 ++- stm32/hal.c | 8 ++++++++ .../examples-common-files/standard-pinmaps.ino | 3 +-- .../arduino/examples/basicmac-abp/basicmac-abp.ino | 14 +++++++++++--- target/arduino/hal/hal.cpp | 7 ++++++- target/arduino/hal/hal.h | 3 ++- 8 files changed, 44 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a4ebb25..d62f5d6 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,13 @@ existing sketches. For full details, see the git changelog. `LMIC_CONTROLLED_BY_DIO3`. With Makefile-based stm32, define `LMIC_DIO3_CONTROLS_TCXO` on the compiler commandline. + - 2020-07-14: On SX126x, DIO2 is no longer configured to control a TXRX + antenna switch by default. If your board needs this (most likely it + does), it must be explicitly enabled. On Arduino, set the `tx` + field of the pin map to `LMIC_CONTROLLED_BY_DIO2`. With + Makefile-based stm32, define `LMIC_DIO2_CONTROLS_TXRX` on the + compiler commandline. + ### Relation to other projects BasicMAC is a privately developed fork of LMIC, which was released diff --git a/lmic/hal.h b/lmic/hal.h index 5bd091c..a1c0d87 100644 --- a/lmic/hal.h +++ b/lmic/hal.h @@ -69,6 +69,13 @@ void hal_irqmask_set (int mask); * somehow? */ bool hal_dio3_controls_tcxo (void); + +/* + * Returns true if DIO2 should control the RXTX switch. + * TODO: Reconsider this HAL function, maybe integrate with + * hal_ant_switch somehow? + */ +bool hal_dio2_controls_rxtx (void); #endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) /* diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index b5fe975..abf7baf 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -546,7 +546,8 @@ void radio_sleep (void) { // Do config common to all RF modes static void CommonSetup (void) { SetRegulatorMode(REGMODE_DCDC); - SetDIO2AsRfSwitchCtrl(1); + if (hal_dio2_controls_rxtx()) + SetDIO2AsRfSwitchCtrl(1); if (hal_dio3_controls_tcxo()) SetDIO3AsTcxoCtrl(); } diff --git a/stm32/hal.c b/stm32/hal.c index 0f2fa7f..94c00e0 100644 --- a/stm32/hal.c +++ b/stm32/hal.c @@ -824,6 +824,14 @@ bool hal_dio3_controls_tcxo (void) { return false; #endif } + +bool hal_dio2_controls_rxtx (void) { + #if defined(LMIC_DIO2_CONTROLS_RXTX) + return true; + #else + return false; + #endif +} #endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) #define DIO_UPDATE(dio,mask,time) do { \ diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino index 5a950a5..3854ca6 100644 --- a/target/arduino/examples-common-files/standard-pinmaps.ino +++ b/target/arduino/examples-common-files/standard-pinmaps.ino @@ -78,8 +78,7 @@ const lmic_pinmap lmic_pins = { // This uses E22_* constants from the board variant file const lmic_pinmap lmic_pins = { .nss = E22_NSS, // PD2 - // TXEN is controlled through DIO2 by the SX1262 directly - .tx = LMIC_UNUSED_PIN, + .tx = LMIC_CONTROLLED_BY_DIO2, .rx = E22_RXEN, // PC4 .rst = E22_NRST, // PA4 .dio = {LMIC_UNUSED_PIN, E22_DIO1 /* PC7 */, LMIC_UNUSED_PIN}, diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index 1dceeec..dfcde9d 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -72,9 +72,17 @@ const lmic_pinmap lmic_pins = { // If needed, these pins control the RX/TX antenna switch (active // high outputs). When you have both, the antenna switch can // powerdown when unused. If you just have a RXTX pin it should - // usually be set to .tx, reverting to RX mode when idle). - // Often, the antenna switch is controlled directly by the radio - // chip, through is RXTX (SX127x) or DIO2 (SX126x) output pins. + // usually be assigned to .tx, reverting to RX mode when idle). + // + // The SX127x has an RXTX pin that can automatically control the + // antenna switch (if internally connected on the transceiver + // board). This pin is always active, so no configuration is needed + // for that here. + // On SX126x, the DIO2 can be used for the same thing, but this is + // disabled by default. To enable this, set .tx to + // LMIC_CONTROLLED_BY_DIO2 below (this seems to be common and + // enabling it when not needed is probably harmless, unless DIO2 is + // connected to GND or VCC directly inside the transceiver board). .tx = LMIC_UNUSED_PIN, .rx = LMIC_UNUSED_PIN, // Radio reset output pin (active high for SX1276, active low for diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index cef5f02..b53e9f1 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -33,12 +33,12 @@ static void hal_io_init () { // Checks below assume that all special pin values are >= LMIC_UNUSED_PIN, so check that. #if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) LMIC_STATIC_ASSERT(LMIC_UNUSED_PIN < LMIC_CONTROLLED_BY_DIO3, "Unexpected constant values"); + LMIC_STATIC_ASSERT(LMIC_UNUSED_PIN < LMIC_CONTROLLED_BY_DIO2, "Unexpected constant values"); #endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) ASSERT(lmic_pins.nss < LMIC_UNUSED_PIN); ASSERT(lmic_pins.rst <= LMIC_UNUSED_PIN); ASSERT(lmic_pins.rx <= LMIC_UNUSED_PIN); - ASSERT(lmic_pins.tx <= LMIC_UNUSED_PIN); #if defined(BRD_sx1272_radio) || defined(BRD_sx1276_radio) //DIO0 is required, DIO1 is required for LoRa, DIO2 for FSK @@ -47,6 +47,7 @@ static void hal_io_init () { ASSERT(lmic_pins.busy == LMIC_UNUSED_PIN); ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN); + ASSERT(lmic_pins.tx <= LMIC_UNUSED_PIN); #elif defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) // Only DIO1 should be specified ASSERT(lmic_pins.dio[0] == LMIC_UNUSED_PIN); @@ -55,6 +56,7 @@ static void hal_io_init () { ASSERT(lmic_pins.busy <= LMIC_UNUSED_PIN); ASSERT(lmic_pins.tcxo == LMIC_UNUSED_PIN || lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3); + ASSERT(lmic_pins.tx <= LMIC_UNUSED_PIN || lmic_pins.tx == LMIC_CONTROLLED_BY_DIO2); #else #error "Unknown radio type?" #endif @@ -157,6 +159,9 @@ void hal_pin_busy_wait (void) { bool hal_dio3_controls_tcxo (void) { return lmic_pins.tcxo == LMIC_CONTROLLED_BY_DIO3; } +bool hal_dio2_controls_rxtx (void) { + return lmic_pins.tx == LMIC_CONTROLLED_BY_DIO2; +} #endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) // ----------------------------------------------------------------------------- diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h index b16967f..07878a2 100644 --- a/target/arduino/hal/hal.h +++ b/target/arduino/hal/hal.h @@ -28,11 +28,12 @@ struct lmic_pinmap { }; // Use this for any unused pins. -const u1_t LMIC_UNUSED_PIN = 0xfe; +const u1_t LMIC_UNUSED_PIN = 0xfd; #if defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) // Used for lmic_pinmap.tcxo only const u1_t LMIC_CONTROLLED_BY_DIO3 = 0xff; +const u1_t LMIC_CONTROLLED_BY_DIO2 = 0xfe; #endif // defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) // Declared here, to be defined an initialized by the application From 23e33fd66c0c5cdc3a332cb3e95f6446aa5a97a2 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 09:46:01 +0200 Subject: [PATCH 122/137] arduino: Add some more links and comments to standard-pinmaps --- .../examples-common-files/standard-pinmaps.ino | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino index 3854ca6..3c7a19c 100644 --- a/target/arduino/examples-common-files/standard-pinmaps.ino +++ b/target/arduino/examples-common-files/standard-pinmaps.ino @@ -17,7 +17,7 @@ const lmic_pinmap lmic_pins = { // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W .tx = LMIC_UNUSED_PIN, .rx = LMIC_UNUSED_PIN, - // RST is hardwarid to MCU reset + // RST is hardwired to MCU reset .rst = LMIC_UNUSED_PIN, .dio = {1, 2, 3}, .busy = LMIC_UNUSED_PIN, @@ -28,6 +28,8 @@ const lmic_pinmap lmic_pins = { #error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" #endif // Assume this is a Nexus board +// https://github.com/Ideetron/RFM95W_Nexus/blob/master/RFM95W_NEXUS_02.pdf +// Note: BasicMAC is currently too big to fit into the 328p on this board. const lmic_pinmap lmic_pins = { .nss = 10, // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W @@ -44,6 +46,7 @@ const lmic_pinmap lmic_pins = { #error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" #endif // https://github.com/meetjestad/mjs_pcb +// Note: BasicMAC is currently too big to fit into the 328p on this board. const lmic_pinmap lmic_pins = { .nss = 10, // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W @@ -59,12 +62,16 @@ const lmic_pinmap lmic_pins = { #error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" #endif // Assume this a Feather M0 LoRa +// https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module/pinouts const lmic_pinmap lmic_pins = { .nss = 8, // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W .tx = LMIC_UNUSED_PIN, .rx = LMIC_UNUSED_PIN, .rst = 4, + // This needs additional external connections: DIO1 (labeled IO1 in + // docs and board) to 5 and DIO2 (labeled D2 on the board and IO1 in + // the docs) to 6. DIO0 is internally connected. .dio = {3, 5, 6}, .busy = LMIC_UNUSED_PIN, .tcxo = LMIC_UNUSED_PIN, @@ -78,11 +85,13 @@ const lmic_pinmap lmic_pins = { // This uses E22_* constants from the board variant file const lmic_pinmap lmic_pins = { .nss = E22_NSS, // PD2 + // DIO2 connected to TXEN on LS200 board .tx = LMIC_CONTROLLED_BY_DIO2, .rx = E22_RXEN, // PC4 .rst = E22_NRST, // PA4 .dio = {LMIC_UNUSED_PIN, E22_DIO1 /* PC7 */, LMIC_UNUSED_PIN}, .busy = E22_BUSY, // PB12 + // DIO3 connected to TCXO on E22 board .tcxo = LMIC_CONTROLLED_BY_DIO3, }; #else From 5eb2d761eed89c2b89a94799804310ec10ef57ff Mon Sep 17 00:00:00 2001 From: Remko Welling Date: Wed, 8 Jul 2020 09:33:28 +0200 Subject: [PATCH 123/137] arduino: Add ARDUINO_TTGO_LoRa32_V1 board pinout --- .../examples-common-files/standard-pinmaps.ino | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino index 3c7a19c..9f6763b 100644 --- a/target/arduino/examples-common-files/standard-pinmaps.ino +++ b/target/arduino/examples-common-files/standard-pinmaps.ino @@ -94,6 +94,22 @@ const lmic_pinmap lmic_pins = { // DIO3 connected to TCXO on E22 board .tcxo = LMIC_CONTROLLED_BY_DIO3, }; +#elif defined(ARDUINO_TTGO_LoRa32_V1) +#if !defined(BRD_sx1276_radio) +#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" +#endif +// https://github.com/LilyGO/TTGO-LORA32/tree/LilyGO-868-V1.0 +// Pinout: https://github.com/LilyGO/TTGO-LORA32/blob/LilyGO-868-V1.0/images/image1.jpg +const lmic_pinmap lmic_pins = { + .nss = 18, + // RX/TX is controlled through RXTX by the SX1272 directly on the RFM95W + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + .rst = 14, + .dio = {26, 33, 32}, + .busy = LMIC_UNUSED_PIN, + .tcxo = LMIC_UNUSED_PIN, +}; #else #error "Unknown board, no standard pimap available. Define your own in the main sketch file." #endif From 85a7a81d61496bc2bd48f6ca90a543cd75a9f06d Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 10:09:50 +0200 Subject: [PATCH 124/137] github: Compile arduino examples for ESP32 too --- .github/workflows/compile-arduino-examples.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compile-arduino-examples.yml b/.github/workflows/compile-arduino-examples.yml index 9a1117e..adb04af 100644 --- a/.github/workflows/compile-arduino-examples.yml +++ b/.github/workflows/compile-arduino-examples.yml @@ -5,13 +5,14 @@ jobs: name: ${{ matrix.board }} / ${{ matrix.region }} / ${{ matrix.transceiver }} runs-on: ubuntu-latest env: - PLATFORM_DEFAULT_URL: https://arduino.esp8266.com/stable/package_esp8266com_index.json,https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json,https://lacunaspace.github.io/arduino-STM32L4-Lacuna/package_STM32L4_Lacuna_boards_index.json + PLATFORM_DEFAULT_URL: https://arduino.esp8266.com/stable/package_esp8266com_index.json,https://dl.espressif.com/dl/package_esp32_index.json,https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json,https://lacunaspace.github.io/arduino-STM32L4-Lacuna/package_STM32L4_Lacuna_boards_index.json strategy: matrix: board: - arduino:avr:mega - arduino:samd:arduino_zero_edbg - esp8266:esp8266:generic + - esp32:esp32:ttgo-lora32-v1 - lacunaspace:stm32l4:Lacuna-LS200 # Just a random STM32 board - STM32:stm32:Nucleo_64:pnum=NUCLEO_F401RE From 19e29627649f770d61be793759ad33b5f9fa0eaf Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 12:31:02 +0200 Subject: [PATCH 125/137] arduino: Show how to select SX1261 Previously, target-config only showed the SX1262, not the SX1261. This fixes that by adding a commented define. It also completes the guard around the entire block, which is relevant for automatic (Github Action) builds (before this change both `BRD_sx1261_radio` and `BRD_sx1262_radio` might end up defined, which did not cause a compilation failure, so it was not noticed before). --- target/arduino/hal/target-config.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index 7354715..15c9699 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -8,14 +8,16 @@ #define CFG_autojoin -#if !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1262_radio) +#if !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1261_radio) && !defined(BRD_sx1262_radio) // This is the SX1272/SX1273 radio, which is also used on the HopeRF // RFM92 boards. //#define BRD_sx1272_radio 1 // This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on // the HopeRF RFM95 boards. //#define BRD_sx1276_radio 1 -// This is the newer SX1261/SX1261 radio. +// This is the newer SX1261 radio (up to +15dBM). +//#define BRD_sx1261_radio 1 +// This is the newer SX1262 radio (up to +22dBM). #define BRD_sx1262_radio 1 #endif // !defined(BRD_sx1272_radio) && !defined(BRD_sx1276_radio) && !defined(BRD_sx1262_radio) From fdb1f6fe9714c2d9fce5f7f5f691b3460f404c4f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 16:06:38 +0200 Subject: [PATCH 126/137] arduino: Improve region handling This removes the hardcoded EU868 region from examples by just querying BasicMac for the first enabled region instead (thanks to Luiz Henrique Cassettari for suggesting this in #9). Additionally, this puts examples of all regional defines in target-config.h and documents that multiple can be defined at the same time (though multiple regions and other regions than EU868 have not been tested yet, see #8). --- target/arduino/examples/basicmac-abp/basicmac-abp.ino | 5 ++++- target/arduino/examples/basicmac-otaa/basicmac-otaa.ino | 5 +++-- target/arduino/hal/target-config.h | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/target/arduino/examples/basicmac-abp/basicmac-abp.ino b/target/arduino/examples/basicmac-abp/basicmac-abp.ino index dfcde9d..1d50e52 100644 --- a/target/arduino/examples/basicmac-abp/basicmac-abp.ino +++ b/target/arduino/examples/basicmac-abp/basicmac-abp.ino @@ -52,7 +52,10 @@ static const u4_t DEVADDR = 0x03FF0001 ; // <-- Change this address for every no void os_getJoinEui (u1_t* /* buf */) { } void os_getDevEui (u1_t* /* buf */) { } void os_getNwkKey (u1_t* /* buf */) { } -u1_t os_getRegion (void) { return REGCODE_EU868; } + +// The region to use, this just uses the first one (can be changed if +// multiple regions are enabled). +u1_t os_getRegion (void) { return LMIC_regionCode(0); } // Schedule TX every this many milliseconds (might become longer due to duty // cycle limitations). diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino index e417060..cc9c2bd 100644 --- a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino +++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino @@ -51,8 +51,9 @@ void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);} static const u1_t PROGMEM APPKEY[16] = { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }; void os_getNwkKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);} -// The region to use -u1_t os_getRegion (void) { return REGCODE_EU868; } +// The region to use, this just uses the first one (can be changed if +// multiple regions are enabled). +u1_t os_getRegion (void) { return LMIC_regionCode(0); } // Schedule TX every this many milliseconds (might become longer due to duty // cycle limitations). diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index 15c9699..29079c0 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -1,9 +1,16 @@ #ifndef _lmic_arduino_hal_config_h_ #define _lmic_arduino_hal_config_h_ +// This defines the region(s) to use. You can enable more than one and +// then select the right region at runtime using os_getRegion() and/or +// LMIC_reset_ex(). #if !defined(CFG_eu868) && !defined(CFG_us915) #define CFG_eu868 1 //#define CFG_us915 1 +//#define CFG_as923 1 +//#define CFG_il915 1 +//#define CFG_kr920 1 +//#define CFG_au915 1 #endif // !defined(CFG_eu868) && !defined(CFG_us915) #define CFG_autojoin From b704b72364a339c3a047e9334a7feefee769a7f5 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 16:11:43 +0200 Subject: [PATCH 127/137] arduino: Fix TXRX-related comment in basicmac-otaa This comment was updated in the pinmap of basicmac-abp, but forgotten to be copied to basicmac-otaa. --- .../examples/basicmac-otaa/basicmac-otaa.ino | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino index cc9c2bd..4d1e69e 100644 --- a/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino +++ b/target/arduino/examples/basicmac-otaa/basicmac-otaa.ino @@ -76,9 +76,17 @@ const lmic_pinmap lmic_pins = { // If needed, these pins control the RX/TX antenna switch (active // high outputs). When you have both, the antenna switch can // powerdown when unused. If you just have a RXTX pin it should - // usually be set to .tx, reverting to RX mode when idle). - // Often, the antenna switch is controlled directly by the radio - // chip, through is RXTX (SX127x) or DIO2 (SX126x) output pins. + // usually be assigned to .tx, reverting to RX mode when idle). + // + // The SX127x has an RXTX pin that can automatically control the + // antenna switch (if internally connected on the transceiver + // board). This pin is always active, so no configuration is needed + // for that here. + // On SX126x, the DIO2 can be used for the same thing, but this is + // disabled by default. To enable this, set .tx to + // LMIC_CONTROLLED_BY_DIO2 below (this seems to be common and + // enabling it when not needed is probably harmless, unless DIO2 is + // connected to GND or VCC directly inside the transceiver board). .tx = LMIC_UNUSED_PIN, .rx = LMIC_UNUSED_PIN, // Radio reset output pin (active high for SX1276, active low for From 35a3f65cc7d9bb52ddfdf31b3b258ad3882a2536 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 15 Jul 2020 17:01:45 +0200 Subject: [PATCH 128/137] EU868: Add new h0 band This is a band that was somewhat recently made available and can be used for LoRaWAN. The max allowed bandwidth is 350kHz (According to ERC recommendation 70-03), but since BasicMac has no way to encode this limitation currently, this is not enforced (so this should be taken into account when configuring channels instead). --- lmic/lmic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lmic/lmic.c b/lmic/lmic.c index 3a05a35..6ebdef9 100644 --- a/lmic/lmic.c +++ b/lmic/lmic.c @@ -222,6 +222,7 @@ static const region_t REGIONS[REGIONS_COUNT] = { { 865000000, 868000000, CAP_CENTI, 16 }, // h1.4 { 868000000, 868600000, CAP_CENTI, 16 }, // h1.5 { 869700000, 870000000, CAP_CENTI, 16 }, // h1.9 + { 862000000, 863000000, CAP_MILLI, 16 }, // h0, max 350kHz BW (not enforced) { 863000000, 865000000, CAP_MILLI, 16 }, // h1.3 { 868700000, 869200000, CAP_MILLI, 16 }, // h1.6 }, From 22731d9e1fe50b0223273e40919169db93594da9 Mon Sep 17 00:00:00 2001 From: Linar Yusupov Date: Wed, 8 Jul 2020 22:51:50 +0300 Subject: [PATCH 129/137] TTGO T-Beam pinmap --- .../standard-pinmaps.ino | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/target/arduino/examples-common-files/standard-pinmaps.ino b/target/arduino/examples-common-files/standard-pinmaps.ino index 9f6763b..5fa2e87 100644 --- a/target/arduino/examples-common-files/standard-pinmaps.ino +++ b/target/arduino/examples-common-files/standard-pinmaps.ino @@ -110,6 +110,41 @@ const lmic_pinmap lmic_pins = { .busy = LMIC_UNUSED_PIN, .tcxo = LMIC_UNUSED_PIN, }; +#elif defined(ARDUINO_TBeam) +// TTGO T-Beam development board +// https://github.com/LilyGO/TTGO-T-Beam +// This board is available with an SX1276 or SX1262 soldered on. The board is +// otherwise identical and does not have a separate board entry in the +// Arduino IDE, so decide based on the radio constant which one to use. +// +// Uses LoRa SPI bus at 5/19/27 +// This uses LORA_* constants from the board variant file +#if defined(BRD_sx1276_radio) +const lmic_pinmap lmic_pins = { + .nss = LORA_CS, // 18 + // RX/TX is controlled through RXTX by the SX1276/8 directly on the HPD13/4A + .tx = LMIC_UNUSED_PIN, + .rx = LMIC_UNUSED_PIN, + .rst = LORA_RST, // 23 + .dio = {LORA_IO0 /* 26 */ , LORA_IO1 /* 33 */, LORA_IO2 /* 32 */}, + .busy = LMIC_UNUSED_PIN, + .tcxo = LMIC_UNUSED_PIN, +}; +#elif defined(BRD_sx1262_radio) +const lmic_pinmap lmic_pins = { + .nss = LORA_CS, // 18 + // TXEN is controlled through DIO2 by the SX1262 (HPD16A) directly + .tx = LMIC_CONTROLLED_BY_DIO2, + .rx = LMIC_UNUSED_PIN, + .rst = LORA_RST, // 23 + .dio = {LMIC_UNUSED_PIN, LORA_IO1 /* 33 */, LMIC_UNUSED_PIN}, + .busy = LORA_IO2, // 32 + // TCXO is controlled through DIO3 by the SX1262 directly + .tcxo = LMIC_CONTROLLED_BY_DIO3, +}; +#else +#error "Wrong radio defined for this board (fix in BasicMAC target-config.h)" +#endif #else #error "Unknown board, no standard pimap available. Define your own in the main sketch file." #endif From 2c0764d0ff4a6d3563ba3970c0736027d10e5b87 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 1 Nov 2020 12:07:06 +0100 Subject: [PATCH 130/137] arduino: Remove hw.h content This was all commented out and unused, so just remove it. The file must still exist because it is included from elsewhere, though this can probably be changed in the future as well. --- target/arduino/hw.h | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/target/arduino/hw.h b/target/arduino/hw.h index 29090e7..a13ea30 100644 --- a/target/arduino/hw.h +++ b/target/arduino/hw.h @@ -1,45 +1,8 @@ -// __ __ ___ __ __ _________________________________ -// (_ |_ |V| | |_ / |_| (C) 2018-2018 Semtech Corporation -// __)|__| | | |__\__| | All rights reserved #ifndef _hw_h_ #define _hw_h_ -/* -#define PERIPH_EEPROM +// Dummy file, not used by this HAL (but must be present, since other +// files include it). -#define EEPROM_BASE 0x30000000 -#define EEPROM_SZ (8 * 1024) -#define EEPROM_END (EEPROM_BASE + EEPROM_SZ) - -// 0x0000-0x003f 64 B : reserved for bootloader -// 0x0040-0x005f 32 B : reserved for persistent stack data -// 0x0060-0x00ff 160 B : reserved for personalization data -// 0x0100-...... : reserved for application - -#define STACKDATA_BASE (EEPROM_BASE + 0x0040) -#define PERSODATA_BASE (EEPROM_BASE + 0x0060) -#define APPDATA_BASE (EEPROM_BASE + 0x0100) - -#define STACKDATA_SZ (PERSODATA_BASE - STACKDATA_BASE) -#define PERSODATA_SZ (APPDATA_BASE - PERSODATA_BASE) -#define APPDATA_SZ (EEPROM_END - APPDATA_BASE) - - -#define FLASH_BASE 0x20000000 -#define FLASH_SZ (128 * 1024) -#define FLASH_END (FLASH_BASE + FLASH_SZ) -#define FLASH_PAGE_SZ 128 - - -#define PERIPH_USART -#define USART_BR_9600 9600 -#define USART_BR_115200 115200 - -#define PERIPH_PIO -#define PIO_IRQ_LINE(gpio) (gpio) - -#define PERIPH_CRC -#define PERIPH_SHA256 -*/ #endif From 68f76e562c98e2d69625f66ea2c0f7523220f50f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Sun, 1 Nov 2020 12:07:55 +0100 Subject: [PATCH 131/137] arduino: Change license to BSD Some parts of this code were still marked with the EPL license, used by older versions of LMIC. This changes the license to 3-clause BSD, the same as the rest of LMIC. This license is also added to the files that previously did not state any license. All of the affected code was written by me, with no significant contributions from others. --- target/arduino/basicmac.h | 28 ++++++++++++++++++++++++++++ target/arduino/board.h | 28 ++++++++++++++++++++++++++++ target/arduino/export.sh | 30 ++++++++++++++++++++++++++++++ target/arduino/hal/hal.cpp | 28 ++++++++++++++++++++++++---- target/arduino/hal/hal.h | 28 ++++++++++++++++++++++++---- target/arduino/hal/target-config.h | 29 +++++++++++++++++++++++++++++ target/arduino/hw.h | 27 +++++++++++++++++++++++++++ 7 files changed, 190 insertions(+), 8 deletions(-) diff --git a/target/arduino/basicmac.h b/target/arduino/basicmac.h index f2c7935..9dbf0dc 100644 --- a/target/arduino/basicmac.h +++ b/target/arduino/basicmac.h @@ -1,3 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * + * --- Revised 3-Clause BSD License --- + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 SEMTECH 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. + *******************************************************************************/ + #ifdef __cplusplus extern "C"{ #endif diff --git a/target/arduino/board.h b/target/arduino/board.h index 426077a..2a7505e 100644 --- a/target/arduino/board.h +++ b/target/arduino/board.h @@ -1 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * + * --- Revised 3-Clause BSD License --- + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 SEMTECH 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 "../hal/target-config.h" diff --git a/target/arduino/export.sh b/target/arduino/export.sh index 433034e..bbed1ad 100755 --- a/target/arduino/export.sh +++ b/target/arduino/export.sh @@ -1,4 +1,34 @@ #!/bin/sh +# +# Copyright (C) 2016, Matthijs Kooijman +# +# --- Revised 3-Clause BSD License --- +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 SEMTECH 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. + +# +# Export script for unix operating systems (e.g. Linux and OSX). +# SRC=$(cd "$(dirname "$0")"; pwd) diff --git a/target/arduino/hal/hal.cpp b/target/arduino/hal/hal.cpp index b53e9f1..b275b7f 100644 --- a/target/arduino/hal/hal.cpp +++ b/target/arduino/hal/hal.cpp @@ -1,9 +1,29 @@ /******************************************************************************* * Copyright (c) 2015 Matthijs Kooijman - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html + * + * --- Revised 3-Clause BSD License --- + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 SEMTECH 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. * * This the HAL to run LMIC on top of the Arduino environment. *******************************************************************************/ diff --git a/target/arduino/hal/hal.h b/target/arduino/hal/hal.h index 07878a2..4f8fb75 100644 --- a/target/arduino/hal/hal.h +++ b/target/arduino/hal/hal.h @@ -1,9 +1,29 @@ /******************************************************************************* * Copyright (c) 2015 Matthijs Kooijman - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html + * + * --- Revised 3-Clause BSD License --- + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 SEMTECH 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. * * This the HAL to run LMIC on top of the Arduino environment. *******************************************************************************/ diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index 29079c0..c9e43d5 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -1,3 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * + * --- Revised 3-Clause BSD License --- + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 SEMTECH 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. + * + * This file defines the configuration of the library and HAL. + *******************************************************************************/ #ifndef _lmic_arduino_hal_config_h_ #define _lmic_arduino_hal_config_h_ diff --git a/target/arduino/hw.h b/target/arduino/hw.h index a13ea30..a4a149e 100644 --- a/target/arduino/hw.h +++ b/target/arduino/hw.h @@ -1,3 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2015 Matthijs Kooijman + * + * --- Revised 3-Clause BSD License --- + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 SEMTECH 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. + *******************************************************************************/ #ifndef _hw_h_ #define _hw_h_ From b506988a8237383448419ce8f711d89ec9cec391 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 2 Nov 2020 15:43:52 +0100 Subject: [PATCH 132/137] Fix link in README This pointed to the wrong commit, and it turns out github does not autolink commits in the README, so also make this an explicit link. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d62f5d6..b97f778 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,9 @@ not currently tested. BasicMAC is based on LMIC and largely works in the same way. Sketches can likely be largely reused, though some small changes need to be made. -See for example commit 3ccfb5b for the changes needed to the examples. +See for example [this commit][migrate-examples] for the changes needed to the examples. Additionally the pinmap was significantly changed, so look at one of the current examples to see how that looks now. + +[migrate-examples]: https://github.com/LacunaSpace/basicmac/commit/1505722c912c8cb0cfff2e18b115f9f2c1a62d0f From 04035d87acc19dd9998845f9f7137842318d924e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 3 Nov 2020 16:28:17 +0100 Subject: [PATCH 133/137] arduino: Fix typo in (disabled) config macro --- target/arduino/hal/target-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arduino/hal/target-config.h b/target/arduino/hal/target-config.h index c9e43d5..a124d27 100644 --- a/target/arduino/hal/target-config.h +++ b/target/arduino/hal/target-config.h @@ -85,7 +85,7 @@ // When this is defined, the standard libc printf function will print to // this Stream. You should probably use CFG_DEBUG and debug_printf() // instead, though. -//#define LMIC_PRINTF_FO +//#define LMIC_PRINTF_TO Serial // Remove/comment this to enable code related to beacon tracking. #define DISABLE_CLASSB From f67f43f049cbf2e44f5f5f8023ae9289f3618767 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 3 Nov 2020 17:28:07 +0100 Subject: [PATCH 134/137] sx126x: Fix GetRandom When using DIO2 or DIO3 to control the TCXO or RxTx switch and GetRandom is called while the radio sleeps, the random generator will not be able to receive noise and return only zeroes. This fixes this by setting up the settings whenever GetRandom is called. To make sure CommonSetup is visible, also move GetRandom a bit downward in the file. --- lmic/radio-sx126x.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index abf7baf..7708140 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -521,20 +521,6 @@ static void SetCrc16 (uint16_t seed, uint16_t polynomial) { WriteRegs(REG_CRCPOLYVALMSB, buf, 2); } -static uint32_t GetRandom (void) __attribute__((__unused__)); // Ok if unused -static uint32_t GetRandom (void) { - uint32_t value; - // continuous rx - SetRx(0xFFFFFF); - // wait 1ms - hal_waitUntil(os_getTime() + ms2osticks(1)); - // read random register - ReadRegs(REG_RANDOMNUMBERGEN0, (uint8_t*)&value, sizeof(value)); - // standby - SetStandby(STDBY_RC); - return value; -} - void radio_sleep (void) { // cache sleep state to avoid unneccessary wakeup (waking up from cold sleep takes about 4ms) if (state.sleeping == 0) { @@ -552,6 +538,24 @@ static void CommonSetup (void) { SetDIO3AsTcxoCtrl(); } +static uint32_t GetRandom (void) __attribute__((__unused__)); // Ok if unused +static uint32_t GetRandom (void) { + uint32_t value; + + // Set up oscillator and rx/tx + CommonSetup(); + + // continuous rx + SetRx(0xFFFFFF); + // wait 1ms + hal_waitUntil(os_getTime() + ms2osticks(1)); + // read random register + ReadRegs(REG_RANDOMNUMBERGEN0, (uint8_t*)&value, sizeof(value)); + // standby + SetStandby(STDBY_RC); + return value; +} + static void txlora (void) { CommonSetup(); SetStandby(STDBY_RC); From cfb7f3d8fbf3127a19c78b2799e3e864ab21be0f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 3 Nov 2020 17:00:51 +0100 Subject: [PATCH 135/137] sx126x: Use random generator for random seed This radio has a random generator built in, so use it to seed the internal random generator. For the sx127x, the old, imperfect, method (based on DevEUI and compilation time is still used. This could maybe use RSSI measurements like LMIC did? Previously, the random generator was seeded on first use, but to prevent needing the radio while it is already operating, this now seeds the generator on startup. In particular, this means that on sx126x, the channel selection and join nonces are now properly randomized. --- lmic/oslmic.c | 17 ++++++++++------- lmic/oslmic.h | 1 + lmic/radio-sx126x.c | 5 +++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lmic/oslmic.c b/lmic/oslmic.c index e637403..03ccc2a 100644 --- a/lmic/oslmic.c +++ b/lmic/oslmic.c @@ -18,12 +18,15 @@ static struct { } /* anonymous */; } OS; +void rng_init (void); + void os_init (void* bootarg) { memset(&OS, 0x00, sizeof(OS)); hal_init(bootarg); #ifndef CFG_noradio radio_init(false); #endif + rng_init(); LMIC_init(); } @@ -33,21 +36,21 @@ void os_init (void* bootarg) { void rng_init (void) { #ifdef PERIPH_TRNG trng_next(OS.randwrds, 4); +#elif defined(BRD_sx1261_radio) || defined(BRD_sx1262_radio) + radio_generate_random(OS.randwrds, 4); #else memcpy(OS.randbuf, __TIME__, 8); os_getDevEui(OS.randbuf + 8); #endif + OS.randbuf[0] = 16; } u1_t os_getRndU1 (void) { u1_t i = OS.randbuf[0]; - switch( i ) { - case 0: - rng_init(); // lazy initialization - // fall-thru - case 16: - os_aes(AES_ENC, OS.randbuf, 16); // encrypt seed with any key - i = 0; + ASSERT(i != 0); + if (i == 16) { + os_aes(AES_ENC, OS.randbuf, 16); // encrypt seed with any key + i = 0; } u1_t v = OS.randbuf[i++]; OS.randbuf[0] = i; diff --git a/lmic/oslmic.h b/lmic/oslmic.h index 701d331..af1961a 100644 --- a/lmic/oslmic.h +++ b/lmic/oslmic.h @@ -369,6 +369,7 @@ void radio_sleep (void); void radio_cca (void); void radio_cad (void); void radio_cw (void); +void radio_generate_random (u4_t *words, u1_t len); #ifdef __cplusplus } // extern "C" diff --git a/lmic/radio-sx126x.c b/lmic/radio-sx126x.c index 7708140..27d4578 100644 --- a/lmic/radio-sx126x.c +++ b/lmic/radio-sx126x.c @@ -800,6 +800,11 @@ void radio_init (bool calibrate) { hal_enableIRQs(); } +void radio_generate_random(u4_t *words, u1_t len) { + while (len--) + *words++ = GetRandom (); +} + // (run by irqjob) bool radio_irq_process (ostime_t irqtime, u1_t diomask) { (void)diomask; // unused From 3e92fb143ce0d24d0827192cb7a3603b2081e4d0 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 6 Nov 2020 20:35:09 +0100 Subject: [PATCH 136/137] github: Use standard version of arduino-test-compile This previously used a customized version, but all customizations have been merged upstream, so use the standard version again. --- .github/workflows/compile-arduino-examples.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/compile-arduino-examples.yml b/.github/workflows/compile-arduino-examples.yml index adb04af..83ce17a 100644 --- a/.github/workflows/compile-arduino-examples.yml +++ b/.github/workflows/compile-arduino-examples.yml @@ -55,13 +55,7 @@ jobs: ./target/arduino/export.sh BasicMacArduino - name: Compile all examples - # This uses a fork that also works for .c files and runs outside - # of docker (for performance, but also inside docker the lacuna - # build somehow fails). - # TODO: Convert back to the original once merged: - # https://github.com/ArminJo/arduino-test-compile/pull/14 - # https://github.com/ArminJo/arduino-test-compile/pull/16 - uses: matthijskooijman/arduino-test-compile@extra-flags-for-c-and-S-and-no-docker + uses: ArminJo/arduino-test-compile@v3 with: arduino-board-fqbn: ${{ matrix.board }} platform-default-url: ${{ env.PLATFORM_DEFAULT_URL }} @@ -69,8 +63,8 @@ jobs: # Normally, all .ino files are treated as separate sketches, but # this is just a secondary .ino file that does not need to be # separately compiled. - examples-exclude: standard-pinmaps - examples-build-properties: > + sketches-exclude: standard-pinmaps + build-properties: > {"All": " -DBASICMAC_DUMMY_PINMAP -D${{matrix.region}} From 001a3aaee1f2fc2dabb82695319b31b9c32a09db Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 26 Nov 2020 16:28:58 +0100 Subject: [PATCH 137/137] github: Create and store Arduino library --- .github/workflows/create-arduino-lib.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/create-arduino-lib.yml diff --git a/.github/workflows/create-arduino-lib.yml b/.github/workflows/create-arduino-lib.yml new file mode 100644 index 0000000..42f178b --- /dev/null +++ b/.github/workflows/create-arduino-lib.yml @@ -0,0 +1,21 @@ +name: Create Arduino Library +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Generate Arduino library + run: | + # Create two directory levels, so the inner one ends up in the + # generated zip + mkdir ArduinoLibrary + ./target/arduino/export.sh ArduinoLibrary/Basicmac + + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: ArduinoLibrary + path: ArduinoLibrary