Lora Transport Abstraction Layer for sx1272

Dependents:   lora-transceiver

LoraTransport.cpp

Committer:
trusch
Date:
2016-10-13
Revision:
0:518c8dac75f3

File content as of revision 0:518c8dac75f3:

#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