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 May 26 23:07:01 2017 +0000
Revision:
5:c108560af4c3
Parent:
4:d9201410c87b
Child:
7:e238827f0e47
add sx1272 debug pin, prevent uplink config from being changed by beacon

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 0:8f0d0ae0a077 21
dudmuck 0:8f0d0ae0a077 22 Serial pc( USBTX, USBRX );
dudmuck 0:8f0d0ae0a077 23 char pcbuf[128];
dudmuck 0:8f0d0ae0a077 24 int pcbuf_len;
dudmuck 0:8f0d0ae0a077 25
dudmuck 0:8f0d0ae0a077 26 /*!
dudmuck 0:8f0d0ae0a077 27 * Defines the application data transmission duty cycle. 5s, value in [ms].
dudmuck 0:8f0d0ae0a077 28 */
dudmuck 0:8f0d0ae0a077 29 #define APP_TX_DUTYCYCLE 5000
dudmuck 0:8f0d0ae0a077 30
dudmuck 0:8f0d0ae0a077 31 /*!
dudmuck 0:8f0d0ae0a077 32 * Defines a random delay for application data transmission duty cycle. 1s,
dudmuck 0:8f0d0ae0a077 33 * value in [ms].
dudmuck 0:8f0d0ae0a077 34 */
dudmuck 0:8f0d0ae0a077 35 #define APP_TX_DUTYCYCLE_RND 1000
dudmuck 0:8f0d0ae0a077 36
dudmuck 0:8f0d0ae0a077 37 /*!
dudmuck 0:8f0d0ae0a077 38 * Default datarate
dudmuck 0:8f0d0ae0a077 39 */
dudmuck 0:8f0d0ae0a077 40 #define LORAWAN_DEFAULT_DATARATE DR_10
dudmuck 0:8f0d0ae0a077 41
dudmuck 0:8f0d0ae0a077 42 /*!
dudmuck 0:8f0d0ae0a077 43 * LoRaWAN confirmed messages
dudmuck 0:8f0d0ae0a077 44 */
dudmuck 0:8f0d0ae0a077 45 #define LORAWAN_CONFIRMED_MSG_ON true
dudmuck 0:8f0d0ae0a077 46
dudmuck 0:8f0d0ae0a077 47 /*!
dudmuck 0:8f0d0ae0a077 48 * LoRaWAN application port
dudmuck 0:8f0d0ae0a077 49 */
dudmuck 0:8f0d0ae0a077 50 #define LORAWAN_APP_PORT 15
dudmuck 0:8f0d0ae0a077 51
dudmuck 0:8f0d0ae0a077 52 /*!
dudmuck 0:8f0d0ae0a077 53 * User application data buffer size
dudmuck 0:8f0d0ae0a077 54 */
dudmuck 0:8f0d0ae0a077 55 #if ( LORAWAN_CONFIRMED_MSG_ON == 1 )
dudmuck 0:8f0d0ae0a077 56 #define LORAWAN_APP_DATA_SIZE 6
dudmuck 0:8f0d0ae0a077 57
dudmuck 0:8f0d0ae0a077 58 #else
dudmuck 0:8f0d0ae0a077 59 #define LORAWAN_APP_DATA_SIZE 1
dudmuck 0:8f0d0ae0a077 60
dudmuck 0:8f0d0ae0a077 61 #endif
dudmuck 0:8f0d0ae0a077 62
dudmuck 0:8f0d0ae0a077 63 static uint8_t DevEui[] = LORAWAN_DEVICE_EUI;
dudmuck 0:8f0d0ae0a077 64 static uint8_t AppEui[] = LORAWAN_APPLICATION_EUI;
dudmuck 0:8f0d0ae0a077 65 static uint8_t AppKey[] = LORAWAN_APPLICATION_KEY;
dudmuck 0:8f0d0ae0a077 66
dudmuck 0:8f0d0ae0a077 67 #if( OVER_THE_AIR_ACTIVATION == 0 )
dudmuck 0:8f0d0ae0a077 68
dudmuck 0:8f0d0ae0a077 69 static uint8_t NwkSKey[] = LORAWAN_NWKSKEY;
dudmuck 0:8f0d0ae0a077 70 static uint8_t AppSKey[] = LORAWAN_APPSKEY;
dudmuck 0:8f0d0ae0a077 71
dudmuck 0:8f0d0ae0a077 72 /*!
dudmuck 0:8f0d0ae0a077 73 * Device address
dudmuck 0:8f0d0ae0a077 74 */
dudmuck 0:8f0d0ae0a077 75 static uint32_t DevAddr = LORAWAN_DEVICE_ADDRESS;
dudmuck 0:8f0d0ae0a077 76
dudmuck 0:8f0d0ae0a077 77 #endif
dudmuck 0:8f0d0ae0a077 78
dudmuck 0:8f0d0ae0a077 79 /*!
dudmuck 0:8f0d0ae0a077 80 * Application port
dudmuck 0:8f0d0ae0a077 81 */
dudmuck 0:8f0d0ae0a077 82 static uint8_t AppPort = LORAWAN_APP_PORT;
dudmuck 0:8f0d0ae0a077 83
dudmuck 0:8f0d0ae0a077 84 /*!
dudmuck 0:8f0d0ae0a077 85 * User application data size
dudmuck 0:8f0d0ae0a077 86 */
dudmuck 0:8f0d0ae0a077 87 static uint8_t AppDataSize = LORAWAN_APP_DATA_SIZE;
dudmuck 0:8f0d0ae0a077 88 static unsigned int uplink_length; // user assigned
dudmuck 0:8f0d0ae0a077 89
dudmuck 0:8f0d0ae0a077 90 /*!
dudmuck 0:8f0d0ae0a077 91 * User application data buffer size
dudmuck 0:8f0d0ae0a077 92 */
dudmuck 0:8f0d0ae0a077 93 #define LORAWAN_APP_DATA_MAX_SIZE 128
dudmuck 0:8f0d0ae0a077 94
dudmuck 0:8f0d0ae0a077 95 /*!
dudmuck 0:8f0d0ae0a077 96 * User application data
dudmuck 0:8f0d0ae0a077 97 */
dudmuck 0:8f0d0ae0a077 98 static uint8_t AppData[LORAWAN_APP_DATA_MAX_SIZE];
dudmuck 0:8f0d0ae0a077 99
dudmuck 0:8f0d0ae0a077 100 /*!
dudmuck 0:8f0d0ae0a077 101 * Indicates if the node is sending confirmed or unconfirmed messages
dudmuck 0:8f0d0ae0a077 102 */
dudmuck 0:8f0d0ae0a077 103 static uint8_t IsTxConfirmed = LORAWAN_CONFIRMED_MSG_ON;
dudmuck 0:8f0d0ae0a077 104
dudmuck 0:8f0d0ae0a077 105 /*!
dudmuck 0:8f0d0ae0a077 106 * Defines the application data transmission duty cycle
dudmuck 0:8f0d0ae0a077 107 */
dudmuck 0:8f0d0ae0a077 108 static uint32_t TxDutyCycleTime;
dudmuck 0:8f0d0ae0a077 109
dudmuck 0:8f0d0ae0a077 110 /*!
dudmuck 0:8f0d0ae0a077 111 * Timer to handle the application data transmission duty cycle
dudmuck 0:8f0d0ae0a077 112 */
dudmuck 0:8f0d0ae0a077 113 LowPowerTimeout TxNextPacketTimer;
dudmuck 0:8f0d0ae0a077 114
dudmuck 0:8f0d0ae0a077 115 volatile bool send_at_beacon;
dudmuck 0:8f0d0ae0a077 116 volatile bool awaiting_mcps_indic;
dudmuck 0:8f0d0ae0a077 117 /*!
dudmuck 0:8f0d0ae0a077 118 * Specifies the state of the application LED
dudmuck 0:8f0d0ae0a077 119 */
dudmuck 0:8f0d0ae0a077 120 static bool AppLedStateOn = false;
dudmuck 0:8f0d0ae0a077 121
dudmuck 0:8f0d0ae0a077 122 /*!
dudmuck 0:8f0d0ae0a077 123 * Indicates if a new packet can be sent
dudmuck 0:8f0d0ae0a077 124 */
dudmuck 0:8f0d0ae0a077 125 static bool NextTx = true;
dudmuck 0:8f0d0ae0a077 126
dudmuck 0:8f0d0ae0a077 127 /*!
dudmuck 0:8f0d0ae0a077 128 * Device states
dudmuck 0:8f0d0ae0a077 129 */
dudmuck 0:8f0d0ae0a077 130 static enum eDeviceState
dudmuck 0:8f0d0ae0a077 131 {
dudmuck 0:8f0d0ae0a077 132 DEVICE_STATE_INIT,
dudmuck 0:8f0d0ae0a077 133 DEVICE_STATE_JOIN,
dudmuck 0:8f0d0ae0a077 134 DEVICE_STATE_SEND,
dudmuck 0:8f0d0ae0a077 135 DEVICE_STATE_CYCLE,
dudmuck 0:8f0d0ae0a077 136 DEVICE_STATE_SLEEP
dudmuck 0:8f0d0ae0a077 137 }DeviceState;
dudmuck 0:8f0d0ae0a077 138
dudmuck 0:8f0d0ae0a077 139 /*!
dudmuck 0:8f0d0ae0a077 140 * Strucure containing the Uplink status
dudmuck 0:8f0d0ae0a077 141 */
dudmuck 0:8f0d0ae0a077 142 struct sLoRaMacUplinkStatus
dudmuck 0:8f0d0ae0a077 143 {
dudmuck 0:8f0d0ae0a077 144 uint8_t Acked;
dudmuck 0:8f0d0ae0a077 145 //int8_t Datarate;
dudmuck 0:8f0d0ae0a077 146 uint16_t UplinkCounter;
dudmuck 0:8f0d0ae0a077 147 uint8_t Port;
dudmuck 0:8f0d0ae0a077 148 uint8_t *Buffer;
dudmuck 0:8f0d0ae0a077 149 uint8_t BufferSize;
dudmuck 0:8f0d0ae0a077 150 }LoRaMacUplinkStatus;
dudmuck 0:8f0d0ae0a077 151 //volatile bool UplinkStatusUpdated = false;
dudmuck 0:8f0d0ae0a077 152
dudmuck 0:8f0d0ae0a077 153 /*!
dudmuck 0:8f0d0ae0a077 154 * Strucure containing the Downlink status
dudmuck 0:8f0d0ae0a077 155 */
dudmuck 0:8f0d0ae0a077 156 struct sLoRaMacDownlinkStatus
dudmuck 0:8f0d0ae0a077 157 {
dudmuck 0:8f0d0ae0a077 158 int16_t Rssi;
dudmuck 0:8f0d0ae0a077 159 int8_t Snr;
dudmuck 0:8f0d0ae0a077 160 uint16_t DownlinkCounter;
dudmuck 0:8f0d0ae0a077 161 bool RxData;
dudmuck 0:8f0d0ae0a077 162 uint8_t Port;
dudmuck 0:8f0d0ae0a077 163 uint8_t *Buffer;
dudmuck 0:8f0d0ae0a077 164 uint8_t BufferSize;
dudmuck 0:8f0d0ae0a077 165 }LoRaMacDownlinkStatus;
dudmuck 0:8f0d0ae0a077 166
dudmuck 2:f2d9aa163652 167 static uint8_t missed_count;
dudmuck 2:f2d9aa163652 168
dudmuck 0:8f0d0ae0a077 169
dudmuck 0:8f0d0ae0a077 170 /*!
dudmuck 0:8f0d0ae0a077 171 * \brief Prepares the payload of the frame
dudmuck 0:8f0d0ae0a077 172 */
dudmuck 0:8f0d0ae0a077 173 static void PrepareTxFrame( uint8_t port )
dudmuck 0:8f0d0ae0a077 174 {
dudmuck 0:8f0d0ae0a077 175 switch( port )
dudmuck 0:8f0d0ae0a077 176 {
dudmuck 0:8f0d0ae0a077 177 case 15:
dudmuck 0:8f0d0ae0a077 178 {
dudmuck 0:8f0d0ae0a077 179 AppData[0] = AppLedStateOn;
dudmuck 0:8f0d0ae0a077 180 if( IsTxConfirmed == true )
dudmuck 0:8f0d0ae0a077 181 {
dudmuck 0:8f0d0ae0a077 182 AppData[1] = LoRaMacDownlinkStatus.DownlinkCounter >> 8;
dudmuck 0:8f0d0ae0a077 183 AppData[2] = LoRaMacDownlinkStatus.DownlinkCounter;
dudmuck 0:8f0d0ae0a077 184 AppData[3] = LoRaMacDownlinkStatus.Rssi >> 8;
dudmuck 0:8f0d0ae0a077 185 AppData[4] = LoRaMacDownlinkStatus.Rssi;
dudmuck 0:8f0d0ae0a077 186 AppData[5] = LoRaMacDownlinkStatus.Snr;
dudmuck 0:8f0d0ae0a077 187 }
dudmuck 0:8f0d0ae0a077 188 }
dudmuck 0:8f0d0ae0a077 189 break;
dudmuck 0:8f0d0ae0a077 190 default:
dudmuck 0:8f0d0ae0a077 191 break;
dudmuck 0:8f0d0ae0a077 192 }
dudmuck 0:8f0d0ae0a077 193 }
dudmuck 0:8f0d0ae0a077 194
dudmuck 0:8f0d0ae0a077 195 void
dudmuck 0:8f0d0ae0a077 196 LoRaMacEventInfoStatus_to_string(LoRaMacEventInfoStatus_t status, char* dst)
dudmuck 0:8f0d0ae0a077 197 {
dudmuck 0:8f0d0ae0a077 198 const char* ptr = NULL;
dudmuck 0:8f0d0ae0a077 199
dudmuck 0:8f0d0ae0a077 200 switch (status) {
dudmuck 0:8f0d0ae0a077 201 case LORAMAC_EVENT_INFO_STATUS_RX_ERROR: ptr = "RX_ERROR"; break;
dudmuck 0:8f0d0ae0a077 202 case LORAMAC_EVENT_INFO_STATUS_OK: ptr = "OK"; break;
dudmuck 2:f2d9aa163652 203 case LORAMAC_EVENT_INFO_STATUS_ERROR_RX_MTYPE: ptr = "ERROR_RX_MTYPE"; break;
dudmuck 2:f2d9aa163652 204 case LORAMAC_EVENT_INFO_STATUS_ERROR_SEND: ptr = "ERROR_SEND"; break;
dudmuck 2:f2d9aa163652 205 case LORAMAC_EVENT_INFO_STATUS_ERROR_JOIN_ACCEPT: ptr = "ERROR_JOIN_ACCEPT"; break;
dudmuck 2:f2d9aa163652 206 case LORAMAC_EVENT_INFO_STATUS_ERROR_MLMEREQ: ptr = "ERROR_MLMEREQ"; break;
dudmuck 2:f2d9aa163652 207 case LORAMAC_EVENT_INFO_STATUS_ERROR_MCPSREQ: ptr = "ERROR_MCPSREQ"; break;
dudmuck 2:f2d9aa163652 208 //case LORAMAC_EVENT_INFO_STATUS_ERROR: ptr = "ERROR"; break;
dudmuck 2:f2d9aa163652 209
dudmuck 0:8f0d0ae0a077 210 case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT: ptr = "TX_TIMEOUT"; break;
dudmuck 0:8f0d0ae0a077 211 case LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT: ptr = "RX_TIMEOUT"; break;
dudmuck 0:8f0d0ae0a077 212 case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL: ptr = "JOIN_FAIL"; break;
dudmuck 0:8f0d0ae0a077 213 case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED: ptr = "DOWNLINK_REPEATED"; break;
dudmuck 0:8f0d0ae0a077 214 case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR: ptr = "TX_DR_PAYLOAD_SIZE_ERROR"; break;
dudmuck 0:8f0d0ae0a077 215 case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS: ptr = "DOWNLINK_TOO_MANY_FRAMES_LOSS"; break;
dudmuck 0:8f0d0ae0a077 216 case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL: ptr = "ADDRESS_FAIL"; break;
dudmuck 0:8f0d0ae0a077 217 case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL: ptr = "MIC_FAIL"; break;
dudmuck 0:8f0d0ae0a077 218 case LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED: ptr = "BEACON_LOCKED"; break;
dudmuck 0:8f0d0ae0a077 219 case LORAMAC_EVENT_INFO_STATUS_BEACON_LOST: ptr = "BEACON_LOST"; break;
dudmuck 0:8f0d0ae0a077 220 case LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND: ptr = "BEACON_NOT_FOUND"; break;
dudmuck 0:8f0d0ae0a077 221 }
dudmuck 0:8f0d0ae0a077 222
dudmuck 0:8f0d0ae0a077 223 if (ptr != NULL)
dudmuck 0:8f0d0ae0a077 224 strcpy(dst, ptr);
dudmuck 0:8f0d0ae0a077 225 }
dudmuck 0:8f0d0ae0a077 226
dudmuck 0:8f0d0ae0a077 227 void
dudmuck 0:8f0d0ae0a077 228 LoRaMacStatus_to_string(LoRaMacStatus_t status, char* dst)
dudmuck 0:8f0d0ae0a077 229 {
dudmuck 0:8f0d0ae0a077 230 const char* ptr = NULL;
dudmuck 0:8f0d0ae0a077 231
dudmuck 0:8f0d0ae0a077 232 switch (status) {
dudmuck 0:8f0d0ae0a077 233 case LORAMAC_STATUS_OK: ptr = "OK"; break;
dudmuck 0:8f0d0ae0a077 234 case LORAMAC_STATUS_BUSY: ptr = "BUSY"; break;
dudmuck 0:8f0d0ae0a077 235 case LORAMAC_STATUS_SERVICE_UNKNOWN: ptr = "SERVICE_UNKNOWN"; break;
dudmuck 0:8f0d0ae0a077 236 case LORAMAC_STATUS_PARAMETER_INVALID: ptr = "PARAMETER_INVALID"; break;
dudmuck 0:8f0d0ae0a077 237 case LORAMAC_STATUS_FREQUENCY_INVALID: ptr = "FREQUENCY_INVALID"; break;
dudmuck 0:8f0d0ae0a077 238 case LORAMAC_STATUS_DATARATE_INVALID: ptr = "DATARATE_INVALID"; break;
dudmuck 0:8f0d0ae0a077 239 case LORAMAC_STATUS_FREQ_AND_DR_INVALID: ptr = "FREQ_AND_DR_INVALID"; break;
dudmuck 0:8f0d0ae0a077 240 case LORAMAC_STATUS_NO_NETWORK_JOINED: ptr = "NO_NETWORK_JOINED"; break;
dudmuck 0:8f0d0ae0a077 241 case LORAMAC_STATUS_LENGTH_ERROR: ptr = "LENGTH_ERROR"; break;
dudmuck 0:8f0d0ae0a077 242 case LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR: ptr = "MAC_CMD_LENGTH_ERROR"; break;
dudmuck 0:8f0d0ae0a077 243 case LORAMAC_STATUS_DEVICE_OFF: ptr = "DEVICE_OFF"; break;
dudmuck 0:8f0d0ae0a077 244 }
dudmuck 0:8f0d0ae0a077 245 if (ptr != NULL)
dudmuck 0:8f0d0ae0a077 246 strcpy(dst, ptr);
dudmuck 0:8f0d0ae0a077 247 }
dudmuck 0:8f0d0ae0a077 248
dudmuck 0:8f0d0ae0a077 249 /*!
dudmuck 5:c108560af4c3 250 * \brief Function executed on TxNextPacket Timeout event
dudmuck 5:c108560af4c3 251 */
dudmuck 5:c108560af4c3 252 static void OnTxNextPacketTimerEvent( void )
dudmuck 5:c108560af4c3 253 {
dudmuck 5:c108560af4c3 254 MibRequestConfirm_t mibReq;
dudmuck 5:c108560af4c3 255 LoRaMacStatus_t status;
dudmuck 5:c108560af4c3 256
dudmuck 5:c108560af4c3 257 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 5:c108560af4c3 258 status = LoRaMacMibGetRequestConfirm( &mibReq );
dudmuck 5:c108560af4c3 259
dudmuck 5:c108560af4c3 260 if( status == LORAMAC_STATUS_OK )
dudmuck 5:c108560af4c3 261 {
dudmuck 5:c108560af4c3 262 if( mibReq.Param.IsNetworkJoined == true )
dudmuck 5:c108560af4c3 263 {
dudmuck 5:c108560af4c3 264 DeviceState = DEVICE_STATE_SEND;
dudmuck 5:c108560af4c3 265 NextTx = true;
dudmuck 5:c108560af4c3 266 }
dudmuck 5:c108560af4c3 267 else
dudmuck 5:c108560af4c3 268 {
dudmuck 5:c108560af4c3 269 DeviceState = DEVICE_STATE_JOIN;
dudmuck 5:c108560af4c3 270 }
dudmuck 5:c108560af4c3 271 }
dudmuck 5:c108560af4c3 272 }
dudmuck 5:c108560af4c3 273
dudmuck 5:c108560af4c3 274 /*!
dudmuck 0:8f0d0ae0a077 275 * \brief Prepares the payload of the frame
dudmuck 0:8f0d0ae0a077 276 *
dudmuck 0:8f0d0ae0a077 277 * \retval [0: frame could be send, 1: error]
dudmuck 0:8f0d0ae0a077 278 */
dudmuck 0:8f0d0ae0a077 279 static bool SendFrame( void )
dudmuck 0:8f0d0ae0a077 280 {
dudmuck 0:8f0d0ae0a077 281 McpsReq_t mcpsReq;
dudmuck 0:8f0d0ae0a077 282 LoRaMacTxInfo_t txInfo;
dudmuck 0:8f0d0ae0a077 283 LoRaMacStatus_t status;
dudmuck 0:8f0d0ae0a077 284 char str[64];
dudmuck 0:8f0d0ae0a077 285
dudmuck 0:8f0d0ae0a077 286 if( LoRaMacQueryTxPossible( AppDataSize, &txInfo ) != LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 287 {
dudmuck 0:8f0d0ae0a077 288 // Send empty frame in order to flush MAC commands
dudmuck 0:8f0d0ae0a077 289 mcpsReq.Type = MCPS_UNCONFIRMED;
dudmuck 0:8f0d0ae0a077 290 mcpsReq.Req.Unconfirmed.fBuffer = NULL;
dudmuck 0:8f0d0ae0a077 291 mcpsReq.Req.Unconfirmed.fBufferSize = 0;
dudmuck 0:8f0d0ae0a077 292 //mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
dudmuck 0:8f0d0ae0a077 293
dudmuck 0:8f0d0ae0a077 294 LoRaMacUplinkStatus.Acked = false;
dudmuck 0:8f0d0ae0a077 295 LoRaMacUplinkStatus.Port = 0;
dudmuck 0:8f0d0ae0a077 296 LoRaMacUplinkStatus.Buffer = NULL;
dudmuck 0:8f0d0ae0a077 297 LoRaMacUplinkStatus.BufferSize = 0;
dudmuck 0:8f0d0ae0a077 298 }
dudmuck 0:8f0d0ae0a077 299 else
dudmuck 0:8f0d0ae0a077 300 {
dudmuck 0:8f0d0ae0a077 301 LoRaMacUplinkStatus.Acked = false;
dudmuck 0:8f0d0ae0a077 302 LoRaMacUplinkStatus.Port = AppPort;
dudmuck 0:8f0d0ae0a077 303 LoRaMacUplinkStatus.Buffer = AppData;
dudmuck 0:8f0d0ae0a077 304 LoRaMacUplinkStatus.BufferSize = AppDataSize;
dudmuck 0:8f0d0ae0a077 305
dudmuck 0:8f0d0ae0a077 306 if( IsTxConfirmed == false )
dudmuck 0:8f0d0ae0a077 307 {
dudmuck 0:8f0d0ae0a077 308 mcpsReq.Type = MCPS_UNCONFIRMED;
dudmuck 0:8f0d0ae0a077 309 mcpsReq.Req.Unconfirmed.fPort = AppPort;
dudmuck 0:8f0d0ae0a077 310 mcpsReq.Req.Unconfirmed.fBuffer = AppData;
dudmuck 0:8f0d0ae0a077 311 mcpsReq.Req.Unconfirmed.fBufferSize = AppDataSize;
dudmuck 0:8f0d0ae0a077 312 //mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
dudmuck 0:8f0d0ae0a077 313 }
dudmuck 0:8f0d0ae0a077 314 else
dudmuck 0:8f0d0ae0a077 315 {
dudmuck 0:8f0d0ae0a077 316 mcpsReq.Type = MCPS_CONFIRMED;
dudmuck 0:8f0d0ae0a077 317 mcpsReq.Req.Confirmed.fPort = AppPort;
dudmuck 0:8f0d0ae0a077 318 mcpsReq.Req.Confirmed.fBuffer = AppData;
dudmuck 0:8f0d0ae0a077 319 mcpsReq.Req.Confirmed.fBufferSize = AppDataSize;
dudmuck 0:8f0d0ae0a077 320 mcpsReq.Req.Confirmed.NbTrials = 8;
dudmuck 0:8f0d0ae0a077 321 //mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
dudmuck 0:8f0d0ae0a077 322 }
dudmuck 0:8f0d0ae0a077 323 }
dudmuck 0:8f0d0ae0a077 324
dudmuck 0:8f0d0ae0a077 325 status = LoRaMacMcpsRequest( &mcpsReq );
dudmuck 0:8f0d0ae0a077 326 if( status == LORAMAC_STATUS_OK )
dudmuck 0:8f0d0ae0a077 327 {
dudmuck 0:8f0d0ae0a077 328 awaiting_mcps_indic = true;
dudmuck 0:8f0d0ae0a077 329 return false;
dudmuck 0:8f0d0ae0a077 330 }
dudmuck 0:8f0d0ae0a077 331 LoRaMacStatus_to_string(status, str);
dudmuck 1:53c30224eda8 332 isr_printf("send failed:%s\r\n", str);
dudmuck 5:c108560af4c3 333 if (status == LORAMAC_STATUS_NO_NETWORK_JOINED) {
dudmuck 5:c108560af4c3 334 TxNextPacketTimer.attach_us(&OnTxNextPacketTimerEvent, randr(1000000, 5000000) + 1000000);
dudmuck 5:c108560af4c3 335 }
dudmuck 0:8f0d0ae0a077 336 send_at_beacon = true;
dudmuck 0:8f0d0ae0a077 337 return true;
dudmuck 0:8f0d0ae0a077 338 }
dudmuck 0:8f0d0ae0a077 339
dudmuck 0:8f0d0ae0a077 340
dudmuck 0:8f0d0ae0a077 341 void
dudmuck 0:8f0d0ae0a077 342 send_uplink()
dudmuck 0:8f0d0ae0a077 343 {
dudmuck 0:8f0d0ae0a077 344 AppDataSize = uplink_length;
dudmuck 1:53c30224eda8 345 NextTx = true;
dudmuck 1:53c30224eda8 346 DeviceState = DEVICE_STATE_SEND;
dudmuck 0:8f0d0ae0a077 347 }
dudmuck 0:8f0d0ae0a077 348
dudmuck 0:8f0d0ae0a077 349
dudmuck 0:8f0d0ae0a077 350 /*!
dudmuck 0:8f0d0ae0a077 351 * \brief MCPS-Confirm event function
dudmuck 0:8f0d0ae0a077 352 *
dudmuck 0:8f0d0ae0a077 353 * \param [IN] mcpsConfirm - Pointer to the confirm structure,
dudmuck 0:8f0d0ae0a077 354 * containing confirm attributes.
dudmuck 0:8f0d0ae0a077 355 */
dudmuck 0:8f0d0ae0a077 356 static void McpsConfirm( McpsConfirm_t *mcpsConfirm )
dudmuck 0:8f0d0ae0a077 357 {
dudmuck 2:f2d9aa163652 358 static uint8_t fail_count = 0;
dudmuck 0:8f0d0ae0a077 359 char str[64];
dudmuck 2:f2d9aa163652 360
dudmuck 1:53c30224eda8 361 isr_printf("McpsConfirm ");
dudmuck 0:8f0d0ae0a077 362 if( mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 363 {
dudmuck 2:f2d9aa163652 364 fail_count = 0;
dudmuck 1:53c30224eda8 365 isr_printf("OK ");
dudmuck 0:8f0d0ae0a077 366 switch( mcpsConfirm->McpsRequest )
dudmuck 0:8f0d0ae0a077 367 {
dudmuck 0:8f0d0ae0a077 368 case MCPS_UNCONFIRMED:
dudmuck 0:8f0d0ae0a077 369 {
dudmuck 1:53c30224eda8 370 isr_printf("UNCONFIRMED");
dudmuck 0:8f0d0ae0a077 371 // Check Datarate
dudmuck 0:8f0d0ae0a077 372 // Check TxPower
dudmuck 0:8f0d0ae0a077 373 break;
dudmuck 0:8f0d0ae0a077 374 }
dudmuck 0:8f0d0ae0a077 375 case MCPS_CONFIRMED:
dudmuck 0:8f0d0ae0a077 376 {
dudmuck 1:53c30224eda8 377 isr_printf("CONFIRMED");
dudmuck 0:8f0d0ae0a077 378 // Check Datarate
dudmuck 0:8f0d0ae0a077 379 // Check TxPower
dudmuck 0:8f0d0ae0a077 380 // Check AckReceived
dudmuck 0:8f0d0ae0a077 381 // Check NbTrials
dudmuck 0:8f0d0ae0a077 382 LoRaMacUplinkStatus.Acked = mcpsConfirm->AckReceived;
dudmuck 0:8f0d0ae0a077 383 break;
dudmuck 0:8f0d0ae0a077 384 }
dudmuck 0:8f0d0ae0a077 385 case MCPS_PROPRIETARY:
dudmuck 0:8f0d0ae0a077 386 {
dudmuck 0:8f0d0ae0a077 387 break;
dudmuck 0:8f0d0ae0a077 388 }
dudmuck 0:8f0d0ae0a077 389 default:
dudmuck 0:8f0d0ae0a077 390 break;
dudmuck 0:8f0d0ae0a077 391 }
dudmuck 0:8f0d0ae0a077 392 //LoRaMacUplinkStatus.Datarate = mcpsConfirm->Datarate;
dudmuck 0:8f0d0ae0a077 393 LoRaMacUplinkStatus.UplinkCounter = mcpsConfirm->UpLinkCounter;
dudmuck 0:8f0d0ae0a077 394
dudmuck 0:8f0d0ae0a077 395 } else {
dudmuck 0:8f0d0ae0a077 396 LoRaMacEventInfoStatus_to_string(mcpsConfirm->Status, str);
dudmuck 1:53c30224eda8 397 isr_printf("%s ", str);
dudmuck 0:8f0d0ae0a077 398
dudmuck 0:8f0d0ae0a077 399 #ifndef MANUAL_UPLINK
dudmuck 0:8f0d0ae0a077 400 /* mcpsIndication may not come. last uplink done, send another uplink */
dudmuck 0:8f0d0ae0a077 401 TxNextPacketTimer.attach_us(&send_uplink, 100000);
dudmuck 0:8f0d0ae0a077 402 #endif /* !MANUAL_UPLINK */
dudmuck 2:f2d9aa163652 403 if (++fail_count > 10) {
dudmuck 2:f2d9aa163652 404 /* cause re-join */
dudmuck 2:f2d9aa163652 405 MibRequestConfirm_t mibReq;
dudmuck 2:f2d9aa163652 406 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 2:f2d9aa163652 407 mibReq.Param.IsNetworkJoined = false;
dudmuck 2:f2d9aa163652 408 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 2:f2d9aa163652 409 }
dudmuck 0:8f0d0ae0a077 410 }
dudmuck 0:8f0d0ae0a077 411
dudmuck 0:8f0d0ae0a077 412 NextTx = true;
dudmuck 1:53c30224eda8 413 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 414 }
dudmuck 0:8f0d0ae0a077 415
dudmuck 0:8f0d0ae0a077 416 /*!
dudmuck 0:8f0d0ae0a077 417 * \brief MCPS-Indication event function
dudmuck 0:8f0d0ae0a077 418 *
dudmuck 0:8f0d0ae0a077 419 * \param [IN] mcpsIndication - Pointer to the indication structure,
dudmuck 0:8f0d0ae0a077 420 * containing indication attributes.
dudmuck 0:8f0d0ae0a077 421 */
dudmuck 0:8f0d0ae0a077 422 static void McpsIndication( McpsIndication_t *mcpsIndication )
dudmuck 0:8f0d0ae0a077 423 {
dudmuck 1:53c30224eda8 424 isr_printf("McpsIndication ");
dudmuck 0:8f0d0ae0a077 425
dudmuck 0:8f0d0ae0a077 426 #ifndef MANUAL_UPLINK
dudmuck 0:8f0d0ae0a077 427 /* last uplink done, send another uplink */
dudmuck 0:8f0d0ae0a077 428 TxNextPacketTimer.attach_us(&send_uplink, 100000);
dudmuck 0:8f0d0ae0a077 429 #endif /* !MANUAL_UPLINK */
dudmuck 0:8f0d0ae0a077 430
dudmuck 0:8f0d0ae0a077 431 if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 432 {
dudmuck 1:53c30224eda8 433 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 434 return;
dudmuck 0:8f0d0ae0a077 435 }
dudmuck 0:8f0d0ae0a077 436
dudmuck 0:8f0d0ae0a077 437 awaiting_mcps_indic = false;
dudmuck 0:8f0d0ae0a077 438
dudmuck 0:8f0d0ae0a077 439 switch( mcpsIndication->McpsIndication )
dudmuck 0:8f0d0ae0a077 440 {
dudmuck 5:c108560af4c3 441 /* this refers to the downlink from gateway, not the uplink that was sent to it */
dudmuck 0:8f0d0ae0a077 442 case MCPS_UNCONFIRMED:
dudmuck 0:8f0d0ae0a077 443 {
dudmuck 1:53c30224eda8 444 isr_printf("UNCONFIRMED ");
dudmuck 0:8f0d0ae0a077 445 break;
dudmuck 0:8f0d0ae0a077 446 }
dudmuck 0:8f0d0ae0a077 447 case MCPS_CONFIRMED:
dudmuck 0:8f0d0ae0a077 448 {
dudmuck 5:c108560af4c3 449 /* downlink has requested Ack */
dudmuck 1:53c30224eda8 450 isr_printf("CONFIRMED ");
dudmuck 0:8f0d0ae0a077 451 break;
dudmuck 0:8f0d0ae0a077 452 }
dudmuck 0:8f0d0ae0a077 453 case MCPS_PROPRIETARY:
dudmuck 0:8f0d0ae0a077 454 {
dudmuck 0:8f0d0ae0a077 455 break;
dudmuck 0:8f0d0ae0a077 456 }
dudmuck 0:8f0d0ae0a077 457 case MCPS_MULTICAST:
dudmuck 0:8f0d0ae0a077 458 {
dudmuck 0:8f0d0ae0a077 459 break;
dudmuck 0:8f0d0ae0a077 460 }
dudmuck 0:8f0d0ae0a077 461 default:
dudmuck 0:8f0d0ae0a077 462 break;
dudmuck 0:8f0d0ae0a077 463 }
dudmuck 0:8f0d0ae0a077 464
dudmuck 0:8f0d0ae0a077 465 // Check Multicast
dudmuck 0:8f0d0ae0a077 466 // Check Port
dudmuck 0:8f0d0ae0a077 467 // Check Datarate
dudmuck 0:8f0d0ae0a077 468 // Check FramePending
dudmuck 0:8f0d0ae0a077 469 // Check Buffer
dudmuck 0:8f0d0ae0a077 470 // Check BufferSize
dudmuck 0:8f0d0ae0a077 471 // Check Rssi
dudmuck 0:8f0d0ae0a077 472 // Check Snr
dudmuck 0:8f0d0ae0a077 473 // Check RxSlot
dudmuck 0:8f0d0ae0a077 474 LoRaMacDownlinkStatus.Rssi = mcpsIndication->Rssi;
dudmuck 0:8f0d0ae0a077 475 if( mcpsIndication->Snr & 0x80 ) // The SNR sign bit is 1
dudmuck 0:8f0d0ae0a077 476 {
dudmuck 0:8f0d0ae0a077 477 // Invert and divide by 4
dudmuck 0:8f0d0ae0a077 478 LoRaMacDownlinkStatus.Snr = ( ( ~mcpsIndication->Snr + 1 ) & 0xFF ) >> 2;
dudmuck 0:8f0d0ae0a077 479 LoRaMacDownlinkStatus.Snr = -LoRaMacDownlinkStatus.Snr;
dudmuck 0:8f0d0ae0a077 480 }
dudmuck 0:8f0d0ae0a077 481 else
dudmuck 0:8f0d0ae0a077 482 {
dudmuck 0:8f0d0ae0a077 483 // Divide by 4
dudmuck 0:8f0d0ae0a077 484 LoRaMacDownlinkStatus.Snr = ( mcpsIndication->Snr & 0xFF ) >> 2;
dudmuck 0:8f0d0ae0a077 485 }
dudmuck 0:8f0d0ae0a077 486 LoRaMacDownlinkStatus.DownlinkCounter++;
dudmuck 0:8f0d0ae0a077 487 LoRaMacDownlinkStatus.RxData = mcpsIndication->RxData;
dudmuck 0:8f0d0ae0a077 488 LoRaMacDownlinkStatus.Port = mcpsIndication->Port;
dudmuck 0:8f0d0ae0a077 489 LoRaMacDownlinkStatus.Buffer = mcpsIndication->Buffer;
dudmuck 0:8f0d0ae0a077 490 LoRaMacDownlinkStatus.BufferSize = mcpsIndication->BufferSize;
dudmuck 0:8f0d0ae0a077 491
dudmuck 0:8f0d0ae0a077 492 if( mcpsIndication->RxData == true )
dudmuck 0:8f0d0ae0a077 493 {
dudmuck 0:8f0d0ae0a077 494 int i;
dudmuck 1:53c30224eda8 495 isr_printf("RxData %u ", mcpsIndication->BufferSize);
dudmuck 0:8f0d0ae0a077 496 for (i = 0; i < mcpsIndication->BufferSize; i++) {
dudmuck 1:53c30224eda8 497 isr_printf("%02x ", mcpsIndication->Buffer[i]);
dudmuck 0:8f0d0ae0a077 498 }
dudmuck 1:53c30224eda8 499 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 500
dudmuck 0:8f0d0ae0a077 501 switch( mcpsIndication->Port )
dudmuck 0:8f0d0ae0a077 502 {
dudmuck 0:8f0d0ae0a077 503 case 1: // The application LED can be controlled on port 1 or 2
dudmuck 0:8f0d0ae0a077 504 case 2:
dudmuck 0:8f0d0ae0a077 505 if( mcpsIndication->BufferSize == 1 )
dudmuck 0:8f0d0ae0a077 506 {
dudmuck 0:8f0d0ae0a077 507 AppLedStateOn = mcpsIndication->Buffer[0] & 0x01;
dudmuck 0:8f0d0ae0a077 508 //Led3StateChanged = true;
dudmuck 0:8f0d0ae0a077 509 }
dudmuck 0:8f0d0ae0a077 510 break;
dudmuck 0:8f0d0ae0a077 511 default:
dudmuck 0:8f0d0ae0a077 512 break;
dudmuck 0:8f0d0ae0a077 513 }
dudmuck 0:8f0d0ae0a077 514 }
dudmuck 0:8f0d0ae0a077 515
dudmuck 1:53c30224eda8 516 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 517 }
dudmuck 0:8f0d0ae0a077 518
dudmuck 0:8f0d0ae0a077 519 /*!
dudmuck 0:8f0d0ae0a077 520 * \brief MLME-Confirm event function
dudmuck 0:8f0d0ae0a077 521 *
dudmuck 0:8f0d0ae0a077 522 * \param [IN] mlmeConfirm - Pointer to the confirm structure,
dudmuck 0:8f0d0ae0a077 523 * containing confirm attributes.
dudmuck 0:8f0d0ae0a077 524 */
dudmuck 0:8f0d0ae0a077 525 static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm )
dudmuck 0:8f0d0ae0a077 526 {
dudmuck 1:53c30224eda8 527 isr_printf("MlmeConfirm ");
dudmuck 0:8f0d0ae0a077 528 switch( mlmeConfirm->MlmeRequest )
dudmuck 0:8f0d0ae0a077 529 {
dudmuck 0:8f0d0ae0a077 530 case MLME_JOIN:
dudmuck 0:8f0d0ae0a077 531 {
dudmuck 1:53c30224eda8 532 isr_printf("MLME_JOIN ");
dudmuck 0:8f0d0ae0a077 533 if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 534 {
dudmuck 0:8f0d0ae0a077 535 // Status is OK, node has joined the network
dudmuck 0:8f0d0ae0a077 536 //DeviceState = DEVICE_STATE_SEND;
dudmuck 0:8f0d0ae0a077 537 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 1:53c30224eda8 538 isr_printf("ok");
dudmuck 2:f2d9aa163652 539 missed_count = 0;
dudmuck 0:8f0d0ae0a077 540 }
dudmuck 0:8f0d0ae0a077 541 else
dudmuck 0:8f0d0ae0a077 542 {
dudmuck 0:8f0d0ae0a077 543 // Join was not successful. Try to join again
dudmuck 0:8f0d0ae0a077 544 DeviceState = DEVICE_STATE_JOIN;
dudmuck 1:53c30224eda8 545 isr_printf("fail");
dudmuck 0:8f0d0ae0a077 546 }
dudmuck 0:8f0d0ae0a077 547 break;
dudmuck 0:8f0d0ae0a077 548 }
dudmuck 0:8f0d0ae0a077 549 case MLME_LINK_CHECK:
dudmuck 0:8f0d0ae0a077 550 {
dudmuck 1:53c30224eda8 551 isr_printf("LINK_CHECK");
dudmuck 0:8f0d0ae0a077 552 if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
dudmuck 0:8f0d0ae0a077 553 {
dudmuck 0:8f0d0ae0a077 554 // Check DemodMargin
dudmuck 0:8f0d0ae0a077 555 //mlmeConfirm->DemodMargin;
dudmuck 0:8f0d0ae0a077 556 // Check NbGateways
dudmuck 0:8f0d0ae0a077 557 //mlmeConfirm->NbGateways;
dudmuck 0:8f0d0ae0a077 558 }
dudmuck 0:8f0d0ae0a077 559 break;
dudmuck 0:8f0d0ae0a077 560 }
dudmuck 0:8f0d0ae0a077 561 default:
dudmuck 0:8f0d0ae0a077 562 break;
dudmuck 0:8f0d0ae0a077 563 }
dudmuck 0:8f0d0ae0a077 564 NextTx = true;
dudmuck 1:53c30224eda8 565 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 566 }
dudmuck 0:8f0d0ae0a077 567
dudmuck 0:8f0d0ae0a077 568
dudmuck 0:8f0d0ae0a077 569 static void MlmeIndication( MlmeIndication_t *MlmeIndication )
dudmuck 0:8f0d0ae0a077 570 {
dudmuck 0:8f0d0ae0a077 571 char str[64];
dudmuck 0:8f0d0ae0a077 572 //MibRequestConfirm_t mibReq;
dudmuck 0:8f0d0ae0a077 573
dudmuck 1:53c30224eda8 574 isr_printf("MlmeIndication ");
dudmuck 0:8f0d0ae0a077 575 switch( MlmeIndication->MlmeIndication )
dudmuck 0:8f0d0ae0a077 576 {
dudmuck 0:8f0d0ae0a077 577 case MLME_BEACON:
dudmuck 0:8f0d0ae0a077 578 {
dudmuck 2:f2d9aa163652 579 isr_printf("missed:%u BEACON ", missed_count);
dudmuck 0:8f0d0ae0a077 580 LoRaMacEventInfoStatus_to_string(MlmeIndication->Status, str);
dudmuck 1:53c30224eda8 581 isr_printf("%s ", str);
dudmuck 0:8f0d0ae0a077 582 #ifndef MANUAL_UPLINK
dudmuck 0:8f0d0ae0a077 583 if (send_at_beacon) {
dudmuck 0:8f0d0ae0a077 584 TxNextPacketTimer.attach_us(&send_uplink, 100000);
dudmuck 0:8f0d0ae0a077 585 send_at_beacon = false;
dudmuck 0:8f0d0ae0a077 586 }
dudmuck 0:8f0d0ae0a077 587 #endif /* !MANUAL_UPLINK */
dudmuck 2:f2d9aa163652 588 if (LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED == MlmeIndication->Status)
dudmuck 2:f2d9aa163652 589 missed_count = 0;
dudmuck 2:f2d9aa163652 590 else if (++missed_count > 4) {
dudmuck 2:f2d9aa163652 591 /* cause re-join */
dudmuck 2:f2d9aa163652 592 MibRequestConfirm_t mibReq;
dudmuck 2:f2d9aa163652 593 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 2:f2d9aa163652 594 mibReq.Param.IsNetworkJoined = false;
dudmuck 2:f2d9aa163652 595 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 2:f2d9aa163652 596 TxNextPacketTimer.attach_us(&OnTxNextPacketTimerEvent, randr(1000000, 5000000) + 1000000);
dudmuck 2:f2d9aa163652 597 }
dudmuck 0:8f0d0ae0a077 598 break;
dudmuck 0:8f0d0ae0a077 599
dudmuck 0:8f0d0ae0a077 600 }
dudmuck 0:8f0d0ae0a077 601 default:
dudmuck 1:53c30224eda8 602 isr_printf("<%d> ", MlmeIndication->MlmeIndication);
dudmuck 0:8f0d0ae0a077 603 break;
dudmuck 0:8f0d0ae0a077 604 }
dudmuck 0:8f0d0ae0a077 605
dudmuck 1:53c30224eda8 606 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 607 }
dudmuck 0:8f0d0ae0a077 608
dudmuck 0:8f0d0ae0a077 609 void
dudmuck 0:8f0d0ae0a077 610 send_pcbuf_uplink()
dudmuck 0:8f0d0ae0a077 611 {
dudmuck 0:8f0d0ae0a077 612 bool ret;
dudmuck 0:8f0d0ae0a077 613 memcpy(AppData, pcbuf, pcbuf_len);
dudmuck 0:8f0d0ae0a077 614 AppDataSize = pcbuf_len;
dudmuck 0:8f0d0ae0a077 615
dudmuck 0:8f0d0ae0a077 616 ret = SendFrame( );
dudmuck 1:53c30224eda8 617 isr_printf("%d = SendFrame()\r\n", ret);
dudmuck 0:8f0d0ae0a077 618 }
dudmuck 0:8f0d0ae0a077 619
dudmuck 0:8f0d0ae0a077 620 void cmd_status(uint8_t idx)
dudmuck 0:8f0d0ae0a077 621 {
dudmuck 2:f2d9aa163652 622 MibRequestConfirm_t mibReq;
dudmuck 2:f2d9aa163652 623
dudmuck 2:f2d9aa163652 624 isr_printf("DevEUI ");
dudmuck 2:f2d9aa163652 625 for (int i = 0; i < 8; i++)
dudmuck 2:f2d9aa163652 626 isr_printf("%02x ", DevEui[i]);
dudmuck 2:f2d9aa163652 627 mibReq.Type = MIB_DEV_ADDR;
dudmuck 2:f2d9aa163652 628 LoRaMacMibGetRequestConfirm( &mibReq );
dudmuck 2:f2d9aa163652 629 isr_printf(", DevAddr:%x\r\n", mibReq.Param.DevAddr);
dudmuck 2:f2d9aa163652 630
dudmuck 1:53c30224eda8 631 isr_printf("DeviceState:%d\r\n", DeviceState);
dudmuck 1:53c30224eda8 632 isr_printf("send_at_beacon:%d\r\n", send_at_beacon);
dudmuck 1:53c30224eda8 633 isr_printf("awaiting_mcps_indic:%d\r\n", awaiting_mcps_indic);
dudmuck 0:8f0d0ae0a077 634 loramac_print_status();
dudmuck 0:8f0d0ae0a077 635 }
dudmuck 0:8f0d0ae0a077 636
dudmuck 0:8f0d0ae0a077 637 void cmd_uplink_length(uint8_t idx)
dudmuck 0:8f0d0ae0a077 638 {
dudmuck 0:8f0d0ae0a077 639 if (pcbuf[idx] >= '0' && pcbuf[idx] <= '9') {
dudmuck 0:8f0d0ae0a077 640 sscanf(pcbuf+idx, "%u", &uplink_length);
dudmuck 0:8f0d0ae0a077 641 }
dudmuck 1:53c30224eda8 642 isr_printf("uplink_length:%u\r\n", uplink_length);
dudmuck 0:8f0d0ae0a077 643 }
dudmuck 0:8f0d0ae0a077 644
dudmuck 0:8f0d0ae0a077 645 typedef struct {
dudmuck 0:8f0d0ae0a077 646 const char* const cmd;
dudmuck 0:8f0d0ae0a077 647 void (*handler)(uint8_t args_at);
dudmuck 0:8f0d0ae0a077 648 const char* const arg_descr;
dudmuck 0:8f0d0ae0a077 649 const char* const description;
dudmuck 0:8f0d0ae0a077 650 } menu_item_t;
dudmuck 0:8f0d0ae0a077 651
dudmuck 0:8f0d0ae0a077 652 void cmd_help(uint8_t args_at);
dudmuck 0:8f0d0ae0a077 653
dudmuck 0:8f0d0ae0a077 654 const menu_item_t menu_items[] =
dudmuck 0:8f0d0ae0a077 655 { /* after first character, command names must be [A-Za-z] */
dudmuck 0:8f0d0ae0a077 656 { "?", cmd_help, "","show available commands"},
dudmuck 0:8f0d0ae0a077 657 { ".", cmd_status, "","print status"},
dudmuck 0:8f0d0ae0a077 658 { "ul", cmd_uplink_length, "","set uplink payload length"},
dudmuck 0:8f0d0ae0a077 659 { NULL, NULL, NULL, NULL }
dudmuck 0:8f0d0ae0a077 660 };
dudmuck 0:8f0d0ae0a077 661
dudmuck 0:8f0d0ae0a077 662 void cmd_help(uint8_t args_at)
dudmuck 0:8f0d0ae0a077 663 {
dudmuck 0:8f0d0ae0a077 664 int i;
dudmuck 0:8f0d0ae0a077 665
dudmuck 0:8f0d0ae0a077 666 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 1:53c30224eda8 667 isr_printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
dudmuck 0:8f0d0ae0a077 668 }
dudmuck 0:8f0d0ae0a077 669
dudmuck 0:8f0d0ae0a077 670 }
dudmuck 0:8f0d0ae0a077 671
dudmuck 0:8f0d0ae0a077 672 void
dudmuck 0:8f0d0ae0a077 673 console()
dudmuck 0:8f0d0ae0a077 674 {
dudmuck 0:8f0d0ae0a077 675 bool parsed;
dudmuck 0:8f0d0ae0a077 676 int i;
dudmuck 0:8f0d0ae0a077 677 uint8_t user_cmd_len;
dudmuck 0:8f0d0ae0a077 678
dudmuck 0:8f0d0ae0a077 679 if (pcbuf_len < 0) { // ctrl-C
dudmuck 1:53c30224eda8 680 //isr_printf("abort\r\n");
dudmuck 0:8f0d0ae0a077 681 return;
dudmuck 0:8f0d0ae0a077 682 }
dudmuck 0:8f0d0ae0a077 683 if (pcbuf_len == 0)
dudmuck 0:8f0d0ae0a077 684 return;
dudmuck 0:8f0d0ae0a077 685
dudmuck 1:53c30224eda8 686 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 687
dudmuck 0:8f0d0ae0a077 688 /* get end of user-entered command */
dudmuck 0:8f0d0ae0a077 689 user_cmd_len = 1; // first character can be any character
dudmuck 0:8f0d0ae0a077 690 for (i = 1; i <= pcbuf_len; i++) {
dudmuck 0:8f0d0ae0a077 691 if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
dudmuck 0:8f0d0ae0a077 692 user_cmd_len = i;
dudmuck 0:8f0d0ae0a077 693 break;
dudmuck 0:8f0d0ae0a077 694 }
dudmuck 0:8f0d0ae0a077 695 }
dudmuck 0:8f0d0ae0a077 696
dudmuck 0:8f0d0ae0a077 697 parsed = false;
dudmuck 0:8f0d0ae0a077 698 for (i = 0; menu_items[i].cmd != NULL ; i++) {
dudmuck 0:8f0d0ae0a077 699 int mi_len = strlen(menu_items[i].cmd);
dudmuck 0:8f0d0ae0a077 700
dudmuck 0:8f0d0ae0a077 701 if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
dudmuck 0:8f0d0ae0a077 702 while (pcbuf[mi_len] == ' ') // skip past spaces
dudmuck 0:8f0d0ae0a077 703 mi_len++;
dudmuck 0:8f0d0ae0a077 704 menu_items[i].handler(mi_len);
dudmuck 0:8f0d0ae0a077 705 parsed = true;
dudmuck 0:8f0d0ae0a077 706 break;
dudmuck 0:8f0d0ae0a077 707 }
dudmuck 0:8f0d0ae0a077 708 }
dudmuck 0:8f0d0ae0a077 709
dudmuck 0:8f0d0ae0a077 710 if (!parsed)
dudmuck 0:8f0d0ae0a077 711 send_pcbuf_uplink();
dudmuck 0:8f0d0ae0a077 712
dudmuck 0:8f0d0ae0a077 713 pcbuf_len = 0;
dudmuck 1:53c30224eda8 714 isr_printf("> ");
dudmuck 0:8f0d0ae0a077 715 fflush(stdout);
dudmuck 0:8f0d0ae0a077 716 }
dudmuck 0:8f0d0ae0a077 717
dudmuck 0:8f0d0ae0a077 718 void rx_callback()
dudmuck 0:8f0d0ae0a077 719 {
dudmuck 0:8f0d0ae0a077 720 static uint8_t pcbuf_idx = 0;
dudmuck 0:8f0d0ae0a077 721 static uint8_t prev_len = 0;;
dudmuck 0:8f0d0ae0a077 722 char c = pc.getc();
dudmuck 0:8f0d0ae0a077 723
dudmuck 0:8f0d0ae0a077 724 if (c == 8) {
dudmuck 0:8f0d0ae0a077 725 if (pcbuf_idx > 0) {
dudmuck 0:8f0d0ae0a077 726 pc.putc(8);
dudmuck 0:8f0d0ae0a077 727 pc.putc(' ');
dudmuck 0:8f0d0ae0a077 728 pc.putc(8);
dudmuck 0:8f0d0ae0a077 729 pcbuf_idx--;
dudmuck 0:8f0d0ae0a077 730 }
dudmuck 0:8f0d0ae0a077 731 } else if (c == 3) { // ctrl-C
dudmuck 0:8f0d0ae0a077 732 pcbuf_len = -1;
dudmuck 0:8f0d0ae0a077 733 } else if (c == '\r') {
dudmuck 0:8f0d0ae0a077 734 if (pcbuf_idx == 0) {
dudmuck 0:8f0d0ae0a077 735 pcbuf_len = prev_len;
dudmuck 0:8f0d0ae0a077 736 } else {
dudmuck 0:8f0d0ae0a077 737 pcbuf[pcbuf_idx] = 0; // null terminate
dudmuck 0:8f0d0ae0a077 738 prev_len = pcbuf_idx;
dudmuck 0:8f0d0ae0a077 739 pcbuf_idx = 0;
dudmuck 0:8f0d0ae0a077 740 pcbuf_len = prev_len;
dudmuck 0:8f0d0ae0a077 741 }
dudmuck 0:8f0d0ae0a077 742 } else if (pcbuf_idx < sizeof(pcbuf)) {
dudmuck 0:8f0d0ae0a077 743 pcbuf[pcbuf_idx++] = c;
dudmuck 0:8f0d0ae0a077 744 pc.putc(c);
dudmuck 0:8f0d0ae0a077 745 }
dudmuck 0:8f0d0ae0a077 746 }
dudmuck 0:8f0d0ae0a077 747
dudmuck 0:8f0d0ae0a077 748 /**
dudmuck 0:8f0d0ae0a077 749 * Main application entry point.
dudmuck 0:8f0d0ae0a077 750 */
dudmuck 0:8f0d0ae0a077 751 int main( void )
dudmuck 0:8f0d0ae0a077 752 {
dudmuck 0:8f0d0ae0a077 753 LoRaMacPrimitives_t LoRaMacPrimitives;
dudmuck 0:8f0d0ae0a077 754 LoRaMacCallback_t LoRaMacCallbacks;
dudmuck 0:8f0d0ae0a077 755 MibRequestConfirm_t mibReq;
dudmuck 0:8f0d0ae0a077 756
dudmuck 0:8f0d0ae0a077 757 BoardInit( );
dudmuck 4:d9201410c87b 758 pc.baud(38400);
dudmuck 1:53c30224eda8 759 isr_printf("\r\nreset\r\n");
dudmuck 0:8f0d0ae0a077 760 DeviceState = DEVICE_STATE_INIT;
dudmuck 0:8f0d0ae0a077 761
dudmuck 0:8f0d0ae0a077 762 while( 1 )
dudmuck 0:8f0d0ae0a077 763 {
dudmuck 0:8f0d0ae0a077 764 console();
dudmuck 1:53c30224eda8 765 if (pc.readable())
dudmuck 1:53c30224eda8 766 rx_callback();
dudmuck 0:8f0d0ae0a077 767
dudmuck 0:8f0d0ae0a077 768 switch( DeviceState )
dudmuck 0:8f0d0ae0a077 769 {
dudmuck 0:8f0d0ae0a077 770 case DEVICE_STATE_INIT:
dudmuck 0:8f0d0ae0a077 771 {
dudmuck 1:53c30224eda8 772 isr_printf("DEVICE_STATE_INIT\r\n");
dudmuck 0:8f0d0ae0a077 773 uplink_length = 2;
dudmuck 0:8f0d0ae0a077 774 LoRaMacPrimitives.MacMcpsConfirm = McpsConfirm;
dudmuck 0:8f0d0ae0a077 775 LoRaMacPrimitives.MacMcpsIndication = McpsIndication;
dudmuck 0:8f0d0ae0a077 776 LoRaMacPrimitives.MacMlmeConfirm = MlmeConfirm;
dudmuck 0:8f0d0ae0a077 777 LoRaMacPrimitives.MacMlmeIndication = MlmeIndication;
dudmuck 0:8f0d0ae0a077 778 LoRaMacCallbacks.GetBatteryLevel = BoardGetBatteryLevel;
dudmuck 0:8f0d0ae0a077 779 if (LORAMAC_STATUS_OK != LoRaMacInitialization( &LoRaMacPrimitives, &LoRaMacCallbacks )) {
dudmuck 1:53c30224eda8 780 isr_printf("LoRaMacInitialization() failed\r\n");
dudmuck 0:8f0d0ae0a077 781 for (;;) ;
dudmuck 0:8f0d0ae0a077 782 }
dudmuck 1:53c30224eda8 783 isr_printf("INIT-ok\r\n");
dudmuck 0:8f0d0ae0a077 784
dudmuck 0:8f0d0ae0a077 785 mibReq.Type = MIB_PUBLIC_NETWORK;
dudmuck 0:8f0d0ae0a077 786 mibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK;
dudmuck 0:8f0d0ae0a077 787 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 788
dudmuck 0:8f0d0ae0a077 789 LoRaMacDownlinkStatus.DownlinkCounter = 0;
dudmuck 0:8f0d0ae0a077 790
dudmuck 0:8f0d0ae0a077 791 DeviceState = DEVICE_STATE_JOIN;
dudmuck 0:8f0d0ae0a077 792 break;
dudmuck 0:8f0d0ae0a077 793 }
dudmuck 0:8f0d0ae0a077 794 case DEVICE_STATE_JOIN:
dudmuck 0:8f0d0ae0a077 795 {
dudmuck 0:8f0d0ae0a077 796 send_at_beacon = true;
dudmuck 0:8f0d0ae0a077 797 #if( OVER_THE_AIR_ACTIVATION != 0 )
dudmuck 0:8f0d0ae0a077 798 MlmeReq_t mlmeReq;
dudmuck 0:8f0d0ae0a077 799 // override software definition with hardware value
dudmuck 0:8f0d0ae0a077 800 BoardGetUniqueId(DevEui);
dudmuck 1:53c30224eda8 801 isr_printf("DevEUI ");
dudmuck 0:8f0d0ae0a077 802 for (int i = 0; i < 8; i++)
dudmuck 1:53c30224eda8 803 isr_printf("%02x ", DevEui[i]);
dudmuck 1:53c30224eda8 804 isr_printf("\r\n");
dudmuck 0:8f0d0ae0a077 805
dudmuck 0:8f0d0ae0a077 806 mlmeReq.Type = MLME_JOIN;
dudmuck 0:8f0d0ae0a077 807
dudmuck 0:8f0d0ae0a077 808 mlmeReq.Req.Join.DevEui = DevEui;
dudmuck 0:8f0d0ae0a077 809 mlmeReq.Req.Join.AppEui = AppEui;
dudmuck 0:8f0d0ae0a077 810 mlmeReq.Req.Join.AppKey = AppKey;
dudmuck 0:8f0d0ae0a077 811 mlmeReq.Req.Join.NbTrials = 255;
dudmuck 0:8f0d0ae0a077 812
dudmuck 0:8f0d0ae0a077 813 if( NextTx == true )
dudmuck 0:8f0d0ae0a077 814 {
dudmuck 0:8f0d0ae0a077 815 LoRaMacMlmeRequest( &mlmeReq );
dudmuck 0:8f0d0ae0a077 816 }
dudmuck 0:8f0d0ae0a077 817 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 0:8f0d0ae0a077 818 #else
dudmuck 0:8f0d0ae0a077 819 mibReq.Type = MIB_NET_ID;
dudmuck 0:8f0d0ae0a077 820 mibReq.Param.NetID = LORAWAN_NETWORK_ID;
dudmuck 0:8f0d0ae0a077 821 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 822
dudmuck 0:8f0d0ae0a077 823 mibReq.Type = MIB_DEV_ADDR;
dudmuck 0:8f0d0ae0a077 824 mibReq.Param.DevAddr = DevAddr;
dudmuck 0:8f0d0ae0a077 825 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 826
dudmuck 0:8f0d0ae0a077 827 mibReq.Type = MIB_NWK_SKEY;
dudmuck 0:8f0d0ae0a077 828 mibReq.Param.NwkSKey = NwkSKey;
dudmuck 0:8f0d0ae0a077 829 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 830
dudmuck 0:8f0d0ae0a077 831 mibReq.Type = MIB_APP_SKEY;
dudmuck 0:8f0d0ae0a077 832 mibReq.Param.AppSKey = AppSKey;
dudmuck 0:8f0d0ae0a077 833 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 834
dudmuck 0:8f0d0ae0a077 835 mibReq.Type = MIB_NETWORK_JOINED;
dudmuck 0:8f0d0ae0a077 836 mibReq.Param.IsNetworkJoined = true;
dudmuck 0:8f0d0ae0a077 837 LoRaMacMibSetRequestConfirm( &mibReq );
dudmuck 0:8f0d0ae0a077 838
dudmuck 0:8f0d0ae0a077 839 DeviceState = DEVICE_STATE_SEND;
dudmuck 0:8f0d0ae0a077 840 #endif
dudmuck 0:8f0d0ae0a077 841 break;
dudmuck 0:8f0d0ae0a077 842 }
dudmuck 0:8f0d0ae0a077 843 case DEVICE_STATE_SEND:
dudmuck 0:8f0d0ae0a077 844 {
dudmuck 0:8f0d0ae0a077 845 if( NextTx == true )
dudmuck 0:8f0d0ae0a077 846 {
dudmuck 0:8f0d0ae0a077 847 PrepareTxFrame( AppPort );
dudmuck 0:8f0d0ae0a077 848
dudmuck 0:8f0d0ae0a077 849 NextTx = SendFrame( );
dudmuck 0:8f0d0ae0a077 850 }
dudmuck 2:f2d9aa163652 851
dudmuck 0:8f0d0ae0a077 852 // Schedule next packet transmission
dudmuck 0:8f0d0ae0a077 853 TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );
dudmuck 2:f2d9aa163652 854 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 0:8f0d0ae0a077 855 break;
dudmuck 0:8f0d0ae0a077 856 }
dudmuck 0:8f0d0ae0a077 857 case DEVICE_STATE_CYCLE:
dudmuck 0:8f0d0ae0a077 858 {
dudmuck 0:8f0d0ae0a077 859 DeviceState = DEVICE_STATE_SLEEP;
dudmuck 0:8f0d0ae0a077 860
dudmuck 0:8f0d0ae0a077 861 // Schedule next packet transmission
dudmuck 0:8f0d0ae0a077 862 TxNextPacketTimer.attach_us(&OnTxNextPacketTimerEvent, TxDutyCycleTime * 1000);
dudmuck 0:8f0d0ae0a077 863 break;
dudmuck 0:8f0d0ae0a077 864 }
dudmuck 0:8f0d0ae0a077 865 case DEVICE_STATE_SLEEP:
dudmuck 0:8f0d0ae0a077 866 {
dudmuck 1:53c30224eda8 867 //isr_printf("sleep %u\r\n", queue.tick());
dudmuck 0:8f0d0ae0a077 868 // Wake up through events
dudmuck 0:8f0d0ae0a077 869 break;
dudmuck 0:8f0d0ae0a077 870 }
dudmuck 0:8f0d0ae0a077 871 default:
dudmuck 0:8f0d0ae0a077 872 {
dudmuck 0:8f0d0ae0a077 873 DeviceState = DEVICE_STATE_INIT;
dudmuck 0:8f0d0ae0a077 874 break;
dudmuck 0:8f0d0ae0a077 875 }
dudmuck 0:8f0d0ae0a077 876 }
dudmuck 1:53c30224eda8 877
dudmuck 1:53c30224eda8 878 bottom_half();
dudmuck 1:53c30224eda8 879
dudmuck 1:53c30224eda8 880 } // ..while( 1 )
dudmuck 0:8f0d0ae0a077 881 }