Synchronous wireless star LoRa network, central device.

Dependencies:   SX127x sx12xx_hal

radio chip selection

Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.


Alternate to this project gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Synchronous Star Network

This project acts as central node for LoRaWAN-like network operating on single radio channel. Intended for use where network infrastructure would never exist due to cost and/or complexity of standard network. This project uses the class-B method of beacon generation to synchronize the end nodes with the gateway. OTA mode must be used. End-node will be allocated an uplink time slot upon joining. End node may transmit uplink at this assigned timeslot, if it desires to do so. This time slot is always referenced to the beacon sent by gateway.

LoRaWAN server is not necessary. All network control is implemented by this project. User can observe network activity on the mbed serial port. Downlinks can be scheduled using command on serial port.

This implementation must not be used on radio channels requiring duty-cycle transmit limiting.

alterations from LoRaWAN specification

This mode of operation uses a single datarate on single radio channel. ADR is not implemented, because datarate cannot be changed. OTA mode must be used. When join-accept is sent by gateway, it will have appended (instead of CFlist) the beacon timing answer to inform of when next beacon occurs, and two timing values: the time slot allocated to this end-node and the periodicity of the network. Periodicity means how often the end-node may again transmit. /media/uploads/dudmuck/class-b-single.png Beacon is sent for purpose of providing timing reference to end-nodes. The beacon payload may contain a broadcast command to end nodes. Time value is not sent in beacon payload. The time at which beacon is sent provides timing reference: every 128 seconds as standard.

Rx2 receive window is not implemented. Only Rx1 is used because a single radio channel is used. Rx1 delay is reduced to 100 milliseconds. Original LoRaWAN has 1000 millisecond Rx1 delay to accommodate internet latency.

LoRaWAN standard class-B beacon requires GPS timing reference. This implementation does not use GPS, instead a hardware timer peripheral generates interrupts to send beacons. Absolute accuracy is not required, only relative crystal drift between gateway and end-nodes is considered.

Timing values are always specified as 30ms per step as in LoRaWAN standard. Each beacon period has 4096 30ms steps per beacon period.

join OTA procedure

The join procedure has changed the join-accept delay to 100 milliseconds (standard is 5 seconds). This allows end-node to hunt for gateway on several channels during joining. When gateway starts, it will scan available channels for the optimal choice based on ambient noise on the channels. End node will retry join request until it finds the gateway. Gateway might change channel during operation if it deems current channel too busy.

configuration of network

End nodes must be provisioned by editing file Comissioning.h. The array motes lists every end node permitted on network. It contains appEui, devEUI and appKey just as specified in standard LoRaWAN. All provisioning is hard-coded; changing provisioning requires reprogramming gateway. When changing number of motes, N_MOTES definition must be changed in lorawan.h.

lorawan.h

#define N_MOTES     8
extern ota_mote_t motes[N_MOTES];   /* from Comissioning.h */

configuring number of end-nodes vs transmit rate

Trade-off can be selected between number of end-nodes on network vs. how often each end-node can transmit.
In this example, where DR_13 is SF7 at 500KHz:

lorawan.cpp

    #elif (LORAMAC_DEFAULT_DATARATE == DR_13)
        #define TX_SLOT_STEPPING        8  //approx 0.25 seconds
        #define PERIODICITY_SLOTS       (TX_SLOT_STEPPING * 6)
    #endif

Here, each end-node is given time of 240ms = 8 * 30ms; accommodating maximum payload length for both uplink and downlink.
6 in this code is the maximum count of end nodes using this gateway. Each end-node can transmit every 1.44 seconds, in this example.
If you wanted to change 6 to 20 end-nodes, each would be able to use network every 4.8 seconds.
Another example: If you wanted to use DR_12 = SF8, you would have approx 2.5 to 3dB more gain compared to SF7, but each end-node must be given double time, resulting in 20 nodes able to use network every 9.6 seconds at DR_12.

network capacity limitation

The number of end-nodes which can be supported is determined by number of SLOT_STEPPING's which can occur during BEACON_PERIOD. Beacon guard is implemented same as standard LoRaWAN, which is 3 seconds prior to beacon start and 2.12 seconds after beacon start, which gives 122.88 seconds for network traffic for each beacon period.

gateway configuration

spreading factor is declared at #define LORAMAC_DEFAULT_DATARATE in lorawan.h, valid rates are DR_8 to DR_13 (sf12 to sf7). In the end-node, the same definition must be configured in LoRaMac-definitions.h. This network operates at this constant datarate for all packets.
Band plan can be selected by defining USE_BAND_* in lorawan.h. 434MHz can be used on SX1276 shield. TypeABZ module and sx1272 only support 800/900MHz channels band.

end-node provisioning

Security permits only matching DevEUI / AppEui to join the network, due to unique AES root key for each end device; in this case the DevEUI must be programmed in advance into gateway. However, if the same AES root key could be used for each end device , then any number of end devices could be added at a later time, without checking DevEUI on the gateway when an end device joins the network. On the gateway, the end device join acceptance is performed in file lorawan.cpp LoRaWan::parse_receive() where MType == MTYPE_JOIN_REQ. A memcmp() is performed on both DevEUI and AppEUI.

If you wish to allow any DevEUI to join, define ANY_DEVEUI at top of lorawan.cpp . In this configuration, all end devices must use same AppEUI and AES key. N_MOTES must be defined to the maximum number of end devices expected. Test end device by commenting BoardGetUniqueId() in end node, and replace DevEui[] with 8 arbitrary bytes to verify gateway permits any DevEUI to join.

RAM usage

For gateway CPU, recommend to consider larger RAM size depending on number of end devices required. ota_motes_t has size of 123 bytes. Each end device has one of these, however if less RAM usage is required for more end-devices, the MAC command queue size may be reduced.

hardware support

The radio driver supports both SX1272 and SX1276, sx126x kit, sx126x radio-only shield, and sx128x 2.4GHz.. The selection of radio chip type is made by your choice of importing radio driver library.



Beacon generation requires low power ticker to be clocked from crystal, not internal RC oscillator.

Gateway Serial Interface

Gateway serial port operates at 115200bps.

commandargumentdescription
list-list joined end nodes
?-list available commands
dl<mote-hex-dev-addr> <hex-payload>send downlink, sent after uplink received
gpo<mote-hex-dev-addr> <0 or 1>set output PC6 pin level on end device
b32bit hex valueset beacon payload to be sent at next beacon
. (period)-print current status
opdBmconfigure TX power of gateway
sbcountskip sending beacons, for testing end node
fhex devAddrprinter filtering, show only single end node
hm-print filtering, hide MAC layer printing
hp-print filtering, hide APP layer printing
sa-print filtering, show all, undo hm and hp

Any received uplink will be printed with DevAddr and decrypted payload.

