end node on synchronous star LoRa network.

Dependencies:   SX127x sx12xx_hal TSL2561

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.

This project for use with LoRaWAN_singlechannel_gateway project.

Alternately gateway running on raspberry pi can be used as gateway.

LoRaWAN on single radio channel

Network description is at gateway project page. Synchronous star network.

Hardware Support

This project supports SX1276 and SX1272, sx126x kit, sx126x shield, and sx128x 2.4GHz. The ST board B-L072Z-LRWAN1 is also supported (TypeABZ module). When B-L072Z-LRWAN1 target is selected, TARGET_DISCO_L072CZ_LRWAN1 is defined by tools, allowing correct radio driver configuration for this platform. Alternately, any mbed board that can use LoRa radio shield board should work, but NUCLEO boards are tested.

End-node Unique ID

DevEUI is created from CPU serial number. AppEUI and AppKey are declared as software constants.

End-node Configuration

Data rate definition LORAMAC_DEFAULT_DATARATE configured in LoRaMac-definitions.h. See gateway project page for configuration of gateway.
LoRaWAN addressing is configured in Comissioning.h; only OTA mode is functional.
Header file board/lora_config.h, selects application layer options (i.e. sensors) to be compiled in.

Serial Interface

Serial port operates at 115200bps.
Application layer single_us915_main.cpp User button triggers uplink (i.e. blue button on nucleo board), or jumper enables continuously sends repeated uplink packets. The MAC layer holds each uplink request until the allocated timeslot.

commandargumentsdescription
?-print available commands
. (period)-print status (DevEUI, DevAddr, etc)
ullength integerset payload length of test uplink packets

sensor demo

Selected grove sensors may be plugged into SX1272 shield.
To enable, edit lora_config.h to define SENSORS.

Sensor connections on SX1272MB2xAS:

D8 D9: buttonRX TX: (unused)A3 A4: Rotary Angle Sensor
D6 D7: RGB LEDSCL SDA: digital light sensorA1 A2: Rotary Angle Sensor

Digital input pin, state reported via uplink: PC8
Digital output pin, controlled via downlink: PC6
PWM out: PB_10

Jumper enables auto-repeated transmit: PC10 and PC12 on NUCLEO board, located on end of morpho headers nearby JP4.

