Reorganize directories

This commit is contained in:
Manuel Bleichenbacher
2018-10-02 20:25:40 +02:00
parent 10decb6886
commit 0f97ce4e0f
16 changed files with 16 additions and 16 deletions

68
src/lmic/config.h Normal file
View File

@ -0,0 +1,68 @@
/*******************************************************************************
*
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
*
* Copyright (c) 2018 Manuel Bleichenbacher
*
* Licensed under MIT License
* https://opensource.org/licenses/MIT
*
* This the hardware abstraction layer to run LMIC in on ESP32 using ESP-iDF.
*******************************************************************************/
#ifndef _lmic_config_h_
#define _lmic_config_h_
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(CONFIG_TTN_LORA_FREQ_EU_868)
#define CFG_eu868 1
#elif defined(CONFIG_TTN_LORA_FREQ_US_915)
#define CFG_us915 1
#else
#define TTN_IS_DISABLED 1
#define CFG_eu868 1
#endif
#if defined(CONFIG_TTN_RADIO_SX1272_73)
#define CFG_sx1272_radio 1
#elif defined(CONFIG_TTN_RADIO_SX1276_77_78_79)
#define CFG_sx1276_radio 1
#else
#error TTN LoRa radio chip must be configured using 'make menuconfig'
#endif
#if defined(CONFIG_TTN_TIMER_0_GROUP_0)
#define TTN_TIMER TIMER_0
#define TTN_TIMER_GROUP TIMER_GROUP_0
#define TTN_CLEAR_TIMER_ALARM TIMERG0.int_clr_timers.t0 = 1
#elif defined(CONFIG_TTN_TIMER_1_GROUP_0)
#define TTN_TIMER TIMER_1
#define TTN_TIMER_GROUP TIMER_GROUP_0
#define TTN_CLEAR_TIMER_ALARM TIMERG0.int_clr_timers.t1 = 1
#elif defined(CONFIG_TTN_TIMER_0_GROUP_1)
#define TTN_TIMER TIMER_0
#define TTN_TIMER_GROUP TIMER_GROUP_1
#define TTN_CLEAR_TIMER_ALARM TIMERG1.int_clr_timers.t0 = 1
#elif defined(CONFIG_TTN_TIMER_1_GROUP_1)
#define TTN_TIMER TIMER_1
#define TTN_TIMER_GROUP TIMER_GROUP_1
#define TTN_CLEAR_TIMER_ALARM TIMERG1.int_clr_timers.t1 = 1
#else
#error TTN timer must be configured using 'make menuconfig'
#endif
// 16 μs per tick
// LMIC requires ticks to be 15.5μs - 100 μs long
#define US_PER_OSTICK 16
#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK)
#ifdef __cplusplus
}
#endif
#endif // _lmic_config_h_

119
src/lmic/hal.h Executable file
View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* All rights reserved.
*
* 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 <organization> 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 <COPYRIGHT HOLDER> 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 _hal_hpp_
#define _hal_hpp_
#include "oslmic.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* initialize hardware (IO, SPI, TIMER, IRQ).
*/
void hal_init (void);
/*
* drive radio NSS pin (0=low, 1=high).
*/
void hal_pin_nss (u1_t val);
/*
* drive radio RX/TX pins (0=rx, 1=tx).
*/
void hal_pin_rxtx (u1_t val);
/*
* control radio RST pin (0=low, 1=high, 2=floating)
*/
void hal_pin_rst (u1_t val);
/*
* perform SPI write transaction with radio
* - write the command byte 'cmd'
* - write 'len' bytes in 'buf'
*/
void hal_spi_write(u1_t cmd, const u1_t* buf, int len);
/*
* perform SPI read transaction with radio
* - write the command byte 'cmd'
* - read 'len' bytes into 'buf'
*/
void hal_spi_read(u1_t cmd, u1_t* buf, int len);
/*
* disable all CPU interrupts.
* - might be invoked nested
* - will be followed by matching call to hal_enableIRQs()
*/
void hal_disableIRQs (void);
/*
* enable CPU interrupts.
*/
void hal_enableIRQs (void);
/*
* put system and CPU in low-power mode, sleep until interrupt.
*/
void hal_sleep (void);
/*
* return 32-bit system time in ticks.
*/
u4_t hal_ticks (void);
/*
* busy-wait until specified timestamp (in ticks) is reached.
*/
void hal_waitUntil (u4_t time);
/*
* check and rewind timer for target time.
* - return 1 if target time is close
* - otherwise rewind timer for target time or full period and return 0
*/
u1_t hal_checkTimer (u4_t targettime);
/*
* perform fatal failure action.
* - called by assertions
* - action could be HALT or reboot
*/
void hal_failed (const char *file, u2_t line);
#ifdef __cplusplus
}
#endif
#endif // _hal_hpp_

2400
src/lmic/lmic.c Executable file

File diff suppressed because it is too large Load Diff

340
src/lmic/lmic.h Executable file
View File

