Wireless interface using LoRa technology

Dependencies:   AlohaTransceiver RingBuffer SX1276Lib_inAir SerialInterfaceProtocol mbed L3PDU

main.cpp

Committer:
rba90
Date:
2016-09-02
Revision:
25:2fff6d1b4eb6
Parent:
24:e41b5098ed0a
Child:
27:9196cb5859f7

File content as of revision 25:2fff6d1b4eb6:

#include "mbed.h"
#include "AlohaTransceiver.h"
#include "buffer.h"
#include "SerialInterfaceProtocol.h"
#include "AlohaFrame.h"
#include "ControlPacket.h"
#include "DataBlockPacket.h"

Serial pc(USBTX, USBRX);

// sip uses two buffer queues
CircularBuffer<uint8_t> SerialInputBuffer(128);
CircularBuffer<uint8_t> SerialOutputBuffer(128);
SerialInterfaceProtocol SIP(&SerialInputBuffer, &SerialOutputBuffer);

// aloha transceiver
AlohaTransceiver aloha(DEVICE_ID);
AlohaFrame txFrame;
Timer timer;
InterruptIn button(USER_BUTTON);

// sensors
#define SUPPLY_VOLTAGE 3.3f;
AnalogIn TempSensor(PA_0);
InterruptIn door(D15);

// convert fp32 to 4 byte string
typedef union
{
    float fp32;
    uint8_t bytes[4];
} float_bytes_32;

typedef union
{
    int16_t int16;
    uint8_t bytes[2];
} int_string_16;

float getTemperature() 
{
    float reading = TempSensor.read();
    float output_voltage = reading * SUPPLY_VOLTAGE;
    return (output_voltage - 0.25) / 0.028;
}

bool getDoorState()
{
    return door.read();
}

void doorStateChangeHandler() 
{
    static uint8_t seqid = 0;
    bool doorState = getDoorState();
    
    DataBlockPacket doorEvent;
                            
    // set sequence id (the sequence id is configured as incoming sequence id + 1)
    doorEvent.setSequenceID(seqid++);
    
    // set source id (the source id is the current device ID)
    doorEvent.setSourceID(aloha.getDeviceID());
    
    // set destination id (send to base station by default)
    doorEvent.setDestinationID(0x0);
    
    // set source type (door state)
    doorEvent.setSourceType(0x1);
    
    // set payload type (boolean)
    doorEvent.setPayloadType(0x8);
    
    // copy door state (first byte)
    doorEvent.setData(0, doorState);
    
    // calculate crc
    doorEvent.generateCrc();
    
    aloha.send(&doorEvent);
}

void serialInterruptHandler() {
    // Note: you need to actually read from the serial to clear the RX interrupt
    int c = pc.getc();
    
    // add to buffer
    if (SerialInputBuffer.isLocked())
    {
        printf("Mutex Locked\r\n");
    }
    else
    {
        SerialInputBuffer.enqueue((uint8_t) c);   
    }
}

int toggleChecksum(uint8_t *payload, uint8_t payload_length, uint8_t *response, uint8_t *response_length)
{
    // one payload
    if (payload_length != 1)
    {
        sprintf((char *) response, "Wrong Payload Length\r\n");
        *response_length = 22;
        return 1;
    } 
    
    if ((bool) payload[0])
    {
        SIP.enableChecksum();
    }
    else
    {
        SIP.disableChecksum();
    }
    
    return 0;
}

int queryServiceQuality(uint8_t *payload, uint8_t payload_length, uint8_t *response, uint8_t *response_length)
{
    // decode user message
    uint8_t node_id = payload[0];
    uint8_t source_type = 0x2;          // query for service quality
    
    // create a command packet
    ControlPacket packet;
    
    // layer 3 sequence id is not used in this case
    packet.setSequenceID(0x0);
    
    // set source id as current device id
    packet.setSourceID(aloha.getDeviceID());
    
    // set destination id as node id
    packet.setDestinationID(node_id);
    
    // set command as query (treat it similar to sensor)
    packet.setCommand(0x0);
    
    // store sensor type in data block 0
    packet.setData(0, source_type);
    
    // generate crc
    packet.generateCrc();
    
    aloha.send(&packet);
    
    return 0;
}

/*
 * Format: 
 * <        :start flag
 * 02       :command
 * xx       :length
 * xx:      :00: get, 01: set
 * xx       :index for parameters
 * ...
 * ff       :checksum
 * >        :end flag
 */