Committer:
dudmuck
Date:
Wed Aug 02 11:33:56 2017 -0700
Revision:
13:fa2095be01c4
Parent:
12:9a8c13c4298b
Child:
14:c6284654d1b0
add pwm broadcast and 434MHz band

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dudmuck 0:2ff18de8d48b 1 #include "lorawan.h"
dudmuck 0:2ff18de8d48b 2 #include "tim.h"
dudmuck 8:307f7faeb594 3 #include "commands.h"
dudmuck 13:fa2095be01c4 4 #define __STDC_FORMAT_MACROS
dudmuck 13:fa2095be01c4 5 #include <inttypes.h>
dudmuck 0:2ff18de8d48b 6
dudmuck 12:9a8c13c4298b 7 #define TYPE_ABZ
dudmuck 4:7e743e402681 8 RawSerial pc(USBTX, USBRX);
dudmuck 0:2ff18de8d48b 9 Timer timer;
dudmuck 0:2ff18de8d48b 10
dudmuck 10:6783623cc886 11 //#if defined(TARGET_DISCO_L072CZ_LRWAN1)
dudmuck 10:6783623cc886 12 #if defined(TARGET_NUCLEO_L073RZ) && defined(TYPE_ABZ)
dudmuck 2:9628d5e4b1bf 13 SPI spi(PA_7, PA_6, PB_3); // mosi, miso, sclk
dudmuck 2:9628d5e4b1bf 14 // dio0, dio1, nss, spi, rst
dudmuck 2:9628d5e4b1bf 15 SX127x radio(PB_4, PB_1, PA_15, spi, PC_0); // sx1276 arduino shield
dudmuck 2:9628d5e4b1bf 16
dudmuck 2:9628d5e4b1bf 17 #define CRF1 PA_1
dudmuck 2:9628d5e4b1bf 18 #define CRF2 PC_2
dudmuck 2:9628d5e4b1bf 19 #define CRF3 PC_1
dudmuck 2:9628d5e4b1bf 20 DigitalOut Vctl1(CRF1);
dudmuck 2:9628d5e4b1bf 21 DigitalOut Vctl2(CRF2);
dudmuck 2:9628d5e4b1bf 22 DigitalOut Vctl3(CRF3);
dudmuck 2:9628d5e4b1bf 23
dudmuck 2:9628d5e4b1bf 24 void rfsw_callback()
dudmuck 2:9628d5e4b1bf 25 {
dudmuck 2:9628d5e4b1bf 26 if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER) {
dudmuck 2:9628d5e4b1bf 27 Vctl1 = 0;
dudmuck 2:9628d5e4b1bf 28 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 2:9628d5e4b1bf 29 Vctl2 = 0;
dudmuck 2:9628d5e4b1bf 30 Vctl3 = 1;
dudmuck 2:9628d5e4b1bf 31 } else {
dudmuck 2:9628d5e4b1bf 32 Vctl2 = 1;
dudmuck 2:9628d5e4b1bf 33 Vctl3 = 0;
dudmuck 2:9628d5e4b1bf 34 }
dudmuck 2:9628d5e4b1bf 35 } else {
dudmuck 2:9628d5e4b1bf 36 if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER || radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER_SINGLE)
dudmuck 2:9628d5e4b1bf 37 Vctl1 = 1;
dudmuck 2:9628d5e4b1bf 38 else
dudmuck 2:9628d5e4b1bf 39 Vctl1 = 0;
dudmuck 2:9628d5e4b1bf 40
dudmuck 2:9628d5e4b1bf 41 Vctl2 = 0;
dudmuck 2:9628d5e4b1bf 42 Vctl3 = 0;
dudmuck 2:9628d5e4b1bf 43 }
dudmuck 2:9628d5e4b1bf 44 }
dudmuck 2:9628d5e4b1bf 45 #else
dudmuck 2:9628d5e4b1bf 46 SPI spi(D11, D12, D13); // mosi, miso, sclk
dudmuck 2:9628d5e4b1bf 47 // dio0, dio1, nss, spi, rst
dudmuck 2:9628d5e4b1bf 48 SX127x radio( D2, D3, D10, spi, A0); // sx1276 arduino shield
dudmuck 2:9628d5e4b1bf 49
dudmuck 2:9628d5e4b1bf 50 DigitalInOut rfsw(A4);
dudmuck 2:9628d5e4b1bf 51
dudmuck 2:9628d5e4b1bf 52 void rfsw_callback()
dudmuck 2:9628d5e4b1bf 53 {
dudmuck 2:9628d5e4b1bf 54 if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER)
dudmuck 2:9628d5e4b1bf 55 rfsw = 1;
dudmuck 2:9628d5e4b1bf 56 else
dudmuck 2:9628d5e4b1bf 57 rfsw = 0;
dudmuck 2:9628d5e4b1bf 58 }
dudmuck 2:9628d5e4b1bf 59 #endif
dudmuck 0:2ff18de8d48b 60
dudmuck 0:2ff18de8d48b 61 char pcbuf[64];
dudmuck 0:2ff18de8d48b 62 int pcbuf_len;
dudmuck 9:a0ce66c18ec0 63 uint8_t beacon_payload[4];
dudmuck 12:9a8c13c4298b 64 uint32_t tx_ms;
dudmuck 0:2ff18de8d48b 65
dudmuck 0:2ff18de8d48b 66 unsigned int skip_beacon_cnt;
dudmuck 0:2ff18de8d48b 67
dudmuck 0:2ff18de8d48b 68 SX127x_lora lora(radio);
dudmuck 0:2ff18de8d48b 69
dudmuck 12:9a8c13c4298b 70 float starting_bg_rssi;
dudmuck 0:2ff18de8d48b 71
dudmuck 0:2ff18de8d48b 72 #define N_SAMPLES 64
dudmuck 0:2ff18de8d48b 73 void channel_scan()
dudmuck 0:2ff18de8d48b 74 {
dudmuck 0:2ff18de8d48b 75 int min_ch, ch;
dudmuck 0:2ff18de8d48b 76 uint32_t hz = LORAMAC_FIRST_CHANNEL;
dudmuck 0:2ff18de8d48b 77 int acc[LORA_MAX_NB_CHANNELS];
dudmuck 0:2ff18de8d48b 78
dudmuck 0:2ff18de8d48b 79 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 80
dudmuck 0:2ff18de8d48b 81 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 82 int i;
dudmuck 0:2ff18de8d48b 83 float MHz = (float)hz / 1e6;
dudmuck 0:2ff18de8d48b 84 radio.set_frf_MHz(MHz);
dudmuck 0:2ff18de8d48b 85 radio.set_opmode(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 86 acc[ch] = 0;
dudmuck 0:2ff18de8d48b 87 for (i = 0; i < N_SAMPLES; i++) {
dudmuck 0:2ff18de8d48b 88 int rssi = lora.get_current_rssi();
dudmuck 0:2ff18de8d48b 89 acc[ch] += rssi;
dudmuck 0:2ff18de8d48b 90 wait(0.01);
dudmuck 0:2ff18de8d48b 91 }
dudmuck 0:2ff18de8d48b 92 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 93 printf("ch%u: %f\r\n", ch, acc[ch] / (float)N_SAMPLES);
dudmuck 0:2ff18de8d48b 94 hz += LORAMAC_STEPWIDTH_CHANNEL;
dudmuck 0:2ff18de8d48b 95 radio.set_frf_MHz((float)hz/1e6);
dudmuck 0:2ff18de8d48b 96 }
dudmuck 0:2ff18de8d48b 97
dudmuck 0:2ff18de8d48b 98 int min = 0x7fffffff;
dudmuck 0:2ff18de8d48b 99 min_ch = 0;
dudmuck 0:2ff18de8d48b 100 for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
dudmuck 0:2ff18de8d48b 101 if (acc[ch] < min) {
dudmuck 0:2ff18de8d48b 102 min = acc[ch];
dudmuck 0:2ff18de8d48b 103 min_ch = ch;
dudmuck 0:2ff18de8d48b 104 }
dudmuck 0:2ff18de8d48b 105 }
dudmuck 0:2ff18de8d48b 106 hz = LORAMAC_FIRST_CHANNEL + (min_ch * LORAMAC_STEPWIDTH_CHANNEL);
dudmuck 0:2ff18de8d48b 107 printf("using ch%u, %luhz\r\n", min_ch, hz);
dudmuck 0:2ff18de8d48b 108 radio.set_frf_MHz((float)hz/1e6);
dudmuck 12:9a8c13c4298b 109
dudmuck 12:9a8c13c4298b 110 starting_bg_rssi = acc[min_ch] / (float)N_SAMPLES;
dudmuck 12:9a8c13c4298b 111 }
dudmuck 12:9a8c13c4298b 112
dudmuck 12:9a8c13c4298b 113 void measure_ambient()
dudmuck 12:9a8c13c4298b 114 {
dudmuck 12:9a8c13c4298b 115 int i, acc = 0;
dudmuck 12:9a8c13c4298b 116 float bg_rssi;
dudmuck 12:9a8c13c4298b 117 float diff;
dudmuck 12:9a8c13c4298b 118 static unsigned cnt = 0;
dudmuck 12:9a8c13c4298b 119
dudmuck 12:9a8c13c4298b 120 for (i = 0; i < N_SAMPLES; i++) {
dudmuck 12:9a8c13c4298b 121 int rssi = lora.get_current_rssi();
dudmuck 12:9a8c13c4298b 122 acc += rssi;
dudmuck 12:9a8c13c4298b 123 wait(0.01);
dudmuck 12:9a8c13c4298b 124 }
dudmuck 12:9a8c13c4298b 125 bg_rssi = acc / (float)N_SAMPLES;
dudmuck 12:9a8c13c4298b 126 diff = bg_rssi - starting_bg_rssi;
dudmuck 12:9a8c13c4298b 127 printf("bg_rssi:%.1fdBm vs %1.fdBm, diff:%.1f, %d\r\n", bg_rssi, starting_bg_rssi, diff, cnt);
dudmuck 12:9a8c13c4298b 128 if (diff > 10) {
dudmuck 12:9a8c13c4298b 129 if (++cnt > 3) {
dudmuck 12:9a8c13c4298b 130 /* find better channel */
dudmuck 12:9a8c13c4298b 131 channel_scan();
dudmuck 12:9a8c13c4298b 132 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 12:9a8c13c4298b 133 cnt = 0;
dudmuck 12:9a8c13c4298b 134 }
dudmuck 12:9a8c13c4298b 135 } else
dudmuck 12:9a8c13c4298b 136 cnt = 0;
dudmuck 0:2ff18de8d48b 137 }
dudmuck 0:2ff18de8d48b 138
dudmuck 0:2ff18de8d48b 139 void init_radio()
dudmuck 0:2ff18de8d48b 140 {
dudmuck 0:2ff18de8d48b 141 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 142
dudmuck 0:2ff18de8d48b 143 radio.RegPaConfig.bits.OutputPower = 15;
dudmuck 0:2ff18de8d48b 144 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
dudmuck 0:2ff18de8d48b 145 lora.enable();
dudmuck 13:fa2095be01c4 146 lora.setBw_KHz(BANDWIDTH_KHZ);
dudmuck 0:2ff18de8d48b 147 lora.setSf(LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 148 printf("using sf%u\r\n", LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
dudmuck 0:2ff18de8d48b 149
dudmuck 0:2ff18de8d48b 150 channel_scan();
dudmuck 0:2ff18de8d48b 151
dudmuck 0:2ff18de8d48b 152 radio.write_reg(REG_LR_SYNC_BYTE, LORA_MAC_PUBLIC_SYNCWORD);
dudmuck 0:2ff18de8d48b 153 radio.write_reg(REG_LR_RX_MAX_PAYLOADLENGTH, 255);
dudmuck 0:2ff18de8d48b 154 }
dudmuck 0:2ff18de8d48b 155
dudmuck 0:2ff18de8d48b 156 void printLoraIrqs(bool clear)
dudmuck 0:2ff18de8d48b 157 {
dudmuck 0:2ff18de8d48b 158 printf("\r\nIrqFlags:");
dudmuck 0:2ff18de8d48b 159 if (lora.RegIrqFlags.bits.CadDetected)
dudmuck 0:2ff18de8d48b 160 printf("CadDetected ");
dudmuck 0:2ff18de8d48b 161 if (lora.RegIrqFlags.bits.FhssChangeChannel) {
dudmuck 0:2ff18de8d48b 162 printf("FhssChangeChannel:%d ", lora.RegHopChannel.bits.FhssPresentChannel);
dudmuck 0:2ff18de8d48b 163 }
dudmuck 0:2ff18de8d48b 164 if (lora.RegIrqFlags.bits.CadDone)
dudmuck 0:2ff18de8d48b 165 printf("CadDone ");
dudmuck 0:2ff18de8d48b 166 if (lora.RegIrqFlags.bits.TxDone)
dudmuck 12:9a8c13c4298b 167 printf("TxDone-dio0:%d ", radio.dio0.read());
dudmuck 0:2ff18de8d48b 168 if (lora.RegIrqFlags.bits.ValidHeader)
dudmuck 0:2ff18de8d48b 169 printf("ValidHeader ");
dudmuck 0:2ff18de8d48b 170 if (lora.RegIrqFlags.bits.PayloadCrcError)
dudmuck 0:2ff18de8d48b 171 printf("PayloadCrcError ");
dudmuck 0:2ff18de8d48b 172 if (lora.RegIrqFlags.bits.RxDone)
dudmuck 0:2ff18de8d48b 173 printf("RxDone ");
dudmuck 0:2ff18de8d48b 174 if (lora.RegIrqFlags.bits.RxTimeout)
dudmuck 0:2ff18de8d48b 175 printf("RxTimeout ");
dudmuck 0:2ff18de8d48b 176
dudmuck 0:2ff18de8d48b 177 printf("\r\n");
dudmuck 0:2ff18de8d48b 178
dudmuck 0:2ff18de8d48b 179 if (clear)
dudmuck 0:2ff18de8d48b 180 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 0:2ff18de8d48b 181 }
dudmuck 0:2ff18de8d48b 182
dudmuck 0:2ff18de8d48b 183 void printOpMode()
dudmuck 0:2ff18de8d48b 184 {
dudmuck 0:2ff18de8d48b 185 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 0:2ff18de8d48b 186 switch (radio.RegOpMode.bits.Mode) {
dudmuck 0:2ff18de8d48b 187 case RF_OPMODE_SLEEP: printf("sleep"); break;
dudmuck 0:2ff18de8d48b 188 case RF_OPMODE_STANDBY: printf("stby"); break;
dudmuck 0:2ff18de8d48b 189 case RF_OPMODE_SYNTHESIZER_TX: printf("fstx"); break;
dudmuck 0:2ff18de8d48b 190 case RF_OPMODE_TRANSMITTER: printf("tx"); break;
dudmuck 0:2ff18de8d48b 191 case RF_OPMODE_SYNTHESIZER_RX: printf("fsrx"); break;
dudmuck 0:2ff18de8d48b 192 case RF_OPMODE_RECEIVER: printf("rx"); break;
dudmuck 0:2ff18de8d48b 193 case 6:
dudmuck 0:2ff18de8d48b 194 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 195 printf("rxs");
dudmuck 0:2ff18de8d48b 196 else
dudmuck 0:2ff18de8d48b 197 printf("-6-");
dudmuck 0:2ff18de8d48b 198 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 199 case 7:
dudmuck 0:2ff18de8d48b 200 if (radio.RegOpMode.bits.LongRangeMode)
dudmuck 0:2ff18de8d48b 201 printf("cad");
dudmuck 0:2ff18de8d48b 202 else
dudmuck 0:2ff18de8d48b 203 printf("-7-");
dudmuck 0:2ff18de8d48b 204 break; // todo: different lora/fsk
dudmuck 0:2ff18de8d48b 205 }
dudmuck 0:2ff18de8d48b 206 }
dudmuck 0:2ff18de8d48b 207
dudmuck 9:a0ce66c18ec0 208 #define GPO_IDX 6
dudmuck 9:a0ce66c18ec0 209 void decrypted_uplink(uint8_t* buf, uint8_t buflen, uint8_t port)
dudmuck 9:a0ce66c18ec0 210 {
dudmuck 9:a0ce66c18ec0 211 if (port == SENSOR_PORT) {
dudmuck 9:a0ce66c18ec0 212 uint16_t a_1, a_3, lum;
dudmuck 9:a0ce66c18ec0 213 a_1 = buf[0] << 8;
dudmuck 9:a0ce66c18ec0 214 a_1 += buf[1];
dudmuck 9:a0ce66c18ec0 215 a_3 = buf[2] << 8;
dudmuck 9:a0ce66c18ec0 216 a_3 += buf[3];
dudmuck 9:a0ce66c18ec0 217 lum = buf[4] << 8;
dudmuck 9:a0ce66c18ec0 218 lum += buf[5];
dudmuck 10:6783623cc886 219 printf("SENSOR gpi:%u, gpo:%u, a1:%u, a3:%u, lum:%u\r\n", (buf[GPO_IDX] & 2) >> 1, buf[GPO_IDX] & 1, a_1, a_3, lum);
dudmuck 9:a0ce66c18ec0 220 } else if (port == TEXT_PORT) {
dudmuck 9:a0ce66c18ec0 221 buf[buflen] = 0;
dudmuck 9:a0ce66c18ec0 222 printf("TEXT %s\r\n", buf);
dudmuck 9:a0ce66c18ec0 223 } else {
dudmuck 9:a0ce66c18ec0 224 int i;
dudmuck 9:a0ce66c18ec0 225 printf("port%u: ", port);
dudmuck 9:a0ce66c18ec0 226 for (i = 0; i < buflen; i++)
dudmuck 9:a0ce66c18ec0 227 printf("%02x ", buf[i]);
dudmuck 9:a0ce66c18ec0 228 printf("\r\n");
dudmuck 9:a0ce66c18ec0 229 }
dudmuck 9:a0ce66c18ec0 230 }
dudmuck 9:a0ce66c18ec0 231
dudmuck 0:2ff18de8d48b 232 EventQueue queue;
dudmuck 3:7c01b8978638 233
dudmuck 3:7c01b8978638 234 volatile bool get_tx_done;
dudmuck 3:7c01b8978638 235 volatile bool beacon_guard;
dudmuck 0:2ff18de8d48b 236 bool restore_tx_invert;
dudmuck 3:7c01b8978638 237 bool restore_header_mode;
dudmuck 0:2ff18de8d48b 238
dudmuck 0:2ff18de8d48b 239 void send_downlink()
dudmuck 0:2ff18de8d48b 240 {
dudmuck 12:9a8c13c4298b 241 if (!beacon_guard && LoRaWan::do_downlink) {
dudmuck 0:2ff18de8d48b 242 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 0:2ff18de8d48b 243 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 244 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 245 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 246 lora.setRxPayloadCrcOn(false);
dudmuck 0:2ff18de8d48b 247 lora.start_tx(lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 248 LoRaWan::do_downlink = false;
dudmuck 3:7c01b8978638 249 get_tx_done = true;
dudmuck 12:9a8c13c4298b 250 tx_ms = timer.read_ms();
dudmuck 0:2ff18de8d48b 251 }
dudmuck 0:2ff18de8d48b 252 }
dudmuck 0:2ff18de8d48b 253
dudmuck 12:9a8c13c4298b 254
dudmuck 0:2ff18de8d48b 255 void
dudmuck 0:2ff18de8d48b 256 service_radio()
dudmuck 0:2ff18de8d48b 257 {
dudmuck 0:2ff18de8d48b 258 service_action_e act = lora.service();
dudmuck 0:2ff18de8d48b 259
dudmuck 0:2ff18de8d48b 260 switch (act) {
dudmuck 0:2ff18de8d48b 261 case SERVICE_ERROR:
dudmuck 0:2ff18de8d48b 262 printf("SERVICE_ERROR\r\n");
dudmuck 0:2ff18de8d48b 263 case SERVICE_TX_DONE:
dudmuck 0:2ff18de8d48b 264 case SERVICE_NONE:
dudmuck 0:2ff18de8d48b 265 break;
dudmuck 0:2ff18de8d48b 266 case SERVICE_READ_FIFO:
dudmuck 3:7c01b8978638 267 LoRaWan::rx_slot = tim_get_current_slot();
dudmuck 3:7c01b8978638 268 LoRaWan::rx_ms = timer.read_ms();
dudmuck 13:fa2095be01c4 269 printf("%lu, %.1fdB, %ddBm, ", time(NULL), lora.RegPktSnrValue / 4.0, lora.get_pkt_rssi());
dudmuck 12:9a8c13c4298b 270 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 12:9a8c13c4298b 271 LoRaWan::parse_receive();
dudmuck 12:9a8c13c4298b 272 if (!LoRaWan::do_downlink) {
dudmuck 12:9a8c13c4298b 273 /* if not sending downlink, start receiver now */
dudmuck 3:7c01b8978638 274 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 275 }
dudmuck 12:9a8c13c4298b 276
dudmuck 0:2ff18de8d48b 277 break;
dudmuck 0:2ff18de8d48b 278 } // ..switch (act)
dudmuck 0:2ff18de8d48b 279 }
dudmuck 0:2ff18de8d48b 280
dudmuck 0:2ff18de8d48b 281 volatile float prev_beacon_send_at;
dudmuck 0:2ff18de8d48b 282 volatile float beacon_send_at;
dudmuck 0:2ff18de8d48b 283 volatile float beacon_loaded_at;
dudmuck 0:2ff18de8d48b 284
dudmuck 0:2ff18de8d48b 285 volatile bool beacon_loaded;
dudmuck 0:2ff18de8d48b 286 void
dudmuck 0:2ff18de8d48b 287 send_beacon()
dudmuck 0:2ff18de8d48b 288 {
dudmuck 0:2ff18de8d48b 289 prev_beacon_send_at = beacon_send_at;
dudmuck 0:2ff18de8d48b 290 beacon_send_at = timer.read();
dudmuck 0:2ff18de8d48b 291
dudmuck 0:2ff18de8d48b 292 if (!beacon_loaded)
dudmuck 0:2ff18de8d48b 293 return;
dudmuck 0:2ff18de8d48b 294
dudmuck 0:2ff18de8d48b 295 radio.set_opmode(RF_OPMODE_TRANSMITTER);
dudmuck 0:2ff18de8d48b 296 beacon_loaded = false;
dudmuck 3:7c01b8978638 297 get_tx_done = true;
dudmuck 12:9a8c13c4298b 298 tx_ms = timer.read_ms();
dudmuck 0:2ff18de8d48b 299 }
dudmuck 0:2ff18de8d48b 300
dudmuck 0:2ff18de8d48b 301 static uint16_t beacon_crc( uint8_t *buffer, uint16_t length )
dudmuck 0:2ff18de8d48b 302 {
dudmuck 0:2ff18de8d48b 303 // The CRC calculation follows CCITT
dudmuck 0:2ff18de8d48b 304 const uint16_t polynom = 0x1021;
dudmuck 0:2ff18de8d48b 305 // CRC initial value
dudmuck 0:2ff18de8d48b 306 uint16_t crc = 0x0000;
dudmuck 0:2ff18de8d48b 307
dudmuck 0:2ff18de8d48b 308 if( buffer == NULL )
dudmuck 0:2ff18de8d48b 309 {
dudmuck 0:2ff18de8d48b 310 return 0;
dudmuck 0:2ff18de8d48b 311 }
dudmuck 0:2ff18de8d48b 312
dudmuck 0:2ff18de8d48b 313 for( uint16_t i = 0; i < length; ++i )
dudmuck 0:2ff18de8d48b 314 {
dudmuck 0:2ff18de8d48b 315 crc ^= ( uint16_t ) buffer[i] << 8;
dudmuck 0:2ff18de8d48b 316 for( uint16_t j = 0; j < 8; ++j )
dudmuck 0:2ff18de8d48b 317 {
dudmuck 0:2ff18de8d48b 318 crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
dudmuck 0:2ff18de8d48b 319 }
dudmuck 0:2ff18de8d48b 320 }
dudmuck 0:2ff18de8d48b 321
dudmuck 0:2ff18de8d48b 322 return crc;
dudmuck 0:2ff18de8d48b 323 }
dudmuck 0:2ff18de8d48b 324
dudmuck 0:2ff18de8d48b 325 void
dudmuck 3:7c01b8978638 326 _load_beacon()
dudmuck 0:2ff18de8d48b 327 {
dudmuck 0:2ff18de8d48b 328 uint16_t crc;
dudmuck 12:9a8c13c4298b 329 radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 12:9a8c13c4298b 330 // clear any flags which might have been set during beacon_guard
dudmuck 12:9a8c13c4298b 331 radio.write_reg(REG_LR_IRQFLAGS, 0xff);
dudmuck 0:2ff18de8d48b 332 lora.RegPayloadLength = BEACON_SIZE;
dudmuck 0:2ff18de8d48b 333 radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 334 lora.setHeaderMode(true);
dudmuck 3:7c01b8978638 335 restore_header_mode = true;
dudmuck 0:2ff18de8d48b 336
dudmuck 0:2ff18de8d48b 337 if (skip_beacon_cnt > 0) {
dudmuck 1:107435401168 338 //printf("skip_beacon_cnt:%d\r\n", skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 339 lora.invert_tx(true);
dudmuck 0:2ff18de8d48b 340 restore_tx_invert = true;
dudmuck 0:2ff18de8d48b 341 skip_beacon_cnt--;
dudmuck 0:2ff18de8d48b 342 }
dudmuck 0:2ff18de8d48b 343
dudmuck 9:a0ce66c18ec0 344 radio.tx_buf[0] = beacon_payload[0];
dudmuck 9:a0ce66c18ec0 345 radio.tx_buf[1] = beacon_payload[1];
dudmuck 9:a0ce66c18ec0 346 radio.tx_buf[2] = beacon_payload[2];
dudmuck 9:a0ce66c18ec0 347 radio.tx_buf[3] = beacon_payload[3];
dudmuck 9:a0ce66c18ec0 348 beacon_payload[0] = CMD_NONE;
dudmuck 9:a0ce66c18ec0 349
dudmuck 0:2ff18de8d48b 350 crc = beacon_crc(radio.tx_buf, 4);
dudmuck 0:2ff18de8d48b 351 radio.tx_buf[4] = crc & 0xff;
dudmuck 0:2ff18de8d48b 352 radio.tx_buf[5] = crc >> 8;
dudmuck 0:2ff18de8d48b 353
dudmuck 0:2ff18de8d48b 354 // DIO0 to TxDone
dudmuck 3:7c01b8978638 355 radio.RegDioMapping1.bits.Dio0Mapping = 1;
dudmuck 3:7c01b8978638 356 radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
dudmuck 0:2ff18de8d48b 357
dudmuck 0:2ff18de8d48b 358 // set FifoPtrAddr to FifoTxPtrBase
dudmuck 0:2ff18de8d48b 359 radio.write_reg(REG_LR_FIFOADDRPTR, radio.read_reg(REG_LR_FIFOTXBASEADDR));
dudmuck 0:2ff18de8d48b 360
dudmuck 0:2ff18de8d48b 361 // write PayloadLength bytes to fifo
dudmuck 0:2ff18de8d48b 362 lora.write_fifo(lora.RegPayloadLength);
dudmuck 0:2ff18de8d48b 363
dudmuck 0:2ff18de8d48b 364 // prepare for tx to occur in send_beacon()
dudmuck 0:2ff18de8d48b 365 radio.set_opmode(RF_OPMODE_SYNTHESIZER_TX);
dudmuck 0:2ff18de8d48b 366 beacon_loaded = true;
dudmuck 0:2ff18de8d48b 367
dudmuck 0:2ff18de8d48b 368 beacon_loaded_at = timer.read();
dudmuck 0:2ff18de8d48b 369 }
dudmuck 0:2ff18de8d48b 370
dudmuck 3:7c01b8978638 371 void load_beacon()
dudmuck 3:7c01b8978638 372 {
dudmuck 3:7c01b8978638 373 beacon_guard = true;
dudmuck 12:9a8c13c4298b 374 queue.call_in(200, _load_beacon);
dudmuck 3:7c01b8978638 375 }
dudmuck 3:7c01b8978638 376
dudmuck 0:2ff18de8d48b 377 void get_time_till_beacon()
dudmuck 0:2ff18de8d48b 378 {
dudmuck 0:2ff18de8d48b 379 uint16_t slots = tim_get_current_slot();
dudmuck 0:2ff18de8d48b 380 printf("slots:%u\r\n", slots);
dudmuck 0:2ff18de8d48b 381 }
dudmuck 0:2ff18de8d48b 382
dudmuck 4:7e743e402681 383 void rx_isr()
dudmuck 0:2ff18de8d48b 384 {
dudmuck 0:2ff18de8d48b 385 static uint8_t pcbuf_idx = 0;
dudmuck 0:2ff18de8d48b 386 static uint8_t prev_len = 0;;
dudmuck 0:2ff18de8d48b 387 char c = pc.getc();
dudmuck 0:2ff18de8d48b 388
dudmuck 0:2ff18de8d48b 389 if (c == 8) {
dudmuck 0:2ff18de8d48b 390 if (pcbuf_idx > 0) {
dudmuck 0:2ff18de8d48b 391 pc.putc(8);
dudmuck 0:2ff18de8d48b 392 pc.putc(' ');
dudmuck 0:2ff18de8d48b 393 pc.putc(8);
dudmuck 0:2ff18de8d48b 394 pcbuf_idx--;
dudmuck 0:2ff18de8d48b 395 }
dudmuck 0:2ff18de8d48b 396 } else if (c == 3) { // ctrl-C
dudmuck 0:2ff18de8d48b 397 pcbuf_len = -1;
dudmuck 0:2ff18de8d48b 398 } else if (c == '\r') {
dudmuck 0:2ff18de8d48b 399 if (pcbuf_idx == 0) {
dudmuck 0:2ff18de8d48b 400 pcbuf_len = prev_len;
dudmuck 0:2ff18de8d48b 401 } else {
dudmuck 0:2ff18de8d48b 402 pcbuf[pcbuf_idx] = 0; // null terminate
dudmuck 0:2ff18de8d48b 403 prev_len = pcbuf_idx;
dudmuck 0:2ff18de8d48b 404 pcbuf_idx = 0;
dudmuck 0:2ff18de8d48b 405 pcbuf_len = prev_len;
dudmuck 0:2ff18de8d48b 406 }
dudmuck 0:2ff18de8d48b 407 } else if (pcbuf_idx < sizeof(pcbuf)) {
dudmuck 0:2ff18de8d48b 408 pcbuf[pcbuf_idx++] = c;
dudmuck 0:2ff18de8d48b 409 pc.putc(c);
dudmuck 0:2ff18de8d48b 410 }
dudmuck 0:2ff18de8d48b 411 }
dudmuck 0:2ff18de8d48b 412
dudmuck 0:2ff18de8d48b 413 void cmd_skip_beacon(uint8_t idx)
dudmuck 0:2ff18de8d48b 414 {
dudmuck 0:2ff18de8d48b 415 if (pcbuf[idx] >= '0' && pcbuf[idx] <= '9') {
dudmuck 0:2ff18de8d48b 416 sscanf(pcbuf+idx, "%u", &skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 417 }
dudmuck 0:2ff18de8d48b 418 printf("skip_beacon_cnt:%u\r\n", skip_beacon_cnt);
dudmuck 0:2ff18de8d48b 419 }
dudmuck 0:2ff18de8d48b 420
dudmuck 0:2ff18de8d48b 421 void cmd_list_motes(uint8_t idx)
dudmuck 0:2ff18de8d48b 422 {
dudmuck 0:2ff18de8d48b 423 int i;
dudmuck 0:2ff18de8d48b 424 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 425 if (motes[i].dev_addr != DEVADDR_NONE) {
dudmuck 13:fa2095be01c4 426 LoRaWan::print_octets_rev("", motes[i].dev_eui, LORA_EUI_LENGTH);
dudmuck 13:fa2095be01c4 427 printf(" %" PRIx32 "\r\n", motes[i].dev_addr);
dudmuck 0:2ff18de8d48b 428 }
dudmuck 0:2ff18de8d48b 429 }
dudmuck 0:2ff18de8d48b 430 }
dudmuck 0:2ff18de8d48b 431
dudmuck 0:2ff18de8d48b 432 void
dudmuck 0:2ff18de8d48b 433 cmd_beacon_payload(uint8_t idx)
dudmuck 0:2ff18de8d48b 434 {
dudmuck 9:a0ce66c18ec0 435 uint32_t i;
dudmuck 9:a0ce66c18ec0 436 uint32_t* ptr;
dudmuck 13:fa2095be01c4 437 sscanf(pcbuf+idx, "%" PRIx32, &i);
dudmuck 13:fa2095be01c4 438 printf("beacon_payload:%08" PRIx32 "\r\n", i);
dudmuck 9:a0ce66c18ec0 439 ptr = (uint32_t*)beacon_payload;
dudmuck 9:a0ce66c18ec0 440 *ptr = i;
dudmuck 8:307f7faeb594 441 }
dudmuck 8:307f7faeb594 442
dudmuck 0:2ff18de8d48b 443 void
dudmuck 0:2ff18de8d48b 444 cmd_send_downlink(uint8_t idx)
dudmuck 0:2ff18de8d48b 445 {
dudmuck 0:2ff18de8d48b 446 ota_mote_t* mote = NULL;
dudmuck 0:2ff18de8d48b 447 int i;
dudmuck 0:2ff18de8d48b 448 unsigned int dev_addr;
dudmuck 0:2ff18de8d48b 449 sscanf(pcbuf+idx, "%x", &dev_addr);
dudmuck 0:2ff18de8d48b 450 for (i = 0; i < N_MOTES; i++) {
dudmuck 0:2ff18de8d48b 451 if (motes[i].dev_addr == dev_addr) {
dudmuck 0:2ff18de8d48b 452 break;
dudmuck 0:2ff18de8d48b 453 }
dudmuck 0:2ff18de8d48b 454 }
dudmuck 0:2ff18de8d48b 455 if (i == N_MOTES) {
dudmuck 0:2ff18de8d48b 456 printf("mote %x not found\r\n", dev_addr);
dudmuck 0:2ff18de8d48b 457 return;
dudmuck 0:2ff18de8d48b 458 }
dudmuck 0:2ff18de8d48b 459 mote = &motes[i];
dudmuck 0:2ff18de8d48b 460
dudmuck 0:2ff18de8d48b 461 while (pcbuf[idx] != ' ') {
dudmuck 0:2ff18de8d48b 462 if (pcbuf[++idx] == 0) {
dudmuck 0:2ff18de8d48b 463 printf("hit end\r\n");
dudmuck 0:2ff18de8d48b 464 return;
dudmuck 0:2ff18de8d48b 465 }
dudmuck 0:2ff18de8d48b 466 }
dudmuck 0:2ff18de8d48b 467 idx++; // step past space
dudmuck 0:2ff18de8d48b 468
dudmuck 0:2ff18de8d48b 469 mote->user_downlink_length = 0;
dudmuck 0:2ff18de8d48b 470 while (pcbuf[idx] > ' ') {
dudmuck 0:2ff18de8d48b 471 int o;
dudmuck 0:2ff18de8d48b 472 sscanf(pcbuf+idx, "%02x", &o);
dudmuck 0:2ff18de8d48b 473 LoRaWan::user_downlink[mote->user_downlink_length++] = o;
dudmuck 0:2ff18de8d48b 474 idx += 2;
dudmuck 0:2ff18de8d48b 475 }
dudmuck 0:2ff18de8d48b 476
dudmuck 13:fa2095be01c4 477 printf("%u bytes scheduled for %" PRIx32 "\r\n", mote->user_downlink_length, mote->dev_addr);
dudmuck 0:2ff18de8d48b 478 }
dudmuck 0:2ff18de8d48b 479
dudmuck 9:a0ce66c18ec0 480 void cmd_rx_restart(uint8_t idx)
dudmuck 9:a0ce66c18ec0 481 {
dudmuck 9:a0ce66c18ec0 482 /*radio.set_opmode(RF_OPMODE_STANDBY);
dudmuck 9:a0ce66c18ec0 483 printf("standby\r\n");*/
dudmuck 9:a0ce66c18ec0 484 radio.set_opmode(RF_OPMODE_SLEEP);
dudmuck 9:a0ce66c18ec0 485 printf("sleep\r\n");
dudmuck 9:a0ce66c18ec0 486 wait(0.05);
dudmuck 9:a0ce66c18ec0 487 radio.set_opmode(RF_OPMODE_RECEIVER);
dudmuck 9:a0ce66c18ec0 488 printf("receive\r\n");
dudmuck 9:a0ce66c18ec0 489 }
dudmuck 9:a0ce66c18ec0 490
dudmuck 9:a0ce66c18ec0 491 void cmd_beacon_gpo(uint8_t idx)
dudmuck 9:a0ce66c18ec0 492 {
dudmuck 9:a0ce66c18ec0 493 int gpo;
dudmuck 9:a0ce66c18ec0 494 sscanf(pcbuf+idx, "%d", &gpo);
dudmuck 9:a0ce66c18ec0 495 beacon_payload[0] = CMD_GPIO_OUT;
dudmuck 9:a0ce66c18ec0 496 beacon_payload[1] = gpo;
dudmuck 9:a0ce66c18ec0 497 printf("beacon gpo: %d\r\n", gpo);
dudmuck 9:a0ce66c18ec0 498 }
dudmuck 9:a0ce66c18ec0 499
dudmuck 9:a0ce66c18ec0 500 void cmd_beacon_rgb(uint8_t idx)
dudmuck 9:a0ce66c18ec0 501 {
dudmuck 9:a0ce66c18ec0 502 int r, g ,b;
dudmuck 9:a0ce66c18ec0 503 sscanf(pcbuf+idx, "%d %d %d", &r, &g, &b);
dudmuck 9:a0ce66c18ec0 504 beacon_payload[0] = CMD_LED_RGB;
dudmuck 9:a0ce66c18ec0 505 beacon_payload[1] = r;
dudmuck 9:a0ce66c18ec0 506 beacon_payload[2] = g;
dudmuck 9:a0ce66c18ec0 507 beacon_payload[3] = b;
dudmuck 9:a0ce66c18ec0 508 printf("beacon rgb: %d %d %d\r\n", r, g, b);
dudmuck 9:a0ce66c18ec0 509 }
dudmuck 9:a0ce66c18ec0 510
dudmuck 9:a0ce66c18ec0 511 void cmd_downlink_rgb(uint8_t idx)
dudmuck 9:a0ce66c18ec0 512 {
dudmuck 9:a0ce66c18ec0 513 ota_mote_t* mote = NULL;
dudmuck 9:a0ce66c18ec0 514 int i, r, g ,b;
dudmuck 9:a0ce66c18ec0 515 unsigned int dev_addr;
dudmuck 9:a0ce66c18ec0 516 sscanf(pcbuf+idx, "%x %d %d %d", &dev_addr, &r, &g, &b);
dudmuck 9:a0ce66c18ec0 517 for (i = 0; i < N_MOTES; i++) {
dudmuck 9:a0ce66c18ec0 518 if (motes[i].dev_addr == dev_addr) {
dudmuck 9:a0ce66c18ec0 519 break;
dudmuck 9:a0ce66c18ec0 520 }
dudmuck 9:a0ce66c18ec0 521 }
dudmuck 9:a0ce66c18ec0 522 if (i == N_MOTES) {
dudmuck 9:a0ce66c18ec0 523 printf("mote %x not found\r\n", dev_addr);
dudmuck 9:a0ce66c18ec0 524 return;
dudmuck 9:a0ce66c18ec0 525 }
dudmuck 9:a0ce66c18ec0 526 mote = &motes[i];
dudmuck 9:a0ce66c18ec0 527
dudmuck 9:a0ce66c18ec0 528 mote->user_downlink_length = 0;
dudmuck 9:a0ce66c18ec0 529 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_LED_RGB;
dudmuck 9:a0ce66c18ec0 530 LoRaWan::user_downlink[mote->user_downlink_length++] = r;
dudmuck 9:a0ce66c18ec0 531 LoRaWan::user_downlink[mote->user_downlink_length++] = g;
dudmuck 9:a0ce66c18ec0 532 LoRaWan::user_downlink[mote->user_downlink_length++] = b;
dudmuck 9:a0ce66c18ec0 533
dudmuck 13:fa2095be01c4 534 printf("rgb %d %d %d to mote %" PRIx32 "\r\n", r, g, b, mote->dev_addr);
dudmuck 9:a0ce66c18ec0 535 }
dudmuck 9:a0ce66c18ec0 536
dudmuck 9:a0ce66c18ec0 537 void cmd_downlink_gpo(uint8_t idx)
dudmuck 9:a0ce66c18ec0 538 {
dudmuck 9:a0ce66c18ec0 539 ota_mote_t* mote = NULL;
dudmuck 9:a0ce66c18ec0 540 int i, gpo;
dudmuck 9:a0ce66c18ec0 541 unsigned int dev_addr;
dudmuck 9:a0ce66c18ec0 542 sscanf(pcbuf+idx, "%x %d", &dev_addr, &gpo);
dudmuck 9:a0ce66c18ec0 543 for (i = 0; i < N_MOTES; i++) {
dudmuck 9:a0ce66c18ec0 544 if (motes[i].dev_addr == dev_addr) {
dudmuck 9:a0ce66c18ec0 545 break;
dudmuck 9:a0ce66c18ec0 546 }
dudmuck 9:a0ce66c18ec0 547 }
dudmuck 9:a0ce66c18ec0 548 if (i == N_MOTES) {
dudmuck 9:a0ce66c18ec0 549 printf("mote %x not found\r\n", dev_addr);
dudmuck 9:a0ce66c18ec0 550 return;
dudmuck 9:a0ce66c18ec0 551 }
dudmuck 9:a0ce66c18ec0 552 mote = &motes[i];
dudmuck 9:a0ce66c18ec0 553
dudmuck 9:a0ce66c18ec0 554 mote->user_downlink_length = 0;
dudmuck 9:a0ce66c18ec0 555 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_GPIO_OUT;
dudmuck 9:a0ce66c18ec0 556 LoRaWan::user_downlink[mote->user_downlink_length++] = gpo;
dudmuck 9:a0ce66c18ec0 557
dudmuck 13:fa2095be01c4 558 printf("gpo %d to mote %" PRIx32 "\r\n", gpo, mote->dev_addr);
dudmuck 9:a0ce66c18ec0 559 }
dudmuck 9:a0ce66c18ec0 560
dudmuck 0:2ff18de8d48b 561 void cmd_status(uint8_t idx)
dudmuck 0:2ff18de8d48b 562 {
dudmuck 0:2ff18de8d48b 563 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 2:9628d5e4b1bf 564 printf("%.3fMHz sf%ubw%u ", radio.get_frf_MHz(), lora.getSf(), lora.getBw());
dudmuck 0:2ff18de8d48b 565 printOpMode();
dudmuck 0:2ff18de8d48b 566 if (!radio.RegOpMode.bits.LongRangeMode) {
dudmuck 0:2ff18de8d48b 567 printf("FSK\r\n");
dudmuck 0:2ff18de8d48b 568 return;
dudmuck 0:2ff18de8d48b 569 }
dudmuck 0:2ff18de8d48b 570
dudmuck 0:2ff18de8d48b 571 lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS);
dudmuck 0:2ff18de8d48b 572 printLoraIrqs(false);
dudmuck 0:2ff18de8d48b 573
dudmuck 12:9a8c13c4298b 574 printf(" do_downlink:%u get_tx_done:%u, ", LoRaWan::do_downlink, get_tx_done);
dudmuck 9:a0ce66c18ec0 575 lora.RegTest33.octet = radio.read_reg(REG_LR_TEST33); // invert_i_q
dudmuck 9:a0ce66c18ec0 576 lora.RegDriftInvert.octet = radio.read_reg(REG_LR_DRIFT_INVERT);
dudmuck 9:a0ce66c18ec0 577 printf("modemstat:%02x, rxinv:%x,%x\r\n", radio.read_reg(REG_LR_MODEMSTAT), lora.RegTest33.octet, lora.RegDriftInvert.octet);
dudmuck 2:9628d5e4b1bf 578 radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
dudmuck 2:9628d5e4b1bf 579 printf("\r\nskip_beacon_cnt:%u, currently:%u dio0map:%u\r\n", skip_beacon_cnt, tim_get_current_slot(), radio.RegDioMapping1.bits.Dio0Mapping);
dudmuck 12:9a8c13c4298b 580 printf("FIfoAddrPtr:%02x RxBase:%02x\r\n", radio.read_reg(REG_LR_FIFOADDRPTR), radio.read_reg(REG_LR_FIFORXBASEADDR));
dudmuck 0:2ff18de8d48b 581 }
dudmuck 0:2ff18de8d48b 582
dudmuck 11:50d0558a4e37 583 void cmd_op(uint8_t idx)
dudmuck 11:50d0558a4e37 584 {
dudmuck 11:50d0558a4e37 585 int i, dbm;
dudmuck 11:50d0558a4e37 586 RegPdsTrim1_t pds_trim;
dudmuck 11:50d0558a4e37 587 uint8_t adr;
dudmuck 11:50d0558a4e37 588 if (radio.type == SX1276)
dudmuck 11:50d0558a4e37 589 adr = REG_PDSTRIM1_SX1276;
dudmuck 11:50d0558a4e37 590 else
dudmuck 11:50d0558a4e37 591 adr = REG_PDSTRIM1_SX1272;
dudmuck 11:50d0558a4e37 592
dudmuck 11:50d0558a4e37 593 pds_trim.octet = radio.read_reg(adr);
dudmuck 11:50d0558a4e37 594
dudmuck 11:50d0558a4e37 595 if (pcbuf[idx] >= '0' && (pcbuf[idx] <= '9' || pcbuf[idx] == '-')) {
dudmuck 11:50d0558a4e37 596 sscanf(pcbuf+idx, "%d", &i);
dudmuck 11:50d0558a4e37 597 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 11:50d0558a4e37 598 /* PABOOST used: +2dbm to +17, or +20 */
dudmuck 11:50d0558a4e37 599 if (i == 20) {
dudmuck 11:50d0558a4e37 600 printf("+20dBm PADAC bias\r\n");
dudmuck 11:50d0558a4e37 601 i -= 3;
dudmuck 11:50d0558a4e37 602 pds_trim.bits.prog_txdac = 7;
dudmuck 11:50d0558a4e37 603 radio.write_reg(adr, pds_trim.octet);
dudmuck 11:50d0558a4e37 604 }
dudmuck 11:50d0558a4e37 605 if (i > 1)
dudmuck 11:50d0558a4e37 606 radio.RegPaConfig.bits.OutputPower = i - 2;
dudmuck 11:50d0558a4e37 607 } else {
dudmuck 11:50d0558a4e37 608 /* RFO used: -1 to +14dbm */
dudmuck 11:50d0558a4e37 609 if (i < 15)
dudmuck 11:50d0558a4e37 610 radio.RegPaConfig.bits.OutputPower = i + 1;
dudmuck 11:50d0558a4e37 611 }
dudmuck 11:50d0558a4e37 612 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
dudmuck 11:50d0558a4e37 613 }
dudmuck 11:50d0558a4e37 614 radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
dudmuck 11:50d0558a4e37 615 if (radio.RegPaConfig.bits.PaSelect) {
dudmuck 11:50d0558a4e37 616 printf("PA_BOOST ");
dudmuck 11:50d0558a4e37 617 dbm = radio.RegPaConfig.bits.OutputPower + pds_trim.bits.prog_txdac - 2;
dudmuck 11:50d0558a4e37 618 } else {
dudmuck 11:50d0558a4e37 619 printf("RFO ");
dudmuck 11:50d0558a4e37 620 dbm = radio.RegPaConfig.bits.OutputPower - 1;
dudmuck 11:50d0558a4e37 621 }
dudmuck 11:50d0558a4e37 622 printf("OutputPower:%ddBm\r\n", dbm);
dudmuck 11:50d0558a4e37 623 }
dudmuck 11:50d0558a4e37 624
dudmuck 12:9a8c13c4298b 625 void cmd_set_time(uint8_t idx)
dudmuck 12:9a8c13c4298b 626 {
dudmuck 12:9a8c13c4298b 627 set_time(0);
dudmuck 13:fa2095be01c4 628 printf("time:%" PRIu32 "\r\n", time(NULL));
dudmuck 12:9a8c13c4298b 629 }
dudmuck 12:9a8c13c4298b 630
dudmuck 13:fa2095be01c4 631
dudmuck 13:fa2095be01c4 632 void cmd_pwm(uint8_t idx)
dudmuck 12:9a8c13c4298b 633 {
dudmuck 13:fa2095be01c4 634 ota_mote_t* mote;
dudmuck 12:9a8c13c4298b 635 int i;
dudmuck 13:fa2095be01c4 636 unsigned dev_addr, p, d;
dudmuck 13:fa2095be01c4 637
dudmuck 13:fa2095be01c4 638 if (sscanf(pcbuf+idx, "%x %u %u", &dev_addr, &p, &d) != 3) {
dudmuck 13:fa2095be01c4 639 printf("parse fail\r\n");
dudmuck 13:fa2095be01c4 640 return;
dudmuck 13:fa2095be01c4 641 }
dudmuck 12:9a8c13c4298b 642 for (i = 0; i < N_MOTES; i++) {
dudmuck 12:9a8c13c4298b 643 if (motes[i].dev_addr == dev_addr) {
dudmuck 12:9a8c13c4298b 644 break;
dudmuck 12:9a8c13c4298b 645 }
dudmuck 12:9a8c13c4298b 646 }
dudmuck 12:9a8c13c4298b 647 if (i == N_MOTES) {
dudmuck 12:9a8c13c4298b 648 printf("mote %x not found\r\n", dev_addr);
dudmuck 12:9a8c13c4298b 649 return;
dudmuck 12:9a8c13c4298b 650 }
dudmuck 12:9a8c13c4298b 651
dudmuck 12:9a8c13c4298b 652 mote = &motes[i];
dudmuck 12:9a8c13c4298b 653
dudmuck 12:9a8c13c4298b 654 mote->user_downlink_length = 0;
dudmuck 13:fa2095be01c4 655 LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_PWM;
dudmuck 13:fa2095be01c4 656 LoRaWan::user_downlink[mote->user_downlink_length++] = p;
dudmuck 13:fa2095be01c4 657 LoRaWan::user_downlink[mote->user_downlink_length++] = d;
dudmuck 12:9a8c13c4298b 658
dudmuck 13:fa2095be01c4 659 printf("period:%u duty:%u to mote %" PRIx32 "\r\n", p, d, mote->dev_addr);
dudmuck 13:fa2095be01c4 660 }
dudmuck 13:fa2095be01c4 661
dudmuck 13:fa2095be01c4 662 void cmd_beacon_pwm(uint8_t idx)
dudmuck 13:fa2095be01c4 663 {
dudmuck 13:fa2095be01c4 664 unsigned p, d;
dudmuck 13:fa2095be01c4 665 if (sscanf(pcbuf+idx, "%u %u", &p, &d) != 2) {
dudmuck 13:fa2095be01c4 666 printf("parse fail\r\n");
dudmuck 13:fa2095be01c4 667 return;
dudmuck 13:fa2095be01c4 668 }
dudmuck 13:fa2095be01c4 669
dudmuck 13:fa2095be01c4 670 beacon_payload[0] = CMD_PWM;
dudmuck 13:fa2095be01c4 671 beacon_payload[1] = p;
dudmuck 13:fa2095be01c4 672 beacon_payload[2] = d;
dudmuck 13:fa2095be01c4 673
dudmuck 13:fa2095be01c4 674 printf("period:%u duty:%u\r\n", p, d);
dudmuck 12:9a8c13c4298b 675 }
dudmuck 12:9a8c13c4298b 676
dudmuck 0:2ff18de8d48b 677 void cmd_help(uint8_t);
dudmuck 0:2ff18de8d48b 678
dudmuck 0:2ff18de8d48b 679 typedef struct {
dudmuck 0:2ff18de8d48b 680 const char* const cmd;
dudmuck 0:2ff18de8d48b 681 void (*handler)(uint8_t args_at);
dudmuck 0:2ff18de8d48b 682 const char* const arg_descr;
dudmuck 0:2ff18de8d48b 683 const char* const description;
dudmuck 0:2ff18de8d48b 684 } menu_item_t;
dudmuck 0:2ff18de8d48b 685
dudmuck 0:2ff18de8d48b 686 const menu_item_t menu_items[] =
dudmuck 0:2ff18de8d48b 687 { /* after first character, command names must be [A-Za-z] */
dudmuck 0:2ff18de8d48b 688 { "?", cmd_help, "","show available commands"},
dudmuck 0:2ff18de8d48b 689 { ".", cmd_status, "","read status"},
dudmuck 0:2ff18de8d48b 690 { "b", cmd_beacon_payload, "<%x>","set beacon payload"},
dudmuck 0:2ff18de8d48b 691 { "sb", cmd_skip_beacon, "<%d>","skip beacons"},
dudmuck 0:2ff18de8d48b 692 { "list", cmd_list_motes, "","list active motes"},
dudmuck 0:2ff18de8d48b 693 { "dl", cmd_send_downlink, "[%x %s]","send downlink <mote-hex-dev-addr> <hex-payload>"},
dudmuck 9:a0ce66c18ec0 694 { "rxr", cmd_rx_restart, "", "restart RX"},
dudmuck 9:a0ce66c18ec0 695 { "brgb", cmd_beacon_rgb, "%u %u %u", "load RGB command into next beacon" },
dudmuck 9:a0ce66c18ec0 696 { "rgb", cmd_downlink_rgb, "%x %u %u %u", "load RGB command to mote"},
dudmuck 9:a0ce66c18ec0 697 { "bgpo", cmd_beacon_gpo, "%d", "load output pin command into next beacon"},
dudmuck 9:a0ce66c18ec0 698 { "gpo", cmd_downlink_gpo, "%x %d", "load output pin command to mote"},
dudmuck 12:9a8c13c4298b 699 { "op", cmd_op, "<dBm>","(TX) get/set TX power"},
dudmuck 13:fa2095be01c4 700 { "tz", cmd_set_time, "", "set seconds to zero"},
dudmuck 13:fa2095be01c4 701 { "p", cmd_pwm, "%x %u %u", "send pwm period, duty to dev_addr"},
dudmuck 13:fa2095be01c4 702 { "bp", cmd_beacon_pwm, "%u %u", "send pwm period, duty on beacon"},
dudmuck 0:2ff18de8d48b 703 { NULL, NULL, NULL, NULL }
dudmuck 0:2ff18de8d48b 704 };
dudmuck 0:2ff18de8d48b 705
dudmuck 0:2ff18de8d48b 706 void cmd_help(uint8_t args_at)
dudmuck 0:2ff18de8d48b 707 {
dudmuck 0:2ff18de8d48b 708 int i;
dudmuck 0:2ff18de8d48b 709
dudmuck 0:2ff18de8d48b 710 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 711 printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
dudmuck 0:2ff18de8d48b 712 }
dudmuck 0:2ff18de8d48b 713
dudmuck 0:2ff18de8d48b 714 }
dudmuck 0:2ff18de8d48b 715
dudmuck 0:2ff18de8d48b 716 void
dudmuck 0:2ff18de8d48b 717 console()
dudmuck 0:2ff18de8d48b 718 {
dudmuck 0:2ff18de8d48b 719 int i;
dudmuck 0:2ff18de8d48b 720 uint8_t user_cmd_len;
dudmuck 0:2ff18de8d48b 721
dudmuck 0:2ff18de8d48b 722 if (pcbuf_len < 0) { // ctrl-C
dudmuck 0:2ff18de8d48b 723 //printf("abort\r\n");
dudmuck 0:2ff18de8d48b 724 return;
dudmuck 0:2ff18de8d48b 725 }
dudmuck 0:2ff18de8d48b 726 if (pcbuf_len == 0)
dudmuck 0:2ff18de8d48b 727 return;
dudmuck 0:2ff18de8d48b 728
dudmuck 0:2ff18de8d48b 729 printf("\r\n");
dudmuck 0:2ff18de8d48b 730
dudmuck 0:2ff18de8d48b 731 /* get end of user-entered command */
dudmuck 0:2ff18de8d48b 732 user_cmd_len = 1; // first character can be any character
dudmuck 0:2ff18de8d48b 733 for (i = 1; i <= pcbuf_len; i++) {
dudmuck 0:2ff18de8d48b 734 if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
dudmuck 0:2ff18de8d48b 735 user_cmd_len = i;
dudmuck 0:2ff18de8d48b 736 break;
dudmuck 0:2ff18de8d48b 737 }
dudmuck 0:2ff18de8d48b 738 }
dudmuck 0:2ff18de8d48b 739
dudmuck 0:2ff18de8d48b 740 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:2ff18de8d48b 741 int mi_len = strlen(menu_items[i].cmd);
dudmuck 0:2ff18de8d48b 742
dudmuck 0:2ff18de8d48b 743 if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
dudmuck 0:2ff18de8d48b 744 while (pcbuf[mi_len] == ' ') // skip past spaces
dudmuck 0:2ff18de8d48b 745 mi_len++;
dudmuck 0:2ff18de8d48b 746 menu_items[i].handler(mi_len);
dudmuck 0:2ff18de8d48b 747 break;
dudmuck 0:2ff18de8d48b 748 }
dudmuck 0:2ff18de8d48b 749 }
dudmuck 0:2ff18de8d48b 750
dudmuck 0:2ff18de8d48b 751 pcbuf_len = 0;
dudmuck 0:2ff18de8d48b 752 printf("> ");
dudmuck 0:2ff18de8d48b 753 fflush(stdout);
dudmuck 0:2ff18de8d48b 754 }
dudmuck 0:2ff18de8d48b 755
dudmuck 0:2ff18de8d48b 756 int main()
dudmuck 0:2ff18de8d48b 757 {
dudmuck 0:2ff18de8d48b 758 Thread eventThread;
dudmuck 3:7c01b8978638 759 pc.baud(38400);
dudmuck 12:9a8c13c4298b 760 printf("\r\nreset %f\r\n", radio.get_frf_MHz());
dudmuck 11:50d0558a4e37 761 set_time(0);
dudmuck 0:2ff18de8d48b 762
dudmuck 0:2ff18de8d48b 763 radio.hw_reset();
dudmuck 12:9a8c13c4298b 764 printf("%fMHz\r\n", radio.get_frf_MHz());
dudmuck 0:2ff18de8d48b 765
dudmuck 10:6783623cc886 766 //#ifndef TARGET_DISCO_L072CZ_LRWAN1
dudmuck 10:6783623cc886 767 #ifndef TYPE_ABZ
dudmuck 0:2ff18de8d48b 768 rfsw.input();
dudmuck 0:2ff18de8d48b 769 if (rfsw.read()) {
dudmuck 0:2ff18de8d48b 770 printf("LAS\r\n");
dudmuck 0:2ff18de8d48b 771 /* LAS HF=PA_BOOST LF=RFO */
dudmuck 0:2ff18de8d48b 772 radio.RegPaConfig.bits.PaSelect = 1;
dudmuck 0:2ff18de8d48b 773 } else {
dudmuck 0:2ff18de8d48b 774 printf("MAS\r\n");
dudmuck 0:2ff18de8d48b 775 radio.RegPaConfig.bits.PaSelect = 0;
dudmuck 0:2ff18de8d48b 776 }
dudmuck 0:2ff18de8d48b 777 rfsw.output();
dudmuck 5:ceacaa560cfd 778 #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */
dudmuck 0:2ff18de8d48b 779
dudmuck 0:2ff18de8d48b 780 radio.rf_switch = rfsw_callback;
dudmuck 0:2ff18de8d48b 781
dudmuck 0:2ff18de8d48b 782 init_radio();
dudmuck 0:2ff18de8d48b 783
dudmuck 0:2ff18de8d48b 784 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 0:2ff18de8d48b 785
dudmuck 0:2ff18de8d48b 786 eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
dudmuck 0:2ff18de8d48b 787 LoRaWan::init();
dudmuck 0:2ff18de8d48b 788
dudmuck 0:2ff18de8d48b 789 timer.start();
dudmuck 0:2ff18de8d48b 790 tim_init();
dudmuck 4:7e743e402681 791
dudmuck 4:7e743e402681 792 pc.attach(&rx_isr);
dudmuck 9:a0ce66c18ec0 793
dudmuck 0:2ff18de8d48b 794
dudmuck 0:2ff18de8d48b 795 for (;;) {
dudmuck 0:2ff18de8d48b 796 console();
dudmuck 1:107435401168 797
dudmuck 1:107435401168 798 if (radio.dio0) {
dudmuck 3:7c01b8978638 799 if (get_tx_done) {
dudmuck 3:7c01b8978638 800 get_tx_done = false;
dudmuck 3:7c01b8978638 801 lora.RegIrqFlags.octet = 0;
dudmuck 3:7c01b8978638 802 lora.RegIrqFlags.bits.TxDone = 1;
dudmuck 3:7c01b8978638 803 radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
dudmuck 3:7c01b8978638 804
dudmuck 3:7c01b8978638 805 if (restore_tx_invert) {
dudmuck 3:7c01b8978638 806 lora.invert_tx(false);
dudmuck 3:7c01b8978638 807 restore_tx_invert = false;
dudmuck 3:7c01b8978638 808 }
dudmuck 3:7c01b8978638 809
dudmuck 3:7c01b8978638 810 if (restore_header_mode) {
dudmuck 3:7c01b8978638 811 lora.setHeaderMode(false);
dudmuck 3:7c01b8978638 812 restore_header_mode = false;
dudmuck 3:7c01b8978638 813 }
dudmuck 12:9a8c13c4298b 814
dudmuck 3:7c01b8978638 815 lora.start_rx(RF_OPMODE_RECEIVER);
dudmuck 12:9a8c13c4298b 816
dudmuck 12:9a8c13c4298b 817 if (beacon_guard) { // beacon done transmitting
dudmuck 12:9a8c13c4298b 818 measure_ambient();
dudmuck 12:9a8c13c4298b 819 beacon_guard = false;
dudmuck 12:9a8c13c4298b 820 }
dudmuck 3:7c01b8978638 821 } else {
dudmuck 3:7c01b8978638 822 if (!beacon_guard)
dudmuck 3:7c01b8978638 823 service_radio();
dudmuck 3:7c01b8978638 824 }
dudmuck 12:9a8c13c4298b 825 } else if (get_tx_done) {
dudmuck 12:9a8c13c4298b 826 /* dio0 not yet asserted */
dudmuck 12:9a8c13c4298b 827 uint32_t since = timer.read_ms() - tx_ms;
dudmuck 12:9a8c13c4298b 828 if (since > 4000) {
dudmuck 12:9a8c13c4298b 829 radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
dudmuck 12:9a8c13c4298b 830 if (radio.RegOpMode.bits.Mode == RF_OPMODE_STANDBY) {
dudmuck 12:9a8c13c4298b 831 /* done transmitting, but dio0 not asserted */
dudmuck 12:9a8c13c4298b 832 printf("txDone but no dio0\r\n");
dudmuck 12:9a8c13c4298b 833 radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
dudmuck 12:9a8c13c4298b 834 if (radio.RegDioMapping1.bits.Dio0Mapping != 1) {
dudmuck 12:9a8c13c4298b 835 printf("dio0mapping-fail\r\n");
dudmuck 12:9a8c13c4298b 836 radio.RegDioMapping1.bits.Dio0Mapping = 1;
dudmuck 12:9a8c13c4298b 837 radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
dudmuck 12:9a8c13c4298b 838 }
dudmuck 12:9a8c13c4298b 839 }
dudmuck 12:9a8c13c4298b 840 }
dudmuck 12:9a8c13c4298b 841 }
dudmuck 12:9a8c13c4298b 842
dudmuck 12:9a8c13c4298b 843 if (LoRaWan::do_downlink && !beacon_guard) {
dudmuck 12:9a8c13c4298b 844 uint32_t since = timer.read_ms() - LoRaWan::rx_ms;
dudmuck 12:9a8c13c4298b 845 if (since > 110)
dudmuck 13:fa2095be01c4 846 printf("since-tx:%lu\r\n", since);
dudmuck 12:9a8c13c4298b 847 if (since > 500) {
dudmuck 12:9a8c13c4298b 848 LoRaWan::do_downlink = false;
dudmuck 12:9a8c13c4298b 849 printf("stalled-tx\r\n");
dudmuck 12:9a8c13c4298b 850 cmd_rx_restart(0);
dudmuck 12:9a8c13c4298b 851 }
dudmuck 1:107435401168 852 }
dudmuck 8:307f7faeb594 853
dudmuck 0:2ff18de8d48b 854 } // ..for(;;)
dudmuck 0:2ff18de8d48b 855 }