@ -0,0 +1,340 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* All rights reserved.
*
* 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 <organization> 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 <COPYRIGHT HOLDER> 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.
*/
//! @file
//! @brief LMIC API
#ifndef _lmic_h_
#define _lmic_h_
#include "config.h"
#include "oslmic.h"
#include "lorabase.h"
#ifdef __cplusplus
extern "C" {
#endif
// LMIC version
#define LMIC_VERSION_MAJOR 1
#define LMIC_VERSION_MINOR 6
#define LMIC_VERSION_BUILD 1468577746
enum { MAX_FRAME_LEN = 64 }; //!< Library cap on max frame length
enum { TXCONF_ATTEMPTS = 8 }; //!< Transmit attempts for confirmed frames
enum { MAX_MISSED_BCNS = 20 }; // threshold for triggering rejoin requests
enum { MAX_RXSYMS = 100 }; // stop tracking beacon beyond this
enum { LINK_CHECK_CONT = 12 , // continue with this after reported dead link
LINK_CHECK_DEAD = 24 , // after this UP frames and no response from NWK assume link is dead
LINK_CHECK_INIT = -12 , // UP frame count until we inc datarate
LINK_CHECK_OFF =-128 }; // link check disabled
enum { TIME_RESYNC = 6*128 }; // secs
enum { TXRX_GUARD_ms = 6000 }; // msecs - don't start TX-RX transaction before beacon
enum { JOIN_GUARD_ms = 9000 }; // msecs - don't start Join Req/Acc transaction before beacon
enum { TXRX_BCNEXT_secs = 2 }; // secs - earliest start after beacon time
enum { RETRY_PERIOD_secs = 3 }; // secs - random period for retrying a confirmed send
#if defined(CFG_eu868) // EU868 spectrum ====================================================
enum { MAX_CHANNELS = 16 }; //!< Max supported channels
enum { MAX_BANDS = 4 };
enum { LIMIT_CHANNELS = (1<<4) }; // EU868 will never have more channels
//! \internal
struct band_t {
u2_t txcap; // duty cycle limitation: 1/txcap
s1_t txpow; // maximum TX power
u1_t lastchnl; // last used channel
ostime_t avail; // channel is blocked until this time
};
TYPEDEF_xref2band_t; //!< \internal
#elif defined(CFG_us915) // US915 spectrum =================================================
enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable
enum { MAX_TXPOW_125kHz = 30 };
#endif // ==========================================================================
// Keep in sync with evdefs.hpp::drChange
enum { DRCHG_SET, DRCHG_NOJACC, DRCHG_NOACK, DRCHG_NOADRACK, DRCHG_NWKCMD };
enum { KEEP_TXPOW = -128 };
#if !defined(DISABLE_PING)
//! \internal
struct rxsched_t {
u1_t dr;
u1_t intvExp; // 0..7
u1_t slot; // runs from 0 to 128
u1_t rxsyms;
ostime_t rxbase;
ostime_t rxtime; // start of next spot
u4_t freq;
};
TYPEDEF_xref2rxsched_t; //!< \internal
#endif // !DISABLE_PING
#if !defined(DISABLE_BEACONS)
//! 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)
BCN_FULL = 0x02, //!< Full beacon decoded
BCN_NODRIFT = 0x04, //!< No drift value measured yet
BCN_NODDIFF = 0x08 }; //!< No differential drift measured yet
//! Information about the last and previous beacons.
struct bcninfo_t {
ostime_t txtime; //!< Time when the beacon was sent
s1_t rssi; //!< Adjusted RSSI value of last received beacon
s1_t snr; //!< Scaled SNR value of last received beacon
u1_t flags; //!< Last beacon reception and tracking states. See BCN_* values.
u4_t time; //!< GPS time in seconds of last beacon (received or surrogate)
//
u1_t info; //!< Info field of last beacon (valid only if BCN_FULL set)
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)
};
#endif // !DISABLE_BEACONS
// purpose of receive window - lmic_t.rxState
enum { RADIO_RST=0, RADIO_TX=1, RADIO_RX=2, RADIO_RXON=3 };
// Netid values / lmic_t.netid
enum { NETID_NONE=(int)~0U, NETID_MASK=(int)0xFFFFFF };
// MAC operation modes (lmic_t.opmode).
enum { OP_NONE = 0x0000,
OP_SCAN = 0x0001, // radio scan to find a beacon
OP_TRACK = 0x0002, // track my networks beacon (netid)
OP_JOINING = 0x0004, // device joining in progress (blocks other activities)
OP_TXDATA = 0x0008, // TX user data (buffered in pendTxData)
OP_POLL = 0x0010, // send empty UP frame to ACK confirmed DN/fetch more DN data
OP_REJOIN = 0x0020, // occasionally send JOIN REQUEST
OP_SHUTDOWN = 0x0040, // prevent MAC from doing anything
OP_TXRXPEND = 0x0080, // TX/RX transaction pending
OP_RNDTX = 0x0100, // prevent TX lining up after a beacon
OP_PINGINI = 0x0200, // pingable is initialized and scheduling active
OP_PINGABLE = 0x0400, // we're pingable
OP_NEXTCHNL = 0x0800, // find a new channel
OP_LINKDEAD = 0x1000, // link was reported as dead
OP_TESTMODE = 0x2000, // developer test mode
};
// TX-RX transaction flags - report back to user
enum { TXRX_ACK = 0x80, // confirmed UP frame was acked
TXRX_NACK = 0x40, // confirmed UP frame was not acked
TXRX_NOPORT = 0x20, // set if a frame with a port was RXed, clr if no frame/no port
TXRX_PORT = 0x10, // set if a frame with a port was RXed, LMIC.frame[LMIC.dataBeg-1] => port
TXRX_DNW1 = 0x01, // received in 1st DN slot
TXRX_DNW2 = 0x02, // received in 2dn DN slot
TXRX_PING = 0x04 }; // received in a scheduled RX slot
// Event types for event callback
enum _ev_t { EV_SCAN_TIMEOUT=1, EV_BEACON_FOUND,
EV_BEACON_MISSED, EV_BEACON_TRACKED, EV_JOINING,
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 };
typedef enum _ev_t ev_t;
enum {
// This value represents 100% error in LMIC.clockError
MAX_CLOCK_ERROR = 65536,
};
struct lmic_t {
// Radio settings TX/RX (also accessed by HAL)
ostime_t txend;
ostime_t rxtime;
u4_t freq;
s1_t rssi;
s1_t snr;
rps_t rps;
u1_t rxsyms;
u1_t dndr;
s1_t txpow; // dBm
osjob_t osjob;
// Channel scheduling
#if defined(CFG_eu868)
band_t bands[MAX_BANDS];
u4_t channelFreq[MAX_CHANNELS];
u2_t channelDrMap[MAX_CHANNELS];
u2_t channelMap;
#elif defined(CFG_us915)
u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater)
u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto
u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits
u2_t chRnd; // channel randomizer
#endif
u1_t txChnl; // channel for next TX
u1_t globalDutyRate; // max rate: 1/2^k
ostime_t globalDutyAvail; // time device can send again
u4_t netid; // current network id (~0 - none)
u2_t opmode;
u1_t upRepeat; // configured up repeat
s1_t adrTxPow; // ADR adjusted TX power
u1_t datarate; // current data rate
u1_t errcr; // error coding rate (used for TX only)
u1_t rejoinCnt; // adjustment for rejoin datarate
#if !defined(DISABLE_BEACONS)
s2_t drift; // last measured drift
s2_t lastDriftDiff;
s2_t maxDriftDiff;
#endif
u2_t clockError; // Inaccuracy in the clock. CLOCK_ERROR_MAX
// represents +/-100% error
u1_t pendTxPort;
u1_t pendTxConf; // confirmed data
u1_t pendTxLen; // +0x80 = confirmed
u1_t pendTxData[MAX_LEN_PAYLOAD];
u2_t devNonce; // last generated nonce
u1_t nwkKey[16]; // network session key
u1_t artKey[16]; // application router session key
devaddr_t devaddr;
u4_t seqnoDn; // device level down stream seqno
u4_t seqnoUp;
u1_t dnConf; // dn frame confirm pending: LORA::FCT_ACK or 0
s1_t adrAckReq; // counter until we reset data rate (0=off)
u1_t adrChanged;
u1_t rxDelay; // Rx delay after TX
u1_t margin;
bit_t ladrAns; // link adr adapt answer pending
bit_t devsAns; // device status answer pending
u1_t adrEnabled;
u1_t moreData; // NWK has more data pending
#if !defined(DISABLE_MCMD_DCAP_REQ)
bit_t dutyCapAns; // have to ACK duty cycle settings
#endif
#if !defined(DISABLE_MCMD_SNCH_REQ)
u1_t snchAns; // answer set new channel
#endif
// 2nd RX window (after up stream)
u1_t dn2Dr;
u4_t dn2Freq;
#if !defined(DISABLE_MCMD_DN2P_SET)
u1_t dn2Ans; // 0=no answer pend, 0x80+ACKs
#endif
// Class B state
#if !defined(DISABLE_BEACONS)
u1_t missedBcns; // unable to track last N beacons
u1_t bcninfoTries; // how often to try (scan mode only)
#endif
#if !defined(DISABLE_MCMD_PING_SET) && !defined(DISABLE_PING)
u1_t pingSetAns; // answer set cmd and ACK bits
#endif
#if !defined(DISABLE_PING)
rxsched_t ping; // pingable setup
#endif
// Public part of MAC state
u1_t txCnt;
u1_t txrxFlags; // transaction flags (TX-RX combo)
u1_t dataBeg; // 0 or start of data (dataBeg-1 is port)
u1_t dataLen; // 0 no data or zero length data, >0 byte count of data
u1_t frame[MAX_LEN_FRAME];
#if !defined(DISABLE_BEACONS)
u1_t bcnChnl;
u1_t bcnRxsyms; //
ostime_t bcnRxtime;
bcninfo_t bcninfo; // Last received beacon info
#endif
u1_t noRXIQinversion;
};
//! \var struct lmic_t LMIC
//! The state of LMIC MAC layer is encapsulated in this variable.
DECLARE_LMIC; //!< \internal
//! Construct a bit map of allowed datarates from drlo to drhi (both included).
#define DR_RANGE_MAP(drlo,drhi) (((u2_t)0xFFFF<<(drlo)) & ((u2_t)0xFFFF>>(15-(drhi))))
#if defined(CFG_eu868)
enum { BAND_MILLI=0, BAND_CENTI=1, BAND_DECI=2, BAND_AUX=3 };
bit_t LMIC_setupBand (u1_t bandidx, s1_t txpow, u2_t txcap);
#endif
bit_t LMIC_setupChannel (u1_t channel, u4_t freq, u2_t drmap, s1_t band);
void LMIC_disableChannel (u1_t channel);
#if defined(CFG_us915)
void LMIC_enableChannel (u1_t channel);
void LMIC_enableSubBand (u1_t band);
void LMIC_disableSubBand (u1_t band);
void LMIC_selectSubBand (u1_t band);
#endif
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)
#if !defined(DISABLE_JOIN)
bit_t LMIC_startJoining (void);
#endif
void LMIC_shutdown (void);
void LMIC_init (void);
void LMIC_start (void);
void LMIC_reset (void);
void LMIC_clrTxData (void);
void LMIC_setTxData (void);
int LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed);
void LMIC_sendAlive (void);
#if !defined(DISABLE_BEACONS)
bit_t LMIC_enableTracking (u1_t tryBcnInfo);
void LMIC_disableTracking (void);
#endif
#if !defined(DISABLE_PING)
void LMIC_stopPingable (void);
void LMIC_setPingable (u1_t intvExp);
#endif
#if !defined(DISABLE_JOIN)
void LMIC_tryRejoin (void);
#endif
void LMIC_setSession (u4_t netid, devaddr_t devaddr, xref2u1_t nwkKey, xref2u1_t artKey);
void LMIC_setLinkCheckMode (bit_t enabled);
void LMIC_setClockError(u2_t error);
// 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!!!
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _lmic_h_

407
src/lmic/lorabase.h Executable file
View File