int configureRadio(uint8_t *payload, uint8_t payload_length, uint8_t *response, uint8_t *response_length)
{
    // read settings from radio
#if USE_MODEM_LORA == 1
    AlohaTransceiver::LoRaSettings_t *settings = aloha.getSettings();
#elif USE_MODEM_FSK == 1
    AlohaTransceiver::FskSettings_t *settings = aloha.getSettings();
#else
    #error "Please define a modem in the compiler options."
#endif

    if (payload_length < 2)
    {
        sprintf((char *) response, "Wrong Payload Length\r\n");
        *response_length = 22;
        return 1;
    }
    
    // true is set, false is get
    bool isSet = (bool) payload[0];
    uint8_t idx = payload[1];
    
    switch(idx)
    {
        case 0x00: // Power
        {
            if (isSet)
            {
                int8_t Power = (int8_t) payload[2];
                settings->Power = Power;
                
                return 0;
            }
            else
            {
                response[0] = (uint8_t) settings->Power;
                *response_length = 1;

                return 0;
            }
        }
        
        case 0x01: // Bandwidth
        {
            if (isSet)
            {
                uint32_t Bandwidth = (payload[5]) | 
                                     (payload[4] << 8) |
                                     (payload[3] << 16) | 
                                     (payload[2] << 24);
                settings->Bandwidth = Bandwidth;
                
                return 0;
            }
            else
            {
                response[3] = (uint8_t) (settings->Bandwidth);
                response[2] = (uint8_t) (settings->Bandwidth >> 8);
                response[1] = (uint8_t) (settings->Bandwidth >> 16);
                response[0] = (uint8_t) (settings->Bandwidth >> 24);
                *response_length = 4;
                
                return 0;
            }
            
        }
        
        case 0x02: // Datarate, AKA Spreading Factor
        {
            if (isSet)
            {
                uint32_t Datarate = (payload[5]) | 
                                    (payload[4] << 8) |
                                    (payload[3] << 16) | 
                                    (payload[2] << 24);
                settings->Datarate = Datarate;
                
                return 0;
            }
            else
            {
                response[3] = (uint8_t) (settings->Datarate);
                response[2] = (uint8_t) (settings->Datarate >> 8);
                response[1] = (uint8_t) (settings->Datarate >> 16);
                response[0] = (uint8_t) (settings->Datarate >> 24);
                *response_length = 4;
                
                return 0;
            }
        }
        
        case 0x03: // Coderate
        {
            if (isSet)
            {
                uint8_t Coderate = payload[2];
                settings->Coderate = Coderate;
                
                return 0;
            }
            else
            {
                response[0] = (uint8_t) settings->Coderate;
                *response_length = 1;

                return 0;
            }
        }
        
        case 0x04: //Preamble Length
        {
            if (isSet) 
            {
                uint16_t PreambleLen = payload[3] | (payload[2] << 8);
                settings->PreambleLen = PreambleLen;
                return 0;
            }
            else
            {
                response[1] = (uint8_t) (settings->PreambleLen);
                response[0] = (uint8_t)(settings->PreambleLen >> 8);
                *response_length = 2;
                
                return 0;
            }
        }
        
        case 0x05: //Symbol Timeout
        {
             if (isSet) 
             {
                 uint16_t SymbolTimeout = payload[3] | (payload[2] << 8);
                 settings->SymbolTimeout = SymbolTimeout;
                 return 0;
             }
             else 
             {
                 response[1] = (uint8_t) (settings->SymbolTimeout);
                 response[0] = (uint8_t) (settings->SymbolTimeout >> 8);
                 *response_length = 2;
                 
                 return 0;
             }
        }
        
        case 0x06: //FixLen
        {
             if (isSet) 
             {
                 bool FixLen = payload[2];
                 settings->FixLen = FixLen;
                 return 0;
             }
             else 
             {
                 response[0] = (bool) (settings->SymbolTimeout);
                 *response_length = 1;
                 
                 return 0;
             }
        }
        
        case 0x07: //PayloadLen
        {
             if (isSet)
             {
                 uint8_t PayloadLen = payload[2];
                 settings->PayloadLen = PayloadLen;
                 return 0;
             }
             else 
             {
                 response[0] = (uint8_t) (settings->PayloadLen);
                 
                 return 0;
            }
        }
        
        case 0x08: //CrcOn
        {
            if (isSet) {
                bool CrcOn = payload[2];
                settings->CrcOn = CrcOn;
                return 0;
            }
            else 
            {
                response[0] = (bool) (settings->CrcOn);
                
                return 0;
            }
        }
        
        case 0x09: //FreqHopOn
        {
             if (isSet) {
                 bool FreqHopOn = payload[2];
                 settings->FreqHopOn = FreqHopOn;
                 return 0;
            }
            else 
            {
                response[0] = (bool) (settings->FreqHopOn);
                
                return 0;
            }
        }
        
        case 0x0A: //HopPeriod
        {
             if (isSet) {
                 uint8_t HopPeriod = payload[2];
                 settings->HopPeriod = HopPeriod;
                 return 0;
            }
            else 
            {
                response[0] = (uint8_t) (settings->HopPeriod);
                return 0;
            }
        }
        
        case 0x0B: //IqInverted
        {
             if (isSet) {
                 bool IqInverted = payload[2];
                 settings->IqInverted = IqInverted;
                 return 0;
            }
            else 
            {
                response[0] = (bool) (settings->IqInverted);
                return 0;
            }
        }
        
        case 0x0C: //RxContinuous
        {
             if(isSet) 
             {
                 bool RxContinuous = payload[2];
                 settings->RxContinuous = RxContinuous;
                 return 0;
            }
            else 
            {
                response[0] = (bool) (settings->RxContinuous);
                return 0;
            }
        }
        
        case 0x0D: //TxTimeout
        {
             if (isSet) 
             {
                 uint32_t TxTimeout = (payload[5]) | 
                                (payload[4] << 8) |
                                (payload[3] << 16) | 
                                (payload[2] << 24);
                settings->TxTimeout = TxTimeout;
                return 0;
            }
            else 
            {
                response[3] = (uint8_t) (settings->TxTimeout);
                response[2] = (uint8_t) (settings->TxTimeout >> 8);
                response[1] = (uint8_t) (settings->TxTimeout >> 16);
                response[0] = (uint8_t) (settings->TxTimeout >> 24);
                *response_length = 4;
                
                return 0;
            }
        }
                 
        
        default:
        {
            break;
        }
        
        //case 
    }
        
    
    return 0;
}

