MCU driver/HAL for the Picocell Gateway concentrator board. The firmware implements either a USB CDC protocol or a UART protocol to bridge commands coming from host to the SX1308 SPI interface.

Revision:
0:c76361bd82e8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/MAIN/cmdUSB.cpp	Wed Apr 11 14:42:47 2018 +0000
@@ -0,0 +1,645 @@
+/*
+ / _____)             _              | |
+( (____  _____ ____ _| |_ _____  ____| |__
+ \____ \| ___ |    (_   _) ___ |/ ___)  _ \
+ _____) ) ____| | | || |_| ____( (___| | | |
+(______/|_____)_|_|_| \__)_____)\____)_| |_|
+  (C)2017 Semtech
+
+*/
+
+#include "cmdUSB.h"
+#include "usb_device.h"
+#include "usbd_cdc_if.h"
+#include "loragw_hal.h"
+#include "loragw_reg.h"
+#include "mbed.h"
+#include "board.h"
+
+#define DELAY_COM_INIT 1000
+#define DELAY_RESET 200
+
+/*
+/Class INTERFACE definition
+*/
+
+INTERFACE::INTERFACE()
+{
+}
+
+/*
+/Class COMUSB definition
+*/
+
+COMUSB::COMUSB() : INTERFACE()
+{
+}
+void COMUSB::Init() {
+    __HAL_RCC_USB_OTG_FS_CLK_ENABLE();
+    MX_USB_DEVICE_Init();
+}
+
+void COMUSB::Receive(uint8_t * Buffer, uint32_t * size) {
+    CDC_Receive_FSP(Buffer,size);// wait interrrupt manage in HAL_PCD_DataOutStageCallback
+}
+
+void COMUSB::Transmit(uint8_t * Buffer, uint16_t size) {
+    if ((size % 64) == 0) { // for ZLP
+        size = size + 1;
+    } else {
+        size = size;
+    }
+    while (CDC_Transmit_FS(Buffer, size) != USBD_OK){
+    }
+}
+
+/*
+/Class COMUART definition
+*/
+
+COMUART::COMUART(PinName Tx, PinName Rx) : Serial(Tx, Rx), INTERFACE()
+{
+    //do nothing
+}
+
+void COMUART::Init() {
+    baud(BAUDRATE);
+}
+
+void COMUART::Receive(uint8_t * Buffer, uint32_t * size) {
+    uint16_t localSize = 0;
+    uint16_t cmdLength = 0;
+
+    while (localSize < CMD_HEADER_RX_SIZE) {
+        if (readable() == true) {
+            Buffer[localSize]= getc();
+            localSize++;
+        }
+    }
+    localSize = 0;
+    cmdLength = (Buffer[CMD_LENGTH_MSB] << 8) + Buffer[CMD_LENGTH_LSB];
+    while (localSize < cmdLength){
+        if (readable() == true) {
+            Buffer[localSize + CMD_HEADER_RX_SIZE ]= getc();
+            localSize++;
+        }
+    }
+    *size = (uint32_t)(cmdLength + CMD_HEADER_RX_SIZE);
+}
+
+void COMUART::Transmit(uint8_t * Buffer, uint16_t size) {
+    if ((size % 64) == 0) { // for ZLP Keep the same way than for USB transfer
+        size = size + 1;
+    } else {
+        size = size;
+    }
+    for (int g = 0; g < size; g++){
+        putc(Buffer[g]);
+    }
+} 
+
+#ifdef  USE_UART 
+CMDMANAGER::CMDMANAGER(PinName Tx, PinName Rx) 
+{
+    kill = false;
+    ActiveInterface = (INTERFACE *) new COMUART (Tx, Rx);
+    ActiveCom = ISUARTINTERFACE;
+}
+#else
+CMDMANAGER::CMDMANAGER(PinName Tx, PinName Rx)
+{
+    kill = false;
+    ActiveInterface = (INTERFACE *) new COMUSB ();
+    ActiveCom = ISUSBINTERFACE;
+}
+#endif
+
+void CMDMANAGER::InitBufFromHost() {
+    memset(BufFromHost, 0, sizeof BufFromHost);
+    memset(BufFromHostChunk, 0, sizeof BufFromHostChunk);
+    receivelength[0] = 0;
+    count = 1; 
+}
+
+void CMDMANAGER::InitBufToHost() {
+    memset(BufToHost, 0, sizeof BufToHost);
+}
+
+void CMDMANAGER::Init() {
+    ActiveInterface->Init();
+    wait_ms(DELAY_COM_INIT);
+}
+
+void CMDMANAGER::ReceiveCmd (){
+    InitBufFromHost();
+    if (ActiveCom == ISUARTINTERFACE)
+    {
+        ActiveInterface->Receive(&BufFromHost[0], &receivelength[0]);
+    } else {
+        ActiveInterface->Receive(&BufFromHostChunk[0], &receivelength[0]);
+        count = 1;
+        while (count > 0) {// wait until receive cmd 
+        }
+        count = 1;
+    }
+    InitBufToHost();
+}
+
+void CMDMANAGER::TransmitCmd (){
+    uint16_t size;
+    size = (uint16_t)((BufToHost[CMD_LENGTH_MSB] << 8) + BufToHost[CMD_LENGTH_LSB] + CMD_HEADER_TX_SIZE);
+    ActiveInterface->Transmit(BufToHost, size);
+    /* Check if a reset has been requested */
+    if (kill == true) {
+        wait_ms(DELAY_RESET);
+        NVIC_SystemReset();
+    }
+}
+
+/********************************************************/
+/*   cmd name   |      description                      */
+/*------------------------------------------------------*/
+/*  r           |Read register                          */
+/*  s           |Read long burst First packet           */
+/*  t           |Read long burst Middle packet          */
+/*  u           |Read long burst End packet             */
+/*  p           |Read atomic burst packet               */
+/*  w           |Write register                         */
+/*  x           |Write long burst First packet          */
+/*  y           |Write long burst Middle packet         */
+/*  z           |Write long burst End packet            */
+/*  a           |Write atomic burst packet              */
+/*------------------------------------------------------*/
+/*  b           |lgw_receive cmd                        */
+/*  c           |lgw_rxrf_setconf cmd                   */
+/*  d           |int lgw_rxif_setconf_cmd               */
+/*  f           |int lgw_send cmd                       */
+/*  h           |lgw_txgain_setconf                     */
+/*  q           |lgw_get_trigcnt                        */
+/*  i           |lgw_board_setconf                      */
+/*  j           |lgw_mcu_commit_radio_calibration       */
+/*  l           |lgw_check_fw_version                   */
+/*  m           |Reset SX1308 and STM32                 */
+/*  n           |Jump to bootloader                     */
+/********************************************************/
+int CMDMANAGER::DecodeCmd() {
+    int i = 0;
+    int adressreg;
+    int val;
+    int size;
+    int x;
+    CmdSettings_t cmdSettings_FromHost;
+
+    if (BufFromHost[0] == 0) {
+        return (CMD_ERROR);
+    }
+
+    cmdSettings_FromHost.id = BufFromHost[0];
+
+    if (CheckCmd(cmdSettings_FromHost.id) == false) {
+        BufToHost[0] = 'k';
+        BufToHost[1] = 0;
+        BufToHost[2] = 0;
+        BufToHost[3] = ACK_K0;
+        return(CMD_K0);
+    }
+
+    switch (cmdSettings_FromHost.id) {
+
+        case 'r': { // cmd Read register
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                val = Sx1308.spiRead(adressreg);
+                BufToHost[0] = 'r';
+                BufToHost[1] = 0;
+                BufToHost[2] = 1;
+                BufToHost[3] = ACK_OK;
+                BufToHost[4] = val;
+                return(CMD_OK);
+            }
+        case 's': { // cmd Read burst register first
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                size = (cmdSettings_FromHost.cmd_data[0] << 8) + cmdSettings_FromHost.cmd_data[1];
+                BufToHost[0] = 's';
+                BufToHost[1] = cmdSettings_FromHost.cmd_data[0];
+                BufToHost[2] = cmdSettings_FromHost.cmd_data[1];
+                BufToHost[3] = ACK_OK;
+                Sx1308.spiReadBurstF(adressreg, &BufToHost[4 + 0], size);
+                return(CMD_OK);
+            }
+        case 't': { // cmd Read burst register middle
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                size = (cmdSettings_FromHost.cmd_data[0] << 8) + cmdSettings_FromHost.cmd_data[1];
+
+                BufToHost[0] = 't';
+                BufToHost[1] = cmdSettings_FromHost.cmd_data[0];
+                BufToHost[2] = cmdSettings_FromHost.cmd_data[1];
+                BufToHost[3] = ACK_OK;
+                Sx1308.spiReadBurstM(adressreg, &BufToHost[4 + 0], size);
+                return(CMD_OK);
+            }
+        case 'u': { // cmd Read burst register end
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                size = (cmdSettings_FromHost.cmd_data[0] << 8) + cmdSettings_FromHost.cmd_data[1];
+                BufToHost[0] = 'u';
+                BufToHost[1] = cmdSettings_FromHost.cmd_data[0];
+                BufToHost[2] = cmdSettings_FromHost.cmd_data[1];
+                BufToHost[3] = ACK_OK;
+                Sx1308.spiReadBurstE(adressreg, &BufToHost[4 + 0], size);
+                return(CMD_OK);
+            }
+        case 'p': { // cmd Read burst register atomic
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                size = (cmdSettings_FromHost.cmd_data[0] << 8) + cmdSettings_FromHost.cmd_data[1];
+                BufToHost[0] = 'p';
+                BufToHost[1] = cmdSettings_FromHost.cmd_data[0];
+                BufToHost[2] = cmdSettings_FromHost.cmd_data[1];
+                BufToHost[3] = ACK_OK;
+                Sx1308.spiReadBurst(adressreg, &BufToHost[4 + 0], size);
+                return(CMD_OK);
+            }
+        case 'w': { // cmd write register
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                val = cmdSettings_FromHost.cmd_data[0];
+                Sx1308.spiWrite(adressreg, val);
+                BufToHost[0] = 'w';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                return(CMD_OK);
+            }
+        case 'x': { // cmd write burst register
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                size = cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8);
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                Sx1308.spiWriteBurstF(adressreg, &cmdSettings_FromHost.cmd_data[0], size);
+                BufToHost[0] = 'x';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                return(CMD_OK);
+            }
+        case 'y': { // cmd write burst register
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                size = cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8);
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                Sx1308.spiWriteBurstM(adressreg, &cmdSettings_FromHost.cmd_data[0], size);
+                BufToHost[0] = 'y';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                return(CMD_OK);
+            }
+        case 'z': { // cmd write burst register
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                size = cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8);
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                Sx1308.spiWriteBurstE(adressreg, &cmdSettings_FromHost.cmd_data[0], size);
+                BufToHost[0] = 'z';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                return(CMD_OK);
+            }
+        case 'a': { // cmd write burst atomic register
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                size = cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8);
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                Sx1308.spiWriteBurst(adressreg, &cmdSettings_FromHost.cmd_data[0], size);
+                BufToHost[0] = 'a';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                return(CMD_OK);
+            }
+        case 'b': { // lgw_receive
+                static struct lgw_pkt_rx_s pkt_data[16]; //16 max packets TBU
+                int nbpacket = 0;
+                int j = 0;
+                int sizeatomic = sizeof(lgw_pkt_rx_s) / sizeof(uint8_t);
+                int cptalc = 0;
+                int pt = 0;
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                adressreg = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                nbpacket = lgw_receive(cmdSettings_FromHost.cmd_data[0], &pkt_data[0]);
+                BufToHost[0] = 'b';
+                BufToHost[3] = ((nbpacket >= 0) ? ACK_OK : ACK_K0); 
+                BufToHost[4] = nbpacket;
+                for (j = 0; j < nbpacket; j++) {
+                    for (i = 0; i < (pkt_data[j].size + (sizeatomic - 256)); i++) {
+                        BufToHost[5 + i + pt] = *((uint8_t *)(&pkt_data[j]) + i);
+                        cptalc++;
+                    }
+                    pt = cptalc;
+                }
+                cptalc = cptalc + 1; // + 1 for nbpacket
+                BufToHost[1] = (uint8_t)((cptalc >> 8) & 0xFF);
+                BufToHost[2] = (uint8_t)((cptalc >> 0) & 0xFF);
+                return(CMD_OK);
+            }
+        case 'c': { // lgw_rxrf_setconf
+                uint8_t rf_chain;
+                uint8_t conf[20];
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                rf_chain = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    conf[i] = BufFromHost[4 + i];
+                }
+                x = lgw_rxrf_setconf(rf_chain, *(lgw_conf_rxrf_s *)conf);
+                BufToHost[0] = 'c';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ((x == 0) ? ACK_OK : ACK_K0);
+                return(CMD_OK);
+            }
+        case 'h': { // lgw_txgain_setconf
+                uint8_t conf[(LGW_MULTI_NB * TX_GAIN_LUT_SIZE_MAX) + 4];
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    conf[i] = BufFromHost[4 + i];
+                }
+                x = lgw_txgain_setconf((lgw_tx_gain_lut_s *)conf);
+                BufToHost[0] = 'h';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ((x == 0) ? ACK_OK : ACK_K0);
+                return(CMD_OK);
+            }
+        case 'd': { // lgw_rxif_setconf
+                uint8_t if_chain;
+                uint8_t conf[(sizeof(struct lgw_conf_rxif_s) / sizeof(uint8_t))];
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                if_chain = BufFromHost[3];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    conf[i] = BufFromHost[4 + i];
+                }
+                x = lgw_rxif_setconf(if_chain, *(lgw_conf_rxif_s *)conf);
+                BufToHost[0] = 'd';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ((x == 0) ? ACK_OK : ACK_K0);
+                return(CMD_OK);
+            }
+        case 'f': { // lgw_send
+                uint32_t count_us;
+                int32_t txcontinuous;
+                uint8_t conf[(sizeof(struct lgw_pkt_tx_s) / sizeof(uint8_t))];
+                Timer timer_tx_timeout;
+
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    conf[i] = BufFromHost[4 + i];
+                }
+
+                /* Switch off SX1308 correlators to reduce power consumption during transmit */
+#ifdef V2
+                HSCLKEN = 0;
+#else
+                lgw_reg_w(LGW_CLKHS_EN, 0);
+#endif
+
+                /* Send packet */
+                Sx1308.txongoing = 1;
+                Sx1308.waittxend = 1;
+                x = lgw_send(*(lgw_pkt_tx_s *)conf);
+                if (x < 0) {
+                    //pc.printf("lgw_send() failed\n");
+                }
+                
+                /* In case of TX continuous, return the answer immediatly */
+                lgw_reg_r(LGW_TX_MODE, &txcontinuous); // to switch off the timeout in case of tx continuous
+                if (txcontinuous == 1) {
+                    BufToHost[0] = 'f';
+                    BufToHost[1] = 0;
+                    BufToHost[2] = 0;
+                    BufToHost[3] = ACK_OK;
+                    return(CMD_OK);
+                }
+
+                /* Wait for TX_DONE interrupt, or 10 seconds timeout */
+                timer_tx_timeout.reset();
+                timer_tx_timeout.start();
+                while (Sx1308.waittxend && (timer_tx_timeout.read() < (float)10.0)) {
+                }
+                timer_tx_timeout.stop();
+
+                /* Align SX1308 internal counter and STM32 counter */
+                if (Sx1308.firsttx == true) {
+                    lgw_get_trigcnt(&count_us);
+                    Sx1308.offtmstpstm32ref = (Sx1308.timerstm32ref.read_us() - count_us) + 60;
+                    Sx1308.firsttx = false;
+                }
+
+                /* reset Sx1308 */
+                Sx1308.dig_reset();
+
+                /* Switch SX1308 correlators back on  */
+#ifdef V2
+                HSCLKEN = 1;
+#else
+                lgw_reg_w(LGW_CLKHS_EN, 1);
+#endif
+
+                /* restart SX1308 */
+                x = lgw_start();
+                if (x < 0) {
+                    //pc.printf("lgw_start() failed\n");
+                }
+
+                /* Send command answer */
+                BufToHost[0] = 'f';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ((x == 0) ? ACK_OK : ACK_K0);
+
+                return(CMD_OK);
+            }
+        case 'q': { // lgw_get_trigcnt
+                uint32_t timestamp;
+                x = lgw_get_trigcnt(&timestamp);
+                timestamp += Sx1308.offtmstpstm32;
+                BufToHost[0] = 'q';
+                BufToHost[1] = 0;
+                BufToHost[2] = 4;
+                BufToHost[3] = ((x == 0) ? ACK_OK : ACK_K0);
+                BufToHost[4] = (uint8_t)(timestamp >> 24);
+                BufToHost[5] = (uint8_t)((timestamp & 0x00FF0000) >> 16);
+                BufToHost[6] = (uint8_t)((timestamp & 0x0000FF00) >> 8);
+                BufToHost[7] = (uint8_t)((timestamp & 0x000000FF));
+                return(CMD_OK);
+            }
+        case 'i': { // lgw_board_setconf
+                uint8_t  conf[(sizeof(struct lgw_conf_board_s) / sizeof(uint8_t))];
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    conf[i] = BufFromHost[4 + i];
+                }
+
+                x = lgw_board_setconf(*(lgw_conf_board_s *)conf);
+                BufToHost[0] = 'i';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ((x == 0) ? ACK_OK : ACK_K0);
+                return(CMD_OK);
+            }
+        case 'j': { // lgw_calibration_snapshot
+                lgw_calibration_offset_transfer(BufFromHost[4], BufFromHost[5]);
+                BufToHost[0] = 'j';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                return(CMD_OK);
+            }
+        case 'l': { // lgw_mcu_commit_radio_calibration
+                int fwfromhost;
+                cmdSettings_FromHost.len_msb = BufFromHost[1];
+                cmdSettings_FromHost.len_lsb = BufFromHost[2];
+                for (i = 0; i < cmdSettings_FromHost.len_lsb + (cmdSettings_FromHost.len_msb << 8); i++) {
+                    cmdSettings_FromHost.cmd_data[i] = BufFromHost[4 + i];
+                }
+                fwfromhost = (BufFromHost[4] << 24) + (BufFromHost[5] << 16) + (BufFromHost[6] << 8) + (BufFromHost[7]);
+                BufToHost[0] = 'l';
+                BufToHost[1] = 0;
+                BufToHost[2] = 8;
+                if (fwfromhost == FWVERSION) {
+                    BufToHost[3] = ACK_OK;
+                } else {
+                    BufToHost[3] = ACK_K0;
+                }
+                BufToHost[4] = *(uint8_t *)0x1fff7a18;   //unique STM32 register base adresse
+                BufToHost[5] = *(uint8_t *)0x1fff7a19;
+                BufToHost[6] = *(uint8_t *)0x1fff7a1a;
+                BufToHost[7] = *(uint8_t *)0x1fff7a1b;
+                BufToHost[8] = *(uint8_t *)0x1fff7a10;
+                BufToHost[9] = *(uint8_t *)0x1fff7a11;
+                BufToHost[10] = *(uint8_t *)0x1fff7a12;
+                BufToHost[11] = *(uint8_t *)0x1fff7a13;
+                return(CMD_OK);
+            }
+        case 'm': { // Reset SX1308 and STM32
+                /* reset SX1308 */
+                lgw_soft_reset();
+                /* Prepare command answer */
+                BufToHost[0] = 'm';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                /* Indicate that STM32 reset has to be triggered */
+                kill = true;
+                return(CMD_OK);
+            }
+        case 'n': { // Jump to bootloader to allow reflashing (DFU, UART bootloader...)
+                FLASH_Prog(DATA_EEPROM_BASE, GOTO_BOOTLOADER);
+                BufToHost[0] = 'n';
+                BufToHost[1] = 0;
+                BufToHost[2] = 0;
+                BufToHost[3] = ACK_OK;
+                kill = true;
+                return(CMD_OK);
+            }
+        default:
+            BufToHost[0] = 'k';
+            BufToHost[1] = 0;
+            BufToHost[2] = 0;
+            BufToHost[3] = ACK_K0;
+            return(CMD_K0);
+    }
+}
+
+bool CMDMANAGER::CheckCmd(char id) {
+    switch (id) {
+        case 'r': /* read register */
+        case 's': /* read burst - first chunk */
+        case 't': /* read burst - middle chunk */
+        case 'u': /* read burst - end chunk */
+        case 'p': /* read burst - atomic */
+        case 'w': /* write register */
+        case 'x': /* write burst - first chunk */
+        case 'y': /* write burst - middle chunk */
+        case 'z': /* write burst - end chunk */
+        case 'a': /* write burst - atomic */
+        case 'b': /* lgw_receive */
+        case 'c': /* lgw_rxrf_setconf */
+        case 'd': /* lgw_rxif_setconf */
+        case 'f': /* lgw_send */
+        case 'h': /* lgw_txgain_setconf */
+        case 'q': /* lgw_get_trigcnt */
+        case 'i': /* lgw_board_setconf */
+        case 'j': /* lgw_mcu_commit_radio_calibration */
+        case 'l': /* lgw_check_fw_version */
+        case 'm': /* reset STM32 */
+        case 'n': /* Go to Bootloader */
+            return true;
+        default:
+            return false;
+    }
+}
+
+int CMDMANAGER::Convert2charsToByte(uint8_t a, uint8_t  b) {
+    if (a > 96) {
+        a = a - 87;
+    } else {
+        a = a - 48;
+    }
+    if (b > 96) {
+        b = b - 87;
+    } else {
+        b = b - 48;
+    }
+    return(b + (a << 4));
+}