Committer:
dudmuck
Date:
Fri Aug 11 15:34:49 2017 -0700
Revision:
26:8227a4edb216
Parent:
25:fed9d5b77183
Child:
27:c8b432b2a4a8
sensor sampling asychronous to uplink activity

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dudmuck 0:8f0d0ae0a077 1 /*
dudmuck 0:8f0d0ae0a077 2 / _____) _ | |
dudmuck 0:8f0d0ae0a077 3 ( (____ _____ ____ _| |_ _____ ____| |__
dudmuck 0:8f0d0ae0a077 4 \____ \| ___ | (_ _) ___ |/ ___) _ \
dudmuck 0:8f0d0ae0a077 5 _____) ) ____| | | || |_| ____( (___| | | |
dudmuck 0:8f0d0ae0a077 6 (______/|_____)_|_|_| \__)_____)\____)_| |_|
dudmuck 0:8f0d0ae0a077 7 (C)2015 Semtech
dudmuck 0:8f0d0ae0a077 8
dudmuck 0:8f0d0ae0a077 9 Description: LoRaMac classA device implementation
dudmuck 0:8f0d0ae0a077 10
dudmuck 0:8f0d0ae0a077 11 License: Revised BSD License, see LICENSE.TXT file include in the project
dudmuck 0:8f0d0ae0a077 12
dudmuck 0:8f0d0ae0a077 13 Maintainer: Miguel Luis and Gregory Cristian
dudmuck 0:8f0d0ae0a077 14 */
dudmuck 0:8f0d0ae0a077 15 #include "mbed.h"
dudmuck 0:8f0d0ae0a077 16 #include "board.h"
dudmuck 0:8f0d0ae0a077 17 #include "radio.h"
dudmuck 0:8f0d0ae0a077 18
dudmuck 0:8f0d0ae0a077 19 #include "LoRaMac.h"
dudmuck 0:8f0d0ae0a077 20 #include "Commissioning.h"
dudmuck 11:4c337f5bbe4c 21 #include "commands.h"
dudmuck 0:8f0d0ae0a077 22
dudmuck 23:a862b5601663 23 #ifdef ENABLE_LUMINOSITY
dudmuck 12:ed33c53afcaf 24 #include "TSL2561.h"
dudmuck 12:ed33c53afcaf 25 //TSL2561 tsl2561(TSL2561_ADDR_FLOAT); // https://developer.mbed.org/components/Grove-Digital-Light-Sensor/
dudmuck 12:ed33c53afcaf 26 TSL2561 tsl2561(TSL2561_ADDR_LOW);
dudmuck 23:a862b5601663 27 #endif
dudmuck 23:a862b5601663 28
dudmuck 23:a862b5601663 29 #ifdef ENABLE_RGB
dudmuck 23:a862b5601663 30 #include "ChainableLED.h"
dudmuck 23:a862b5601663 31 ChainableLED rgb(D6, D7, 1); // https://developer.mbed.org/components/Grove-Chainable-RGB-LED/
dudmuck 10:00997daeb0c0 32 #endif
dudmuck 10:00997daeb0c0 33
dudmuck 25:fed9d5b77183 34 #ifdef TYPE_ABZ
dudmuck 25:fed9d5b77183 35 InterruptIn user_button(PB_2);
dudmuck 25:fed9d5b77183 36 #else
dudmuck 25:fed9d5b77183 37 InterruptIn user_button(USER_BUTTON);
dudmuck 25:fed9d5b77183 38 #endif
dudmuck 25:fed9d5b77183 39
dudmuck 23:a862b5601663 40 #ifdef SENSORS
dudmuck 23:a862b5601663 41 #ifdef TYPE_ABZ /* DISCO_L072CZ_LRWAN1 */
dudmuck 23:a862b5601663 42 PwmOut pwm(PB_5);
dudmuck 23:a862b5601663 43 DigitalIn pc8_in(PB_13);
dudmuck 23:a862b5601663 44 DigitalOut pc6_out(PB_14);
dudmuck 23:a862b5601663 45 AnalogIn a0(PA_0);
dudmuck 23:a862b5601663 46 AnalogIn a2(PA_4);
dudmuck 23:a862b5601663 47 #else
dudmuck 23:a862b5601663 48 PwmOut pwm(PB_11);
dudmuck 23:a862b5601663 49 DigitalIn pc8_in(PC_8);
dudmuck 23:a862b5601663 50 DigitalOut pc6_out(PC_6);
dudmuck 23:a862b5601663 51 AnalogIn a1(A1); // https://developer.mbed.org/teams/Seeed/wiki/Potentiometer
dudmuck 23:a862b5601663 52 AnalogIn a3(A3); // https://developer.mbed.org/teams/Seeed/wiki/Potentiometer
dudmuck 23:a862b5601663 53 #endif
dudmuck 26:8227a4edb216 54
dudmuck 26:8227a4edb216 55 #define N_SAMPLES 4
dudmuck 26:8227a4edb216 56 struct sens {
dudmuck 26:8227a4edb216 57 uint16_t ain_A, ain_B;
dudmuck 26:8227a4edb216 58 uint8_t pins;
dudmuck 26:8227a4edb216 59 } sensor[N_SAMPLES];
dudmuck 26:8227a4edb216 60 uint8_t sensor_in_idx, sensor_out_idx;
dudmuck 26:8227a4edb216 61
dudmuck 26:8227a4edb216 62 void sensor_poll()
dudmuck 26:8227a4edb216 63 {
dudmuck 26:8227a4edb216 64 #ifdef TYPE_ABZ
dudmuck 26:8227a4edb216 65 sensor[sensor_in_idx].ain_A = a0.read_u16();
dudmuck 26:8227a4edb216 66 sensor[sensor_in_idx].ain_B = a2.read_u16();
dudmuck 26:8227a4edb216 67 #else
dudmuck 26:8227a4edb216 68 sensor[sensor_in_idx].ain_A = a1.read_u16();
dudmuck 26:8227a4edb216 69 sensor[sensor_in_idx].ain_B = a3.read_u16();
dudmuck 26:8227a4edb216 70 #endif
dudmuck 26:8227a4edb216 71 sensor[sensor_in_idx].pins = pc8_in.read();
dudmuck 26:8227a4edb216 72 sensor[sensor_in_idx].pins <<= 1;
dudmuck 26:8227a4edb216 73 sensor[sensor_in_idx].pins = pc6_out.read();
dudmuck 26:8227a4edb216 74 if (++sensor_in_idx == N_SAMPLES)
dudmuck 26:8227a4edb216 75 sensor_in_idx = 0;
dudmuck 26:8227a4edb216 76 }
dudmuck 26:8227a4edb216 77 LowPowerTicker sensor_ticker;
dudmuck 23:a862b5601663 78 #endif /* SENSORS */
dudmuck 23:a862b5601663 79
dudmuck 20:42839629a5dc 80 DigitalOut pc2(PC_2);
dudmuck 12:ed33c53afcaf 81
dudmuck 23:a862b5601663 82 #if defined(TARGET_DISCO_L072CZ_LRWAN1) || ( defined(TARGET_NUCLEO_L073RZ) && defined(TYPE_ABZ) )
dudmuck 23:a862b5601663 83 DigitalOut jumper_out(PA_11);
dudmuck 23:a862b5601663 84 InterruptIn jumper_in(PA_12);
dudmuck 23:a862b5601663 85 #else
dudmuck 23:a862b5601663 86 DigitalOut jumper_out(PC_10);
dudmuck 23:a862b5601663 87 InterruptIn jumper_in(PC_12);
dudmuck 23:a862b5601663 88 #endif /* murata */
dudmuck 10:00997daeb0c0 89
dudmuck 0:8f0d0ae0a077 90 char pcbuf[128];
dudmuck 0:8f0d0ae0a077 91 int pcbuf_len;
dudmuck 0:8f0d0ae0a077 92
dudmuck 0:8f0d0ae0a077 93 /*!
dudmuck 0:8f0d0ae0a077 94 * Defines the application data transmission duty cycle. 5s, value in [ms].
dudmuck 0:8f0d0ae0a077 95 */
dudmuck 0:8f0d0ae0a077 96 #define APP_TX_DUTYCYCLE 5000
dudmuck 0:8f0d0ae0a077 97
dudmuck 0:8f0d0ae0a077 98 /*!
dudmuck 0:8f0d0ae0a077 99 * Defines a random delay for application data transmission duty cycle. 1s,
dudmuck 0:8f0d0ae0a077 100 * value in [ms].
dudmuck 0:8f0d0ae0a077 101 */
dudmuck 0:8f0d0ae0a077 102 #define APP_TX_DUTYCYCLE_RND 1000
dudmuck 0:8f0d0ae0a077 103
dudmuck 0:8f0d0ae0a077 104 /*!
dudmuck 0:8f0d0ae0a077 105 * Default datarate
dudmuck 0:8f0d0ae0a077 106 */
dudmuck 0:8f0d0ae0a077 107 #define LORAWAN_DEFAULT_DATARATE DR_10
dudmuck 0:8f0d0ae0a077 108
dudmuck 0:8f0d0ae0a077 109 /*!
dudmuck 0:8f0d0ae0a077 110 * LoRaWAN confirmed messages
dudmuck 0:8f0d0ae0a077 111 */
dudmuck 0:8f0d0ae0a077 112 #define LORAWAN_CONFIRMED_MSG_ON true
dudmuck 0:8f0d0ae0a077 113
dudmuck 0:8f0d0ae0a077 114 /*!
dudmuck 0:8f0d0ae0a077 115 * LoRaWAN application port
dudmuck 0:8f0d0ae0a077 116 */
dudmuck 0:8f0d0ae0a077 117 #define LORAWAN_APP_PORT 15
dudmuck 0:8f0d0ae0a077 118
dudmuck 0:8f0d0ae0a077 119 /*!
dudmuck 0:8f0d0ae0a077 120 * User application data buffer size
dudmuck 0:8f0d0ae0a077 121 */
dudmuck 0:8f0d0ae0a077 122 #if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
dudmuck 0:8f0d0ae0a077 123 #define LORAWAN_APP_DATA_SIZE 6
dudmuck 0:8f0d0ae0a077 124
dudmuck 0:8f0d0ae0a077 125 #else
dudmuck 0:8f0d0ae0a077 126 #define LORAWAN_APP_DATA_SIZE 1
dudmuck 0:8f0d0ae0a077 127
dudmuck 0:8f0d0ae0a077 128 #endif
dudmuck 0:8f0d0ae0a077 129
dudmuck 0:8f0d0ae0a077 130 static uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
dudmuck 0:8f0d0ae0a077 131 static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI;
dudmuck 0:8f0d0ae0a077 132 static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY;
dudmuck 0:8f0d0ae0a077 133
dudmuck 0:8f0d0ae0a077 134 #if( OVER_THE_AIR_ACTIVATION == 0 )
dudmuck 0:8f0d0ae0a077 135
dudmuck 0:8f0d0ae0a077 136 static uint8_t NwkSKey[] = LORAWAN_NWKSKEY;
dudmuck 0:8f0d0ae0a077 137 static uint8_t AppSKey[] = LORAWAN_APPSKEY;
dudmuck 0:8f0d0ae0a077 138
dudmuck 0:8f0d0ae0a077 139 /*!
dudmuck 0:8f0d0ae0a077 140 * Device address
dudmuck 0:8f0d0ae0a077 141 */
dudmuck 0:8f0d0ae0a077 142 static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
dudmuck 0:8f0d0ae0a077 143
dudmuck 0:8f0d0ae0a077 144 #endif
dudmuck 0:8f0d0ae0a077 145
dudmuck 0:8f0d0ae0a077 146 /*!
dudmuck 0:8f0d0ae0a077 147 * Application port
dudmuck 0:8f0d0ae0a077 148 */
dudmuck 23:a862b5601663 149 #if defined(SENSORS)
dudmuck 10:00997daeb0c0 150 static uint8_t AppPort = SENSOR_PORT;
dudmuck 10:00997daeb0c0 151 #else
dudmuck 10:00997daeb0c0 152 static uint8_t AppPort = LORAWAN_APP_PORT;
dudmuck 10:00997daeb0c0 153 #endif
dudmuck 0:8f0d0ae0a077 154
dudmuck 0:8f0d0ae0a077 155 /*!
dudmuck 0:8f0d0ae0a077 156 * User application data size
dudmuck 0:8f0d0ae0a077 157 */
dudmuck 0:8f0d0ae0a077 158 static uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE;
dudmuck 0:8f0d0ae0a077 159 static unsigned int uplink_length; // user assigned
dudmuck 0:8f0d0ae0a077 160
dudmuck 0:8f0d0ae0a077 161 /*!
dudmuck 0:8f0d0ae0a077 162 * User application data buffer size
dudmuck 0:8f0d0ae0a077 163 */
dudmuck 0:8f0d0ae0a077 164 #define LORAWAN_APP_DATA_MAX_SIZE 128
dudmuck 0:8f0d0ae0a077 165
dudmuck 0:8f0d0ae0a077 166 /*!
dudmuck 0:8f0d0ae0a077 167 * User application data
dudmuck 0:8f0d0ae0a077 168 */
dudmuck 0:8f0d0ae0a077 169 static uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE];
dudmuck 0:8f0d0ae0a077 170
dudmuck 0:8f0d0ae0a077 171 /*!
dudmuck 0:8f0d0ae0a077 172 * Indicates if the node is sending confirmed or unconfirmed messages
dudmuck 0:8f0d0ae0a077 173 */
dudmuck 0:8f0d0ae0a077 174 static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
dudmuck 0:8f0d0ae0a077 175
dudmuck 0:8f0d0ae0a077 176 /*!
dudmuck 0:8f0d0ae0a077 177 * Defines the application data transmission duty cycle
dudmuck 0:8f0d0ae0a077 178 */
dudmuck 0:8f0d0ae0a077 179 static uint32_t TxDutyCycleTime;
dudmuck 0:8f0d0ae0a077 180
dudmuck 0:8f0d0ae0a077 181 /*!
dudmuck 0:8f0d0ae0a077 182 * Timer to handle the application data transmission duty cycle
dudmuck 0:8f0d0ae0a077 183 */
dudmuck 17:3215f12051f9 184 LowPowerTimeout TxNextPacketTimeout;
dudmuck 0:8f0d0ae0a077 185
dudmuck 0:8f0d0ae0a077 186 volatile bool send_at_beacon;
dudmuck 0:8f0d0ae0a077 187 volatile bool awaiting_mcps_indic;
dudmuck 21:500ff43d8424 188 bool join_retry;
dudmuck 0:8f0d0ae0a077 189 /*!
dudmuck 0:8f0d0ae0a077 190 * Specifies the state of the application LED
dudmuck 0:8f0d0ae0a077 191 */
dudmuck 0:8f0d0ae0a077 192 static bool AppLedStateOn = false;
dudmuck 0:8f0d0ae0a077 193
dudmuck 0:8f0d0ae0a077 194 /*!
dudmuck 0:8f0d0ae0a077 195 * Device states
dudmuck 0:8f0d0ae0a077 196 */
dudmuck 0:8f0d0ae0a077 197 static enum eDeviceState
dudmuck 0:8f0d0ae0a077 198 {
dudmuck 0:8f0d0ae0a077 199 DEVICE_STATE_INIT,
dudmuck 0:8f0d0ae0a077 200 DEVICE_STATE_JOIN,
dudmuck 0:8f0d0ae0a077 201 DEVICE_STATE_CYCLE,
dudmuck 16:915815632c1f 202 DEVICE_STATE_SLEEP,
dudmuck 18:9ac71c0eb70d 203 DEVICE_STATE_REJOIN,
dudmuck 18:9ac71c0eb70d 204 DEVICE_STATE_UPLINK
dudmuck 0:8f0d0ae0a077 205 }DeviceState;
dudmuck 0:8f0d0ae0a077 206
dudmuck 0:8f0d0ae0a077 207 /*!
dudmuck 0:8f0d0ae0a077 208 * Strucure containing the Uplink status
dudmuck 0:8f0d0ae0a077 209 */
dudmuck 0:8f0d0ae0a077 210 struct sLoRaMacUplinkStatus
dudmuck 0:8f0d0ae0a077 211 {
dudmuck 0:8f0d0ae0a077 212 uint8_t Acked;
dudmuck 0:8f0d0ae0a077 213 //int8_t Datarate;
dudmuck 0:8f0d0ae0a077 214 uint16_t UplinkCounter;
dudmuck 0:8f0d0ae0a077 215 uint8_t Port;
dudmuck 0:8f0d0ae0a077 216 uint8_t *Buffer;
dudmuck 0:8f0d0ae0a077 217 uint8_t BufferSize;
dudmuck 0:8f0d0ae0a077 218 }LoRaMacUplinkStatus;
dudmuck 0:8f0d0ae0a077 219 //volatile bool UplinkStatusUpdated = false;
dudmuck 0:8f0d0ae0a077 220
dudmuck 0:8f0d0ae0a077 221 /*!
dudmuck 0:8f0d0ae0a077 222 * Strucure containing the Downlink status
dudmuck 0:8f0d0ae0a077 223 */
dudmuck 0:8f0d0ae0a077 224 struct sLoRaMacDownlinkStatus
dudmuck 0:8f0d0ae0a077 225 {
dudmuck 0:8f0d0ae0a077 226 int16_t Rssi;
dudmuck 0:8f0d0ae0a077 227 int8_t Snr;
dudmuck 0:8f0d0ae0a077 228 uint16_t DownlinkCounter;
dudmuck 0:8f0d0ae0a077 229 bool RxData;
dudmuck 0:8f0d0ae0a077 230 uint8_t Port;
dudmuck 0:8f0d0ae0a077 231 uint8_t *Buffer;
dudmuck 0:8f0d0ae0a077 232 uint8_t BufferSize;
dudmuck 0:8f0d0ae0a077 233 }LoRaMacDownlinkStatus;
dudmuck 0:8f0d0ae0a077 234
dudmuck 2:f2d9aa163652 235 static uint8_t missed_count;
dudmuck 2:f2d9aa163652 236
dudmuck 0:8f0d0ae0a077 237
dudmuck 0:8f0d0ae0a077 238 /*!
dudmuck 0:8f0d0ae0a077 239 * \brief Prepares the payload of the frame
dudmuck 0:8f0d0ae0a077 240 */
dudmuck 0:8f0d0ae0a077 241 static void PrepareTxFrame( uint8_t port )
dudmuck 0:8f0d0ae0a077 242 {
dudmuck 0:8f0d0ae0a077 243 switch( port )
dudmuck 0:8f0d0ae0a077 244 {
dudmuck 0:8f0d0ae0a077 245 case 15:
dudmuck 0:8f0d0ae0a077 246 {
dudmuck 0:8f0d0ae0a077 247 AppData[0] = AppLedStateOn;
dudmuck 0:8f0d0ae0a077 248 if( IsTxConfirmed == true )
dudmuck 0:8f0d0ae0a077 249 {
dudmuck 0:8f0d0ae0a077 250 AppData[1] = LoRaMacDownlinkStatus.DownlinkCounter >> 8;
dudmuck 0:8f0d0ae0a077 251 AppData[2] = LoRaMacDownlinkStatus.DownlinkCounter;
dudmuck 0:8f0d0ae0a077 252 AppData[3] = LoRaMacDownlinkStatus.Rssi >> 8;
dudmuck 0:8f0d0ae0a077 253 AppData[4] = LoRaMacDownlinkStatus.Rssi;
dudmuck 0:8f0d0ae0a077 254 AppData[5] = LoRaMacDownlinkStatus.Snr;
dudmuck 0:8f0d0ae0a077 255 }
dudmuck 0:8f0d0ae0a077 256 }
dudmuck 0:8f0d0ae0a077 257 break;
dudmuck 23:a862b5601663 258 #if defined(SENSORS)
dudmuck 10:00997daeb0c0 259 case SENSOR_PORT:
dudmuck 12:ed33c53afcaf 260 {
dudmuck 26:8227a4edb216 261 uint16_t x = 0xffff;
dudmuck 23:a862b5601663 262 #ifdef ENABLE_LUMINOSITY
dudmuck 23:a862b5601663 263 x = tsl2561.getLuminosity(TSL2561_VISIBLE);
dudmuck 23:a862b5601663 264 #endif
dudmuck 26:8227a4edb216 265 AppDataSize = 0;
dudmuck 26:8227a4edb216 266 AppData[AppDataSize++] = x >> 8;
dudmuck 26:8227a4edb216 267 AppData[AppDataSize++] = x & 0xff;
dudmuck 26:8227a4edb216 268 while (sensor_in_idx != sensor_out_idx) {
dudmuck 26:8227a4edb216 269 AppData[AppDataSize++] = sensor[sensor_out_idx].ain_A >> 8;
dudmuck 26:8227a4edb216 270 AppData[AppDataSize++] = sensor[sensor_out_idx].ain_A & 0xff;
dudmuck 26:8227a4edb216 271 AppData[AppDataSize++] = sensor[sensor_out_idx].ain_B >> 8;
dudmuck 26:8227a4edb216 272 AppData[AppDataSize++] = sensor[sensor_out_idx].ain_B & 0xff;
dudmuck 26:8227a4edb216 273 AppData[AppDataSize++] = sensor[sensor_out_idx].pins;
dudmuck 26:8227a4edb216 274 if (++sensor_out_idx == N_SAMPLES)
dudmuck 26:8227a4edb216 275 sensor_out_idx = 0;
dudmuck 26:8227a4edb216 276 }
dudmuck 12:ed33c53afcaf 277 }
dudmuck 10:00997daeb0c0 278 break;
dudmuck 23:a862b5601663 279 #endif /* SENSORS */
dudmuck 0:8f0d0ae0a077 280 default:
dudmuck 0:8f0d0ae0a077 281 break;
dudmuck 0:8f0d0ae0a077 282 }
dudmuck 0:8f0d0ae0a077 283 }
dudmuck 0:8f0d0ae0a077 284
dudmuck 0:8f0d0ae0a077 285 void
dudmuck 0:8f0d0ae0a077 286 LoRaMacEventInfoStatus_to_string(LoRaMacEventInfoStatus_t status, char* dst)
dudmuck 0:8f0d0ae0a077 287 {
dudmuck 0:8f0d0ae0a077 288 const char* ptr = NULL;
dudmuck 0:8f0d0ae0a077 289
dudmuck 0:8f0d0ae0a077 290 switch (status) {
dudmuck 0:8f0d0ae0a077 291 case LORAMAC_EVENT_INFO_STATUS_RX_ERROR: ptr = "RX_ERROR"; break;
dudmuck 0:8f0d0ae0a077 292 case LORAMAC_EVENT_INFO_STATUS_OK: ptr = "OK"; break;
dudmuck 2:f2d9aa163652 293 case LORAMAC_EVENT_INFO_STATUS_ERROR_RX_MTYPE: ptr = "ERROR_RX_MTYPE"; break;
dudmuck 2:f2d9aa163652 294 case LORAMAC_EVENT_INFO_STATUS_ERROR_SEND: ptr = "ERROR_SEND"; break;
dudmuck 2:f2d9aa163652 295 case LORAMAC_EVENT_INFO_STATUS_ERROR_JOIN_ACCEPT: ptr = "ERROR_JOIN_ACCEPT"; break;
dudmuck 2:f2d9aa163652 296 case LORAMAC_EVENT_INFO_STATUS_ERROR_MLMEREQ: ptr = "ERROR_MLMEREQ"; break;
dudmuck 2:f2d9aa163652 297 case LORAMAC_EVENT_INFO_STATUS_ERROR_MCPSREQ: ptr = "ERROR_MCPSREQ"; break;
dudmuck 2:f2d9aa163652 298 //case LORAMAC_EVENT_INFO_STATUS_ERROR: ptr = "ERROR"; break;
dudmuck 2:f2d9aa163652 299
dudmuck 0:8f0d0ae0a077 300 case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT: ptr = "TX_TIMEOUT"; break;
dudmuck 0:8f0d0ae0a077 301 case LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT: ptr = "RX_TIMEOUT"; break;
dudmuck 0:8f0d0ae0a077 302 case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL: ptr = "JOIN_FAIL"; break;
dudmuck 0:8f0d0ae0a077 303 case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED: ptr = "DOWNLINK_REPEATED"; break;
dudmuck 0:8f0d0ae0a077 304 case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR: ptr = "TX_DR_PAYLOAD_SIZE_ERROR"; break;
dudmuck 0:8f0d0ae0a077 305 case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS: ptr = "DOWNLINK_TOO_MANY_FRAMES_LOSS"; break;
dudmuck 0:8f0d0ae0a077 306 case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL: ptr = "ADDRESS_FAIL"; break;
dudmuck 0:8f0d0ae0a077 307 case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL: ptr = "MIC_FAIL"; break;
dudmuck 0:8f0d0ae0a077 308 case LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED: ptr = "BEACON_LOCKED"; break;
dudmuck 0:8f0d0ae0a077 309 case LORAMAC_EVENT_INFO_STATUS_BEACON_LOST: ptr = "BEACON_LOST"; break;
dudmuck 0:8f0d0ae0a077 310 case LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND: ptr = "BEACON_NOT_FOUND"; break;
dudmuck 16:915815632c1f 311 case LORAMAC_EVENT_INFO_STATUS_STOP: ptr = "STOP"; break;
dudmuck 0:8f0d0ae0a077 312 }
dudmuck 0:8f0d0ae0a077 313
dudmuck 0:8f0d0ae0a077 314 if (ptr != NULL)
dudmuck 0:8f0d0ae0a077 315 strcpy(dst, ptr);
dudmuck 0:8f0d0ae0a077 316 }
dudmuck 0:8f0d0ae0a077 317
dudmuck 0:8f0d0ae0a077 318 void
dudmuck 0:8f0d0ae0a077 319 LoRaMacStatus_to_string(LoRaMacStatus_t status, char* dst)
dudmuck 0:8f0d0ae0a077 320 {
dudmuck 0:8f0d0ae0a077 321 const char* ptr = NULL;
dudmuck 0:8f0d0ae0a077 322
dudmuck 0:8f0d0ae0a077 323 switch (status) {
dudmuck 0:8f0d0ae0a077 324 case LORAMAC_STATUS_OK: ptr = "OK"; break;
dudmuck 0:8f0d0ae0a077 325 case LORAMAC_STATUS_BUSY: ptr = "BUSY"; break;
dudmuck 0:8f0d0ae0a077 326 case LORAMAC_STATUS_SERVICE_UNKNOWN: ptr = "SERVICE_UNKNOWN"; break;
dudmuck 0:8f0d0ae0a077 327 case LORAMAC_STATUS_PARAMETER_INVALID: ptr = "PARAMETER_INVALID"; break;
dudmuck 0:8f0d0ae0a077 328 case LORAMAC_STATUS_FREQUENCY_INVALID: ptr = "FREQUENCY_INVALID"; break;
dudmuck 0:8f0d0ae0a077 329 case LORAMAC_STATUS_DATARATE_INVALID: ptr = "DATARATE_INVALID"; break;
dudmuck 0:8f0d0ae0a077 330 case LORAMAC_STATUS_FREQ_AND_DR_INVALID: ptr = "FREQ_AND_DR_INVALID"; break;
dudmuck 0:8f0d0ae0a077 331 case LORAMAC_STATUS_NO_NETWORK_JOINED: ptr = "NO_NETWORK_JOINED"; break;
dudmuck 0:8f0d0ae0a077 332 case LORAMAC_STATUS_LENGTH_ERROR: ptr = "LENGTH_ERROR"; break;
dudmuck 0:8f0d0ae0a077 333 case LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR: ptr = "MAC_CMD_LENGTH_ERROR"; break;
dudmuck 0:8f0d0ae0a077 334 case LORAMAC_STATUS_DEVICE_OFF: ptr = "DEVICE_OFF"; break;
dudmuck 0:8f0d0ae0a077 335 }
dudmuck 0:8f0d0ae0a077 336 if (ptr != NULL)
dudmuck 0:8f0d0ae0a077 337 strcpy(dst, ptr);
dudmuck 0:8f0d0ae0a077 338 }
dudmuck 0:8f0d0ae0a077 339
dudmuck 17:3215f12051f9 340 static void OnTxNextPacketTimerEvent( void );
dudmuck 5:c108560af4c3 341
dudmuck 5:c108560af4c3 342 /*!
dudmuck 0:8f0d0ae0a077 343 * \brief Prepares the payload of the frame
dudmuck 0:8f0d0ae0a077 344 *
dudmuck 0:8f0d0ae0a077 345 * \retval [0: frame could be send, 1: error]
dudmuck 0:8f0d0ae0a077 346 */
dudmuck 17:3215f12051f9 347 static bool SendFrame(uint8_t port)
dudmuck 0:8f0d0ae0a077 348 {
dudmuck 0:8f0d0ae0a077 349 McpsReq_t mcpsReq;
dudmuck 0:8f0d0ae0a077 350 LoRaMacTxInfo_t txInfo;
dudmuck 0:8f0d0ae0a077 351 LoRaMacStatus_t status;
dudmuck 0:8f0d0ae0a077 352 char str[64];
dudmuck 0:8f0d0ae0a077 353
dudmuck 0:8f0d0ae0a077 354 if( LoRaMacQueryTxPossible( AppDataSize, &txInfo ) != LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 355 {
dudmuck 0:8f0d0ae0a077 356 // Send empty frame in order to flush MAC commands
dudmuck 0:8f0d0ae0a077 357 mcpsReq.Type = MCPS_UNCONFIRMED;
dudmuck 0:8f0d0ae0a077 358 mcpsReq.Req.Unconfirmed.fBuffer = NULL;
dudmuck 0:8f0d0ae0a077 359 mcpsReq.Req.Unconfirmed.fBufferSize = 0;
dudmuck 0:8f0d0ae0a077 360
dudmuck 0:8f0d0ae0a077 361 LoRaMacUplinkStatus.Acked = false;
dudmuck 0:8f0d0ae0a077 362 LoRaMacUplinkStatus.Port = 0;
dudmuck 0:8f0d0ae0a077 363 LoRaMacUplinkStatus.Buffer = NULL;
dudmuck 0:8f0d0ae0a077 364 LoRaMacUplinkStatus.BufferSize = 0;
dudmuck 0:8f0d0ae0a077 365 }
dudmuck 0:8f0d0ae0a077 366 else
dudmuck 0:8f0d0ae0a077 367 {
dudmuck 0:8f0d0ae0a077 368 LoRaMacUplinkStatus.Acked = false;
dudmuck 12:ed33c53afcaf 369 LoRaMacUplinkStatus.Port = port;
dudmuck 0:8f0d0ae0a077 370 LoRaMacUplinkStatus.Buffer = AppData;
dudmuck 0:8f0d0ae0a077 371 LoRaMacUplinkStatus.BufferSize = AppDataSize;
dudmuck 0:8f0d0ae0a077 372
dudmuck 0:8f0d0ae0a077 373 if( IsTxConfirmed == false )
dudmuck 0:8f0d0ae0a077 374 {
dudmuck 0:8f0d0ae0a077 375 mcpsReq.Type = MCPS_UNCONFIRMED;
dudmuck 12:ed33c53afcaf 376 mcpsReq.Req.Unconfirmed.fPort = port;
dudmuck 0:8f0d0ae0a077 377 mcpsReq.Req.Unconfirmed.fBuffer = AppData;
dudmuck 0:8f0d0ae0a077 378 mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize;
dudmuck 0:8f0d0ae0a077 379 //mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
dudmuck 0:8f0d0ae0a077 380 }
dudmuck 0:8f0d0ae0a077 381 else
dudmuck 0:8f0d0ae0a077 382 {
dudmuck 0:8f0d0ae0a077 383 mcpsReq.Type = MCPS_CONFIRMED;
dudmuck 12:ed33c53afcaf 384 mcpsReq.Req.Confirmed.fPort = port;
dudmuck 0:8f0d0ae0a077 385 mcpsReq.Req.Confirmed.fBuffer = AppData;
dudmuck 0:8f0d0ae0a077 386 mcpsReq.Req.Confirmed.fBufferSize = AppDataSize;
dudmuck 0:8f0d0ae0a077 387 mcpsReq.Req.Confirmed.NbTrials = 8;
dudmuck 0:8f0d0ae0a077 388 //mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
dudmuck 0:8f0d0ae0a077 389 }
dudmuck 0:8f0d0ae0a077 390 }
dudmuck 0:8f0d0ae0a077 391
dudmuck 0:8f0d0ae0a077 392 status = LoRaMacMcpsRequest( &mcpsReq );
dudmuck 0:8f0d0ae0a077 393 if( status == LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 394 {
dudmuck 0:8f0d0ae0a077 395 awaiting_mcps_indic = true;
dudmuck 0:8f0d0ae0a077 396 return false;
dudmuck 0:8f0d0ae0a077 397 }
dudmuck 0:8f0d0ae0a077 398 LoRaMacStatus_to_string(status, str);
dudmuck 1:53c30224eda8 399 isr_printf("send failed:%s\r\n", str);
dudmuck 16:915815632c1f 400
dudmuck 21:500ff43d8424 401 if (status == LORAMAC_STATUS_NO_NETWORK_JOINED) {
dudmuck 21:500ff43d8424 402 join_retry = true;
dudmuck 26:8227a4edb216 403 #ifdef SENSORS
dudmuck 26:8227a4edb216 404 sensor_ticker.detach();
dudmuck 26:8227a4edb216 405 #endif
dudmuck 21:500ff43d8424 406 } else {
dudmuck 17:3215f12051f9 407 AppPort = port;
dudmuck 17:3215f12051f9 408 TxNextPacketTimeout.attach_us(&OnTxNextPacketTimerEvent, randr(1000000, 5000000) + 1000000);
dudmuck 22:999a7e7698a8 409 if (status == LORAMAC_STATUS_BUSY)
dudmuck 22:999a7e7698a8 410 send_at_beacon = true;
dudmuck 17:3215f12051f9 411 }
dudmuck 16:915815632c1f 412
dudmuck 0:8f0d0ae0a077 413 return true;
dudmuck 0:8f0d0ae0a077 414 }
dudmuck 0:8f0d0ae0a077 415
dudmuck 17:3215f12051f9 416 /*!
dudmuck 17:3215f12051f9 417 * \brief Function executed on TxNextPacket Timeout event
dudmuck 17:3215f12051f9 418 */
dudmuck 17:3215f12051f9 419 static void OnTxNextPacketTimerEvent( void )
dudmuck 17:3215f12051f9 420 {
dudmuck 17:3215f12051f9 421 MibRequestConfirm_t mibReq;
dudmuck 17:3215f12051f9 422 LoRaMacStatus_t status;
dudmuck 17:3215f12051f9 423
dudmuck 17:3215f12051f9 424 if (DeviceState == DEVICE_STATE_REJOIN) {
dudmuck 17:3215f12051f9 425 isr_printf("unjoin\r\n");
dudmuck 17:3215f12051f9 426 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 17:3215f12051f9 427 mibReq.Param.IsNetworkJoined = false;
dudmuck 26:8227a4edb216 428 #ifdef SENSORS
dudmuck 26:8227a4edb216 429 sensor_ticker.detach();
dudmuck 26:8227a4edb216 430 #endif
dudmuck 17:3215f12051f9 431 if (LoRaMacMibSetRequestConfirm( &mibReq ) != LORAMAC_STATUS_OK) {
dudmuck 17:3215f12051f9 432 TxNextPacketTimeout.attach_us(&OnTxNextPacketTimerEvent, 500000);
dudmuck 17:3215f12051f9 433 return;
dudmuck 17:3215f12051f9 434 }
dudmuck 17:3215f12051f9 435 }
dudmuck 17:3215f12051f9 436
dudmuck 17:3215f12051f9 437 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 17:3215f12051f9 438 status = LoRaMacMibGetRequestConfirm( &mibReq );
dudmuck 17:3215f12051f9 439
dudmuck 17:3215f12051f9 440 isr_printf("OnTxNextPacketTimerEvent() ");
dudmuck 17:3215f12051f9 441 if( status == LORAMAC_STATUS_OK )
dudmuck 17:3215f12051f9 442 {
dudmuck 17:3215f12051f9 443 if( mibReq.Param.IsNetworkJoined == true )
dudmuck 17:3215f12051f9 444 {
dudmuck 17:3215f12051f9 445 isr_printf("send");
dudmuck 18:9ac71c0eb70d 446 DeviceState = DEVICE_STATE_UPLINK;
dudmuck 17:3215f12051f9 447 }
dudmuck 17:3215f12051f9 448 else
dudmuck 17:3215f12051f9 449 {
dudmuck 17:3215f12051f9 450 isr_printf("join");
dudmuck 17:3215f12051f9 451 DeviceState = DEVICE_STATE_JOIN;
dudmuck 17:3215f12051f9 452 }
dudmuck 17:3215f12051f9 453 }
dudmuck 17:3215f12051f9 454 isr_printf("\r\n");
dudmuck 17:3215f12051f9 455 }
dudmuck 17:3215f12051f9 456
dudmuck 0:8f0d0ae0a077 457 void
dudmuck 0:8f0d0ae0a077 458 send_uplink()
dudmuck 0:8f0d0ae0a077 459 {
dudmuck 0:8f0d0ae0a077 460 AppDataSize = uplink_length;
dudmuck 18:9ac71c0eb70d 461 DeviceState = DEVICE_STATE_UPLINK;
dudmuck 0:8f0d0ae0a077 462 }
dudmuck 0:8f0d0ae0a077 463
dudmuck 0:8f0d0ae0a077 464 /*!
dudmuck 0:8f0d0ae0a077 465 * \brief MCPS-Confirm event function
dudmuck 0:8f0d0ae0a077 466 *
dudmuck 0:8f0d0ae0a077 467 * \param [IN] mcpsConfirm - Pointer to the confirm structure,
dudmuck 0:8f0d0ae0a077 468 * containing confirm attributes.
dudmuck 0:8f0d0ae0a077 469 */
dudmuck 0:8f0d0ae0a077 470 static void McpsConfirm( McpsConfirm_t *mcpsConfirm )
dudmuck 0:8f0d0ae0a077 471 {
dudmuck 2:f2d9aa163652 472 static uint8_t fail_count = 0;
dudmuck 0:8f0d0ae0a077 473 char str[64];
dudmuck 2:f2d9aa163652 474
dudmuck 1:53c30224eda8 475 isr_printf("McpsConfirm ");
dudmuck 0:8f0d0ae0a077 476 if( mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 477 {
dudmuck 2:f2d9aa163652 478 fail_count = 0;
dudmuck 1:53c30224eda8 479 isr_printf("OK ");
dudmuck 0:8f0d0ae0a077 480 switch( mcpsConfirm->McpsRequest )
dudmuck 0:8f0d0ae0a077 481 {
dudmuck 0:8f0d0ae0a077 482 case MCPS_UNCONFIRMED:
dudmuck 0:8f0d0ae0a077 483 {
dudmuck 1:53c30224eda8 484 isr_printf("UNCONFIRMED");
dudmuck 0:8f0d0ae0a077 485 // Check Datarate
dudmuck 0:8f0d0ae0a077 486 // Check TxPower
dudmuck 0:8f0d0ae0a077 487 break;
dudmuck 0:8f0d0ae0a077 488 }
dudmuck 0:8f0d0ae0a077 489 case MCPS_CONFIRMED:
dudmuck 0:8f0d0ae0a077 490 {
dudmuck 1:53c30224eda8 491 isr_printf("CONFIRMED");
dudmuck 0:8f0d0ae0a077 492 // Check Datarate
dudmuck 0:8f0d0ae0a077 493 // Check TxPower
dudmuck 0:8f0d0ae0a077 494 // Check AckReceived
dudmuck 0:8f0d0ae0a077 495 // Check NbTrials
dudmuck 0:8f0d0ae0a077 496 LoRaMacUplinkStatus.Acked = mcpsConfirm->AckReceived;
dudmuck 0:8f0d0ae0a077 497 break;
dudmuck 0:8f0d0ae0a077 498 }
dudmuck 0:8f0d0ae0a077 499 case MCPS_PROPRIETARY:
dudmuck 0:8f0d0ae0a077 500 {
dudmuck 0:8f0d0ae0a077 501 break;
dudmuck 0:8f0d0ae0a077 502 }
dudmuck 0:8f0d0ae0a077 503 default:
dudmuck 0:8f0d0ae0a077 504 break;
dudmuck 0:8f0d0ae0a077 505 }
dudmuck 0:8f0d0ae0a077 506 //LoRaMacUplinkStatus.Datarate = mcpsConfirm->Datarate;
dudmuck 0:8f0d0ae0a077 507 LoRaMacUplinkStatus.UplinkCounter = mcpsConfirm->UpLinkCounter;
dudmuck 0:8f0d0ae0a077 508
dudmuck 0:8f0d0ae0a077 509 } else {
dudmuck 0:8f0d0ae0a077 510 LoRaMacEventInfoStatus_to_string(mcpsConfirm->Status, str);
dudmuck 1:53c30224eda8 511 isr_printf("%s ", str);
dudmuck 0:8f0d0ae0a077 512
dudmuck 0:8f0d0ae0a077 513 /* mcpsIndication may not come. last uplink done, send another uplink */
dudmuck 12:ed33c53afcaf 514 if (jumper_in.read()) { /* jumper installed: auto uplink */
dudmuck 17:3215f12051f9 515 TxNextPacketTimeout.attach_us(&send_uplink, 100000);
dudmuck 12:ed33c53afcaf 516 }
dudmuck 16:915815632c1f 517
dudmuck 2:f2d9aa163652 518 if (++fail_count > 10) {
dudmuck 2:f2d9aa163652 519 /* cause re-join */
dudmuck 2:f2d9aa163652 520 MibRequestConfirm_t mibReq;
dudmuck 2:f2d9aa163652 521 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 2:f2d9aa163652 522 mibReq.Param.IsNetworkJoined = false;
dudmuck 2:f2d9aa163652 523 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 16:915815632c1f 524 fail_count = 0;
dudmuck 26:8227a4edb216 525 #ifdef SENSORS
dudmuck 26:8227a4edb216 526 sensor_ticker.detach();
dudmuck 26:8227a4edb216 527 #endif
dudmuck 2:f2d9aa163652 528 }
dudmuck 0:8f0d0ae0a077 529 }
dudmuck 0:8f0d0ae0a077 530
dudmuck 1:53c30224eda8 531 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 532 }
dudmuck 0:8f0d0ae0a077 533
dudmuck 0:8f0d0ae0a077 534 /*!
dudmuck 0:8f0d0ae0a077 535 * \brief MCPS-Indication event function
dudmuck 0:8f0d0ae0a077 536 *
dudmuck 0:8f0d0ae0a077 537 * \param [IN] mcpsIndication - Pointer to the indication structure,
dudmuck 0:8f0d0ae0a077 538 * containing indication attributes.
dudmuck 0:8f0d0ae0a077 539 */
dudmuck 0:8f0d0ae0a077 540 static void McpsIndication( McpsIndication_t *mcpsIndication )
dudmuck 0:8f0d0ae0a077 541 {
dudmuck 11:4c337f5bbe4c 542 char str[64];
dudmuck 22:999a7e7698a8 543 MibRequestConfirm_t mibReq;
dudmuck 22:999a7e7698a8 544
dudmuck 1:53c30224eda8 545 isr_printf("McpsIndication ");
dudmuck 0:8f0d0ae0a077 546
dudmuck 0:8f0d0ae0a077 547 /* last uplink done, send another uplink */
dudmuck 16:915815632c1f 548 if (jumper_in.read() && mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_STOP) {
dudmuck 16:915815632c1f 549 /* jumper installed: auto uplink */
dudmuck 17:3215f12051f9 550 TxNextPacketTimeout.attach_us(&send_uplink, 100000);
dudmuck 12:ed33c53afcaf 551 }
dudmuck 0:8f0d0ae0a077 552
dudmuck 0:8f0d0ae0a077 553 if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 554 {
dudmuck 11:4c337f5bbe4c 555 LoRaMacEventInfoStatus_to_string(mcpsIndication->Status, str);
dudmuck 11:4c337f5bbe4c 556 isr_printf("%s\r\n", str);
dudmuck 0:8f0d0ae0a077 557 return;
dudmuck 0:8f0d0ae0a077 558 }
dudmuck 0:8f0d0ae0a077 559
dudmuck 0:8f0d0ae0a077 560 awaiting_mcps_indic = false;
dudmuck 0:8f0d0ae0a077 561
dudmuck 0:8f0d0ae0a077 562 switch( mcpsIndication->McpsIndication )
dudmuck 0:8f0d0ae0a077 563 {
dudmuck 5:c108560af4c3 564 /* this refers to the downlink from gateway, not the uplink that was sent to it */
dudmuck 0:8f0d0ae0a077 565 case MCPS_UNCONFIRMED:
dudmuck 0:8f0d0ae0a077 566 {
dudmuck 1:53c30224eda8 567 isr_printf("UNCONFIRMED ");
dudmuck 0:8f0d0ae0a077 568 break;
dudmuck 0:8f0d0ae0a077 569 }
dudmuck 0:8f0d0ae0a077 570 case MCPS_CONFIRMED:
dudmuck 0:8f0d0ae0a077 571 {
dudmuck 5:c108560af4c3 572 /* downlink has requested Ack */
dudmuck 1:53c30224eda8 573 isr_printf("CONFIRMED ");
dudmuck 0:8f0d0ae0a077 574 break;
dudmuck 0:8f0d0ae0a077 575 }
dudmuck 0:8f0d0ae0a077 576 case MCPS_PROPRIETARY:
dudmuck 0:8f0d0ae0a077 577 {
dudmuck 0:8f0d0ae0a077 578 break;
dudmuck 0:8f0d0ae0a077 579 }
dudmuck 0:8f0d0ae0a077 580 case MCPS_MULTICAST:
dudmuck 0:8f0d0ae0a077 581 {
dudmuck 10:00997daeb0c0 582 isr_printf("MCPS_MULTICAST ");
dudmuck 12:ed33c53afcaf 583 /*if (mcpsIndication->RxData) {
dudmuck 12:ed33c53afcaf 584 }*/
dudmuck 0:8f0d0ae0a077 585 break;
dudmuck 0:8f0d0ae0a077 586 }
dudmuck 0:8f0d0ae0a077 587 default:
dudmuck 0:8f0d0ae0a077 588 break;
dudmuck 0:8f0d0ae0a077 589 }
dudmuck 0:8f0d0ae0a077 590
dudmuck 0:8f0d0ae0a077 591 // Check Multicast
dudmuck 0:8f0d0ae0a077 592 // Check Port
dudmuck 0:8f0d0ae0a077 593 // Check Datarate
dudmuck 0:8f0d0ae0a077 594 // Check FramePending
dudmuck 0:8f0d0ae0a077 595 // Check Buffer
dudmuck 0:8f0d0ae0a077 596 // Check BufferSize
dudmuck 0:8f0d0ae0a077 597 // Check Rssi
dudmuck 0:8f0d0ae0a077 598 // Check Snr
dudmuck 0:8f0d0ae0a077 599 // Check RxSlot
dudmuck 0:8f0d0ae0a077 600 LoRaMacDownlinkStatus.Rssi = mcpsIndication->Rssi;
dudmuck 0:8f0d0ae0a077 601 if( mcpsIndication->Snr & 0x80 ) // The SNR sign bit is 1
dudmuck 0:8f0d0ae0a077 602 {
dudmuck 0:8f0d0ae0a077 603 // Invert and divide by 4
dudmuck 0:8f0d0ae0a077 604 LoRaMacDownlinkStatus.Snr = ( ( ~mcpsIndication->Snr + 1 ) & 0xFF ) >> 2;
dudmuck 0:8f0d0ae0a077 605 LoRaMacDownlinkStatus.Snr = -LoRaMacDownlinkStatus.Snr;
dudmuck 0:8f0d0ae0a077 606 }
dudmuck 0:8f0d0ae0a077 607 else
dudmuck 0:8f0d0ae0a077 608 {
dudmuck 0:8f0d0ae0a077 609 // Divide by 4
dudmuck 0:8f0d0ae0a077 610 LoRaMacDownlinkStatus.Snr = ( mcpsIndication->Snr & 0xFF ) >> 2;
dudmuck 0:8f0d0ae0a077 611 }
dudmuck 0:8f0d0ae0a077 612 LoRaMacDownlinkStatus.DownlinkCounter++;
dudmuck 0:8f0d0ae0a077 613 LoRaMacDownlinkStatus.RxData = mcpsIndication->RxData;
dudmuck 0:8f0d0ae0a077 614 LoRaMacDownlinkStatus.Port = mcpsIndication->Port;
dudmuck 0:8f0d0ae0a077 615 LoRaMacDownlinkStatus.Buffer = mcpsIndication->Buffer;
dudmuck 0:8f0d0ae0a077 616 LoRaMacDownlinkStatus.BufferSize = mcpsIndication->BufferSize;
dudmuck 0:8f0d0ae0a077 617
dudmuck 0:8f0d0ae0a077 618 if( mcpsIndication->RxData == true )
dudmuck 0:8f0d0ae0a077 619 {
dudmuck 0:8f0d0ae0a077 620 int i;
dudmuck 1:53c30224eda8 621 isr_printf("RxData %u ", mcpsIndication->BufferSize);
dudmuck 0:8f0d0ae0a077 622 for (i = 0; i < mcpsIndication->BufferSize; i++) {
dudmuck 1:53c30224eda8 623 isr_printf("%02x ", mcpsIndication->Buffer[i]);
dudmuck 0:8f0d0ae0a077 624 }
dudmuck 1:53c30224eda8 625 isr_printf("\r\n");
dudmuck 12:ed33c53afcaf 626
dudmuck 12:ed33c53afcaf 627 switch (mcpsIndication->Buffer[0]) {
dudmuck 12:ed33c53afcaf 628 default:
dudmuck 12:ed33c53afcaf 629 case CMD_NONE: break;
dudmuck 23:a862b5601663 630 #if defined (ENABLE_RGB)
dudmuck 12:ed33c53afcaf 631 case CMD_LED_RGB:
dudmuck 12:ed33c53afcaf 632 rgb.setColorRGB(0,
dudmuck 12:ed33c53afcaf 633 mcpsIndication->Buffer[1], // R
dudmuck 12:ed33c53afcaf 634 mcpsIndication->Buffer[2], // G
dudmuck 12:ed33c53afcaf 635 mcpsIndication->Buffer[3] // B
dudmuck 12:ed33c53afcaf 636 );
dudmuck 16:915815632c1f 637 isr_printf("rgb %u %u %u\r\n",
dudmuck 16:915815632c1f 638 mcpsIndication->Buffer[1],
dudmuck 16:915815632c1f 639 mcpsIndication->Buffer[2],
dudmuck 16:915815632c1f 640 mcpsIndication->Buffer[3]
dudmuck 16:915815632c1f 641 );
dudmuck 12:ed33c53afcaf 642 break;
dudmuck 23:a862b5601663 643 #endif /* ENABLE_RGB */
dudmuck 23:a862b5601663 644 #if defined(SENSORS)
dudmuck 12:ed33c53afcaf 645 case CMD_GPIO_OUT:
dudmuck 12:ed33c53afcaf 646 pc6_out = mcpsIndication->Buffer[1];
dudmuck 16:915815632c1f 647 isr_printf("gpo %d\r\n", mcpsIndication->Buffer[1]);
dudmuck 16:915815632c1f 648 break;
dudmuck 18:9ac71c0eb70d 649 case CMD_PWM:
dudmuck 18:9ac71c0eb70d 650 pwm.period(1.0 / mcpsIndication->Buffer[1]);
dudmuck 18:9ac71c0eb70d 651 pwm.write(mcpsIndication->Buffer[2] / 255.0);
dudmuck 18:9ac71c0eb70d 652 isr_printf("pwm %u %u\r\n", mcpsIndication->Buffer[1], mcpsIndication->Buffer[2]);
dudmuck 22:999a7e7698a8 653 break;
dudmuck 23:a862b5601663 654 #endif /* SENSORS */
dudmuck 22:999a7e7698a8 655 case CMD_TX_POWER:
dudmuck 22:999a7e7698a8 656 isr_printf("txp %u\r\n", mcpsIndication->Buffer[1]);
dudmuck 22:999a7e7698a8 657 mibReq.Type = MIB_CHANNELS_TX_POWER;
dudmuck 22:999a7e7698a8 658 mibReq.Param.ChannelsTxPower = mcpsIndication->Buffer[1];
dudmuck 22:999a7e7698a8 659 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 12:ed33c53afcaf 660 break;
dudmuck 12:ed33c53afcaf 661 } // ..switch (mcpsIndication->Buffer[3])
dudmuck 0:8f0d0ae0a077 662
dudmuck 0:8f0d0ae0a077 663 switch( mcpsIndication->Port )
dudmuck 0:8f0d0ae0a077 664 {
dudmuck 0:8f0d0ae0a077 665 case 1: // The application LED can be controlled on port 1 or 2
dudmuck 0:8f0d0ae0a077 666 case 2:
dudmuck 0:8f0d0ae0a077 667 if( mcpsIndication->BufferSize == 1 )
dudmuck 0:8f0d0ae0a077 668 {
dudmuck 0:8f0d0ae0a077 669 AppLedStateOn = mcpsIndication->Buffer[0] & 0x01;
dudmuck 0:8f0d0ae0a077 670 //Led3StateChanged = true;
dudmuck 0:8f0d0ae0a077 671 }
dudmuck 0:8f0d0ae0a077 672 break;
dudmuck 0:8f0d0ae0a077 673 default:
dudmuck 0:8f0d0ae0a077 674 break;
dudmuck 0:8f0d0ae0a077 675 }
dudmuck 0:8f0d0ae0a077 676 }
dudmuck 16:915815632c1f 677
dudmuck 16:915815632c1f 678 if (mcpsIndication->Status == LORAMAC_EVENT_INFO_STATUS_STOP) {
dudmuck 17:3215f12051f9 679 isr_printf(" STOP-SCC");
dudmuck 17:3215f12051f9 680 TxNextPacketTimeout.detach();
dudmuck 16:915815632c1f 681 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 16:915815632c1f 682 }
dudmuck 17:3215f12051f9 683
dudmuck 17:3215f12051f9 684 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 685 }
dudmuck 0:8f0d0ae0a077 686
dudmuck 0:8f0d0ae0a077 687 /*!
dudmuck 0:8f0d0ae0a077 688 * \brief MLME-Confirm event function
dudmuck 0:8f0d0ae0a077 689 *
dudmuck 0:8f0d0ae0a077 690 * \param [IN] mlmeConfirm - Pointer to the confirm structure,
dudmuck 0:8f0d0ae0a077 691 * containing confirm attributes.
dudmuck 0:8f0d0ae0a077 692 */
dudmuck 0:8f0d0ae0a077 693 static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm )
dudmuck 0:8f0d0ae0a077 694 {
dudmuck 16:915815632c1f 695 char str[64];
dudmuck 16:915815632c1f 696 LoRaMacEventInfoStatus_to_string(mlmeConfirm->Status, str);
dudmuck 16:915815632c1f 697 isr_printf("MlmeConfirm %s ", str);
dudmuck 16:915815632c1f 698
dudmuck 0:8f0d0ae0a077 699 switch( mlmeConfirm->MlmeRequest )
dudmuck 0:8f0d0ae0a077 700 {
dudmuck 0:8f0d0ae0a077 701 case MLME_JOIN:
dudmuck 0:8f0d0ae0a077 702 {
dudmuck 1:53c30224eda8 703 isr_printf("MLME_JOIN ");
dudmuck 0:8f0d0ae0a077 704 if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 705 {
dudmuck 0:8f0d0ae0a077 706 // Status is OK, node has joined the network
dudmuck 0:8f0d0ae0a077 707 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 26:8227a4edb216 708 #ifdef SENSORS
dudmuck 26:8227a4edb216 709 sensor_ticker.attach_us(&sensor_poll, 1000000);
dudmuck 26:8227a4edb216 710 sensor_in_idx = 0;
dudmuck 26:8227a4edb216 711 sensor_out_idx = 0;
dudmuck 26:8227a4edb216 712 #endif
dudmuck 2:f2d9aa163652 713 missed_count = 0;
dudmuck 16:915815632c1f 714 if (jumper_in.read()) /* jumper installed: auto uplink */
dudmuck 17:3215f12051f9 715 TxNextPacketTimeout.attach_us(&send_uplink, 100000);
dudmuck 0:8f0d0ae0a077 716 }
dudmuck 0:8f0d0ae0a077 717 else
dudmuck 0:8f0d0ae0a077 718 {
dudmuck 0:8f0d0ae0a077 719 // Join was not successful. Try to join again
dudmuck 0:8f0d0ae0a077 720 DeviceState = DEVICE_STATE_JOIN;
dudmuck 0:8f0d0ae0a077 721 }
dudmuck 0:8f0d0ae0a077 722 break;
dudmuck 0:8f0d0ae0a077 723 }
dudmuck 0:8f0d0ae0a077 724 case MLME_LINK_CHECK:
dudmuck 0:8f0d0ae0a077 725 {
dudmuck 1:53c30224eda8 726 isr_printf("LINK_CHECK");
dudmuck 0:8f0d0ae0a077 727 if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 728 {
dudmuck 0:8f0d0ae0a077 729 // Check DemodMargin
dudmuck 0:8f0d0ae0a077 730 //mlmeConfirm->DemodMargin;
dudmuck 0:8f0d0ae0a077 731 // Check NbGateways
dudmuck 0:8f0d0ae0a077 732 //mlmeConfirm->NbGateways;
dudmuck 0:8f0d0ae0a077 733 }
dudmuck 0:8f0d0ae0a077 734 break;
dudmuck 0:8f0d0ae0a077 735 }
dudmuck 0:8f0d0ae0a077 736 default:
dudmuck 0:8f0d0ae0a077 737 break;
dudmuck 0:8f0d0ae0a077 738 }
dudmuck 17:3215f12051f9 739
dudmuck 1:53c30224eda8 740 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 741 }
dudmuck 0:8f0d0ae0a077 742
dudmuck 0:8f0d0ae0a077 743
dudmuck 0:8f0d0ae0a077 744 static void MlmeIndication( MlmeIndication_t *MlmeIndication )
dudmuck 0:8f0d0ae0a077 745 {
dudmuck 0:8f0d0ae0a077 746 char str[64];
dudmuck 0:8f0d0ae0a077 747
dudmuck 1:53c30224eda8 748 isr_printf("MlmeIndication ");
dudmuck 0:8f0d0ae0a077 749 switch( MlmeIndication->MlmeIndication )
dudmuck 0:8f0d0ae0a077 750 {
dudmuck 0:8f0d0ae0a077 751 case MLME_BEACON:
dudmuck 0:8f0d0ae0a077 752 {
dudmuck 2:f2d9aa163652 753 isr_printf("missed:%u BEACON ", missed_count);
dudmuck 0:8f0d0ae0a077 754 LoRaMacEventInfoStatus_to_string(MlmeIndication->Status, str);
dudmuck 1:53c30224eda8 755 isr_printf("%s ", str);
dudmuck 16:915815632c1f 756
dudmuck 0:8f0d0ae0a077 757 if (send_at_beacon) {
dudmuck 17:3215f12051f9 758 TxNextPacketTimeout.attach_us(&send_uplink, 100000);
dudmuck 0:8f0d0ae0a077 759 send_at_beacon = false;
dudmuck 0:8f0d0ae0a077 760 }
dudmuck 16:915815632c1f 761
dudmuck 2:f2d9aa163652 762 if (LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED == MlmeIndication->Status)
dudmuck 2:f2d9aa163652 763 missed_count = 0;
dudmuck 16:915815632c1f 764 else {
dudmuck 16:915815632c1f 765 if (++missed_count > 4) {
dudmuck 16:915815632c1f 766 /* cause re-join */
dudmuck 16:915815632c1f 767 LoRaMacStatus_t status;
dudmuck 16:915815632c1f 768 MibRequestConfirm_t mibReq;
dudmuck 26:8227a4edb216 769 #ifdef SENSORS
dudmuck 26:8227a4edb216 770 sensor_ticker.detach();
dudmuck 26:8227a4edb216 771 #endif
dudmuck 16:915815632c1f 772 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 16:915815632c1f 773 mibReq.Param.IsNetworkJoined = false;
dudmuck 16:915815632c1f 774 status = LoRaMacMibSetRequestConfirm(&mibReq);
dudmuck 16:915815632c1f 775 if (status != LORAMAC_STATUS_OK)
dudmuck 16:915815632c1f 776 DeviceState = DEVICE_STATE_REJOIN;
dudmuck 16:915815632c1f 777
dudmuck 16:915815632c1f 778 LoRaMacStatus_to_string(status, str);
dudmuck 16:915815632c1f 779 isr_printf("app-rejoin %s\r\n", str);
dudmuck 17:3215f12051f9 780 TxNextPacketTimeout.attach_us(&OnTxNextPacketTimerEvent, randr(1000000, 5000000) + 1000000);
dudmuck 16:915815632c1f 781 }
dudmuck 2:f2d9aa163652 782 }
dudmuck 0:8f0d0ae0a077 783 break;
dudmuck 0:8f0d0ae0a077 784
dudmuck 0:8f0d0ae0a077 785 }
dudmuck 10:00997daeb0c0 786 case MLME_TXDONE:
dudmuck 10:00997daeb0c0 787 isr_printf("MLME_TXDONE ");
dudmuck 10:00997daeb0c0 788 break;
dudmuck 0:8f0d0ae0a077 789 default:
dudmuck 1:53c30224eda8 790 isr_printf("<%d> ", MlmeIndication->MlmeIndication);
dudmuck 0:8f0d0ae0a077 791 break;
dudmuck 0:8f0d0ae0a077 792 }
dudmuck 0:8f0d0ae0a077 793
dudmuck 1:53c30224eda8 794 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 795 }
dudmuck 0:8f0d0ae0a077 796
dudmuck 0:8f0d0ae0a077 797 void
dudmuck 0:8f0d0ae0a077 798 send_pcbuf_uplink()
dudmuck 0:8f0d0ae0a077 799 {
dudmuck 0:8f0d0ae0a077 800 bool ret;
dudmuck 0:8f0d0ae0a077 801 memcpy(AppData, pcbuf, pcbuf_len);
dudmuck 0:8f0d0ae0a077 802 AppDataSize = pcbuf_len;
dudmuck 0:8f0d0ae0a077 803
dudmuck 12:ed33c53afcaf 804 ret = SendFrame(TEXT_PORT);
dudmuck 1:53c30224eda8 805 isr_printf("%d = SendFrame()\r\n", ret);
dudmuck 0:8f0d0ae0a077 806 }
dudmuck 0:8f0d0ae0a077 807
dudmuck 10:00997daeb0c0 808
dudmuck 0:8f0d0ae0a077 809 void cmd_status(uint8_t idx)
dudmuck 0:8f0d0ae0a077 810 {
dudmuck 2:f2d9aa163652 811 MibRequestConfirm_t mibReq;
dudmuck 2:f2d9aa163652 812
dudmuck 2:f2d9aa163652 813 isr_printf("DevEUI ");
dudmuck 2:f2d9aa163652 814 for (int i = 0; i < 8; i++)
dudmuck 2:f2d9aa163652 815 isr_printf("%02x ", DevEui[i]);
dudmuck 2:f2d9aa163652 816 mibReq.Type = MIB_DEV_ADDR;
dudmuck 2:f2d9aa163652 817 LoRaMacMibGetRequestConfirm( &mibReq );
dudmuck 2:f2d9aa163652 818 isr_printf(", DevAddr:%x\r\n", mibReq.Param.DevAddr);
dudmuck 2:f2d9aa163652 819
dudmuck 23:a862b5601663 820 #if defined(SENSORS)
dudmuck 23:a862b5601663 821 #ifdef TYPE_ABZ
dudmuck 23:a862b5601663 822 isr_printf("a0:%u a2:%u\r\n", a0.read_u16(), a2.read_u16());
dudmuck 23:a862b5601663 823 #else
dudmuck 23:a862b5601663 824 isr_printf("a1:%u a3:%u\r\n", a1.read_u16(), a3.read_u16());
dudmuck 23:a862b5601663 825 #endif
dudmuck 25:fed9d5b77183 826 isr_printf("button:%d ", user_button.read());
dudmuck 10:00997daeb0c0 827 #endif
dudmuck 17:3215f12051f9 828 isr_printf("DEVICE_STATE_");
dudmuck 17:3215f12051f9 829 switch (DeviceState) {
dudmuck 17:3215f12051f9 830 case DEVICE_STATE_JOIN: printf("JOIN"); break;
dudmuck 17:3215f12051f9 831 case DEVICE_STATE_CYCLE: printf("CYCLE"); break;
dudmuck 17:3215f12051f9 832 case DEVICE_STATE_SLEEP: printf("SLEEP"); break;
dudmuck 17:3215f12051f9 833 case DEVICE_STATE_REJOIN: printf("REJOIN"); break;
dudmuck 18:9ac71c0eb70d 834 case DEVICE_STATE_INIT: printf("INIT"); break;
dudmuck 18:9ac71c0eb70d 835 case DEVICE_STATE_UPLINK: printf("UPLINK"); break;
dudmuck 17:3215f12051f9 836 }
dudmuck 17:3215f12051f9 837 isr_printf(" send_at_beacon:%d\r\n", send_at_beacon);
dudmuck 1:53c30224eda8 838 isr_printf("awaiting_mcps_indic:%d\r\n", awaiting_mcps_indic);
dudmuck 12:ed33c53afcaf 839
dudmuck 0:8f0d0ae0a077 840 loramac_print_status();
dudmuck 10:00997daeb0c0 841
dudmuck 0:8f0d0ae0a077 842 }
dudmuck 0:8f0d0ae0a077 843
dudmuck 0:8f0d0ae0a077 844 void cmd_uplink_length(uint8_t idx)
dudmuck 0:8f0d0ae0a077 845 {
dudmuck 0:8f0d0ae0a077 846 if (pcbuf[idx] >= '0' && pcbuf[idx] <= '9') {
dudmuck 0:8f0d0ae0a077 847 sscanf(pcbuf+idx, "%u", &uplink_length);
dudmuck 0:8f0d0ae0a077 848 }
dudmuck 1:53c30224eda8 849 isr_printf("uplink_length:%u\r\n", uplink_length);
dudmuck 0:8f0d0ae0a077 850 }
dudmuck 0:8f0d0ae0a077 851
dudmuck 23:a862b5601663 852 #if defined(ENABLE_RGB)
dudmuck 12:ed33c53afcaf 853 void cmd_rgb(uint8_t idx)
dudmuck 10:00997daeb0c0 854 {
dudmuck 12:ed33c53afcaf 855 int r, g, b;
dudmuck 12:ed33c53afcaf 856 sscanf(pcbuf+idx, "%d %d %d", &r, &g, &b);
dudmuck 12:ed33c53afcaf 857 rgb.setColorRGB(0, r, g, b);
dudmuck 16:915815632c1f 858 isr_printf("\r\nrgb: %d %d %d\r\n", r, g, b);
dudmuck 16:915815632c1f 859 }
dudmuck 23:a862b5601663 860 #endif /* ENABLE_RGB */
dudmuck 16:915815632c1f 861
dudmuck 23:a862b5601663 862 #if defined(SENSORS)
dudmuck 18:9ac71c0eb70d 863 void cmd_pwm(uint8_t idx)
dudmuck 16:915815632c1f 864 {
dudmuck 18:9ac71c0eb70d 865 float period, duty;
dudmuck 18:9ac71c0eb70d 866 unsigned p, d;
dudmuck 18:9ac71c0eb70d 867
dudmuck 18:9ac71c0eb70d 868 if (sscanf(pcbuf+idx, "%u %u", &p, &d) == 2) {
dudmuck 18:9ac71c0eb70d 869 period = 1.0 / p;
dudmuck 18:9ac71c0eb70d 870 duty = d / 255.0;
dudmuck 18:9ac71c0eb70d 871 pwm.period(period);
dudmuck 18:9ac71c0eb70d 872 pwm.write(duty);
dudmuck 18:9ac71c0eb70d 873 printf("pwm period:%f, duty:%f\r\n", period, duty);
dudmuck 18:9ac71c0eb70d 874 } else
dudmuck 18:9ac71c0eb70d 875 printf("pwm parse fail\r\n");
dudmuck 16:915815632c1f 876 }
dudmuck 16:915815632c1f 877
dudmuck 10:00997daeb0c0 878 #endif /* SENSORS */
dudmuck 10:00997daeb0c0 879
dudmuck 15:9023bf4cf168 880 void cmd_ChannelsTxPower(uint8_t idx)
dudmuck 15:9023bf4cf168 881 {
dudmuck 15:9023bf4cf168 882 MibRequestConfirm_t mibReq;
dudmuck 15:9023bf4cf168 883 unsigned int i;
dudmuck 15:9023bf4cf168 884
dudmuck 15:9023bf4cf168 885 if (pcbuf[idx] >= '0' && pcbuf[idx] <= '9') {
dudmuck 15:9023bf4cf168 886 sscanf(pcbuf+idx, "%u", &i);
dudmuck 15:9023bf4cf168 887 mibReq.Type = MIB_CHANNELS_TX_POWER;
dudmuck 15:9023bf4cf168 888 mibReq.Param.ChannelsTxPower = i;
dudmuck 15:9023bf4cf168 889 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 15:9023bf4cf168 890 }
dudmuck 15:9023bf4cf168 891
dudmuck 15:9023bf4cf168 892 mibReq.Type = MIB_CHANNELS_TX_POWER;
dudmuck 15:9023bf4cf168 893 LoRaMacMibGetRequestConfirm( &mibReq );
dudmuck 15:9023bf4cf168 894 isr_printf("ChannelsTxPower:%u\r\n", mibReq.Param.ChannelsTxPower);
dudmuck 15:9023bf4cf168 895 }
dudmuck 15:9023bf4cf168 896
dudmuck 0:8f0d0ae0a077 897 typedef struct {
dudmuck 0:8f0d0ae0a077 898 const char* const cmd;
dudmuck 0:8f0d0ae0a077 899 void (*handler)(uint8_t args_at);
dudmuck 0:8f0d0ae0a077 900 const char* const arg_descr;
dudmuck 0:8f0d0ae0a077 901 const char* const description;
dudmuck 0:8f0d0ae0a077 902 } menu_item_t;
dudmuck 0:8f0d0ae0a077 903
dudmuck 0:8f0d0ae0a077 904 void cmd_help(uint8_t args_at);
dudmuck 0:8f0d0ae0a077 905
dudmuck 0:8f0d0ae0a077 906 const menu_item_t menu_items[] =
dudmuck 0:8f0d0ae0a077 907 { /* after first character, command names must be [A-Za-z] */
dudmuck 0:8f0d0ae0a077 908 { "?", cmd_help, "","show available commands"},
dudmuck 0:8f0d0ae0a077 909 { ".", cmd_status, "","print status"},
dudmuck 10:00997daeb0c0 910 { "ul", cmd_uplink_length, "%u","set uplink payload length"},
dudmuck 15:9023bf4cf168 911 { "ctxp", cmd_ChannelsTxPower, "%u","get/set ChannelsTxPower"},
dudmuck 23:a862b5601663 912 #if defined(ENABLE_RGB)
dudmuck 18:9ac71c0eb70d 913 { "rgb", cmd_rgb, "%d %d %d", "set led R G B"},
dudmuck 23:a862b5601663 914 #endif
dudmuck 23:a862b5601663 915 #if defined(SENSORS)
dudmuck 18:9ac71c0eb70d 916 { "p", cmd_pwm, "%u %u", "set pwm period, duty"},
dudmuck 23:a862b5601663 917 #endif /* SENSORS */
dudmuck 0:8f0d0ae0a077 918 { NULL, NULL, NULL, NULL }
dudmuck 0:8f0d0ae0a077 919 };
dudmuck 0:8f0d0ae0a077 920
dudmuck 0:8f0d0ae0a077 921 void cmd_help(uint8_t args_at)
dudmuck 0:8f0d0ae0a077 922 {
dudmuck 0:8f0d0ae0a077 923 int i;
dudmuck 0:8f0d0ae0a077 924
dudmuck 0:8f0d0ae0a077 925 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 1:53c30224eda8 926 isr_printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
dudmuck 0:8f0d0ae0a077 927 }
dudmuck 0:8f0d0ae0a077 928
dudmuck 0:8f0d0ae0a077 929 }
dudmuck 0:8f0d0ae0a077 930
dudmuck 0:8f0d0ae0a077 931 void
dudmuck 0:8f0d0ae0a077 932 console()
dudmuck 0:8f0d0ae0a077 933 {
dudmuck 0:8f0d0ae0a077 934 bool parsed;
dudmuck 0:8f0d0ae0a077 935 int i;
dudmuck 0:8f0d0ae0a077 936 uint8_t user_cmd_len;
dudmuck 0:8f0d0ae0a077 937
dudmuck 0:8f0d0ae0a077 938 if (pcbuf_len < 0) { // ctrl-C
dudmuck 0:8f0d0ae0a077 939 return;
dudmuck 0:8f0d0ae0a077 940 }
dudmuck 0:8f0d0ae0a077 941 if (pcbuf_len == 0)
dudmuck 0:8f0d0ae0a077 942 return;
dudmuck 0:8f0d0ae0a077 943
dudmuck 1:53c30224eda8 944 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 945
dudmuck 0:8f0d0ae0a077 946 /* get end of user-entered command */
dudmuck 0:8f0d0ae0a077 947 user_cmd_len = 1; // first character can be any character
dudmuck 0:8f0d0ae0a077 948 for (i = 1; i <= pcbuf_len; i++) {
dudmuck 0:8f0d0ae0a077 949 if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
dudmuck 0:8f0d0ae0a077 950 user_cmd_len = i;
dudmuck 0:8f0d0ae0a077 951 break;
dudmuck 0:8f0d0ae0a077 952 }
dudmuck 0:8f0d0ae0a077 953 }
dudmuck 0:8f0d0ae0a077 954
dudmuck 0:8f0d0ae0a077 955 parsed = false;
dudmuck 0:8f0d0ae0a077 956 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:8f0d0ae0a077 957 int mi_len = strlen(menu_items[i].cmd);
dudmuck 0:8f0d0ae0a077 958
dudmuck 0:8f0d0ae0a077 959 if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
dudmuck 0:8f0d0ae0a077 960 while (pcbuf[mi_len] == ' ') // skip past spaces
dudmuck 0:8f0d0ae0a077 961 mi_len++;
dudmuck 0:8f0d0ae0a077 962 menu_items[i].handler(mi_len);
dudmuck 0:8f0d0ae0a077 963 parsed = true;
dudmuck 0:8f0d0ae0a077 964 break;
dudmuck 0:8f0d0ae0a077 965 }
dudmuck 0:8f0d0ae0a077 966 }
dudmuck 0:8f0d0ae0a077 967
dudmuck 0:8f0d0ae0a077 968 if (!parsed)
dudmuck 0:8f0d0ae0a077 969 send_pcbuf_uplink();
dudmuck 0:8f0d0ae0a077 970
dudmuck 0:8f0d0ae0a077 971 pcbuf_len = 0;
dudmuck 1:53c30224eda8 972 isr_printf("> ");
dudmuck 0:8f0d0ae0a077 973 fflush(stdout);
dudmuck 0:8f0d0ae0a077 974 }
dudmuck 0:8f0d0ae0a077 975
dudmuck 0:8f0d0ae0a077 976 void rx_callback()
dudmuck 0:8f0d0ae0a077 977 {
dudmuck 0:8f0d0ae0a077 978 static uint8_t pcbuf_idx = 0;
dudmuck 0:8f0d0ae0a077 979 static uint8_t prev_len = 0;;
dudmuck 0:8f0d0ae0a077 980 char c = pc.getc();
dudmuck 0:8f0d0ae0a077 981
dudmuck 0:8f0d0ae0a077 982 if (c == 8) {
dudmuck 0:8f0d0ae0a077 983 if (pcbuf_idx > 0) {
dudmuck 0:8f0d0ae0a077 984 pc.putc(8);
dudmuck 0:8f0d0ae0a077 985 pc.putc(' ');
dudmuck 0:8f0d0ae0a077 986 pc.putc(8);
dudmuck 0:8f0d0ae0a077 987 pcbuf_idx--;
dudmuck 0:8f0d0ae0a077 988 }
dudmuck 0:8f0d0ae0a077 989 } else if (c == 3) { // ctrl-C
dudmuck 0:8f0d0ae0a077 990 pcbuf_len = -1;
dudmuck 0:8f0d0ae0a077 991 } else if (c == '\r') {
dudmuck 0:8f0d0ae0a077 992 if (pcbuf_idx == 0) {
dudmuck 0:8f0d0ae0a077 993 pcbuf_len = prev_len;
dudmuck 0:8f0d0ae0a077 994 } else {
dudmuck 0:8f0d0ae0a077 995 pcbuf[pcbuf_idx] = 0; // null terminate
dudmuck 0:8f0d0ae0a077 996 prev_len = pcbuf_idx;
dudmuck 0:8f0d0ae0a077 997 pcbuf_idx = 0;
dudmuck 0:8f0d0ae0a077 998 pcbuf_len = prev_len;
dudmuck 0:8f0d0ae0a077 999 }
dudmuck 0:8f0d0ae0a077 1000 } else if (pcbuf_idx < sizeof(pcbuf)) {
dudmuck 0:8f0d0ae0a077 1001 pcbuf[pcbuf_idx++] = c;
dudmuck 0:8f0d0ae0a077 1002 pc.putc(c);
dudmuck 0:8f0d0ae0a077 1003 }
dudmuck 0:8f0d0ae0a077 1004 }
dudmuck 0:8f0d0ae0a077 1005
dudmuck 10:00997daeb0c0 1006 void button_isr()
dudmuck 10:00997daeb0c0 1007 {
dudmuck 10:00997daeb0c0 1008 isr_printf("button_isr\r\n");
dudmuck 19:c3af34457b6d 1009
dudmuck 19:c3af34457b6d 1010 AppPort = SENSOR_PORT;
dudmuck 19:c3af34457b6d 1011 send_uplink();
dudmuck 10:00997daeb0c0 1012 }
dudmuck 10:00997daeb0c0 1013
dudmuck 0:8f0d0ae0a077 1014 /**
dudmuck 0:8f0d0ae0a077 1015 * Main application entry point.
dudmuck 0:8f0d0ae0a077 1016 */
dudmuck 0:8f0d0ae0a077 1017 int main( void )
dudmuck 0:8f0d0ae0a077 1018 {
dudmuck 0:8f0d0ae0a077 1019 LoRaMacPrimitives_t LoRaMacPrimitives;
dudmuck 0:8f0d0ae0a077 1020 LoRaMacCallback_t LoRaMacCallbacks;
dudmuck 0:8f0d0ae0a077 1021 MibRequestConfirm_t mibReq;
dudmuck 0:8f0d0ae0a077 1022
dudmuck 4:d9201410c87b 1023 pc.baud(38400);
dudmuck 7:e238827f0e47 1024 pc.attach(&rx_callback);
dudmuck 16:915815632c1f 1025 isr_printf("\r\nreset %s\r\n", FW_VERS);
dudmuck 10:00997daeb0c0 1026
dudmuck 10:00997daeb0c0 1027 BoardInit( );
dudmuck 10:00997daeb0c0 1028
dudmuck 0:8f0d0ae0a077 1029 DeviceState = DEVICE_STATE_INIT;
dudmuck 10:00997daeb0c0 1030
dudmuck 20:42839629a5dc 1031 while (!user_button) {
dudmuck 20:42839629a5dc 1032 printf("button-lo\r\n");
dudmuck 20:42839629a5dc 1033 wait(0.01);
dudmuck 10:00997daeb0c0 1034 }
dudmuck 20:42839629a5dc 1035 user_button.fall(&button_isr);
dudmuck 12:ed33c53afcaf 1036
dudmuck 23:a862b5601663 1037 #ifdef ENABLE_LUMINOSITY
dudmuck 16:915815632c1f 1038 isr_printf("TSL2561 Sensor ");
dudmuck 12:ed33c53afcaf 1039 if (tsl2561.begin()) {
dudmuck 16:915815632c1f 1040 isr_printf("Found\r\n");
dudmuck 12:ed33c53afcaf 1041 } else {
dudmuck 16:915815632c1f 1042 isr_printf("not-found\r\n");
dudmuck 12:ed33c53afcaf 1043 }
dudmuck 12:ed33c53afcaf 1044
dudmuck 12:ed33c53afcaf 1045 // You can change the gain on the fly, to adapt to brighter/dimmer tsl2561 situations
dudmuck 12:ed33c53afcaf 1046 tsl2561.setGain(TSL2561_GAIN_0X); // set no gain (for bright situtations)
dudmuck 12:ed33c53afcaf 1047 //tsl2561.setGain(TSL2561_GAIN_16X); // set 16x gain (for dim situations)
dudmuck 12:ed33c53afcaf 1048
dudmuck 12:ed33c53afcaf 1049 // Changing the integration time gives you a longer time over which to sense tsl2561
dudmuck 12:ed33c53afcaf 1050 // longer timelines are slower, but are good in very low tsl2561 situtations!
dudmuck 12:ed33c53afcaf 1051 //tsl2561.setTiming(TSL2561_INTEGRATIONTIME_13MS); // shortest integration time (bright tsl2561)
dudmuck 12:ed33c53afcaf 1052 //tsl2561.setTiming(TSL2561_INTEGRATIONTIME_101MS); // medium integration time (medium tsl2561)
dudmuck 12:ed33c53afcaf 1053 tsl2561.setTiming(TSL2561_INTEGRATIONTIME_402MS); // longest integration time (dim tsl2561)
dudmuck 23:a862b5601663 1054 #endif /* ENABLE_LUMINOSITY */
dudmuck 10:00997daeb0c0 1055
dudmuck 10:00997daeb0c0 1056 jumper_out = 1;
dudmuck 10:00997daeb0c0 1057 jumper_in.mode(PullDown);
dudmuck 20:42839629a5dc 1058 pc2 = 0;
dudmuck 0:8f0d0ae0a077 1059
dudmuck 0:8f0d0ae0a077 1060 while( 1 )
dudmuck 0:8f0d0ae0a077 1061 {
dudmuck 0:8f0d0ae0a077 1062 console();
dudmuck 12:ed33c53afcaf 1063
dudmuck 0:8f0d0ae0a077 1064 switch( DeviceState )
dudmuck 0:8f0d0ae0a077 1065 {
dudmuck 0:8f0d0ae0a077 1066 case DEVICE_STATE_INIT:
dudmuck 0:8f0d0ae0a077 1067 {
dudmuck 1:53c30224eda8 1068 isr_printf("DEVICE_STATE_INIT\r\n");
dudmuck 23:a862b5601663 1069 #if defined(SENSORS)
dudmuck 14:7ac0add1123e 1070 uplink_length = 7;
dudmuck 14:7ac0add1123e 1071 #else
dudmuck 0:8f0d0ae0a077 1072 uplink_length = 2;
dudmuck 14:7ac0add1123e 1073 #endif
dudmuck 0:8f0d0ae0a077 1074 LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
dudmuck 0:8f0d0ae0a077 1075 LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
dudmuck 0:8f0d0ae0a077 1076 LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
dudmuck 0:8f0d0ae0a077 1077 LoRaMacPrimitives.MacMlmeIndication = MlmeIndication;
dudmuck 0:8f0d0ae0a077 1078 LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel;
dudmuck 0:8f0d0ae0a077 1079 if (LORAMAC_STATUS_OK != LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks )) {
dudmuck 1:53c30224eda8 1080 isr_printf("LoRaMacInitialization() failed\r\n");
dudmuck 0:8f0d0ae0a077 1081 for (;;) ;
dudmuck 0:8f0d0ae0a077 1082 }
dudmuck 1:53c30224eda8 1083 isr_printf("INIT-ok\r\n");
dudmuck 0:8f0d0ae0a077 1084
dudmuck 0:8f0d0ae0a077 1085 mibReq.Type = MIB_PUBLIC_NETWORK;
dudmuck 0:8f0d0ae0a077 1086 mibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK;
dudmuck 0:8f0d0ae0a077 1087 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 1088
dudmuck 0:8f0d0ae0a077 1089 LoRaMacDownlinkStatus.DownlinkCounter = 0;
dudmuck 0:8f0d0ae0a077 1090
dudmuck 0:8f0d0ae0a077 1091 DeviceState = DEVICE_STATE_JOIN;
dudmuck 0:8f0d0ae0a077 1092 break;
dudmuck 0:8f0d0ae0a077 1093 }
dudmuck 0:8f0d0ae0a077 1094 case DEVICE_STATE_JOIN:
dudmuck 0:8f0d0ae0a077 1095 {
dudmuck 10:00997daeb0c0 1096 if (jumper_in.read()) /* if jumper installed: auto uplink */
dudmuck 10:00997daeb0c0 1097 send_at_beacon = true;
dudmuck 0:8f0d0ae0a077 1098 #if( OVER_THE_AIR_ACTIVATION != 0 )
dudmuck 0:8f0d0ae0a077 1099 MlmeReq_t mlmeReq;
dudmuck 0:8f0d0ae0a077 1100 // override software definition with hardware value
dudmuck 0:8f0d0ae0a077 1101 BoardGetUniqueId(DevEui);
dudmuck 1:53c30224eda8 1102 isr_printf("DevEUI ");
dudmuck 0:8f0d0ae0a077 1103 for (int i = 0; i < 8; i++)
dudmuck 1:53c30224eda8 1104 isr_printf("%02x ", DevEui[i]);
dudmuck 1:53c30224eda8 1105 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 1106
dudmuck 0:8f0d0ae0a077 1107 mlmeReq.Type = MLME_JOIN;
dudmuck 0:8f0d0ae0a077 1108
dudmuck 0:8f0d0ae0a077 1109 mlmeReq.Req.Join.DevEui = DevEui;
dudmuck 0:8f0d0ae0a077 1110 mlmeReq.Req.Join.AppEui = AppEui;
dudmuck 0:8f0d0ae0a077 1111 mlmeReq.Req.Join.AppKey = AppKey;
dudmuck 0:8f0d0ae0a077 1112 mlmeReq.Req.Join.NbTrials = 255;
dudmuck 0:8f0d0ae0a077 1113
dudmuck 16:915815632c1f 1114 LoRaMacStatus_t status = LoRaMacMlmeRequest( &mlmeReq );
dudmuck 16:915815632c1f 1115 if (status != LORAMAC_STATUS_OK) {
dudmuck 16:915815632c1f 1116 char str[48];
dudmuck 16:915815632c1f 1117 LoRaMacStatus_to_string(status, str);
dudmuck 16:915815632c1f 1118 isr_printf("join-req-failed:%s\r\n", str);
dudmuck 0:8f0d0ae0a077 1119 }
dudmuck 0:8f0d0ae0a077 1120 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 0:8f0d0ae0a077 1121 #else
dudmuck 0:8f0d0ae0a077 1122 mibReq.Type = MIB_NET_ID;
dudmuck 0:8f0d0ae0a077 1123 mibReq.Param.NetID = LORAWAN_NETWORK_ID;
dudmuck 0:8f0d0ae0a077 1124 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 1125
dudmuck 0:8f0d0ae0a077 1126 mibReq.Type = MIB_DEV_ADDR;
dudmuck 0:8f0d0ae0a077 1127 mibReq.Param.DevAddr = DevAddr;
dudmuck 0:8f0d0ae0a077 1128 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 1129
dudmuck 0:8f0d0ae0a077 1130 mibReq.Type = MIB_NWK_SKEY;
dudmuck 0:8f0d0ae0a077 1131 mibReq.Param.NwkSKey = NwkSKey;
dudmuck 0:8f0d0ae0a077 1132 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 1133
dudmuck 0:8f0d0ae0a077 1134 mibReq.Type = MIB_APP_SKEY;
dudmuck 0:8f0d0ae0a077 1135 mibReq.Param.AppSKey = AppSKey;
dudmuck 0:8f0d0ae0a077 1136 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 1137
dudmuck 0:8f0d0ae0a077 1138 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 0:8f0d0ae0a077 1139 mibReq.Param.IsNetworkJoined = true;
dudmuck 0:8f0d0ae0a077 1140 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 1141 #endif
dudmuck 21:500ff43d8424 1142 join_retry = false;
dudmuck 0:8f0d0ae0a077 1143 break;
dudmuck 0:8f0d0ae0a077 1144 }
dudmuck 0:8f0d0ae0a077 1145 case DEVICE_STATE_CYCLE:
dudmuck 0:8f0d0ae0a077 1146 {
dudmuck 0:8f0d0ae0a077 1147 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 0:8f0d0ae0a077 1148
dudmuck 0:8f0d0ae0a077 1149 // Schedule next packet transmission
dudmuck 17:3215f12051f9 1150 TxNextPacketTimeout.attach_us(&OnTxNextPacketTimerEvent, TxDutyCycleTime * 1000);
dudmuck 0:8f0d0ae0a077 1151 break;
dudmuck 0:8f0d0ae0a077 1152 }
dudmuck 0:8f0d0ae0a077 1153 case DEVICE_STATE_SLEEP:
dudmuck 0:8f0d0ae0a077 1154 {
dudmuck 20:42839629a5dc 1155 bottom_half();
dudmuck 20:42839629a5dc 1156 LoRaMacBottomHalf();
dudmuck 0:8f0d0ae0a077 1157 // Wake up through events
dudmuck 20:42839629a5dc 1158 pc2 = 1;
dudmuck 20:42839629a5dc 1159 sleep();
dudmuck 20:42839629a5dc 1160 pc2 = 0;
dudmuck 0:8f0d0ae0a077 1161 break;
dudmuck 0:8f0d0ae0a077 1162 }
dudmuck 18:9ac71c0eb70d 1163 case DEVICE_STATE_UPLINK:
dudmuck 18:9ac71c0eb70d 1164 PrepareTxFrame(AppPort);
dudmuck 18:9ac71c0eb70d 1165 SendFrame(AppPort);
dudmuck 18:9ac71c0eb70d 1166 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 18:9ac71c0eb70d 1167 break;
dudmuck 0:8f0d0ae0a077 1168 default:
dudmuck 0:8f0d0ae0a077 1169 {
dudmuck 0:8f0d0ae0a077 1170 DeviceState = DEVICE_STATE_INIT;
dudmuck 0:8f0d0ae0a077 1171 break;
dudmuck 0:8f0d0ae0a077 1172 }
dudmuck 0:8f0d0ae0a077 1173 }
dudmuck 1:53c30224eda8 1174
dudmuck 1:53c30224eda8 1175 bottom_half();
dudmuck 20:42839629a5dc 1176 LoRaMacBottomHalf();
dudmuck 1:53c30224eda8 1177
dudmuck 21:500ff43d8424 1178 if (join_retry) {
dudmuck 21:500ff43d8424 1179 isr_printf("join_retry\r\n");
dudmuck 21:500ff43d8424 1180 DeviceState = DEVICE_STATE_JOIN;
dudmuck 21:500ff43d8424 1181 join_retry = false;
dudmuck 21:500ff43d8424 1182 }
dudmuck 21:500ff43d8424 1183
dudmuck 1:53c30224eda8 1184 } // ..while( 1 )
dudmuck 0:8f0d0ae0a077 1185 }