int radioUpdateSettings(uint8_t *payload, uint8_t payload_length, uint8_t *response, uint8_t *response_length)
{
    aloha.updateSettings();
    
    return 0;
}


void AlohaDataPacketHandler(uint8_t *payload, uint8_t payload_length, uint8_t src_addr)
{
    // try to decode packet
    BasicPacket packet(payload);
    
    // verify crc
    // skip for current development
    
    // process the packet based on different feature id
    BasicPacket::L3Fid_t fid = (BasicPacket::L3Fid_t) packet.getFid();
    
    // we don't care about the type conversion. just create a new one.
    switch (fid)
    {
        case BasicPacket::L3ControlPacket:
        {
            ControlPacket controlPacket(payload);
            
            // execute command
            uint8_t command = controlPacket.getCommand();
            switch(command)
            {
                case 0x0:   // query
                {
                    uint8_t sensor_type = controlPacket.getData(0);
                    switch (sensor_type)
                    {
                        case 0x0:   // temperature sensor
                        {
                            uint8_t temp = getTemperature();
                            
                            // create a response
                            DataBlockPacket response;
                            
                            // set sequence id (the sequence id is configured as incoming sequence id + 1)
                            response.setSequenceID(controlPacket.getSequenceID() + 1);
                            
                            // set source id (the source id is the current device ID)
                            response.setSourceID(aloha.getDeviceID());
                            
                            // set destination id (the destination ID is the original source address)
                            response.setDestinationID(controlPacket.getSourceID());
                            
                            // set source type (temperature)
                            response.setSourceType(0x0);
                            
                            // set payload type (signed integer)
                            response.setPayloadType(0x9);
                            
                            // copy temperature (use first byte)
                            response.setData(0, temp);
                            
                            // calculate crc
                            response.generateCrc();
                            
                            aloha.send(&response);
                            
                            break;
                        }
                        case 0x1:   // door sensor
                        {
                            bool doorState = getDoorState();
                            
                            // create a response
                            DataBlockPacket response;
                            
                            // set sequence id (the sequence id is configured as incoming sequence id + 1)
                            response.setSequenceID(controlPacket.getSequenceID() + 1);
                            
                            // set source id (the source id is the current device ID)
                            response.setSourceID(aloha.getDeviceID());
                            
                            // set destination id (the destination ID is the original source address)
                            response.setDestinationID(controlPacket.getSourceID());
                            
                            // set source type (door state)
                            response.setSourceType(0x1);
                            
                            // set payload type (boolean)
                            response.setPayloadType(0x8);
                            
                            // copy door state (first byte)
                            response.setData(0, doorState);
                            
                            // calculate crc
                            response.generateCrc();
                            
                            aloha.send(&response);
                            
                            break;
                        }
                        case 0x2:   // service quality
                        {
                            int_string_16 rssi;
                            uint8_t snr;
                            
                            // get rssi and snr
                            rssi.int16 = aloha.getRssi();
                            snr = aloha.getSnr();
                            
                            // create a response
                            DataBlockPacket response;
                            
                            // set sequence id (the sequence id is configured as incoming sequence id + 1)
                            response.setSequenceID(controlPacket.getSequenceID() + 1);
                            
                            // set source id (the source id is the current device ID)
                            response.setSourceID(aloha.getDeviceID());
                            
                            // set destination id (the destination ID is the original source address)
                            response.setDestinationID(controlPacket.getSourceID());
                            
                            // set source type (service quality)
                            response.setSourceType(0x2);
                            
                            // set payload type (byte)
                            response.setPayloadType(0x0);
                            
                            // set payload
                            response.setData(0, (uint8_t) snr);             // store SNR
                            response.setData(1, (uint8_t) rssi.bytes[0]);   // store higher bits of RSSI
                            response.setData(2, (uint8_t) rssi.bytes[1]);   // store lower bits of RSSI
                            
                            // calculate crc
                            response.generateCrc();
                            
                            aloha.send(&response);
                            
                            break;
                        }
                        default:
                            break;
                    }
                    
                    break;
                }
                
                default:
                    break;
            }
            
            break;
        }
        case BasicPacket::L3DataBlockPacket:
        {
            DataBlockPacket dataBlockPacket(payload);
            
            uint8_t source_type = dataBlockPacket.getSourceType();
            switch(source_type)
            {
                case 0x2:   // service quality
                {
                    uint8_t payload[8];
                    
                    // copy source type
                    payload[0] = 0x2;
                    
                    // copy sensor data
                    payload[1] = dataBlockPacket.getSourceID();
                    
                    // first byte is the snr of foreign host
                    payload[2] = dataBlockPacket.getData(0);
                    
                    // second and third byte are the rssi of foreign host
                    payload[3] = dataBlockPacket.getData(1);
                    payload[4] = dataBlockPacket.getData(2);
                    
                    // fourth byte is the snr of local host
                    payload[5] = (uint8_t) aloha.getSnr();
                    
                    // fifth and sixth byte are the rssi of local host
                    int_string_16 rssi;
                    rssi.int16 = aloha.getRssi();
                    
                    payload[6] = rssi.bytes[0];
                    payload[7] = rssi.bytes[1];
                    
                    // make response
                    SIP.respond(0xf0, payload, 8);
                    
                    break;
                }
                
                default:
                    break;
            }
            
            break;
        }
        default:
            break;
    }
}

