Lora Transport Abstraction Layer for sx1272
Diff: LoraTransport.cpp
- Revision:
- 0:518c8dac75f3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LoraTransport.cpp Thu Oct 13 09:40:21 2016 +0000 @@ -0,0 +1,326 @@ +#include "LoraTransport.h" +#include "mbed.h" + +namespace LoraTransport { + + namespace { + + void onTxDone(); + void onTxTimeout(); + void onRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); + void onRxTimeout(); + void onCadDone(bool); + void onRxError(); + + XRange _radio(onTxDone, onTxTimeout, onRxDone, onRxTimeout, onRxError, NULL, onCadDone ); + mbed::Timer _timer; + int _deadline; + uint16_t _addr; + int16_t _rssi_value; + int8_t _snr_value; + State _state; + State _last_error_state; + uint8_t _next_packet_number; + Packet _packet; + Ack _ack; + + /*bool overNineThousand(int n){ + return n > 9000; // https://www.youtube.com/watch?v=LqSg9yVfzV0 + }*/ + + void onCadDone(bool activityDetected){ + _radio.Sleep(); + if(activityDetected){ + _radio.StartCad(); + }else{ + wait_ms((_radio.Random()%100)*10); + if(_state == TX_MSG){ + _radio.Send((uint8_t*)&_packet, _packet.payload_size+5); + }else if(_state == TX_SUCCESS_ACK || _state == TX_ERROR_ACK){ + _radio.Send((uint8_t*)&_ack, sizeof(_ack)); + } + } + } + + void onTxTimeout(){ + _radio.Sleep(); + //printf("tx timeout\r\n"); + //printf("time on air for packet: %f\r\n",_radio.TimeOnAir(MODEM_LORA,_packet.payload_size+5)); + if(_timer.read_us() > _deadline){ + //printf("deadline reached, give up\r\n"); + _timer.stop(); + if(_state == TX_MSG){ + //printf("tx timeout while sending message\r\n"); + // timeout sending message + _last_error_state = TX_MSG_TIMEOUT; + _state = TX_ERROR; + }else if(_state == TX_SUCCESS_ACK){ + //printf("tx timeout while sending success ack\r\n"); + // timeout sending success ack + _last_error_state = TX_SUCCESS_ACK_TIMEOUT; + _state = RX_ERROR; + }else if(_state == TX_ERROR_ACK){ + //printf("tx timeout while sending error ack\r\n"); + // timeout sending error ack + _last_error_state = TX_ERROR_ACK_TIMEOUT; + _state = RX_ERROR; + } + }else{ + _radio.StartCad(); + } + } + + void onTxDone(){ + _radio.Sleep(); + if(_state == TX_MSG){ + // sending message ready, now wait for ack + _state = RX_ACK; + _radio.Rx(RX_TIMEOUT_VALUE); + }else if(_state == TX_SUCCESS_ACK){ + // sending success ack ready -> success! + _timer.stop(); + _state = RX_SUCCESS; + }else if(_state == TX_ERROR_ACK){ + // sending error ack ready + if(_timer.read_us() > _deadline){ + // no time for retry + //printf("error ack transmitted, give up\r\n"); + _last_error_state = RX_MSG_ERROR; + _timer.stop(); + _state = RX_ERROR; + }else{ + // we have time for retry! + //printf("error ack transmitted, retry\r\n"); + _state = RX_MSG; + _radio.Rx(RX_TIMEOUT_VALUE); + } + } + } + + void onRxTimeout(){ + _radio.Sleep(); + if(_state == RX_MSG){ + if(_timer.read_us() > _deadline){ + // no time for retry + //printf("rx msg timeout, give up\r\n"); + _timer.stop(); + _last_error_state = RX_MSG_TIMEOUT; + _state = RX_ERROR; + }else{ + // we have time for retry! + //printf("rx msg timeout, retry\r\n"); + _radio.Rx(RX_TIMEOUT_VALUE); + } + }else if(_state == RX_ACK){ + // no ack received + if(_timer.read_us() > _deadline){ + // no time for retry + //printf("rx ack timeout, give up\r\n"); + _timer.stop(); + _last_error_state = RX_ACK_TIMEOUT; + _state = TX_ERROR; + }else{ + // we have time for retry! + //printf("rx ack timeout, retry\r\n"); + _state = TX_MSG; + _radio.StartCad(); + } + } + } + + void onRxError(){ + _radio.Sleep(); + if(_state == RX_MSG){ + if(_timer.read_us() > _deadline){ + //printf("rx message error, give up\r\n"); + // no time for retry + _timer.stop(); + _last_error_state = RX_MSG_ERROR; + _state = RX_ERROR; + }else{ + // we have time for retry -> send fail ack; + //printf("rx message error, send fail ack\r\n"); + _ack.from_addr = _addr; + _ack.to_addr = 0; + _ack.success = false; + _state = TX_ERROR_ACK; + _radio.StartCad(); + } + }else if(_state == RX_ACK){ + // no ack received + if(_timer.read_us() > _deadline){ + // no time for retry + //printf("rx ack error, give up\r\n"); + _timer.stop(); + _last_error_state = RX_ACK_TIMEOUT; + _state = TX_ERROR; + }else{ + // we have time for retry! + //printf("rx ack error, retry\r\n"); + _state = TX_MSG; + _radio.StartCad(); + } + } + } + + void onRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ){ + _radio.Sleep(); + _rssi_value = rssi; + _snr_value = snr; + if(_state == RX_MSG){ + _packet.to_addr = *((uint16_t*)(payload+2)); + if(_packet.to_addr != _addr && _packet.to_addr != 0){ + //packet is not for us + if(_timer.read_us() > _deadline){ + // no time for retry + _timer.stop(); + _last_error_state = RX_MSG_TIMEOUT; + _state = RX_ERROR; + }else{ + //printf("packet is not for us, retry\r\n"); + _state = RX_MSG; + _radio.Rx(RX_TIMEOUT_VALUE); + } + }else{ + //packet is for us + _packet.from_addr = *((uint16_t*)(payload)); + _packet.packet_number = *(payload+4); + _packet.payload_size = size-5; + memcpy(_packet.payload, payload+5, _packet.payload_size); + _ack.packet_number = _packet.packet_number; + _ack.from_addr = _packet.to_addr; + _ack.to_addr = _packet.from_addr; + _ack.success = true; + _state = TX_SUCCESS_ACK; + _radio.StartCad(); + } + }else if(_state == RX_ACK){ + _ack.to_addr = *((uint16_t*)(payload+2)); + if(_ack.to_addr != _addr && _ack.to_addr != 0){ + //ack is not for us + if(_timer.read_us() > _deadline){ + //printf("ack is not for us, give up\r\n"); + // no time for retry + _timer.stop(); + _last_error_state = RX_ACK_TIMEOUT; + _state = TX_ERROR; + }else{ + //printf("ack is not for us, retry\r\n"); + _state = RX_ACK; + _radio.Rx(RX_TIMEOUT_VALUE); + } + }else{ + //ack is for us + _ack.from_addr = *((uint16_t*)payload); + _ack.packet_number = *(payload+4); + _ack.success = *(payload+5); + if(_ack.success == true && _ack.packet_number == _packet.packet_number){ + // got success ack -> transmission completed! + _timer.stop(); + _state = TX_SUCCESS; + }else{ + if(_timer.read_us() > _deadline){ + // no time for retry + //printf("got fail ack, give up\r\n"); + _timer.stop(); + _last_error_state = TX_MSG_TIMEOUT; + _state = TX_ERROR; + }else{ + //retry + //printf("got fail ack, retry\r\n"); + _state = TX_MSG; + _radio.StartCad(); + } + } + } + } + } + + }//end anonym namespace + + + void init(){ + _addr = 0; + _rssi_value = 0; + _snr_value = 0; + _state = IDLE; + _last_error_state = IDLE; + _next_packet_number = 0; + _radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, + LORA_SPREADING_FACTOR, LORA_CODINGRATE, + LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, + LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, + LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE ); + _radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, + LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, + LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, + LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, + LORA_IQ_INVERSION_ON, true ); + } + + void setAddress(uint16_t addr){ + _addr = addr; + } + + void send(uint16_t addr, uint8_t *ptr, uint8_t len, uint32_t timeout){ + _packet.from_addr = _addr; + _packet.to_addr = addr; + _packet.packet_number = _next_packet_number++; + memcpy(_packet.payload, ptr, len); + _packet.payload_size = len; + _state = CAD_RUNNING; + _deadline = timeout; + _timer.stop(); + _timer.reset(); + _timer.start(); + _state = TX_MSG; + //printf("send message with %i bytes\r\n",len); + _radio.StartCad(); + } + + void recv(uint32_t timeout){ + _state = RX_MSG; + _deadline = timeout; + _timer.stop(); + _timer.reset(); + _timer.start(); + _state = RX_MSG; + _radio.Rx(RX_TIMEOUT_VALUE); + } + + State getState(){ + return _state; + } + State getLastErrorState(){ + return _last_error_state; + } + Packet& getPacket(){ + return _packet; + } + int16_t getRSSI(){ + return _rssi_value; + } + int8_t getSNR(){ + return _snr_value; + } + + bool done(){ + if( _state == TX_ERROR || + _state == TX_SUCCESS || + _state == RX_ERROR || + _state == RX_SUCCESS || + _state == IDLE) { + return true; + } + return false; + } + + bool success(){ + if(_state == TX_SUCCESS || _state == RX_SUCCESS) { + return true; + } + return false; + } + + +} // end LoraTranport namespace