@ -0,0 +1,407 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* All rights reserved.
*
* 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 <organization> 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 <COPYRIGHT HOLDER> 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 _lorabase_h_
#define _lorabase_h_
#ifdef __cplusplus
extern "C" {
#endif
// ================================================================================
// BEG: Keep in sync with lorabase.hpp
//
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 };
typedef u1_t cr_t;
typedef u1_t sf_t;
typedef u1_t bw_t;
typedef u1_t dr_t;
// Radio parameter set (encodes SF/BW/CR/IH/NOCRC)
typedef u2_t rps_t;
TYPEDEF_xref2rps_t;
enum { ILLEGAL_RPS = 0xFF };
enum { DR_PAGE_EU868 = 0x00 };
enum { DR_PAGE_US915 = 0x10 };
// Global maximum frame length
enum { STD_PREAMBLE_LEN = 8 };
enum { MAX_LEN_FRAME = 64 };
enum { LEN_DEVNONCE = 2 };
enum { LEN_ARTNONCE = 3 };
enum { LEN_NETID = 3 };
enum { DELAY_JACC1 = 5 }; // in secs
enum { DELAY_DNW1 = 1 }; // in secs down window #1
enum { DELAY_EXTDNW2 = 1 }; // in secs
enum { DELAY_JACC2 = DELAY_JACC1+(int)DELAY_EXTDNW2 }; // in secs
enum { DELAY_DNW2 = DELAY_DNW1 +(int)DELAY_EXTDNW2 }; // in secs down window #1
enum { BCN_INTV_exp = 7 };
enum { BCN_INTV_sec = 1<<BCN_INTV_exp };
enum { BCN_INTV_ms = BCN_INTV_sec*1000L };
enum { BCN_INTV_us = BCN_INTV_ms*1000L };
enum { BCN_RESERVE_ms = 2120 }; // space reserved for beacon and NWK management
enum { BCN_GUARD_ms = 3000 }; // end of beacon period to prevent interference with beacon
enum { BCN_SLOT_SPAN_ms = 30 }; // 2^12 reception slots a this span
enum { BCN_WINDOW_ms = BCN_INTV_ms-(int)BCN_GUARD_ms-(int)BCN_RESERVE_ms };
enum { BCN_RESERVE_us = 2120000 };
enum { BCN_GUARD_us = 3000000 };
enum { BCN_SLOT_SPAN_us = 30000 };
#if defined(CFG_eu868) // ==============================================
enum _dr_eu868_t { DR_SF12=0, DR_SF11, DR_SF10, DR_SF9, DR_SF8, DR_SF7, DR_SF7B, DR_FSK, DR_NONE };
enum { DR_DFLTMIN = DR_SF7 };
enum { DR_PAGE = DR_PAGE_EU868 };
// Default frequency plan for EU 868MHz ISM band
// Bands:
// g1 : 1% 14dBm
// g2 : 0.1% 14dBm
// g3 : 10% 27dBm
// freq band datarates
enum { EU868_F1 = 868100000, // g1 SF7-12
EU868_F2 = 868300000, // g1 SF7-12 FSK SF7/250
EU868_F3 = 868500000, // g1 SF7-12
EU868_F4 = 868850000, // g2 SF7-12
EU868_F5 = 869050000, // g2 SF7-12
EU868_F6 = 869525000, // g3 SF7-12
EU868_J4 = 864100000, // g2 SF7-12 used during join
EU868_J5 = 864300000, // g2 SF7-12 ditto
EU868_J6 = 864500000, // g2 SF7-12 ditto
};
enum { EU868_FREQ_MIN = 863000000,
EU868_FREQ_MAX = 870000000 };
enum { CHNL_PING = 5 };
enum { FREQ_PING = EU868_F6 }; // default ping freq
enum { DR_PING = DR_SF9 }; // default ping DR
enum { CHNL_DNW2 = 5 };
enum { FREQ_DNW2 = EU868_F6 };
enum { DR_DNW2 = DR_SF12 };
enum { CHNL_BCN = 5 };
enum { FREQ_BCN = EU868_F6 };
enum { DR_BCN = DR_SF9 };
enum { AIRTIME_BCN = 144384 }; // micros
enum {
// Beacon frame format EU SF9
OFF_BCN_NETID = 0,
OFF_BCN_TIME = 3,
OFF_BCN_CRC1 = 7,
OFF_BCN_INFO = 8,
OFF_BCN_LAT = 9,
OFF_BCN_LON = 12,
OFF_BCN_CRC2 = 15,
LEN_BCN = 17
};
#elif defined(CFG_us915) // =========================================
enum _dr_us915_t { DR_SF10=0, DR_SF9, DR_SF8, DR_SF7, DR_SF8C, DR_NONE,
// Devices behind a router:
DR_SF12CR=8, DR_SF11CR, DR_SF10CR, DR_SF9CR, DR_SF8CR, DR_SF7CR };
enum { DR_DFLTMIN = DR_SF8C };
enum { DR_PAGE = DR_PAGE_US915 };
// Default frequency plan for US 915MHz
enum { US915_125kHz_UPFBASE = 902300000,
US915_125kHz_UPFSTEP = 200000,
US915_500kHz_UPFBASE = 903000000,
US915_500kHz_UPFSTEP = 1600000,
US915_500kHz_DNFBASE = 923300000,
US915_500kHz_DNFSTEP = 600000
};
enum { US915_FREQ_MIN = 902000000,
US915_FREQ_MAX = 928000000 };
enum { CHNL_PING = 0 }; // used only for default init of state (follows beacon - rotating)
enum { FREQ_PING = US915_500kHz_DNFBASE + CHNL_PING*US915_500kHz_DNFSTEP }; // default ping freq
enum { DR_PING = DR_SF10CR }; // default ping DR
enum { CHNL_DNW2 = 0 };
enum { FREQ_DNW2 = US915_500kHz_DNFBASE + CHNL_DNW2*US915_500kHz_DNFSTEP };
enum { DR_DNW2 = DR_SF12CR };
enum { CHNL_BCN = 0 }; // used only for default init of state (rotating beacon scheme)
enum { DR_BCN = DR_SF10CR };
enum { AIRTIME_BCN = 72192 }; // micros
enum {
// Beacon frame format US SF10
OFF_BCN_NETID = 0,
OFF_BCN_TIME = 3,
OFF_BCN_CRC1 = 7,
OFF_BCN_INFO = 9,
OFF_BCN_LAT = 10,
OFF_BCN_LON = 13,
OFF_BCN_RFU1 = 16,
OFF_BCN_CRC2 = 17,
LEN_BCN = 19
};
#endif // ===================================================
enum {
// Join Request frame format
OFF_JR_HDR = 0,
OFF_JR_ARTEUI = 1,
OFF_JR_DEVEUI = 9,
OFF_JR_DEVNONCE = 17,
OFF_JR_MIC = 19,
LEN_JR = 23
};
enum {
// Join Accept frame format
OFF_JA_HDR = 0,
OFF_JA_ARTNONCE = 1,
OFF_JA_NETID = 4,
OFF_JA_DEVADDR = 7,
OFF_JA_RFU = 11,
OFF_JA_DLSET = 11,
OFF_JA_RXDLY = 12,
OFF_CFLIST = 13,
LEN_JA = 17,
LEN_JAEXT = 17+16
};
enum {
// Data frame format
OFF_DAT_HDR = 0,
OFF_DAT_ADDR = 1,
OFF_DAT_FCT = 5,
OFF_DAT_SEQNO = 6,
OFF_DAT_OPTS = 8,
};
enum { MAX_LEN_PAYLOAD = MAX_LEN_FRAME-(int)OFF_DAT_OPTS-4 };
enum {
// Bitfields in frame format octet
HDR_FTYPE = 0xE0,
HDR_RFU = 0x1C,
HDR_MAJOR = 0x03
};
enum { HDR_FTYPE_DNFLAG = 0x20 }; // flags DN frame except for HDR_FTYPE_PROP
enum {
// Values of frame type bit field
HDR_FTYPE_JREQ = 0x00,
HDR_FTYPE_JACC = 0x20,
HDR_FTYPE_DAUP = 0x40, // data (unconfirmed) up
HDR_FTYPE_DADN = 0x60, // data (unconfirmed) dn
HDR_FTYPE_DCUP = 0x80, // data confirmed up
HDR_FTYPE_DCDN = 0xA0, // data confirmed dn
HDR_FTYPE_REJOIN = 0xC0, // rejoin for roaming
HDR_FTYPE_PROP = 0xE0
};
enum {
HDR_MAJOR_V1 = 0x00,
};
enum {
// Bitfields in frame control octet
FCT_ADREN = 0x80,
FCT_ADRARQ = 0x40,
FCT_ACK = 0x20,
FCT_MORE = 0x10, // also in DN direction: Class B indicator
FCT_OPTLEN = 0x0F,
};
enum {
// In UP direction: signals class B enabled
FCT_CLASSB = FCT_MORE
};
enum {
NWKID_MASK = (int)0xFE000000,
NWKID_BITS = 7
};
// MAC uplink commands downwlink too
enum {
// Class A
MCMD_LCHK_REQ = 0x02, // - link check request : -
MCMD_LADR_ANS = 0x03, // - link ADR answer : u1:7-3:RFU, 3/2/1: pow/DR/Ch ACK
MCMD_DCAP_ANS = 0x04, // - duty cycle answer : -
MCMD_DN2P_ANS = 0x05, // - 2nd DN slot status : u1:7-2:RFU 1/0:datarate/channel ack
MCMD_DEVS_ANS = 0x06, // - device status ans : u1:battery 0,1-254,255=?, u1:7-6:RFU,5-0:margin(-32..31)
MCMD_SNCH_ANS = 0x07, // - set new channel : u1: 7-2=RFU, 1/0:DR/freq ACK
// Class B
MCMD_PING_IND = 0x10, // - pingability indic : u1: 7=RFU, 6-4:interval, 3-0:datarate
MCMD_PING_ANS = 0x11, // - ack ping freq : u1: 7-1:RFU, 0:freq ok
MCMD_BCNI_REQ = 0x12, // - next beacon start : -
};
// MAC downlink commands
enum {
// Class A
MCMD_LCHK_ANS = 0x02, // link check answer : u1:margin 0-254,255=unknown margin / u1:gwcnt
MCMD_LADR_REQ = 0x03, // link ADR request : u1:DR/TXPow, u2:chmask, u1:chpage/repeat
MCMD_DCAP_REQ = 0x04, // duty cycle cap : u1:255 dead [7-4]:RFU, [3-0]:cap 2^-k
MCMD_DN2P_SET = 0x05, // 2nd DN window param: u1:7-4:RFU/3-0:datarate, u3:freq
MCMD_DEVS_REQ = 0x06, // device status req : -
MCMD_SNCH_REQ = 0x07, // set new channel : u1:chidx, u3:freq, u1:DRrange
// Class B
MCMD_PING_SET = 0x11, // set ping freq : u3: freq
MCMD_BCNI_ANS = 0x12, // next beacon start : u2: delay(in TUNIT millis), u1:channel
};
enum {
MCMD_BCNI_TUNIT = 30 // time unit of delay value in millis
};
enum {
MCMD_LADR_ANS_RFU = 0xF8, // RFU bits
MCMD_LADR_ANS_POWACK = 0x04, // 0=not supported power level
MCMD_LADR_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_LADR_ANS_CHACK = 0x01, // 0=unknown channel enabled
};
enum {
MCMD_DN2P_ANS_RFU = 0xFC, // RFU bits
MCMD_DN2P_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_DN2P_ANS_CHACK = 0x01, // 0=unknown channel enabled
};
enum {
MCMD_SNCH_ANS_RFU = 0xFC, // RFU bits
MCMD_SNCH_ANS_DRACK = 0x02, // 0=unknown data rate
MCMD_SNCH_ANS_FQACK = 0x01, // 0=rejected channel frequency
};
enum {
MCMD_PING_ANS_RFU = 0xFE,
MCMD_PING_ANS_FQACK = 0x01
};
enum {
MCMD_DEVS_EXT_POWER = 0x00, // external power supply
MCMD_DEVS_BATT_MIN = 0x01, // min battery value
MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
};
// Bit fields byte#3 of MCMD_LADR_REQ payload
enum {
MCMD_LADR_CHP_125ON = 0x60, // special channel page enable, bits applied to 64..71
MCMD_LADR_CHP_125OFF = 0x70, // ditto
MCMD_LADR_N3RFU_MASK = 0x80,
MCMD_LADR_CHPAGE_MASK = 0xF0,
MCMD_LADR_REPEAT_MASK = 0x0F,
MCMD_LADR_REPEAT_1 = 0x01,
MCMD_LADR_CHPAGE_1 = 0x10
};
// Bit fields byte#0 of MCMD_LADR_REQ payload
enum {
MCMD_LADR_DR_MASK = 0xF0,
MCMD_LADR_POW_MASK = 0x0F,
MCMD_LADR_DR_SHIFT = 4,
MCMD_LADR_POW_SHIFT = 0,
#if defined(CFG_eu868)
MCMD_LADR_SF12 = DR_SF12<<4,
MCMD_LADR_SF11 = DR_SF11<<4,
MCMD_LADR_SF10 = DR_SF10<<4,
MCMD_LADR_SF9 = DR_SF9 <<4,
MCMD_LADR_SF8 = DR_SF8 <<4,
MCMD_LADR_SF7 = DR_SF7 <<4,
MCMD_LADR_SF7B = DR_SF7B<<4,
MCMD_LADR_FSK = DR_FSK <<4,
MCMD_LADR_20dBm = 0,
MCMD_LADR_14dBm = 1,
MCMD_LADR_11dBm = 2,
MCMD_LADR_8dBm = 3,
MCMD_LADR_5dBm = 4,
MCMD_LADR_2dBm = 5,
#elif defined(CFG_us915)
MCMD_LADR_SF10 = DR_SF10<<4,
MCMD_LADR_SF9 = DR_SF9 <<4,
MCMD_LADR_SF8 = DR_SF8 <<4,
MCMD_LADR_SF7 = DR_SF7 <<4,
MCMD_LADR_SF8C = DR_SF8C<<4,
MCMD_LADR_SF12CR = DR_SF12CR<<4,
MCMD_LADR_SF11CR = DR_SF11CR<<4,
MCMD_LADR_SF10CR = DR_SF10CR<<4,
MCMD_LADR_SF9CR = DR_SF9CR<<4,
MCMD_LADR_SF8CR = DR_SF8CR<<4,
MCMD_LADR_SF7CR = DR_SF7CR<<4,
MCMD_LADR_30dBm = 0,
MCMD_LADR_28dBm = 1,
MCMD_LADR_26dBm = 2,
MCMD_LADR_24dBm = 3,
MCMD_LADR_22dBm = 4,
MCMD_LADR_20dBm = 5,
MCMD_LADR_18dBm = 6,
MCMD_LADR_16dBm = 7,
MCMD_LADR_14dBm = 8,
MCMD_LADR_12dBm = 9,
MCMD_LADR_10dBm = 10
#endif
};
// Device address
typedef u4_t devaddr_t;
// RX quality (device)
enum { RSSI_OFF=64, SNR_SCALEUP=4 };
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); }
inline rps_t setBw (rps_t params, bw_t cr) { return (rps_t)((params & ~0x18) | (cr<<3)); }
inline cr_t getCr (rps_t params) { return (cr_t)((params >> 5) & 0x3); }
inline rps_t setCr (rps_t params, cr_t cr) { return (rps_t)((params & ~0x60) | (cr<<5)); }
inline int getNocrc(rps_t params) { return ((params >> 7) & 0x1); }
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)))
// 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; }
extern const u1_t _DR2RPS_CRC[];
inline rps_t updr2rps (dr_t dr) { return (rps_t)_DR2RPS_CRC[dr+1]; }
inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); }
inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; }
inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; }
inline dr_t incDR (dr_t dr) { return _DR2RPS_CRC[dr+2]==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate
inline dr_t decDR (dr_t dr) { return _DR2RPS_CRC[dr ]==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate
inline dr_t assertDR (dr_t dr) { return _DR2RPS_CRC[dr+1]==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR
inline bit_t validDR (dr_t dr) { return _DR2RPS_CRC[dr+1]!=ILLEGAL_RPS; } // in range
inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps
//
// BEG: Keep in sync with lorabase.hpp
// ================================================================================
// Convert between dBm values and power codes (MCMD_LADR_XdBm)
s1_t pow2dBm (u1_t mcmd_ladr_p1);
// Calculate airtime
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_

150
src/lmic/oslmic.c Executable file
View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* All rights reserved.
*
* 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 <organization> 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 <COPYRIGHT HOLDER> 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 "lmic.h"
#include "../hal/hal_esp32.h"
#include <stdbool.h>
// RUNTIME STATE
static struct {
osjob_t* scheduledjobs;
osjob_t* runnablejobs;
} OS;
void os_init () {
memset(&OS, 0x00, sizeof(OS));
hal_init();
radio_init();
LMIC_init();
}
ostime_t os_getTime () {
return hal_ticks();
}
static u1_t unlinkjob (osjob_t** pnext, osjob_t* job) {
for( ; *pnext; pnext = &((*pnext)->next)) {
if(*pnext == job) { // unlink
*pnext = job->next;
return 1;
}
}
return 0;
}
// clear scheduled job
void os_clearCallback (osjob_t* job) {
hal_disableIRQs();
u1_t res = unlinkjob(&OS.scheduledjobs, job);
if (res)
unlinkjob(&OS.runnablejobs, job);
hal_enableIRQs();
#if LMIC_DEBUG_LEVEL > 1
if (res)
lmic_printf("%lu: Cleared job %p\n", os_getTime(), job);
#endif
}
// schedule immediately runnable job
void os_setCallback (osjob_t* job, osjobcb_t cb) {
osjob_t** pnext;
hal_disableIRQs();
// remove if job was already queued
os_clearCallback(job);
// fill-in job
job->func = cb;
job->next = NULL;
// add to end of run queue
for(pnext=&OS.runnablejobs; *pnext; pnext=&((*pnext)->next));
*pnext = job;
hal_enableIRQs();
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: Scheduled job %p, cb %p ASAP\n", os_getTime(), job, cb);
#endif
}
// schedule timed job
void os_setTimedCallback (osjob_t* job, ostime_t time, osjobcb_t cb) {
osjob_t** pnext;
hal_disableIRQs();
// remove if job was already queued
os_clearCallback(job);
// fill-in job
job->deadline = time;
job->func = cb;
job->next = NULL;
// insert into schedule
for(pnext=&OS.scheduledjobs; *pnext; pnext=&((*pnext)->next)) {
if((*pnext)->deadline - time > 0) { // (cmp diff, not abs!)
// enqueue before next element and stop
job->next = *pnext;
break;
}
}
*pnext = job;
hal_enableIRQs();
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: Scheduled job %p, cb %p at %lu\n", os_getTime(), job, cb, time);
#endif
}
// execute jobs from timer and from run queue
void os_runloop () {
while(1) {
os_runloop_once();
}
}
void os_runloop_once() {
#if LMIC_DEBUG_LEVEL > 1
bool has_deadline = false;
#endif
osjob_t* j = NULL;
hal_enterCriticalSection();
// check for runnable jobs
if(OS.runnablejobs) {
j = OS.runnablejobs;
OS.runnablejobs = j->next;
} else if(OS.scheduledjobs && hal_checkTimer(OS.scheduledjobs->deadline)) { // check for expired timed jobs
j = OS.scheduledjobs;
OS.scheduledjobs = j->next;
#if LMIC_DEBUG_LEVEL > 1
has_deadline = true;
#endif
}
if(j) { // run job callback
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: Running job %p, cb %p, deadline %lu\n", os_getTime(), j, j->func, has_deadline ? j->deadline : 0);
#endif
j->func(j);
hal_leaveCriticalSection();
} else { // nothing pending
hal_leaveCriticalSection();
hal_sleep(); // wake by irq (timer already restarted)
}
}

242
src/lmic/oslmic.h Executable file
View File

@ -0,0 +1,242 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* All rights reserved.
*
* 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 <organization> 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 <COPYRIGHT HOLDER> 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.
*/
//! \file
#ifndef _oslmic_h_
#define _oslmic_h_
// Dependencies required for the LoRa MAC in C to run.
// These settings can be adapted to the underlying system.
// You should not, however, change the lmic.[hc]
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
//================================================================================
//================================================================================
// Target platform as C library
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 unsigned long u4_t;
typedef long s4_t;
typedef unsigned int uint;
typedef const char* str_t;
#include <string.h>
#include "hal.h"
#define EV(a,b,c) /**/
#define DO_DEVDB(field1,field2) /**/
#if !defined(CFG_noassert)
#define ASSERT(cond) if(!(cond)) hal_failed(__FILE__, __LINE__)
#else
#define ASSERT(cond) /**/
#endif
#define os_clearMem(a,b) memset(a,0,b)
#define os_copyMem(a,b,c) memcpy(a,b,c)
typedef struct osjob_t osjob_t;
typedef struct band_t band_t;
typedef struct chnldef_t chnldef_t;
typedef struct rxsched_t rxsched_t;
typedef struct bcninfo_t bcninfo_t;
typedef const u1_t* xref2cu1_t;
typedef u1_t* xref2u1_t;
#define TYPEDEF_xref2rps_t typedef rps_t* xref2rps_t
#define TYPEDEF_xref2rxsched_t typedef rxsched_t* xref2rxsched_t
#define TYPEDEF_xref2chnldef_t typedef chnldef_t* xref2chnldef_t
#define TYPEDEF_xref2band_t typedef band_t* xref2band_t
#define TYPEDEF_xref2osjob_t typedef osjob_t* xref2osjob_t
#define SIZEOFEXPR(x) sizeof(x)
#define ON_LMIC_EVENT(ev) onEvent(ev)
#define DECL_ON_LMIC_EVENT void onEvent(ev_t e)
typedef s4_t ostime_t;
extern u4_t AESAUX[];
extern u4_t AESKEY[];
#define AESkey ((u1_t*)AESKEY)
#define AESaux ((u1_t*)AESAUX)
#define FUNC_ADDR(func) (&(func))
u1_t radio_rand1 (void);
#define os_getRndU1() radio_rand1()
#define DEFINE_LMIC struct lmic_t LMIC
#define DECLARE_LMIC extern struct lmic_t LMIC
void radio_init (void);
void radio_irq_handler (u1_t dio, ostime_t t);
void os_init (void);
void os_runloop (void);
void os_runloop_once();
//================================================================================
#ifndef RX_RAMPUP
#define RX_RAMPUP (us2osticks(2000))
#endif
#ifndef TX_RAMPUP
#define TX_RAMPUP (us2osticks(2000))
#endif
#ifndef OSTICKS_PER_SEC
#define OSTICKS_PER_SEC 32768
#elif OSTICKS_PER_SEC < 10000 || OSTICKS_PER_SEC > 64516
#error Illegal OSTICKS_PER_SEC - must be in range [10000:64516]. One tick must be 15.5us .. 100us long.
#endif
#if !HAS_ostick_conv
#define us2osticks(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC) / 1000000))
#define ms2osticks(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC) / 1000))
#define sec2osticks(sec) ((ostime_t)( (int64_t)(sec) * OSTICKS_PER_SEC))
#define osticks2ms(os) ((s4_t)(((os)*(int64_t)1000 ) / OSTICKS_PER_SEC))
#define osticks2us(os) ((s4_t)(((os)*(int64_t)1000000 ) / OSTICKS_PER_SEC))
// Special versions
#define us2osticksCeil(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 999999) / 1000000))
#define us2osticksRound(us) ((ostime_t)( ((int64_t)(us) * OSTICKS_PER_SEC + 500000) / 1000000))
#define ms2osticksCeil(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 999) / 1000))
#define ms2osticksRound(ms) ((ostime_t)( ((int64_t)(ms) * OSTICKS_PER_SEC + 500) / 1000))
#endif
struct osjob_t; // fwd decl.
typedef void (*osjobcb_t) (struct osjob_t*);
struct osjob_t {
struct osjob_t* next;
ostime_t deadline;
osjobcb_t func;
};
TYPEDEF_xref2osjob_t;
#ifndef HAS_os_calls
#ifndef os_getDevKey
void os_getDevKey (xref2u1_t buf);
#endif
#ifndef os_getArtEui
void os_getArtEui (xref2u1_t buf);
#endif
#ifndef os_getDevEui
void os_getDevEui (xref2u1_t buf);
#endif
#ifndef os_setCallback
void os_setCallback (xref2osjob_t job, osjobcb_t cb);
#endif
#ifndef os_setTimedCallback
void os_setTimedCallback (xref2osjob_t job, ostime_t time, osjobcb_t cb);
#endif
#ifndef os_clearCallback
void os_clearCallback (xref2osjob_t job);
#endif
#ifndef os_getTime
ostime_t os_getTime (void);
#endif
#ifndef os_getTimeSecs
uint os_getTimeSecs (void);
#endif
#ifndef os_radio
void os_radio (u1_t mode);
#endif
#ifndef os_getBattLevel
u1_t os_getBattLevel (void);
#endif
#ifndef os_rlsbf4
//! Read 32-bit quantity from given pointer in little endian byte order.
u4_t os_rlsbf4 (xref2cu1_t buf);
#endif
#ifndef os_wlsbf4
//! Write 32-bit quntity into buffer in little endian byte order.
void os_wlsbf4 (xref2u1_t buf, u4_t value);
#endif
#ifndef os_rmsbf4
//! Read 32-bit quantity from given pointer in big endian byte order.
u4_t os_rmsbf4 (xref2cu1_t buf);
#endif
#ifndef os_wmsbf4
//! Write 32-bit quntity into buffer in big endian byte order.
void os_wmsbf4 (xref2u1_t buf, u4_t value);
#endif
#ifndef os_rlsbf2
//! Read 16-bit quantity from given pointer in little endian byte order.
u2_t os_rlsbf2 (xref2cu1_t buf);
#endif
#ifndef os_wlsbf2
//! Write 16-bit quntity into buffer in little endian byte order.
void os_wlsbf2 (xref2u1_t buf, u2_t value);
#endif
//! Get random number (default impl for u2_t).
#ifndef os_getRndU2
#define os_getRndU2() ((u2_t)((os_getRndU1()<<8)|os_getRndU1()))
#endif
#ifndef os_crc16
u2_t os_crc16 (xref2u1_t d, uint len);
#endif
#endif // !HAS_os_calls
#define lmic_printf printf
// ======================================================================
// AES support
// !!Keep in sync with lorabase.hpp!!
#ifndef AES_ENC // if AES_ENC is defined as macro all other values must be too
#define AES_ENC 0x00
#define AES_DEC 0x01
#define AES_MIC 0x02
#define AES_CTR 0x04
#define AES_MICNOAUX 0x08
#endif
#ifndef AESkey // if AESkey is defined as macro all other values must be too
extern xref2u1_t AESkey;
extern xref2u1_t AESaux;
#endif
#ifndef os_aes
u4_t os_aes (u1_t mode, xref2u1_t buf, u2_t len);
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _oslmic_h_

896
src/lmic/radio.c Executable file
View File

@ -0,0 +1,896 @@
/*
* Copyright (c) 2014-2016 IBM Corporation.
* All rights reserved.
*
* 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 <organization> 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 <COPYRIGHT HOLDER> 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 "rom/ets_sys.h"
#include "lmic.h"
// ----------------------------------------
// Registers Mapping
#define RegFifo 0x00 // common
#define RegOpMode 0x01 // common
#define FSKRegBitrateMsb 0x02
#define FSKRegBitrateLsb 0x03
#define FSKRegFdevMsb 0x04
#define FSKRegFdevLsb 0x05
#define RegFrfMsb 0x06 // common
#define RegFrfMid 0x07 // common
#define RegFrfLsb 0x08 // common
#define RegPaConfig 0x09 // common
#define RegPaRamp 0x0A // common
#define RegOcp 0x0B // common
#define RegLna 0x0C // common
#define FSKRegRxConfig 0x0D
#define LORARegFifoAddrPtr 0x0D
#define FSKRegRssiConfig 0x0E
#define LORARegFifoTxBaseAddr 0x0E
#define FSKRegRssiCollision 0x0F
#define LORARegFifoRxBaseAddr 0x0F
#define FSKRegRssiThresh 0x10
#define LORARegFifoRxCurrentAddr 0x10
#define FSKRegRssiValue 0x11
#define LORARegIrqFlagsMask 0x11
#define FSKRegRxBw 0x12
#define LORARegIrqFlags 0x12
#define FSKRegAfcBw 0x13
#define LORARegRxNbBytes 0x13
#define FSKRegOokPeak 0x14
#define LORARegRxHeaderCntValueMsb 0x14
#define FSKRegOokFix 0x15
#define LORARegRxHeaderCntValueLsb 0x15
#define FSKRegOokAvg 0x16
#define LORARegRxPacketCntValueMsb 0x16
#define LORARegRxpacketCntValueLsb 0x17
#define LORARegModemStat 0x18
#define LORARegPktSnrValue 0x19
#define FSKRegAfcFei 0x1A
#define LORARegPktRssiValue 0x1A
#define FSKRegAfcMsb 0x1B
#define LORARegRssiValue 0x1B
#define FSKRegAfcLsb 0x1C
#define LORARegHopChannel 0x1C
#define FSKRegFeiMsb 0x1D
#define LORARegModemConfig1 0x1D
#define FSKRegFeiLsb 0x1E
#define LORARegModemConfig2 0x1E
#define FSKRegPreambleDetect 0x1F
#define LORARegSymbTimeoutLsb 0x1F
#define FSKRegRxTimeout1 0x20
#define LORARegPreambleMsb 0x20
#define FSKRegRxTimeout2 0x21
#define LORARegPreambleLsb 0x21
#define FSKRegRxTimeout3 0x22
#define LORARegPayloadLength 0x22
#define FSKRegRxDelay 0x23
#define LORARegPayloadMaxLength 0x23
#define FSKRegOsc 0x24
#define LORARegHopPeriod 0x24
#define FSKRegPreambleMsb 0x25
#define LORARegFifoRxByteAddr 0x25
#define LORARegModemConfig3 0x26
#define FSKRegPreambleLsb 0x26
#define FSKRegSyncConfig 0x27
#define LORARegFeiMsb 0x28
#define FSKRegSyncValue1 0x28
#define LORAFeiMib 0x29
#define FSKRegSyncValue2 0x29
#define LORARegFeiLsb 0x2A
#define FSKRegSyncValue3 0x2A
#define FSKRegSyncValue4 0x2B
#define LORARegRssiWideband 0x2C
#define FSKRegSyncValue5 0x2C
#define FSKRegSyncValue6 0x2D
#define FSKRegSyncValue7 0x2E
#define FSKRegSyncValue8 0x2F
#define FSKRegPacketConfig1 0x30
#define FSKRegPacketConfig2 0x31
#define LORARegDetectOptimize 0x31
#define FSKRegPayloadLength 0x32
#define FSKRegNodeAdrs 0x33
#define LORARegInvertIQ 0x33
#define FSKRegBroadcastAdrs 0x34
#define FSKRegFifoThresh 0x35
#define FSKRegSeqConfig1 0x36
#define FSKRegSeqConfig2 0x37
#define LORARegDetectionThreshold 0x37
#define FSKRegTimerResol 0x38
#define FSKRegTimer1Coef 0x39
#define LORARegSyncWord 0x39
#define FSKRegTimer2Coef 0x3A
#define FSKRegImageCal 0x3B
#define FSKRegTemp 0x3C
#define FSKRegLowBat 0x3D
#define FSKRegIrqFlags1 0x3E
#define FSKRegIrqFlags2 0x3F
#define RegDioMapping1 0x40 // common
#define RegDioMapping2 0x41 // common
#define RegVersion 0x42 // common
// #define RegAgcRef 0x43 // common
// #define RegAgcThresh1 0x44 // common
// #define RegAgcThresh2 0x45 // common
// #define RegAgcThresh3 0x46 // common
// #define RegPllHop 0x4B // common
// #define RegTcxo 0x58 // common
#define RegPaDac 0x5A // common
// #define RegPll 0x5C // common
// #define RegPllLowPn 0x5E // common
// #define RegFormerTemp 0x6C // common
// #define RegBitRateFrac 0x70 // common
// ----------------------------------------
// spread factors and mode for RegModemConfig2
#define SX1272_MC2_FSK 0x00
#define SX1272_MC2_SF7 0x70
#define SX1272_MC2_SF8 0x80
#define SX1272_MC2_SF9 0x90
#define SX1272_MC2_SF10 0xA0
#define SX1272_MC2_SF11 0xB0
#define SX1272_MC2_SF12 0xC0
// bandwidth for RegModemConfig1
#define SX1272_MC1_BW_125 0x00
#define SX1272_MC1_BW_250 0x40
#define SX1272_MC1_BW_500 0x80
// coding rate for RegModemConfig1
#define SX1272_MC1_CR_4_5 0x08
#define SX1272_MC1_CR_4_6 0x10
#define SX1272_MC1_CR_4_7 0x18
#define SX1272_MC1_CR_4_8 0x20
#define SX1272_MC1_IMPLICIT_HEADER_MODE_ON 0x04 // required for receive
#define SX1272_MC1_RX_PAYLOAD_CRCON 0x02
#define SX1272_MC1_LOW_DATA_RATE_OPTIMIZE 0x01 // mandated for SF11 and SF12
// transmit power configuration for RegPaConfig
#define SX1272_PAC_PA_SELECT_PA_BOOST 0x80
#define SX1272_PAC_PA_SELECT_RFIO_PIN 0x00
// sx1276 RegModemConfig1
#define SX1276_MC1_BW_125 0x70
#define SX1276_MC1_BW_250 0x80
#define SX1276_MC1_BW_500 0x90
#define SX1276_MC1_CR_4_5 0x02
#define SX1276_MC1_CR_4_6 0x04
#define SX1276_MC1_CR_4_7 0x06
#define SX1276_MC1_CR_4_8 0x08
#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01
// sx1276 RegModemConfig2
#define SX1276_MC2_RX_PAYLOAD_CRCON 0x04
// sx1276 RegModemConfig3
#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE 0x08
#define SX1276_MC3_AGCAUTO 0x04
// preamble for lora networks (nibbles swapped)
#define LORA_MAC_PREAMBLE 0x34
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1 0x0A
#ifdef CFG_sx1276_radio
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x70
#elif CFG_sx1272_radio
#define RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2 0x74
#endif
// ----------------------------------------
// Constants for radio registers
#define OPMODE_LORA 0x80
#define OPMODE_MASK 0x07
#define OPMODE_SLEEP 0x00
#define OPMODE_STANDBY 0x01
#define OPMODE_FSTX 0x02
#define OPMODE_TX 0x03
#define OPMODE_FSRX 0x04
#define OPMODE_RX 0x05
#define OPMODE_RX_SINGLE 0x06
#define OPMODE_CAD 0x07
// ----------------------------------------
// Bits masking the corresponding IRQs from the radio
#define IRQ_LORA_RXTOUT_MASK 0x80
#define IRQ_LORA_RXDONE_MASK 0x40
#define IRQ_LORA_CRCERR_MASK 0x20
#define IRQ_LORA_HEADER_MASK 0x10
#define IRQ_LORA_TXDONE_MASK 0x08
#define IRQ_LORA_CDDONE_MASK 0x04
#define IRQ_LORA_FHSSCH_MASK 0x02
#define IRQ_LORA_CDDETD_MASK 0x01
#define IRQ_FSK1_MODEREADY_MASK 0x80
#define IRQ_FSK1_RXREADY_MASK 0x40
#define IRQ_FSK1_TXREADY_MASK 0x20
#define IRQ_FSK1_PLLLOCK_MASK 0x10
#define IRQ_FSK1_RSSI_MASK 0x08
#define IRQ_FSK1_TIMEOUT_MASK 0x04
#define IRQ_FSK1_PREAMBLEDETECT_MASK 0x02
#define IRQ_FSK1_SYNCADDRESSMATCH_MASK 0x01
#define IRQ_FSK2_FIFOFULL_MASK 0x80
#define IRQ_FSK2_FIFOEMPTY_MASK 0x40
#define IRQ_FSK2_FIFOLEVEL_MASK 0x20
#define IRQ_FSK2_FIFOOVERRUN_MASK 0x10
#define IRQ_FSK2_PACKETSENT_MASK 0x08
#define IRQ_FSK2_PAYLOADREADY_MASK 0x04
#define IRQ_FSK2_CRCOK_MASK 0x02
#define IRQ_FSK2_LOWBAT_MASK 0x01
// ----------------------------------------
// DIO function mappings D0D1D2D3
#define MAP_DIO0_LORA_RXDONE 0x00 // 00------
#define MAP_DIO0_LORA_TXDONE 0x40 // 01------
#define MAP_DIO1_LORA_RXTOUT 0x00 // --00----
#define MAP_DIO1_LORA_NOP 0x30 // --11----
#define MAP_DIO2_LORA_NOP 0xC0 // ----11--
#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready)
#define MAP_DIO1_FSK_NOP 0x30 // --11----
#define MAP_DIO2_FSK_TXNOP 0x04 // ----01--
#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10--
// FSK IMAGECAL defines
#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F
#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80
#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default
#define RF_IMAGECAL_IMAGECAL_MASK 0xBF
#define RF_IMAGECAL_IMAGECAL_START 0x40
#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20
#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default
// RADIO STATE
// (initialized by radio_init(), used by radio_rand1())
static u1_t randbuf[16];
#ifdef CFG_sx1276_radio
#define LNA_RX_GAIN (0x20|0x1)
#elif CFG_sx1272_radio
#define LNA_RX_GAIN (0x20|0x03)
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif
static void writeReg (u1_t addr, u1_t data ) {
hal_spi_write(addr | 0x80, &data, 1);
/*
hal_pin_nss(0);
hal_spi(addr | 0x80);
hal_spi(data);
hal_pin_nss(1);
*/
}
static u1_t readReg (u1_t addr) {
u1_t buf[1];
hal_spi_read(addr & 0x7f, buf, 1);
return buf[0];
/*
hal_pin_nss(0);
hal_spi(addr & 0x7F);
u1_t val = hal_spi(0x00);
hal_pin_nss(1);
return val;
*/
}
static void writeBuf (u1_t addr, xref2u1_t buf, u1_t len) {
hal_spi_write(addr | 0x80, buf, len);
/*
hal_pin_nss(0);
hal_spi(addr | 0x80);
for (u1_t i=0; i<len; i++) {
hal_spi(buf[i]);
}
hal_pin_nss(1);
*/
}
static void readBuf (u1_t addr, xref2u1_t buf, u1_t len) {
hal_spi_read(addr & 0x7f, buf, len);
/*
hal_pin_nss(0);
hal_spi(addr & 0x7F);
for (u1_t i=0; i<len; i++) {
buf[i] = hal_spi(0x00);
}
hal_pin_nss(1);
*/
}
static void opmode (u1_t mode) {
writeReg(RegOpMode, (readReg(RegOpMode) & ~OPMODE_MASK) | mode);
}
static void opmodeLora() {
u1_t u = OPMODE_LORA;
#ifdef CFG_sx1276_radio
u |= 0x8; // TBD: sx1276 high freq
#endif
writeReg(RegOpMode, u);
}
static void opmodeFSK() {
u1_t u = 0;
#ifdef CFG_sx1276_radio
u |= 0x8; // TBD: sx1276 high freq
#endif
writeReg(RegOpMode, u);
}
// configure LoRa modem (cfg1, cfg2)
static void configLoraModem () {
sf_t sf = getSf(LMIC.rps);
#ifdef CFG_sx1276_radio
u1_t mc1 = 0, mc2 = 0, mc3 = 0;
switch (getBw(LMIC.rps)) {
case BW125: mc1 |= SX1276_MC1_BW_125; break;
case BW250: mc1 |= SX1276_MC1_BW_250; break;
case BW500: mc1 |= SX1276_MC1_BW_500; break;
default:
ASSERT(0);
}
switch( getCr(LMIC.rps) ) {
case CR_4_5: mc1 |= SX1276_MC1_CR_4_5; break;
case CR_4_6: mc1 |= SX1276_MC1_CR_4_6; break;
case CR_4_7: mc1 |= SX1276_MC1_CR_4_7; break;
case CR_4_8: mc1 |= SX1276_MC1_CR_4_8; break;
default:
ASSERT(0);
}
if (getIh(LMIC.rps)) {
mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
}
// set ModemConfig1
writeReg(LORARegModemConfig1, mc1);
mc2 = (SX1272_MC2_SF7 + ((sf-1)<<4));
if (getNocrc(LMIC.rps) == 0) {
mc2 |= SX1276_MC2_RX_PAYLOAD_CRCON;
}
writeReg(LORARegModemConfig2, mc2);
mc3 = SX1276_MC3_AGCAUTO;
if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
mc3 |= SX1276_MC3_LOW_DATA_RATE_OPTIMIZE;
}
writeReg(LORARegModemConfig3, mc3);
#elif CFG_sx1272_radio
u1_t mc1 = (getBw(LMIC.rps)<<6);
switch( getCr(LMIC.rps) ) {
case CR_4_5: mc1 |= SX1272_MC1_CR_4_5; break;
case CR_4_6: mc1 |= SX1272_MC1_CR_4_6; break;
case CR_4_7: mc1 |= SX1272_MC1_CR_4_7; break;
case CR_4_8: mc1 |= SX1272_MC1_CR_4_8; break;
}
if ((sf == SF11 || sf == SF12) && getBw(LMIC.rps) == BW125) {
mc1 |= SX1272_MC1_LOW_DATA_RATE_OPTIMIZE;
}
if (getNocrc(LMIC.rps) == 0) {
mc1 |= SX1272_MC1_RX_PAYLOAD_CRCON;
}
if (getIh(LMIC.rps)) {
mc1 |= SX1272_MC1_IMPLICIT_HEADER_MODE_ON;
writeReg(LORARegPayloadLength, getIh(LMIC.rps)); // required length
}
// set ModemConfig1
writeReg(LORARegModemConfig1, mc1);
// set ModemConfig2 (sf, AgcAutoOn=1 SymbTimeoutHi=00)
writeReg(LORARegModemConfig2, (SX1272_MC2_SF7 + ((sf-1)<<4)) | 0x04);
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif /* CFG_sx1272_radio */
}
static void configChannel () {
// set frequency: FQ = (FRF * 32 Mhz) / (2 ^ 19)
uint64_t frf = ((uint64_t)LMIC.freq << 19) / 32000000;
writeReg(RegFrfMsb, (u1_t)(frf>>16));
writeReg(RegFrfMid, (u1_t)(frf>> 8));
writeReg(RegFrfLsb, (u1_t)(frf>> 0));
}
static void configPower () {
#ifdef CFG_sx1276_radio
// no boost used for now
s1_t pw = (s1_t)LMIC.txpow;
if(pw >= 17) {
pw = 15;
} else if(pw < 2) {
pw = 2;
}
// check board type for BOOST pin
writeReg(RegPaConfig, (u1_t)(0x80|(pw&0xf)));
writeReg(RegPaDac, readReg(RegPaDac)|0x4);
#elif CFG_sx1272_radio
// set PA config (2-17 dBm using PA_BOOST)
s1_t pw = (s1_t)LMIC.txpow;
if(pw > 17) {
pw = 17;
} else if(pw < 2) {
pw = 2;
}
writeReg(RegPaConfig, (u1_t)(0x80|(pw-2)));
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif /* CFG_sx1272_radio */
}
static void txfsk () {
// select FSK modem (from sleep mode)
writeReg(RegOpMode, 0x10); // FSK, BT=0.5
ASSERT(readReg(RegOpMode) == 0x10);
// enter standby mode (required for FIFO loading))
opmode(OPMODE_STANDBY);
// set bitrate
writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
writeReg(FSKRegBitrateLsb, 0x80);
// set frequency deviation
writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
writeReg(FSKRegFdevLsb, 0x99);
// frame and packet handler settings
writeReg(FSKRegPreambleMsb, 0x00);
writeReg(FSKRegPreambleLsb, 0x05);
writeReg(FSKRegSyncConfig, 0x12);
writeReg(FSKRegPacketConfig1, 0xD0);
writeReg(FSKRegPacketConfig2, 0x40);
writeReg(FSKRegSyncValue1, 0xC1);
writeReg(FSKRegSyncValue2, 0x94);
writeReg(FSKRegSyncValue3, 0xC1);
// configure frequency
configChannel();
// configure output power
configPower();
// set the IRQ mapping DIO0=PacketSent DIO1=NOP DIO2=NOP
writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TXNOP);
// initialize the payload size and address pointers
writeReg(FSKRegPayloadLength, LMIC.dataLen+1); // (insert length byte into payload))
// download length byte and buffer to the radio FIFO
writeReg(RegFifo, LMIC.dataLen);
writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// enable antenna switch for TX
hal_pin_rxtx(1);
// now we actually start the transmission
opmode(OPMODE_TX);
}
static void txlora () {
// select LoRa modem (from sleep mode)
//writeReg(RegOpMode, OPMODE_LORA);
opmodeLora();
// can take a moment to change; so try ten times
u1_t reg;
for (int i = 0; i < 10; i++)
{
reg = readReg(RegOpMode);
if ((reg & OPMODE_LORA) != 0)
break;
ets_delay_us(100);
}
ASSERT((reg & OPMODE_LORA) != 0);
// enter standby mode (required for FIFO loading))
opmode(OPMODE_STANDBY);
// configure LoRa modem (cfg1, cfg2)
configLoraModem();
// configure frequency
configChannel();
// configure output power
writeReg(RegPaRamp, (readReg(RegPaRamp) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
configPower();
// set sync word
writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
writeReg(RegDioMapping1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP);
// clear all radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF);
// mask all IRQs but TxDone
writeReg(LORARegIrqFlagsMask, ~IRQ_LORA_TXDONE_MASK);
// initialize the payload size and address pointers
writeReg(LORARegFifoTxBaseAddr, 0x00);
writeReg(LORARegFifoAddrPtr, 0x00);
writeReg(LORARegPayloadLength, LMIC.dataLen);
// download buffer to the radio FIFO
writeBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// enable antenna switch for TX
hal_pin_rxtx(1);
// now we actually start the transmission
opmode(OPMODE_TX);
#if LMIC_DEBUG_LEVEL > 0
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
u1_t bw = getBw(LMIC.rps);
u1_t cr = getCr(LMIC.rps);
lmic_printf("%lu: TXMODE, freq=%lu, len=%d, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
os_getTime(), LMIC.freq, LMIC.dataLen, sf,
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
getIh(LMIC.rps)
);
#endif
}
// start transmitter (buf=LMIC.frame, len=LMIC.dataLen)
static void starttx () {
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
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.
}
enum { RXMODE_SINGLE, RXMODE_SCAN, RXMODE_RSSI };
static const u1_t rxlorairqmask[] = {
[RXMODE_SINGLE] = IRQ_LORA_RXDONE_MASK|IRQ_LORA_RXTOUT_MASK,
[RXMODE_SCAN] = IRQ_LORA_RXDONE_MASK,
[RXMODE_RSSI] = 0x00,
};
// start LoRa receiver (time=LMIC.rxtime, timeout=LMIC.rxsyms, result=LMIC.frame[LMIC.dataLen])
static void rxlora (u1_t rxmode) {
// select LoRa modem (from sleep mode)
opmodeLora();
ASSERT((readReg(RegOpMode) & OPMODE_LORA) != 0);
// enter standby mode (warm up))
opmode(OPMODE_STANDBY);
// don't use MAC settings at startup
if(rxmode == RXMODE_RSSI) { // use fixed settings for rssi scan
writeReg(LORARegModemConfig1, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG1);
writeReg(LORARegModemConfig2, RXLORA_RXMODE_RSSI_REG_MODEM_CONFIG2);
} else { // single or continuous rx mode
// configure LoRa modem (cfg1, cfg2)
configLoraModem();
// configure frequency
configChannel();
}
// set LNA gain
writeReg(RegLna, LNA_RX_GAIN);
// set max payload size
writeReg(LORARegPayloadMaxLength, 64);
#if !defined(DISABLE_INVERT_IQ_ON_RX)
// use inverted I/Q signal (prevent mote-to-mote communication)
// XXX: use flag to switch on/off inversion
if (LMIC.noRXIQinversion) {
writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ) & ~(1<<6));
} else {
writeReg(LORARegInvertIQ, readReg(LORARegInvertIQ)|(1<<6));
}
#endif
// set symbol timeout (for single rx)
writeReg(LORARegSymbTimeoutLsb, LMIC.rxsyms);
// set sync word
writeReg(LORARegSyncWord, LORA_MAC_PREAMBLE);
// configure DIO mapping DIO0=RxDone DIO1=RxTout DIO2=NOP
writeReg(RegDioMapping1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_RXTOUT|MAP_DIO2_LORA_NOP);
// clear all radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF);
// enable required radio IRQs
writeReg(LORARegIrqFlagsMask, ~rxlorairqmask[rxmode]);
// enable antenna switch for RX
hal_pin_rxtx(0);
// now instruct the radio to receive
if (rxmode == RXMODE_SINGLE) { // single rx
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
opmode(OPMODE_RX_SINGLE);
} else { // continous rx (scan or rssi)
opmode(OPMODE_RX);
}
#if LMIC_DEBUG_LEVEL > 0
if (rxmode == RXMODE_RSSI) {
lmic_printf("RXMODE_RSSI\n");
} else {
u1_t sf = getSf(LMIC.rps) + 6; // 1 == SF7
u1_t bw = getBw(LMIC.rps);
u1_t cr = getCr(LMIC.rps);
lmic_printf("%lu: %s, freq=%lu, SF=%d, BW=%d, CR=4/%d, IH=%d\n",
os_getTime(),
rxmode == RXMODE_SINGLE ? "RXMODE_SINGLE" : (rxmode == RXMODE_SCAN ? "RXMODE_SCAN" : "UNKNOWN_RX"),
LMIC.freq, sf,
bw == BW125 ? 125 : (bw == BW250 ? 250 : 500),
cr == CR_4_5 ? 5 : (cr == CR_4_6 ? 6 : (cr == CR_4_7 ? 7 : 8)),
getIh(LMIC.rps)
);
}
#endif
}
static void rxfsk (u1_t rxmode) {
// only single rx (no continuous scanning, no noise sampling)
ASSERT( rxmode == RXMODE_SINGLE );
// select FSK modem (from sleep mode)
//writeReg(RegOpMode, 0x00); // (not LoRa)
opmodeFSK();
ASSERT((readReg(RegOpMode) & OPMODE_LORA) == 0);
// enter standby mode (warm up))
opmode(OPMODE_STANDBY);
// configure frequency
configChannel();
// set LNA gain
//writeReg(RegLna, 0x20|0x03); // max gain, boost enable
writeReg(RegLna, LNA_RX_GAIN);
// configure receiver
writeReg(FSKRegRxConfig, 0x1E); // AFC auto, AGC, trigger on preamble?!?
// set receiver bandwidth
writeReg(FSKRegRxBw, 0x0B); // 50kHz SSb
// set AFC bandwidth
writeReg(FSKRegAfcBw, 0x12); // 83.3kHz SSB
// set preamble detection
writeReg(FSKRegPreambleDetect, 0xAA); // enable, 2 bytes, 10 chip errors
// set sync config
writeReg(FSKRegSyncConfig, 0x12); // no auto restart, preamble 0xAA, enable, fill FIFO, 3 bytes sync
// set packet config
writeReg(FSKRegPacketConfig1, 0xD8); // var-length, whitening, crc, no auto-clear, no adr filter
writeReg(FSKRegPacketConfig2, 0x40); // packet mode
// set sync value
writeReg(FSKRegSyncValue1, 0xC1);
writeReg(FSKRegSyncValue2, 0x94);
writeReg(FSKRegSyncValue3, 0xC1);
// set preamble timeout
writeReg(FSKRegRxTimeout2, 0xFF);//(LMIC.rxsyms+1)/2);
// set bitrate
writeReg(FSKRegBitrateMsb, 0x02); // 50kbps
writeReg(FSKRegBitrateLsb, 0x80);
// set frequency deviation
writeReg(FSKRegFdevMsb, 0x01); // +/- 25kHz
writeReg(FSKRegFdevLsb, 0x99);
// configure DIO mapping DIO0=PayloadReady DIO1=NOP DIO2=TimeOut
writeReg(RegDioMapping1, MAP_DIO0_FSK_READY|MAP_DIO1_FSK_NOP|MAP_DIO2_FSK_TIMEOUT);
// enable antenna switch for RX
hal_pin_rxtx(0);
// now instruct the radio to receive
hal_waitUntil(LMIC.rxtime); // busy wait until exact rx time
opmode(OPMODE_RX); // no single rx mode available in FSK
}
static void startrx (u1_t rxmode) {
ASSERT( (readReg(RegOpMode) & OPMODE_MASK) == OPMODE_SLEEP );
if(getSf(LMIC.rps) == FSK) { // FSK modem
rxfsk(rxmode);
} else { // LoRa modem
rxlora(rxmode);
}
// the radio will go back to STANDBY mode as soon as the RX is finished
// or timed out, and the corresponding IRQ will inform us about completion.
}
// get random seed from wideband noise rssi
void radio_init () {
hal_disableIRQs();
// manually reset radio
#ifdef CFG_sx1276_radio
hal_pin_rst(0); // drive RST pin low
#else
hal_pin_rst(1); // drive RST pin high
#endif
hal_waitUntil(os_getTime()+ms2osticks(1)); // wait >100us
hal_pin_rst(2); // configure RST pin floating!
hal_waitUntil(os_getTime()+ms2osticks(5)); // wait 5ms
opmode(OPMODE_SLEEP);
// some sanity checks, e.g., read version number
u1_t v = readReg(RegVersion);
#ifdef CFG_sx1276_radio
ASSERT(v == 0x12 );
#elif CFG_sx1272_radio
ASSERT(v == 0x22);
#else
#error Missing CFG_sx1272_radio/CFG_sx1276_radio
#endif
// seed 15-byte randomness via noise rssi
rxlora(RXMODE_RSSI);
while( (readReg(RegOpMode) & OPMODE_MASK) != OPMODE_RX ); // continuous rx
for(int i=1; i<16; i++) {
for(int j=0; j<8; j++) {
u1_t b; // wait for two non-identical subsequent least-significant bits
while( (b = readReg(LORARegRssiWideband) & 0x01) == (readReg(LORARegRssiWideband) & 0x01) );
randbuf[i] = (randbuf[i] << 1) | b;
}
}
randbuf[0] = 16; // set initial index
#ifdef CFG_sx1276mb1_board
// chain calibration
writeReg(RegPaConfig, 0);
// Launch Rx chain calibration for LF band
writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
while((readReg(FSKRegImageCal)&RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING){ ; }
// Sets a Frequency in HF band
u4_t frf = 868000000;
writeReg(RegFrfMsb, (u1_t)(frf>>16));
writeReg(RegFrfMid, (u1_t)(frf>> 8));
writeReg(RegFrfLsb, (u1_t)(frf>> 0));
// Launch Rx chain calibration for HF band
writeReg(FSKRegImageCal, (readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_MASK)|RF_IMAGECAL_IMAGECAL_START);
while((readReg(FSKRegImageCal) & RF_IMAGECAL_IMAGECAL_RUNNING) == RF_IMAGECAL_IMAGECAL_RUNNING) { ; }
#endif /* CFG_sx1276mb1_board */
opmode(OPMODE_SLEEP);
hal_enableIRQs();
}
// return next random byte derived from seed buffer
// (buf[0] holds index of next byte to be returned)
u1_t radio_rand1 () {
u1_t i = randbuf[0];
ASSERT( i != 0 );
if( i==16 ) {
os_aes(AES_ENC, randbuf, 16); // encrypt seed with any key
i = 0;
}
u1_t v = randbuf[i++];
randbuf[0] = i;
return v;
}
u1_t radio_rssi () {
hal_disableIRQs();
u1_t r = readReg(LORARegRssiValue);
hal_enableIRQs();
return r;
}
static const u2_t LORA_RXDONE_FIXUP[] = {
[FSK] = us2osticks(0), // ( 0 ticks)
[SF7] = us2osticks(0), // ( 0 ticks)
[SF8] = us2osticks(1648), // ( 54 ticks)
[SF9] = us2osticks(3265), // ( 107 ticks)
[SF10] = us2osticks(7049), // ( 231 ticks)
[SF11] = us2osticks(13641), // ( 447 ticks)
[SF12] = us2osticks(31189), // (1022 ticks)
};
// called by hal ext IRQ handler
// (radio goes to stanby mode after tx/rx operations)
void radio_irq_handler (u1_t dio, ostime_t t) {
if( (readReg(RegOpMode) & OPMODE_LORA) != 0) { // LORA modem
u1_t flags = readReg(LORARegIrqFlags);
#if LMIC_DEBUG_LEVEL > 1
lmic_printf("%lu: irq: dio: 0x%x flags: 0x%x\n", t, dio, flags);
#endif
if( flags & IRQ_LORA_TXDONE_MASK ) {
// save exact tx time
LMIC.txend = t - us2osticks(43); // TXDONE FIXUP
} else if( flags & IRQ_LORA_RXDONE_MASK ) {
// save exact rx time
if(getBw(LMIC.rps) == BW125) {
t -= LORA_RXDONE_FIXUP[getSf(LMIC.rps)];
}
LMIC.rxtime = t;
// read the PDU and inform the MAC that we received something
LMIC.dataLen = (readReg(LORARegModemConfig1) & SX1272_MC1_IMPLICIT_HEADER_MODE_ON) ?
readReg(LORARegPayloadLength) : readReg(LORARegRxNbBytes);
// set FIFO read address pointer
writeReg(LORARegFifoAddrPtr, readReg(LORARegFifoRxCurrentAddr));
// now read the FIFO
readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// read rx quality parameters
LMIC.snr = readReg(LORARegPktSnrValue); // SNR [dB] * 4
LMIC.rssi = readReg(LORARegPktRssiValue) - 125 + 64; // RSSI [dBm] (-196...+63)
} else if( flags & IRQ_LORA_RXTOUT_MASK ) {
// indicate timeout
LMIC.dataLen = 0;
}
// mask all radio IRQs
writeReg(LORARegIrqFlagsMask, 0xFF);
// clear radio IRQ flags
writeReg(LORARegIrqFlags, 0xFF);
} else { // FSK modem
u1_t flags1 = readReg(FSKRegIrqFlags1);
u1_t flags2 = readReg(FSKRegIrqFlags2);
if( flags2 & IRQ_FSK2_PACKETSENT_MASK ) {
// save exact tx time
LMIC.txend = t;
} else if( flags2 & IRQ_FSK2_PAYLOADREADY_MASK ) {
// save exact rx time
LMIC.rxtime = t;
// read the PDU and inform the MAC that we received something
LMIC.dataLen = readReg(FSKRegPayloadLength);
// now read the FIFO
readBuf(RegFifo, LMIC.frame, LMIC.dataLen);
// read rx quality parameters
LMIC.snr = 0; // determine snr
LMIC.rssi = 0; // determine rssi
} else if( flags1 & IRQ_FSK1_TIMEOUT_MASK ) {
// indicate timeout
LMIC.dataLen = 0;
} else {
ASSERT(0);
}
}
// go from stanby to sleep
opmode(OPMODE_SLEEP);
// run os job (use preset func ptr)
os_setCallback(&LMIC.osjob, LMIC.osjob.func);
}
void os_radio (u1_t mode) {
hal_disableIRQs();
switch (mode) {
case RADIO_RST:
// put radio to sleep
opmode(OPMODE_SLEEP);
break;
case RADIO_TX:
// transmit frame now
starttx(); // buf=LMIC.frame, len=LMIC.dataLen
break;
case RADIO_RX:
// receive frame now (exactly at rxtime)
startrx(RXMODE_SINGLE); // buf=LMIC.frame, time=LMIC.rxtime, timeout=LMIC.rxsyms
break;
case RADIO_RXON:
// start scanning for beacon now
startrx(RXMODE_SCAN); // buf=LMIC.frame
break;
}
hal_enableIRQs();
}