void enqueueString(char* s, int len) {
    for (int i = 0; i < len; i++) {
        SerialInputBuffer.enqueue((uint8_t) s[i]);
    }
}

void automaticPacketTransmit()
{
    /*SerialInputBuffer.enqueue((uint8_t) '<');
    SerialInputBuffer.enqueue((uint8_t) '0');
    SerialInputBuffer.enqueue((uint8_t) '1');
    SerialInputBuffer.enqueue((uint8_t) '0');
    SerialInputBuffer.enqueue((uint8_t) '0');
    SerialInputBuffer.enqueue((uint8_t) 'f');
    SerialInputBuffer.enqueue((uint8_t) 'f');
    SerialInputBuffer.enqueue((uint8_t) '>');*/
    enqueueString("<0100ff>", 8);
}



int main() {
    // initialize radio module
    aloha.boardInit();
    aloha.updateSettings();
    aloha.enable();
    
    // attach serial interrupt handler
    pc.attach(&serialInterruptHandler);
    
    // register callback functions for SIP
    SIP.registerCommand(0x00, toggleChecksum);
    SIP.registerCommand(0x01, queryServiceQuality);
    SIP.registerCommand(0x02, configureRadio);
    SIP.registerCommand(0x03, radioUpdateSettings);
    
    // register callback functions for aloha transceiver
    aloha.registerType(AlohaFrame::Aloha_Data, AlohaDataPacketHandler);

    // configure button interrupt
    button.fall(automaticPacketTransmit);
    
    // configure door state
    door.rise(doorStateChangeHandler); //Rising edge occurs when the "door opens"
    door.fall(doorStateChangeHandler); //Falling edge occurs when the "door closes"
    
    while(1) {
        SIP.poll();
        aloha.poll();
        
        while (SerialOutputBuffer.getCounter() > 0)
        {
            uint8_t ch;
            ch = SerialOutputBuffer.dequeue();
            pc.putc(ch);
        }
    }
}