NAMote72 Utility Application – Serial Terminal Monitor control for NAMote72 (note: this application replaces the previous na_mote1 test code application)

Dependencies:   SX127x lib_gps lib_mma8451q lib_mpl3115a2 lib_sx9500 mbed

Fork of na_mote1 by wayne roberts

See wiki Page for a detailed

This is a link to the wiki page

main.cpp

Committer:
dudmuck
Date:
2015-06-30
Revision:
11:0f62a500aedc
Parent:
10:a3e2c558c003
Child:
12:6afe8fed5385

File content as of revision 11:0f62a500aedc:

#include "sx127x_lora.h"
#include "sx127x_fsk.h"

#include "mma8451q.h"
#include "mpl3115a2.h"
#include "sx9500.h"
#include "gps.h"
#include "rtc_alarm.h"

#define LORA_WAN_ENABLE

#define RADIO_RESET              PC_2 //NorAm_Mote Reset_sx
#define RADIO_MOSI               PB_15 //NorAm_Mote SPI2 Mosi
#define RADIO_MISO               PB_14 //NorAm_Mote SPI2 Miso
#define RADIO_SCLK               PB_13 //NorAm_Mote  SPI2 Clk
#define RADIO_NSS                PB_12 //NorAm_Mote SPI2 Nss

#define RADIO_DIO_0              PC_6 //NorAm_Mote DIO0 
#define RADIO_DIO_1              PC_10 //NorAm_Mote DIO1 
#define RADIO_DIO_2              PC_8 //NorAm_Mote DIO2 
#define RADIO_DIO_3              PB_4 //NorAm_Mote DIO3 
#define RADIO_DIO_4              PB_5 //NorAm_Mote DIO4 
#define RADIO_DIO_5              PB_6 //NorAm_Mote DIO5

#define RFSW1                    PC_4 //NorAm_Mote RFSwitch_CNTR_1
#define RFSW2                    PC_13 //NorAm_Mote RFSwitch_CNTR_2

/*
 *
 */
Serial pc(USBTX, USBRX);
#ifdef I2C_PIN_TEST
    DigitalInOut pb8(PB_8);
    DigitalInOut pb9(PB_9);
#else
    I2C i2c(I2C_SDA, I2C_SCL);
    DigitalIn i2c_int_pin(RADIO_DIO_3);
    MMA8451Q mma8451q(i2c, i2c_int_pin);
    MPL3115A2 mpl3115a2(i2c, i2c_int_pin);
    SX9500 sx9500(i2c, PA_9, PA_10);
#endif /* I2C_PIN_TeST */

/*  gps(tx, rx, en); */
GPS gps(PB_6, PB_7, PB_11);
DigitalOut pd2(PD_2);
DigitalOut hdr_fem_ctx(PC_7);
DigitalOut hdr_fem_csd(PC_0);

DigitalOut red_led(PB_1);
#define LED_ON  0
#define LED_OFF 1

Timeout hop_timeout;

InterruptIn dio3(PC_8);
bool clear_valid_header;

typedef enum {
    HOP_TYPE_NONE = 0,
    HOP_TYPE_64CH,
    HOP_TYPE_4CH
} hop_type_e;
hop_type_e hop_type;
float hop_base_MHz = 902.3;
float hop_step_MHz = 0.2;

uint32_t repeated_sleep;

bool abort_key;
bool per_en;
int PacketRxSequencePrev;
uint32_t PacketPerKoCnt;
uint32_t PacketPerOkCnt;
uint32_t PacketNormalCnt;
Timeout per_timeout;
float per_tx_delay = 0.1;
int per_id;
uint32_t PacketTxCnt;

uint8_t tx_cnt;
char pcbuf[64];
bool rx_after_tx;

typedef enum {
    APP_NONE = 0,
    APP_CHAT
} app_e;

app_e app = APP_NONE;

char service_en = 1;

#ifdef LORA_WAN_ENABLE
#define CFG_us915
#include "oslmic.h"
#include "lorabase.h"
char mic_check;
#endif /* LORA_WAN_ENABLE */

#define FSK_LARGE_PKT_THRESHOLD  0x3f

/******************************************************************************/


//  pin:       3     8     1      7    10    12     5   20    18
//             mosi,      miso,      sclk,       cs,        rst,        dio0,         dio1
SX127x radio(RADIO_MOSI, RADIO_MISO, RADIO_SCLK, RADIO_NSS, RADIO_RESET, RADIO_DIO_0, RADIO_DIO_1);
SX127x_fsk fsk(radio);
SX127x_lora lora(radio);

DigitalOut rfsw1(RFSW1);
DigitalOut rfsw2(RFSW2);



void rfsw_callback()
{
    if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER) {  // start of transmission
        if (radio.HF) {
            if (radio.RegPaConfig.bits.PaSelect) { // if PA_BOOST
                rfsw2 = 0;
                rfsw1 = 1;
            } else { // RFO to power amp
                rfsw2 = 1;
                rfsw1 = 0;            
            }
        } else {
            // todo: sx1276
        }
        hdr_fem_csd = 1;    //debug
    } else if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER || radio.RegOpMode.bits.Mode == RF_OPMODE_CAD) { // start of reception
        if (radio.HF) {
            rfsw2 = 1;
            rfsw1 = 1;              
        } else {
            // todo: sx1276
        }
        hdr_fem_csd = 0;    //debug
    } else { // RF switch shutdown
        rfsw2 = 0;
        rfsw1 = 0;     
        hdr_fem_csd = 0;    //debug         
    }
}

void printLoraIrqs_(bool clear)
{
    //in radio class -- RegIrqFlags_t RegIrqFlags;

    //already read RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS);
    printf("\r\nIrqFlags:");
    if (lora.RegIrqFlags.bits.CadDetected)
        printf("CadDetected ");
    if (lora.RegIrqFlags.bits.FhssChangeChannel) {
        //radio.RegHopChannel.octet = radio.read_reg(REG_LR_HOPCHANNEL);
        printf("FhssChangeChannel:%d ", lora.RegHopChannel.bits.FhssPresentChannel);
    }
    if (lora.RegIrqFlags.bits.CadDone)
        printf("CadDone ");
    if (lora.RegIrqFlags.bits.TxDone)
        printf("TxDone ");
    if (lora.RegIrqFlags.bits.ValidHeader)
        printf("ValidHeader ");
    if (lora.RegIrqFlags.bits.PayloadCrcError)
        printf("PayloadCrcError ");
    if (lora.RegIrqFlags.bits.RxDone)
        printf("RxDone ");  
    if (lora.RegIrqFlags.bits.RxTimeout)
        printf("RxTimeout ");

    printf("\r\n");

    if (clear)
        radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);

}

void lora_printCodingRate(bool from_rx)
{
    uint8_t d = lora.getCodingRate(from_rx);
    printf("CodingRate:");
    switch (d) {
        case 1: printf("4/5 "); break;
        case 2: printf("4/6 "); break;
        case 3: printf("4/7 "); break;
        case 4: printf("4/8 "); break;
        default:
            printf("%d ", d);
            break;
    }
}

void lora_printHeaderMode()
{
    if (lora.getHeaderMode())
        printf("implicit ");
    else
        printf("explicit ");
}

void lora_printBw()
{
    uint8_t bw = lora.getBw();
    
    printf("Bw:");
    if (radio.type == SX1276) {
        switch (lora.RegModemConfig.sx1276bits.Bw) {
            case 0: printf("7.8KHz "); break;
            case 1: printf("10.4KHz "); break;
            case 2: printf("15.6KHz "); break;
            case 3: printf("20.8KHz "); break;
            case 4: printf("31.25KHz "); break;
            case 5: printf("41.7KHz "); break;
            case 6: printf("62.5KHz "); break;
            case 7: printf("125KHz "); break;
            case 8: printf("250KHz "); break;
            case 9: printf("500KHz "); break;
            default: printf("%x ", lora.RegModemConfig.sx1276bits.Bw); break;
        }
    } else if (radio.type == SX1272) {
        switch (lora.RegModemConfig.sx1272bits.Bw) {
            case 0: printf("125KHz "); break;
            case 1: printf("250KHz "); break;
            case 2: printf("500KHz "); break;
            case 3: printf("11b "); break;
        }
    }
}

void lora_printAllBw()
{
    int i, s;
    
    if (radio.type == SX1276) {
        s = lora.RegModemConfig.sx1276bits.Bw;    
        for (i = 0; i < 10; i++ ) {
            lora.RegModemConfig.sx1276bits.Bw = i;
            printf("%d ", i);
            lora_printBw();
            printf("\r\n");
        }
        lora.RegModemConfig.sx1276bits.Bw = s;
    } else if (radio.type == SX1272) {
        s = lora.RegModemConfig.sx1272bits.Bw;    
        for (i = 0; i < 3; i++ ) {
            lora.RegModemConfig.sx1272bits.Bw = i;
            printf("%d ", i);
            lora_printBw();
            printf("\r\n");
        }
        lora.RegModemConfig.sx1272bits.Bw = s;    
    }
}

void lora_printSf()
{
    // spreading factor same between sx127[26]
    printf("sf:%d ", lora.getSf());
}

void lora_printRxPayloadCrcOn()
{
    bool on = lora.getRxPayloadCrcOn();
    //printf("RxPayloadCrcOn:%s ", on ? "on" : "off");
    if (on)
        printf("RxPayloadCrcOn:1 = Tx CRC Enabled\r\n");
    else
        printf("RxPayloadCrcOn:1 = no Tx CRC\r\n");
}

void lora_printTxContinuousMode()
{
    printf("TxContinuousMode:%d ", lora.RegModemConfig2.sx1276bits.TxContinuousMode);    // same for sx1272 and sx1276
}

void lora_printAgcAutoOn()
{
    printf("AgcAutoOn:%d", lora.getAgcAutoOn());
}

void lora_print_dio()
{
    radio.RegDioMapping2.octet = radio.read_reg(REG_DIOMAPPING2);
    printf("DIO5:");
    switch (radio.RegDioMapping2.bits.Dio5Mapping) {
        case 0: printf("ModeReady"); break;
        case 1: printf("ClkOut"); break;
        case 2: printf("ClkOut"); break;
    }
    printf(" DIO4:");
    switch (radio.RegDioMapping2.bits.Dio4Mapping) {
        case 0: printf("CadDetected"); break;
        case 1: printf("PllLock"); break;
        case 2: printf("PllLock"); break;
    }    
    radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
    printf(" DIO3:");
    switch (radio.RegDioMapping1.bits.Dio3Mapping) {
        case 0: printf("CadDone"); break;
        case 1: printf("ValidHeader"); break;
        case 2: printf("PayloadCrcError"); break;
    }    
    printf(" DIO2:");
    switch (radio.RegDioMapping1.bits.Dio2Mapping) {
        case 0:
        case 1:
        case 2:
            printf("FhssChangeChannel");
            break;
    }    
    printf(" DIO1:");
    switch (radio.RegDioMapping1.bits.Dio1Mapping) {
        case 0: printf("RxTimeout"); break;
        case 1: printf("FhssChangeChannel"); break;
        case 2: printf("CadDetected"); break;
    }    
    printf(" DIO0:");
    switch (radio.RegDioMapping1.bits.Dio0Mapping) {
        case 0: printf("RxDone"); break;
        case 1: printf("TxDone"); break;
        case 2: printf("CadDone"); break;
    }    
    
    printf("\r\n"); 
}

void fsk_print_dio()
{
    radio.RegDioMapping2.octet = radio.read_reg(REG_DIOMAPPING2);
    
    printf("DIO5:");
    switch (radio.RegDioMapping2.bits.Dio5Mapping) {
        case 0: printf("ClkOut"); break;
        case 1: printf("PllLock"); break;
        case 2:
            if (fsk.RegPktConfig2.bits.DataModePacket)
                printf("data");
            else {
                if (radio.RegDioMapping2.bits.MapPreambleDetect)
                    printf("preamble");
                else
                    printf("rssi");
            }
            break;
        case 3: printf("ModeReady"); break;
    }
    
    printf(" DIO4:");
    switch (radio.RegDioMapping2.bits.Dio4Mapping) {
        case 0: printf("temp/eol"); break;
        case 1: printf("PllLock"); break;
        case 2: printf("TimeOut"); break;
        case 3:
            if (fsk.RegPktConfig2.bits.DataModePacket) {
                if (radio.RegDioMapping2.bits.MapPreambleDetect)
                    printf("preamble");
                else
                    printf("rssi");
            } else
                printf("ModeReady");
            break;
    }
    
    radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
    
    printf(" DIO3:");
    switch (radio.RegDioMapping1.bits.Dio3Mapping) {
        case 0: printf("Timeout"); break;
        case 1:
            if (radio.RegDioMapping2.bits.MapPreambleDetect)
                printf("preamble");
            else
                printf("rssi");
            break;
        case 2: printf("?automode_status?"); break;
        case 3: printf("TempChange/LowBat"); break;
    }
    
    printf(" DIO2:");
    if (fsk.RegPktConfig2.bits.DataModePacket) {
        switch (radio.RegDioMapping1.bits.Dio2Mapping) {
            case 0: printf("FifoFull"); break;
            case 1: printf("RxReady"); break;
            case 2: printf("FifoFull/rx-timeout"); break;
            case 3: printf("FifoFull/rx-syncadrs"); break;
        }
    } else {
        printf("Data");
    }
    
    printf(" DIO1:");
    if (fsk.RegPktConfig2.bits.DataModePacket) {
        switch (radio.RegDioMapping1.bits.Dio1Mapping) {
            case 0: printf("FifoThresh"); break;
            case 1: printf("FifoEmpty"); break;
            case 2: printf("FifoFull"); break;
            case 3: printf("-3-"); break;
        }
    } else {
        switch (radio.RegDioMapping1.bits.Dio1Mapping) {
            case 0: printf("Dclk"); break;
            case 1:
                if (radio.RegDioMapping2.bits.MapPreambleDetect)
                    printf("preamble");
                else
                    printf("rssi");
                break;
            case 2: printf("-2-"); break;
            case 3: printf("-3-"); break;
        }
    }
    
    printf(" DIO0:");
    if (fsk.RegPktConfig2.bits.DataModePacket) {
        switch (radio.RegDioMapping1.bits.Dio0Mapping) {
            case 0: printf("PayloadReady/PacketSent"); break;
            case 1: printf("CrcOk"); break;
            case 2: printf("-2-"); break;
            case 3: printf("TempChange/LowBat"); break;
        }
    } else {
        switch (radio.RegDioMapping1.bits.Dio0Mapping) {
            case 0: printf("SyncAdrs/TxReady"); break;
            case 1:
                if (radio.RegDioMapping2.bits.MapPreambleDetect)
                    printf("preamble");
                else
                    printf("rssi");
                break;
            case 2: printf("RxReady"); break;
            case 3: printf("-3-"); break;
        }
    }
    printf("\r\n"); 
}

void lora_print_status()
{
    uint8_t d;
    
    if (radio.type == SX1276)
        printf("\r\nSX1276 ");
    else if (radio.type == SX1272)
        printf("\r\nSX1272 ");
    
    radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
    if (!radio.RegOpMode.bits.LongRangeMode) {
        printf("FSK\r\n");
        return;
    }
    
    lora_print_dio();
    printf("LoRa ");
    
    // printing LoRa registers at 0x0d -> 0x3f

    lora.RegModemConfig.octet = radio.read_reg(REG_LR_MODEMCONFIG);
    lora.RegModemConfig2.octet = radio.read_reg(REG_LR_MODEMCONFIG2);

    lora_printCodingRate(false); // false: transmitted coding rate
    lora_printHeaderMode();
    lora_printBw();
    lora_printSf();
    lora_printRxPayloadCrcOn();
    // RegModemStat
    printf("ModemStat:0x%02x\r\n", radio.read_reg(REG_LR_MODEMSTAT));

    // fifo ptrs:
    lora.RegPayloadLength = radio.read_reg(REG_LR_PAYLOADLENGTH);
    lora.RegRxMaxPayloadLength = radio.read_reg(REG_LR_RX_MAX_PAYLOADLENGTH);
    printf("fifoptr=0x%02x txbase=0x%02x rxbase=0x%02x payloadLength=0x%02x maxlen=0x%02x",
        radio.read_reg(REG_LR_FIFOADDRPTR),
        radio.read_reg(REG_LR_FIFOTXBASEADDR),
        radio.read_reg(REG_LR_FIFORXBASEADDR),
        lora.RegPayloadLength,
        lora.RegRxMaxPayloadLength
    );

    lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS);
    printLoraIrqs_(false);

    lora.RegHopPeriod = radio.read_reg(REG_LR_HOPPERIOD);
    if (lora.RegHopPeriod != 0) {
        printf("\r\nHopPeriod:0x%02x\r\n", lora.RegHopPeriod);
    }

    printf("SymbTimeout:0x%03x ", radio.read_u16(REG_LR_MODEMCONFIG2) & 0x3ff);

    lora.RegPreamble = radio.read_u16(REG_LR_PREAMBLEMSB);
    printf("PreambleLength:%d ", lora.RegPreamble);

    if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER || radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER_SINGLE) {
        d = radio.read_reg(REG_LR_RSSIVALUE);
        printf("rssi:%ddBm ", d-120);
    }

    lora_printTxContinuousMode();

    printf("\r\n");
    lora_printAgcAutoOn();

    printf("\r\nHeaderCount:%d PacketCount:%d, ",
        radio.read_u16(REG_LR_RXHEADERCNTVALUE_MSB), radio.read_u16(REG_LR_RXPACKETCNTVALUE_MSB));

    printf("Lora detection threshold:%02x\r\n", radio.read_reg(REG_LR_DETECTION_THRESHOLD));
    lora.RegTest31.octet = radio.read_reg(REG_LR_TEST31);
    printf("detect_trig_same_peaks_nb:%d\r\n", lora.RegTest31.bits.detect_trig_same_peaks_nb);

    printf("LowDataRateOptimize:");
    if (radio.type == SX1272) {
        lora.RegModemConfig.octet = radio.read_reg(REG_LR_MODEMCONFIG);
        printf("%d ", lora.RegModemConfig.sx1272bits.LowDataRateOptimize);
    } else if (radio.type == SX1276) {
        lora.RegModemConfig3.octet = radio.read_reg(REG_LR_MODEMCONFIG3);
        printf("%d ", lora.RegModemConfig3.sx1276bits.LowDataRateOptimize);        
    }
    
    printf(" invert: rx=%d tx=%d\r\n", lora.RegTest33.bits.invert_i_q, !lora.RegTest33.bits.chirp_invert_tx);
    
    switch (hop_type) {
        case HOP_TYPE_NONE:
            break;
        case HOP_TYPE_64CH:
            printf("hop 64ch\r\n");
            break;
        case HOP_TYPE_4CH:
            printf("hop 4ch\r\n");
            break;
    }
       
    printf("\r\n");
}

uint16_t
fsk_get_PayloadLength(void)
{
    fsk.RegPktConfig2.word = radio.read_u16(REG_FSK_PACKETCONFIG2);

    return fsk.RegPktConfig2.bits.PayloadLength;
}

void fsk_printAddressFiltering()
{
    uint8_t FSKRegNodeAdrs, FSKRegBroadcastAdrs;
    
    printf(" AddressFiltering:");
    switch (fsk.RegPktConfig1.bits.AddressFiltering) {
        case 0: printf("off"); break;
        case 1: // NodeAddress
            FSKRegNodeAdrs = radio.read_reg(REG_FSK_NODEADRS);
            printf("NodeAdrs:%02x\r\n", FSKRegNodeAdrs);
            break;
        case 2: // NodeAddress & BroadcastAddress
            FSKRegNodeAdrs = radio.read_reg(REG_FSK_NODEADRS);
            printf("NodeAdrs:%02x ", FSKRegNodeAdrs);
            FSKRegBroadcastAdrs = radio.read_reg(REG_FSK_BROADCASTADRS);
            printf("BroadcastAdrs:%02x\r\n", FSKRegBroadcastAdrs );
            break;
        default:
            printf("%d", fsk.RegPktConfig1.bits.AddressFiltering);
            break;
    }
}

void fsk_print_IrqFlags2()
{
    RegIrqFlags2_t RegIrqFlags2;
    
    printf("IrqFlags2: ");
    RegIrqFlags2.octet = radio.read_reg(REG_FSK_IRQFLAGS2);
    if (RegIrqFlags2.bits.FifoFull)
        printf("FifoFull ");
    if (RegIrqFlags2.bits.FifoEmpty)
        printf("FifoEmpty ");
    if (RegIrqFlags2.bits.FifoLevel)
        printf("FifoLevel ");
    if (RegIrqFlags2.bits.FifoOverrun)
        printf("FifoOverrun ");
    if (RegIrqFlags2.bits.PacketSent)
        printf("PacketSent ");
    if (RegIrqFlags2.bits.PayloadReady)
        printf("PayloadReady ");
    if (RegIrqFlags2.bits.CrcOk)
        printf("CrcOk ");
    if (RegIrqFlags2.bits.LowBat)
        printf("LowBat ");
    printf("\r\n");
}

void
fsk_print_status()
{
    //uint16_t s;
    RegIrqFlags1_t RegIrqFlags1;
    
    if (radio.RegOpMode.bits.LongRangeMode) {
        printf("LoRa\r\n");
        return;
    }
    
    if (radio.RegOpMode.bits.ModulationType == 0) {
        printf("FSK ");
        switch (radio.RegOpMode.bits.ModulationShaping) {
            case 1: printf("BT1.0 "); break;
            case 2: printf("BT0.5 "); break;
            case 3: printf("BT0.3 "); break;
        }
    } else if (radio.RegOpMode.bits.ModulationType == 1) {
        printf("OOK ");
    }

    printf("%dbps fdev:%dHz\r\n", fsk.get_bitrate(), fsk.get_tx_fdev_hz());    
    
    fsk.RegPktConfig2.word = radio.read_u16(REG_FSK_PACKETCONFIG2);
    
    fsk_print_dio();
    
    printf("rxbw:%dHz ", fsk.get_rx_bw_hz(REG_FSK_RXBW));
    printf("afcbw:%dHz\r\n", fsk.get_rx_bw_hz(REG_FSK_AFCBW));

    fsk.RegRssiConfig.octet = radio.read_reg(REG_FSK_RSSICONFIG);
    printf("RssiOffset:%ddB smoothing:%dsamples\r\n", fsk.RegRssiConfig.bits.RssiOffset, 1 << (fsk.RegRssiConfig.bits.RssiSmoothing+1));


    fsk.RegSyncConfig.octet = radio.read_reg(REG_FSK_SYNCCONFIG);

    if (fsk.RegPktConfig2.bits.DataModePacket) {
        uint16_t len;
        /* packet mode */
        len = fsk_get_PayloadLength();
        printf("packet RegPayloadLength:0x%03x ", len);

        if (fsk.RegPktConfig2.bits.BeaconOn)
            printf("BeaconOn ");

        fsk.RegFifoThreshold.octet = radio.read_reg(REG_FSK_FIFOTHRESH);
        printf("FifoThreshold:%d TxStartCondition:", fsk.RegFifoThreshold.bits.FifoThreshold);
        if (fsk.RegFifoThreshold.bits.TxStartCondition)
            printf("!FifoEmpty");
        else
            printf("FifoLevel");

        printf("\r\nAutoRestartRxMode:");
        switch (fsk.RegSyncConfig.bits.AutoRestartRxMode) {
            case 0: printf("off "); break;
            case 1: printf("no-pll-wait "); break;
            case 2: printf("pll-wait "); break;
            case 3: printf("3 "); break;
        }
        //...todo

        printf("PreambleSize:%d ", radio.read_u16(REG_FSK_PREAMBLEMSB));

        fsk.RegOokPeak.octet = radio.read_reg(REG_FSK_OOKPEAK);
        if (fsk.RegOokPeak.bits.barker_en)
            printf("barker ");
        if (!fsk.RegOokPeak.bits.BitSyncOn)
            printf("BitSyncOff ");
        //...todo

        fsk.RegPktConfig1.octet = radio.read_reg(REG_FSK_PACKETCONFIG1);
        if (fsk.RegPktConfig1.bits.PacketFormatVariable)
            printf("variable");
        else
            printf("fixed");
        printf("-length\r\ncrc");
        if (fsk.RegPktConfig1.bits.CrcOn) {
            printf("On");
        } else
            printf("Off");
        printf(" crctype:");
        if (fsk.RegPktConfig1.bits.CrCWhiteningType)
            printf("IBM");
        else
            printf("CCITT");
        printf(" dcFree:");
        switch (fsk.RegPktConfig1.bits.DcFree) {
            case 0: printf("none "); break;
            case 1: printf("Manchester "); break;
            case 2: printf("Whitening "); break;
            case 3: printf("reserved "); break;
        }
        fsk_printAddressFiltering();

        printf("\r\n");
        fsk_print_IrqFlags2();
    } else {
        /* continuous mode */
        printf("continuous ");
    }

    fsk.RegPreambleDetect.octet = radio.read_reg(REG_FSK_PREAMBLEDETECT);
    printf("PreambleDetect:");
    if (fsk.RegPreambleDetect.bits.PreambleDetectorOn) {
        printf("size=%d,tol=%d ",
            fsk.RegPreambleDetect.bits.PreambleDetectorSize,
            fsk.RegPreambleDetect.bits.PreambleDetectorTol);
    } else
        printf("Off ");

    printf(" syncsize:%d ", fsk.RegSyncConfig.bits.SyncSize);
    printf(" : %02x ", radio.read_reg(REG_FSK_SYNCVALUE1));
    printf("%02x ", radio.read_reg(REG_FSK_SYNCVALUE2));
    printf("%02x ", radio.read_reg(REG_FSK_SYNCVALUE3));
    printf("%02x ", radio.read_reg(REG_FSK_SYNCVALUE4));
    printf("\r\n");   // end sync config

    fsk.RegAfcFei.octet = radio.read_reg(REG_FSK_AFCFEI);
    printf("afcAutoClear:");
    if (fsk.RegAfcFei.bits.AfcAutoClearOn)
        printf("On");
    else
        printf("OFF");
    printf(" afc:%dHz ", (int)(FREQ_STEP_HZ * radio.read_s16(REG_FSK_AFCMSB)));

    printf("fei:%dHz\r\n", (int)(FREQ_STEP_HZ * radio.read_s16(REG_FSK_FEIMSB)));

    fsk.RegRxConfig.octet = radio.read_reg(REG_FSK_RXCONFIG);
    printf("RxTrigger:");
    switch (fsk.RegRxConfig.bits.RxTrigger) {
        case 0: printf("none "); break;
        case 1: printf("rssi "); break;
        case 6: printf("preamble "); break;
        case 7: printf("both "); break;
        default: printf("-%d- ", fsk.RegRxConfig.bits.RxTrigger); break;
    }
    printf("AfcAuto:");
    if (fsk.RegRxConfig.bits.AfcAutoOn)
        printf("On ");
    else
        printf("OFF ");
    if (fsk.RegRxConfig.bits.AgcAutoOn) {
        printf("AgcAutoOn ");
    } else {
        radio.RegLna.octet = radio.read_reg(REG_LNA);
        printf("AgcAutoOff:G%d ", radio.RegLna.bits.LnaGain);
    }

    fsk.RegTimerResol.octet = radio.read_reg(REG_FSK_TIMERRESOL);
    if (fsk.RegTimerResol.bits.hlm_started)
        printf("hlm_started ");
    else
        printf("hlm_stopped ");

    fsk.RegRssiThresh = radio.read_reg(REG_FSK_RSSITHRESH);
    printf("rssiThreshold:-%.1f@%02x ", fsk.RegRssiThresh / 2.0, REG_FSK_RSSITHRESH);

    radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
    if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER ||
        radio.RegOpMode.bits.Mode == RF_OPMODE_SYNTHESIZER_RX)
    {
        printf("rssi:-%.1f ", radio.read_reg(REG_FSK_RSSIVALUE) / 2.0);
    }

    fsk.RegSeqConfig1.octet = radio.read_reg(REG_FSK_SEQCONFIG1);
    printf("\r\nsequencer: ");
    printf("FromStart:");
    switch (fsk.RegSeqConfig1.bits.FromStart) {
        case 0:
            printf("lowPowerSelection-");
            if (fsk.RegSeqConfig1.bits.LowPowerSelection)
                printf("idle");
            else
                printf("sequencerOff");
            break;
        case 1: printf("rx"); break;
        case 2: printf("tx"); break;
        case 3: printf("tx on fifolevel"); break;
    }
    printf(" lowPowerSelection:");
    if (fsk.RegSeqConfig1.bits.LowPowerSelection)
        printf("idle");
    else
        printf("SequencerOff");
    if (fsk.RegSeqConfig1.bits.FromStart != 0 && 
        fsk.RegSeqConfig1.bits.LowPowerSelection != 0)
    {   // if sequencer enabled:
        printf("\r\nsequencer: IdleMode:");
        if (fsk.RegSeqConfig1.bits.IdleMode)
            printf("Sleep");
        else
            printf("standby");
        printf("\r\nsequencer: FromIdle to:");
        if (fsk.RegSeqConfig1.bits.FromIdle)
            printf("rx");
        else
            printf("tx");
        printf("\r\nsequencer: FromTransmit to:");
        if (fsk.RegSeqConfig1.bits.FromTransmit)
            printf("rx-on-PacketSent");
        else {
            printf("lowPowerSelection-");
            if (fsk.RegSeqConfig1.bits.LowPowerSelection)
                printf("idle");
            else
                printf("SequencerOff");
            printf("-on-PacketSent");
        }
        fsk.RegSeqConfig2.octet = radio.read_reg(REG_FSK_SEQCONFIG2);
        printf("\r\nsequencer: FromReceive:");
        switch (fsk.RegSeqConfig2.bits.FromReceive) {
            case 1: printf("PacketRecevied on PayloadReady"); break;
            case 2: 
                printf("lowPowerSelection-");
                if (fsk.RegSeqConfig1.bits.LowPowerSelection)
                    printf("idle");
                else
                    printf("SequencerOff");
                printf("-on-payloadReady");
                break;
            case 3: printf("PacketRecevied-on-CrcOk"); break;
            case 4: printf("SequencerOff-on-Rssi"); break;
            case 5: printf("SequencerOff-on-SyncAddress"); break;
            case 6: printf("SequencerOff-PreambleDetect"); break;
            default: printf("-%d-", fsk.RegSeqConfig2.bits.FromReceive); break;
        }
        printf("\r\nsequencer: FromRxTimeout:");
        switch (fsk.RegSeqConfig2.bits.FromRxTimeout) {
            case 0: printf("rx"); break;
            case 1: printf("tx"); break;
            case 2:
                printf("lowPowerSelection-");
                if (fsk.RegSeqConfig1.bits.LowPowerSelection)
                    printf("idle");
                else
                    printf("SequencerOff");
                break;
            case 3: printf("SequencerOff"); break;
        }
        printf("\r\nsequencer: FromPacketReceived to:");
        switch (fsk.RegSeqConfig2.bits.FromPacketReceived) {
            case 0: printf("SequencerOff"); break;
            case 1: printf("tx on FifoEmpty"); break;
            case 2:
                printf("lowPowerSelection-");
                if (fsk.RegSeqConfig1.bits.LowPowerSelection)
                printf("idle");
                else
                printf("sequencerOff");
                break;
            case 3: printf("rx via fs"); break;
            case 4: printf("rx"); break;
        }

        fsk.RegTimerResol.octet = radio.read_reg(REG_FSK_TIMERRESOL);
        printf("\r\nsequencer: timer1:");
        switch (fsk.RegTimerResol.bits.timer1_resol) {
            case 0: printf("off"); break;
            case 1: printf("%dus", radio.read_reg(REG_FSK_TIMER1COEF) * 64); break;
            case 2: printf("%.1fms", radio.read_reg(REG_FSK_TIMER1COEF) * 4.1); break;
            case 3: printf("%.1fs", radio.read_reg(REG_FSK_TIMER1COEF) * 0.262); break;
        }

        printf(" timer2:");
        switch (fsk.RegTimerResol.bits.timer2_resol) {
            case 0: printf("off"); break;
            case 1: printf("%dus", radio.read_reg(REG_FSK_TIMER2COEF) * 64); break;
            case 2: printf("%.1fms", radio.read_reg(REG_FSK_TIMER2COEF) * 4.1); break;
            case 3: printf("%.1fs", radio.read_reg(REG_FSK_TIMER2COEF) * 0.262); break;
        }
    } // ..if sequencer enabled

    printf("\r\nIrqFlags1:");
    RegIrqFlags1.octet = radio.read_reg(REG_FSK_IRQFLAGS1);
    if (RegIrqFlags1.bits.ModeReady)
        printf("ModeReady ");
    if (RegIrqFlags1.bits.RxReady)
        printf("RxReady ");
    if (RegIrqFlags1.bits.TxReady)
        printf("TxReady ");
    if (RegIrqFlags1.bits.PllLock)
        printf("PllLock ");
    if (RegIrqFlags1.bits.Rssi)
        printf("Rssi ");
    if (RegIrqFlags1.bits.Timeout)
        printf("Timeout ");
    if (RegIrqFlags1.bits.PreambleDetect)
        printf("PreambleDetect ");
    if (RegIrqFlags1.bits.SyncAddressMatch)
        printf("SyncAddressMatch ");

    printf("\r\n");

/* TODO    if (!SX1272FSK->RegPktConfig1.bits.PacketFormatVariable) { // if fixed-length packet format:
        s = fsk_get_PayloadLength();
        if (s > FSK_LARGE_PKT_THRESHOLD)
            flags.fifo_flow_ctl = 1;
        else
            flags.fifo_flow_ctl = 0;
    }*/

    fsk.RegImageCal.octet = radio.read_reg(REG_FSK_IMAGECAL);
    if (fsk.RegImageCal.bits.TempMonitorOff) {
        printf("TempMonitorOff[\r0m\r\n");
    } else {
        printf("TempThreshold:");
        switch (fsk.RegImageCal.bits.TempThreshold) {
            case 0: printf("5C"); break;
            case 1: printf("10C"); break;
            case 2: printf("15C"); break;
            case 3: printf("20C"); break;
        }
        printf("\r\n");
    }
    if (fsk.RegImageCal.bits.ImageCalRunning)
        printf("ImageCalRunning[\r0m\r\n");

/*    printf("flags.fifo_flow_ctl:%d pktidx:%d rx_pktlen:%d", flags.fifo_flow_ctl, pktidx, rx_pktlen);
    printf("\r\n");

    //printf("DIO0_PIN:%d\r\n", digitalRead(DIO0_PIN));
    printf("pkt_buf_len=%d remaining=%d\r\n", pk*/
}

void printOpMode()
{
    radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
    switch (radio.RegOpMode.bits.Mode) {
        case RF_OPMODE_SLEEP: printf("sleep"); break;
        case RF_OPMODE_STANDBY: printf("stby"); break;
        case RF_OPMODE_SYNTHESIZER_TX: printf("fstx"); break;
        case RF_OPMODE_TRANSMITTER: printf("tx"); break;
        case RF_OPMODE_SYNTHESIZER_RX: printf("fsrx"); break;
        case RF_OPMODE_RECEIVER: printf("rx"); break;
        case 6:
            if (radio.RegOpMode.bits.LongRangeMode)
                printf("rxs");
            else
                printf("-6-");
            break;  // todo: different lora/fsk
        case 7:
            if (radio.RegOpMode.bits.LongRangeMode)
                printf("cad");
            else
                printf("-7-");
            break;  // todo: different lora/fsk
    }
}

void
printPa()
{
    RegPdsTrim1_t pds_trim;
    
    pds_trim.octet = radio.read_reg(REG_PDSTRIM1);
    printf(" txdac=%.1fuA", 2.5 + (pds_trim.bits.prog_txdac * 0.625));
    radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
    if (radio.RegPaConfig.bits.PaSelect) {
        //float output_dBm = 17 - (15-radio.RegPaConfig.bits.OutputPower);
        float output_dBm = (pds_trim.bits.prog_txdac+13) - (15-radio.RegPaConfig.bits.OutputPower);
        printf(" PABOOST OutputPower=%.1fdBm", output_dBm);
    } else {
        float pmax = (0.6*radio.RegPaConfig.bits.MaxPower) + 10.8;
        float output_dBm = pmax - (15-radio.RegPaConfig.bits.OutputPower);
        printf(" RFO pmax=%.1fdBm OutputPower=%.1fdBm", pmax, output_dBm);
    }
}

void /* things always present, whether lora or fsk */
common_print_status()
{
    printf("PD2:%d version:0x%02x %.3fMHz ", pd2.read(), radio.read_reg(REG_VERSION), radio.get_frf_MHz());
    printOpMode();

    printPa();

    radio.RegOcp.octet = radio.read_reg(REG_OCP);
    if (radio.RegOcp.bits.OcpOn) {
        int imax = 0;
        if (radio.RegOcp.bits.OcpTrim < 16)
            imax = 45 + (5 * radio.RegOcp.bits.OcpTrim);
        else if (radio.RegOcp.bits.OcpTrim < 28)
            imax = -30 + (10 * radio.RegOcp.bits.OcpTrim);
        else
            imax = 240;
        printf(" OcpOn %dmA ", imax);
    } else
        printf(" OcpOFF ");

    printf("\r\n");
    
    if (per_en) {
        printf("per_tx_delay:%f\r\n", per_tx_delay);
        printf("PER device ID:%d\r\n", per_id);
    }
    printf("GPS enabled:%d\r\n", gps.enabled());
}

void per_cb()
{
    int i;
    
    PacketTxCnt++;

    radio.tx_buf[0] = per_id;
    radio.tx_buf[1] = PacketTxCnt >> 24;
    radio.tx_buf[2] = PacketTxCnt >> 16;
    radio.tx_buf[3] = PacketTxCnt >> 8;
    radio.tx_buf[4] = PacketTxCnt;
    radio.tx_buf[5] = 'P';
    radio.tx_buf[6] = 'E';
    radio.tx_buf[7] = 'R';
    radio.tx_buf[8] = 0;
    for (i = 0; i < 8; i++)
        radio.tx_buf[8] += radio.tx_buf[i];
    red_led = LED_ON;
    lora.start_tx(lora.RegPayloadLength);
}



void dio3_cb()
{
    //green_led = LED_ON;
    clear_valid_header = true;
}

bool lora_sync_sweep_hi;
uint8_t lora_sync_byte;
void lora_sync_sweep()
{
    if (abort_key) {
        abort_key = false;
        return;
    }
    
    hop_timeout.attach(&lora_sync_sweep, 0.1);
    if (lora_sync_sweep_hi) {
        lora_sync_byte += 0x10;
    } else {
        if ((lora_sync_byte & 0x0f) == 0x0f)
            lora_sync_byte &= 0xf0;
        else
            lora_sync_byte++;
    }
     printf("%02x\r\n", lora_sync_byte);
    radio.write_reg(REG_LR_SYNC_BYTE, lora_sync_byte);
    
    lora.start_tx(lora.RegPayloadLength);
}

float hop_MHz;
bool new_hop;
uint8_t hop_ofs = 0;

void hop_cb()
{
    static uint8_t prev_ofs;
    int shift;
    
    radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
    if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER)
        hop_timeout.attach(&hop_cb, 0.4);
    else
        return;
    
    do {
        shift = rand() & 0x1f;
        if (hop_type == HOP_TYPE_64CH)
            hop_ofs = (rand() >> shift) & 0x3f;
        else if (hop_type == HOP_TYPE_4CH)
            hop_ofs = (rand() >> shift) & 0x3;
    } while (hop_ofs == prev_ofs);
    
    prev_ofs = hop_ofs; 
    hop_MHz = hop_base_MHz + (hop_ofs * hop_step_MHz);
    new_hop = true;
      
    radio.set_frf_MHz(hop_MHz);

    radio.set_opmode(RF_OPMODE_STANDBY);              
    radio.set_opmode(RF_OPMODE_TRANSMITTER);
    
    if (hdr_fem_ctx.read())
        hdr_fem_ctx = 0;
    else
        hdr_fem_ctx = 1;
}

void print_rx_buf(int len)
{
    int i;

    printf("000:");
    for (i = 0; i < len; i++) {
        //printf("(%d)%02x ", i % 16, rx_buf[i]);
        printf("%02x ", radio.rx_buf[i]);
        if (i % 16 == 15 && i != len-1)
            printf("\r\n%03d:", i+1);

    }
    printf("\r\n");
}

#ifdef LORA_WAN_ENABLE
static const u1_t DEVKEY[16] = {
        0x03, 0xfb, 0xb9, 0xb1, 0xa0, 0x62, 0xe0, 0x80, 0x8d, 0xa1, 0x9a, 0xf4, 0x05, 0xb1, 0xa1, 0x13
//<APPKEY>03-   FB   -B9   -B1   -A0   -62   -E0   -80   -8D   -A1   -9A   -F4   -05   -B1   -A1   -13</APPKEY>
};
// provide device key (16 bytes)
void os_getDevKey (u1_t* buf) {
    memcpy(buf, DEVKEY, 16);
}

static void aes_encrypt (xref2u1_t pdu, int len) {
    os_getDevKey(AESkey);
    os_aes(AES_ENC, pdu, len);
}

u4_t os_rmsbf4 (xref2cu1_t buf) {
    return (u4_t)(buf[3] | (buf[2]<<8) | ((u4_t)buf[1]<<16) | ((u4_t)buf[0]<<24));
}
u4_t calc_mic;
u4_t rx_mic;
static int aes_verifyMic0 (xref2u1_t pdu, int len) {
    os_getDevKey(AESkey);
    calc_mic = os_aes(AES_MIC|AES_MICNOAUX, pdu, len);
    rx_mic = os_rmsbf4(pdu+len);
    return calc_mic == rx_mic;
    //return os_aes(AES_MIC|AES_MICNOAUX, pdu, len) == os_rmsbf4(pdu+len);
}
#endif /* LORA_WAN_ENABLE */

void print_rx_verbose(uint8_t dlen)
{
    float dbm;
    printLoraIrqs_(false);
    if (lora.RegHopPeriod > 0) {
        lora.RegHopChannel.octet = radio.read_reg(REG_LR_HOPCHANNEL);
        printf("HopCH:%d ", lora.RegHopChannel.bits.FhssPresentChannel);
    }
    printf("%dHz ", lora.get_freq_error_Hz());    
    lora_printCodingRate(true);  // true: of received packet
    dbm = lora.get_pkt_rssi();
    printf(" crc%s %.1fdB  %.1fdBm\r\n",
        lora.RegHopChannel.bits.RxPayloadCrcOn ? "On" : "OFF",
        lora.RegPktSnrValue / 4.0,
        dbm
    );
    print_rx_buf(/*lora.RegRxNbBytes*/dlen);  
    
#ifdef LORA_WAN_ENABLE
    if (mic_check) {    /* LoraWAN MIC check (join accept check) */
        int a, d;
        u1_t hdr;
        printf("mic_check ");
        if (dlen != LEN_JA && dlen != LEN_JAEXT) {
            printf("dlen fail\r\n");
            return;
        }
        hdr = radio.rx_buf[0];
        if ((hdr & (HDR_FTYPE|HDR_MAJOR)) != (HDR_FTYPE_JACC|HDR_MAJOR_V1) ) {   
            printf("hdr fail\r\n");
            return;
        } 
        
        aes_encrypt(radio.rx_buf+1, dlen-1);
        if (!aes_verifyMic0(radio.rx_buf, dlen-4) ) {       
            printf("%08x != %08x fail\r\n", calc_mic, rx_mic);
        } else
            printf("%08x == %08x\r\n", calc_mic, rx_mic);
            
        for (a = 0x0d; a < 0x40; a++) {
            d = radio.read_reg(a);
            //update_shadow_regs(selected_radio, a, d); 
            printf("%02x: %02x\r\n", a, d);
        }            
    }
#endif /* LORA_WAN_ENABLE */
}
    
void
service_radio()
{
    service_action_e act;
    
    if (radio.RegOpMode.bits.LongRangeMode) {

        act = lora.service();
    
        switch (act) {
            case SERVICE_READ_FIFO:
                //green_led = LED_OFF; // ValidHeader indication
                                   
                if (app == APP_NONE) {   
                    if (per_en) {
                        if (lora.RegRxNbBytes > 8 && radio.rx_buf[5] == 'P' && radio.rx_buf[6] == 'E' && radio.rx_buf[7] == 'R') {
                            int i;
                            float per;
                            red_led = LED_OFF;
                            /* this is PER packet */
                            uint32_t PacketRxSequence = (radio.rx_buf[1] << 24) | (radio.rx_buf[2] << 16) | (radio.rx_buf[3] << 8) | radio.rx_buf[4];
                            PacketPerOkCnt++;
                            //IntervalPerOkCnt++;
                            
                            if( PacketRxSequence <= PacketRxSequencePrev )
                            { // Sequence went back => resynchronization
                                // dont count missed packets this time
                                i = 0;
                            }
                            else
                            {
                                // determine number of missed packets
                                i = PacketRxSequence - PacketRxSequencePrev - 1;
                            }
                            
                            // be ready for the next
                            PacketRxSequencePrev = PacketRxSequence;
                            // increment 'missed' counter for the RX session
                            PacketPerKoCnt += i;
                            //IntervalPerKoCnt += i;
                            printf("%d, ok=%d missed=%d normal=%d ", PacketRxSequence, PacketPerOkCnt, PacketPerKoCnt, PacketNormalCnt);
                            per = ( 1.0 - ( float )PacketPerOkCnt / ( float )( PacketPerOkCnt + PacketPerKoCnt ) ) * 100.0;
                            printf("per:%f\r\n", per);
                        } else {
                            PacketNormalCnt++;
                            print_rx_verbose(lora.RegRxNbBytes);
                        }
                    } else
                        print_rx_verbose(lora.RegRxNbBytes);
                } else if (app == APP_CHAT) {
                    if (lora.RegHopChannel.bits.RxPayloadCrcOn) {
                        if (lora.RegIrqFlags.bits.PayloadCrcError)
                            printf("crcError\r\n");
                        else {
                            int n = lora.RegRxNbBytes;
                            radio.rx_buf[n++] = '\r';
                            radio.rx_buf[n++] = '\n';
                            radio.rx_buf[n] = 0; // null terminate
                            printf((char *)radio.rx_buf);
                        }
                    } else
                        printf("crcOff\r\n");
                        
                    // clear Irq flags
                    radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);
                    // should still be in receive mode
                }
                break;
            case SERVICE_TX_DONE:
                red_led = LED_OFF;
                hdr_fem_csd = 0;    //debug
                if (app == APP_CHAT) {
                    lora.start_rx();
                } else if (per_en)
                    per_timeout.attach(&per_cb, per_tx_delay); // start next TX
                else if (rx_after_tx)
                    lora.start_rx();
                break;
            case SERVICE_ERROR:
                printf("error\r\n");
                break;
        } // ...switch (act)
    } else {
        /* FSK: */
        act = fsk.service();
        
         switch (act) {
             case SERVICE_READ_FIFO:
                if (app == APP_CHAT) {
                    int n = fsk.rx_buf_length;
                    radio.rx_buf[n++] = '\r';
                    radio.rx_buf[n++] = '\n';
                    radio.rx_buf[n] = 0; // null terminate
                    printf((char *)radio.rx_buf);                    
                } else {
                    int i;
                    if (fsk.RegRxConfig.bits.AfcAutoOn)
                        printf("%dHz ", (int)(FREQ_STEP_HZ * fsk.RegAfcValue));
                    printf("%d: ", fsk.rx_buf_length);
                    for (i = 0; i < fsk.rx_buf_length; i++)
                        printf("%02x ", radio.rx_buf[i]);
                    printf("\r\n");
                }
                break;
            case SERVICE_TX_DONE:
                if (app == APP_CHAT) {
                    fsk.start_rx();
                }
                break;                
         } // ...switch (act)
    }
    
    if (clear_valid_header) {
        RegIrqFlags_t irqs;
        irqs.octet = 0;
        irqs.bits.ValidHeader = 1;
        radio.write_reg(REG_LR_IRQFLAGS, irqs.octet);
        clear_valid_header = false;
    }
    
    if (new_hop) {
        new_hop = false;
        printf("%02d  %.1f\r\n", hop_ofs, hop_MHz);
    }    
}

void
gps_service()
{
    gps.service();
    if (gps.enabled()) {
        if (gps.LatitudeBinary != 0) {
            gps.LatitudeBinary = 0;
            printf("gps long:%f, lat:%f\r\n", gps.Longitude, gps.Latitude);
        }
    }
}

void all_peripherals_off()
{
    // TODO: PD2 low, has pulldown

#ifndef USE_DEBUGGER
    /* PA13 to undriven JTMS/SWDIO pin (from AF0 to GPIO), and PA2 */
    GPIOA->MODER &= 0xf7ffffdf;
    GPIOB->MODER &= 0xffffdfff; // PB6 UART_TX to input
#endif

    sx9500.set_active(false);
    mpl3115a2.SetModeStandby();
    mma8451q.set_active(0);
    gps.enable(false);

    if (GPIOB->ODR & 0x1000) {  // if SX1272_NSS (PB12) wasnt forced low
        radio.set_opmode(RF_OPMODE_SLEEP);
    }
}

void restore_from_sleep()
{
#ifndef USE_DEBUGGER
    /* PA13 back to JTMS/SWDIO pin (from GPIO to AF0), and PA2 */
    GPIOA->MODER |= 0x08000020;
    GPIOB->MODER |= 0x00002000; // PB6 input to UART_TX
#endif
}

int get_kbd_str(char* buf, int size)
{
    char c;
    int i;
    static int prev_len;
    static int sleep_start_cnt_dn = 0;
    
    for (i = 0;;) {
        if (sleep_start_cnt_dn > 0) {
            if (--sleep_start_cnt_dn == 0) {
                if (repeated_sleep != 0) {
                    all_peripherals_off();
                    rtc_alarm_start(repeated_sleep);
#ifdef USE_DEBUGGER
                    HAL_EnableDBGStopMode();
                    if (!DBGMCU->CR & DBGMCU_CR_DBG_STOP)
                        for (;;) asm("nop");
#endif /* USE_DEBUGGER */
                    deepsleep();
                    restore_from_sleep();
                }
            }
        }        
        
        if (pc.readable()) {
            if (sleep_start_cnt_dn > 0) {
                repeated_sleep = 0;
                printf("repeated_sleep abort\r\n");
            }
                        
            c = pc.getc();
            if (c == 8) {   // backspace
                if (i > 0) {
                    pc.putc(8);
                    pc.putc(' ');
                    pc.putc(8);
                    i--;
                }
            } else if (c == '\r') {
                if (i == 0) {
                    return prev_len; // repeat previous
                } else {
                    buf[i] = 0; // null terminate
                    prev_len = i;
                    return i;
                }
            } else if (c == 3) {
                // ctrl-C abort
                per_en = false;
                abort_key = true;
                return -1;
            } else if (i < size) {
                buf[i++] = c;
                pc.putc(c);
            }
        } else {
            if (service_en) {
                service_radio();
            }
            gps_service();
            sx9500.service();
            mma8451q.service();
            mpl3115a2.service();

            if (sleep_start_cnt_dn == 0 && repeated_sleep != 0) {
                printf("Alarm Start %d\r\n", repeated_sleep);
                sleep_start_cnt_dn = 3000;
            }
                        
        }
    } // ...for()
}

void
console_chat()
{
    int i, len = get_kbd_str(pcbuf, sizeof(pcbuf));
    if (len < 0) {
        printf("chat abort\r\n");
        app = APP_NONE;
        return;
    } else {
        for (i = 0; i < len; i++)
            radio.tx_buf[i] = pcbuf[i];
        if (radio.RegOpMode.bits.LongRangeMode) {
            lora.RegPayloadLength = len;
            radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
            red_led = LED_ON;
            lora.start_tx(len);
        } else {
            fsk.start_tx(len);
        }
        printf("\r\n");
    }
}

uint8_t last_RxCurrentAddr;
uint8_t last_RxNbBytes;



                
void
console()
{
    int len, i;
    uint32_t ui;
    uint8_t a, d;
    static uint16_t fsk_tx_length;
        
    len = get_kbd_str(pcbuf, sizeof(pcbuf));
    if (len < 0) {
        printf("abort\r\n");
        return;
    }
    
    printf("\r\n");
    if (len == 1) {
        switch (pcbuf[0]) {
            case 'i':
                printf("init\r\n");
                radio.init();
                if (!radio.RegOpMode.bits.LongRangeMode) {
                    fsk.init();   // put FSK modem to some functioning default
                } else {
                    // lora configuration is more simple
                }
                break;
            case 'h':
                printf("hw_reset()\r\n");
                radio.hw_reset();
                break;
            case 'R':
                // read all registers
                for (a = 1; a < 0x71; a++) {
                    d = radio.read_reg(a);
                    //update_shadow_regs(selected_radio, a, d); 
                    printf("%02x: %02x\r\n", a, d);
                }
                break;
            case 'T':
                if (radio.RegOpMode.bits.LongRangeMode) {
                    lora.RegModemConfig2.octet = radio.read_reg(REG_LR_MODEMCONFIG2);
                    //printf("a %02x\r\n", lora.RegModemConfig2.octet);
                    lora.RegModemConfig2.sx1276bits.TxContinuousMode ^= 1;   // same for sx1272 and sx1276
                    //printf("b %02x\r\n", lora.RegModemConfig2.octet);
                    radio.write_reg(REG_LR_MODEMCONFIG2, lora.RegModemConfig2.octet);
                    lora.RegModemConfig2.octet = radio.read_reg(REG_LR_MODEMCONFIG2);
                    //printf("c %02x\r\n", lora.RegModemConfig2.octet);
                    lora_printTxContinuousMode();
                    printf("\r\n");
                } else
                    printf("(fsk)\r\n");
                break;
            case 'C':
                if (radio.RegOpMode.bits.LongRangeMode) {
                    lora.setRxPayloadCrcOn(!lora.getRxPayloadCrcOn());
                    lora_printRxPayloadCrcOn();
                } else {
                    printf("CrcOn:");
                    fsk.RegPktConfig1.bits.CrcOn ^= 1;
                    radio.write_reg(REG_FSK_PACKETCONFIG1, fsk.RegPktConfig1.octet);
                    if (fsk.RegPktConfig1.bits.CrcOn)
                        printf("On\r\n");
                    else
                        printf("Off\r\n");
                    if (fsk.RegPktConfig2.bits.DataModePacket && radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER) {
                        fsk.config_dio0_for_pktmode_rx();
                    }
                }
                printf("\r\n");
                break;
            case 'B':
                radio.RegPaConfig.bits.PaSelect ^= 1;
                radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
                printPa();
                printf("\r\n");
                break;
            case 'L':  
                if (radio.RegOpMode.bits.LongRangeMode)
                    fsk.enable(false);
                else
                    lora.enable();

                radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
                if (radio.RegOpMode.bits.LongRangeMode)
                    printf("LoRa\r\n");
                else
                    printf("FSK\r\n");                
                break;
            case 's':
                if (!radio.RegOpMode.bits.LongRangeMode) {
                    fsk.RegFifoThreshold.bits.TxStartCondition ^= 1;
                    radio.write_reg(REG_FSK_FIFOTHRESH, fsk.RegFifoThreshold.octet);
                    printf("TxStartCondition:");
                    if (fsk.RegFifoThreshold.bits.TxStartCondition)
                        printf("!FifoEmpty\r\n");
                    else
                        printf("FifoLevel\r\n");                    
                }
                break;
            case 'f':
                if (!radio.RegOpMode.bits.LongRangeMode) {
                    printf("PacketFormat:");
                    fsk.RegPktConfig1.bits.PacketFormatVariable ^= 1;
                    radio.write_reg(REG_FSK_PACKETCONFIG1, fsk.RegPktConfig1.octet);
                    if (fsk.RegPktConfig1.bits.PacketFormatVariable)
                        printf("variable\r\n");
                    else
                        printf("fixed\r\n");
                    /*if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER)
                        reset_flow();*/
                }
                break;
            case 'E':
                if (!radio.RegOpMode.bits.LongRangeMode) {
                    RegIrqFlags2_t RegIrqFlags2;
                    RegIrqFlags2.octet = radio.read_reg(REG_FSK_IRQFLAGS2);
                    while (!RegIrqFlags2.bits.FifoEmpty) {
                        if (pc.readable())
                            break;
                        printf("%02x\r\n", radio.read_reg(REG_FIFO));
                        RegIrqFlags2.octet = radio.read_reg(REG_FSK_IRQFLAGS2);
                    }
                }
                break;
            case 'A':
                if (!radio.RegOpMode.bits.LongRangeMode) {
                    fsk.RegRxConfig.bits.AfcAutoOn ^= 1;
                    radio.write_reg(REG_FSK_RXCONFIG, fsk.RegRxConfig.octet);
                    printf("AfcAuto:");
                    if (fsk.RegRxConfig.bits.AfcAutoOn)
                        printf("On\r\n");
                    else
                        printf("OFF\r\n");
                    break;
                }
                break;
            case '?':
                printf("G<portABCD><MO><pin0-f>[0123]       pin control (M=MODER, O=BSRR) see RM0038 section 7.4\r\n");
                printf("cs%%d        deepsleep for seconds\r\n");
                printf("cl%%d        sleep for seconds\r\n");
                printf("CS%%d        repeated sleep for seconds (hold down key to stop)\r\n");
                printf("r %%x       read sx1272 register (addr)\r\n");
                printf("w %%x %%x   write sx1272 register (addr data)\r\n");
                printf("ge          toggle GPS enable\r\n");
                printf("L           toggle LongRangeMode/FSK\r\n");
                printf("i           radio_init\r\n");
                printf("h           hw_reset\r\n");
                printf("tx[%%d]     transmit\r\n");
                printf("rx          receive\r\n");   
                printf("C           toggle crcOn\r\n");
                printf("op[%%d]     get/set output power\r\n");
                printf("d[0-5]      change DIO pin assignment\r\n");
                printf("frf[%%f]    get/set operating frequency (MHz)\r\n");
                printf("pd2         toggle PA_High_Power\r\n");
                printf("bgr[%%d]        get/set prog_txdac BGR bias for TXDAC (7=+20dBm)\r\n"); 
                printf("per         toggle PER enable (\"tx\" to start, ctrl-C to stop)\r\n");
                printf("pin[%%f]         get/set per_tx_delay (seconds)\r\n");    
                printf("pid[%%d]        get/set PER device ID\r\n");
                printf("hop         change hop type (off, 64ch, 4ch)\r\n");
                printf("hb[%%f]     get/set hop base MHz\r\n");
                printf("hs[%%f]     get/set hop step MHz\r\n");
                if (radio.RegOpMode.bits.LongRangeMode) {
                    printf("pl[%%d]     LORA get/set RegPayloadLength\r\n");
                    printf("cr[1234]    LORA set coding rate \r\n");
                    printf("bw[%%d]     LORA get/set bandwidth\r\n");
                    printf("sf[%%d]     LORA get/set spreading factor\r\n");
                    printf("T           LORA toggle TxContinuousMode\r\n");
                    printf("hp[%%d]     LORA get/set hop period\r\n");
                    printf("hm          LORA toggle explicit/explicit header mode\r\n");
                    printf("rin          LORA toggle RX invert_i_q\r\n");
                    printf("tin          LORA toggle chirp_invert_tx\r\n");
                    printf("ld          LORA toggle LowDataRateOptimize\r\n");
                } else {
                    printf("bw[a][%%d] FSK get-set rxbw (bwa=afcbw)\r\n");
                    printf("br[%%d]    FSK get-set bitrate\r\n");
                    printf("fdev[%%d]    FSK get-set TX frequency deviation (hz)\r\n");
                    printf("rt          FSK change RxTrigger\r\n");
                    printf("pd          FSK enable/disable preamble detector\r\n");
                    printf("pt          FSK get-set PreambleDetectorTol\r\n");
                    printf("ss[%%d]    FSK get-set SyncSize\r\n");
                    printf("S[%%x]     FSK get-set sync word\r\n");
                    printf("s           FSK toggle TxStartCondition\r\n");
                    printf("f           FSK toggle PacketFormat fixed-variable\r\n");
                    printf("E           FSK empty out the fifo\r\n");
                    printf("ac          FSK AfcClear\r\n");
                    printf("A           FSK toggle AfcAutoOn\r\n");
                    printf("mp          FSK toggle MapPreambleDetect\r\n");
                    printf("ar          FSK change AutoRestartRxMode\r\n");
                    printf("alc          FSK toggle AfcAutoClearOn\r\n");
                    printf("ag          FSK toggle AgcAutoOn\r\n");
                    printf("pre[%%d}    FSK get-set TX preamble length\r\n");
                }
                printf("mp?     MPL3115 help (tempurature/pressure sensor)\r\n");
                printf("mm?     MMA8451 help (accelerometer)\r\n");
                printf("95?     SX9500 help (touch sensor)\r\n");
                break;
            case '.':
                if (radio.RegOpMode.bits.LongRangeMode)
                    lora_print_status();
                else
                    fsk_print_status();
                common_print_status();
                break;
        } // ...switch (pcbuf[0])
    } else {
        if (pcbuf[0] == 't' && pcbuf[1] == 'x') { // TX
            if (radio.RegOpMode.bits.LongRangeMode) {
                if (per_en) {
                    printf("timeout attach %f\r\n", per_tx_delay);
                    PacketTxCnt = 0;
                    per_timeout.attach(&per_cb, per_tx_delay);
                } else {
                    if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                        sscanf(pcbuf+2, "%d", &i);
                        lora.RegPayloadLength = i;
                    }
                    tx_cnt++;
                    for (i = 0; i < lora.RegPayloadLength; i++)
                        radio.tx_buf[i] = tx_cnt;
                    red_led = LED_ON;
                    lora.start_tx(lora.RegPayloadLength);
                    if (pcbuf[2] == 'r')
                        rx_after_tx = true;
                    else
                        rx_after_tx = false;
                }
            } else {    // FSK:
                if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                    sscanf(pcbuf+2, "%d", &i);
                    fsk_tx_length = i;
                }
                if (radio.RegOpMode.bits.Mode != RF_OPMODE_TRANSMITTER) { // if not already busy transmitting
                    tx_cnt++;
                    for (i = 0; i < fsk_tx_length; i++) {
                        radio.tx_buf[i] = tx_cnt;
                    }
                    fsk.start_tx(fsk_tx_length);
                }
            }
            
            if (hop_type != HOP_TYPE_NONE)
                hop_timeout.attach(&hop_cb, 0.4);
        } else if (pcbuf[0] == 'r' && pcbuf[1] == 'n' && pcbuf[2] == 'd') {
            uint8_t of = rand() & 0x3f;

            printf("%02d %.2f\r\n", of, 902.3 + (of * 0.2));
        } else if (pcbuf[0] == 'h' && pcbuf[1] == 'b') {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%f", &hop_base_MHz);
            }
            printf("hop_base:%f\r\n", hop_base_MHz);
        } else if (pcbuf[0] == 'h' && pcbuf[1] == 's') {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%f", &hop_step_MHz);
            }
            printf("hop_step:%f\r\n", hop_step_MHz);                           
        } else if (pcbuf[0] == 'h' && pcbuf[1] == 'o' && pcbuf[2] == 'p') {
            switch (hop_type) {
                case HOP_TYPE_NONE:
                    hop_type = HOP_TYPE_64CH;
                    printf("64ch hop\r\n");                 
                    break;
                case HOP_TYPE_64CH:
                    hop_type = HOP_TYPE_4CH;
                    printf("4ch hop\r\n");                                 
                    break;
                case HOP_TYPE_4CH:
                    hop_type = HOP_TYPE_NONE;
                    printf("hop off\r\n");                 
                    break;
            }
        } else if (pcbuf[0] == 'h' && pcbuf[1] == 'p' && radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%d", &i);
                lora.RegHopPeriod = i;
                radio.write_reg(REG_LR_HOPPERIOD, lora.RegHopPeriod);
                if (radio.RegDioMapping1.bits.Dio1Mapping != 1) {
                    radio.RegDioMapping1.bits.Dio1Mapping = 1;
                    radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
                }
            }
            lora.RegHopPeriod = radio.read_reg(REG_LR_HOPPERIOD);
            printf("HopPeriod:0x%02x\r\n", lora.RegHopPeriod);
        } else if (pcbuf[0] == 'r' && pcbuf[1] == 't' && !radio.RegOpMode.bits.LongRangeMode) {
            printf("RxTrigger:");
            switch (fsk.RegRxConfig.bits.RxTrigger) {
                case 0: fsk.RegRxConfig.bits.RxTrigger = 1;
                    printf("rssi\r\n");
                    break;
                case 1: fsk.RegRxConfig.bits.RxTrigger = 6;
                    printf("preamble\r\n");
                    break;
                case 6: fsk.RegRxConfig.bits.RxTrigger = 7;
                    printf("both\r\n");
                    break;
                case 7: fsk.RegRxConfig.bits.RxTrigger = 0;
                    printf("none\r\n");
                    break;
                default: fsk.RegRxConfig.bits.RxTrigger = 0;
                    printf("none\r\n");
                    break;
                }
            radio.write_reg(REG_FSK_RXCONFIG, fsk.RegRxConfig.octet);     
        } else if (pcbuf[0] == 'C' && pcbuf[1] == 'S' && pcbuf[2] >= '0' && pcbuf[2] <= '9') {
            all_peripherals_off();
            sscanf(pcbuf+2, "%d", &i);
            repeated_sleep = i;
        } else if (pcbuf[0] == 'c' && (pcbuf[1] == 's' || pcbuf[1] == 'l') && pcbuf[2] >= '0' && pcbuf[2] <= '9') {
             // rtc wakeup test
            all_peripherals_off();
            sscanf(pcbuf+2, "%d", &i);
            printf("alarm_start %d\r\n", i);
            wait(0.1);
            rtc_alarm_start(i);
            if (pcbuf[1] == 'l') {
#ifdef USE_DEBUGGER
                HAL_EnableDBGSleepMode();
                if (!DBGMCU->CR & DBGMCU_CR_DBG_SLEEP)
                    for (;;) asm("nop");
#endif /* USE_DEBUGGER */
                sleep();
            } else {
#ifdef USE_DEBUGGER
                HAL_EnableDBGStopMode();
                if (!DBGMCU->CR & DBGMCU_CR_DBG_STOP)
                    for (;;) asm("nop");
#endif /* USE_DEBUGGER */
                deepsleep();
            }
            restore_from_sleep();                   
        } else if (pcbuf[0] == 'r' && pcbuf[1] == 'x') { // RX
            if (per_en) {
                red_led = LED_OFF;
                //green_led = LED_OFF;
                PacketNormalCnt = 0;
                PacketRxSequencePrev = -1;
                PacketPerKoCnt = 0;
                PacketPerOkCnt = 0;                
                dio3.rise(&dio3_cb);
            }
            if (radio.RegOpMode.bits.LongRangeMode) {
                last_RxCurrentAddr = radio.read_reg(REG_LR_FIFORXCURRENTADDR);
                lora.start_rx();
            } else
                fsk.start_rx();
        } else if (pcbuf[0] == 'r' && pcbuf[1] == ' ') { // read single register
            sscanf(pcbuf+2, "%x", &i);
            printf("%02x: %02x\r\n", i, radio.read_reg(i));
        } else if (pcbuf[0] == 'w' && pcbuf[1] == ' ') { // write single register
            sscanf(pcbuf+2, "%x %x", &i, &len);
            radio.write_reg(i, len);
            printf("%02x: %02x\r\n", i, radio.read_reg(i));
        }
#ifdef I2C_PIN_TEST
        else if (pcbuf[0] == 'b' && pcbuf[1] == '8') {
            printf("SCL:");
            switch (pcbuf[2]) {
                case '0':
                    pb8.output();
                    pb8 = 0;
                    printf("lo");
                    break;
                case '1':
                    pb8.output();
                    pb8 = 1;
                    printf("hi");
                    break;                    
                case 'z':
                    pb8.input();
                    printf("z");
                    break;                    
            }
            printf("\r\n");
        } else if (pcbuf[0] == 'b' && pcbuf[1] == '9') {
            printf("SDA:");
            switch (pcbuf[2]) {
                case '0':
                    pb9.output();
                    pb9 = 0;
                    printf("lo");
                    break;
                case '1':
                    pb9.output();
                    pb9 = 1;
                    printf("hi");
                    break;                    
                case 'z':
                    pb9.input();
                    printf("z");
                    break;                    
            }
            printf("\r\n");                      
        }
#else    
        /************************** MMA8451Q... **************************************/
        else if (pcbuf[0] == 'm' && pcbuf[1] == 'm') {
            if (pcbuf[2] == '?') {
                printf("mm.     get status\r\n");
                printf("mm,     print XYZ\r\n");
                printf("mma     toggle stby/active\r\n");
                printf("mmtd     configure for transient detection\r\n");
            } else if (pcbuf[2] == '.') {
                printf("active:%d\r\n", mma8451q.get_active());
                mma8451q.print_regs();
                printf("i2c_int_pin:%d\r\n", i2c_int_pin.read());
            } else if (pcbuf[2] == ',') {
                mma8451q.read(MMA8451_OUT_X_MSB, mma8451q.out.octets, 6);
                mma8451q.out.v.x >>= 4; // 12bit data
                mma8451q.out.v.y >>= 4; // 12bit data
                mma8451q.out.v.z >>= 4; // 12bit data
                printf("x:%d y:%d z:%d\r\n", mma8451q.out.v.x, mma8451q.out.v.y, mma8451q.out.v.z);
            } else if (pcbuf[2] == 't' && pcbuf[3] == 'd') {   
                printf("transient ");
                if (mma8451q.transient_cfg.bits.ELE) {
                    mma8451q.transient_cfg.octet = 0;
                    mma8451q.write(MMA8451_TRANSIENT_CFG, mma8451q.transient_cfg.octet);
                    mma8451q.ctrl_reg4.bits.INT_EN_TRANS = 0;
                    mma8451q.write(MMA8451_CTRL_REG4, mma8451q.ctrl_reg4.octet);                    
                    printf("off\r\n");
                } else {
                    mma8451q.transient_detect();
                    //poll_timeout.attach(on_poll, 0.3);
                    printf("on\r\n");
                }                         
            } else if (pcbuf[2] == 'a') {
                if (mma8451q.get_active()) {
                    mma8451q.set_active(0);
                } else {
                    mma8451q.set_active(1);
                }
                printf("active:%d\r\n", mma8451q.get_active());
            }
        }
        /************************** MPL3115... **************************************/
        else if (pcbuf[0] == 'm' && pcbuf[1] == 'p') {
            if (pcbuf[2] == '?') {
                printf("mp.     get status\r\n");
                printf("mpt     get temperature\r\n");
                printf("mpa     get altitude\r\n");
            } else if (pcbuf[2] == '.') {
                printf("active:%d\r\n", mpl3115a2.GetModeActive());
                printf("int src:%02x\r\n", mpl3115a2.read(INT_SOURCE_REG));
                /* only INT1 is connected */
                printf("CTRL3 int ctl:%02x\r\n", mpl3115a2.read(CTRL_REG3));    /* TODO: PP_OD1 for open-drain operation */
                printf("CTRL4 int  en:%02x\r\n", mpl3115a2.read(CTRL_REG4));
                printf("CTRL5 int cfg:%02x\r\n", mpl3115a2.read(CTRL_REG5));
                printf("i2c_int_pin:%d\r\n", i2c_int_pin.read());
            } else if (pcbuf[2] == 't') {
                printf("temp:%f\r\n", mpl3115a2.ReadTemperature());
            } else if (pcbuf[2] == 'a') {
                printf("alt:%f\r\n", mpl3115a2.ReadAltitude());
                
            }
        }
        /************************** sx9500... **************************************/
        else if (pcbuf[0] == '9' && pcbuf[1] == '5') {
            if (pcbuf[2] == '?') {
                printf("95R     reset\r\n");
                printf("95.     read status\r\n");
                printf("95t[%%d]   get/set PROXTHRESH\r\n");
                printf("95s[%%d]   get/set SCANPERIOD\r\n");
                printf("95a         toggle txen\r\n");
            } else if (pcbuf[2] == '.') {
                printf("(txen) active:%d\r\n", sx9500.get_active());
                printf("RegStat:%02x\r\n", sx9500.read_single(SX9500_REG_STAT));
                printf("RegProxCtrl0:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL0));
                printf("RegProxCtrl1:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL1));
                printf("RegProxCtrl2:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL2));
                printf("RegProxCtrl3:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL3));
                printf("RegProxCtrl4:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL4));
                printf("RegProxCtrl5:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL5));
                printf("RegProxCtrl6:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL6));
            } else if (pcbuf[2] == 'a') {
                if (sx9500.get_active()) {
                    sx9500.set_active(false);
                } else {
                    sx9500.RegProxCtrl0.octet = sx9500.read_single(SX9500_REG_PROXCTRL0);
                    sx9500.RegProxCtrl0.bits.sensor_en = 3;    // CS0 and CS1 on
                    sx9500.write(SX9500_REG_PROXCTRL0, sx9500.RegProxCtrl0.octet);
                    sx9500.write(SX9500_REG_PROXCTRL6, 1);  // threshold to 20 for CS1 release
                    sx9500.write(SX9500_REG_IRQMSK, 0x60);     // enable near and far interrupts
                    sx9500.set_active(true);
                    printf("RegProxCtrl0:%02x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL0));    /* sensor-enable and scan period */
                }
                printf("(txen) active:%d\r\n", sx9500.get_active());
            } else if (pcbuf[2] == 'R') {
                sx9500.reset();
            } else if (pcbuf[2] == 'i') {
                if (pcbuf[3] != 0) {
                    sscanf(pcbuf+3, "%x", &i);
                    sx9500.write(SX9500_REG_IRQMSK, i);
                }
                printf("irqmsk:%02x\r\n", sx9500.read_single(SX9500_REG_IRQMSK));
            } else if (pcbuf[2] == 't') {
                if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                    sscanf(pcbuf+3, "%d", &i);
                    sx9500.write(SX9500_REG_PROXCTRL6, i);           
                }   
                printf("proxthresh:0x%x\r\n", sx9500.read_single(SX9500_REG_PROXCTRL6));
            } else if (pcbuf[2] == 'p') {
                if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                    sscanf(pcbuf+2, "%d", &i);
                    sx9500.RegProxCtrl0.bits.scan_period = i;
                    sx9500.write(SX9500_REG_PROXCTRL0, sx9500.RegProxCtrl0.octet);
                }
                sx9500.RegProxCtrl0.octet = sx9500.read_single(SX9500_REG_PROXCTRL0);
                printf("scan period:%d\r\n", sx9500.RegProxCtrl0.bits.scan_period);
            }
        } 
#endif /* !I2C_PIN_TEST */
#ifdef LORA_WAN_ENABLE
        else if (pcbuf[0] == 'm' && pcbuf[1] == 'i') {
            mic_check ^= 1;
            printf("mic_check:%d\r\n", mic_check);
        }
#endif /* LORA_WAN_ENABLE */        
        else if (pcbuf[0] == 'm' && pcbuf[1] == 'p' && !radio.RegOpMode.bits.LongRangeMode) {
            radio.RegDioMapping2.bits.MapPreambleDetect ^= 1;
            radio.write_reg(REG_DIOMAPPING2, radio.RegDioMapping2.octet);
            printf("MapPreambleDetect:");
            if (radio.RegDioMapping2.bits.MapPreambleDetect)
                printf("preamble\r\n");
            else
                printf("rssi\r\n");
        } else if (pcbuf[0] == 'o' && pcbuf[1] == 'p') {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%d", &i);
                radio.RegPaConfig.bits.OutputPower = i;
                radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
            }
            radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
            printf("OutputPower:%d\r\n", radio.RegPaConfig.bits.OutputPower);
        } else if (pcbuf[0] == 'c' && pcbuf[1] == 'r' && radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9')
                lora.setCodingRate(pcbuf[2] - '0');
             lora.RegModemConfig.octet = radio.read_reg(REG_LR_MODEMCONFIG);
             lora_printCodingRate(false);    // false: transmitted
             printf("\r\n");
        } else if (pcbuf[0] == 'h' && pcbuf[1] == 'm' && radio.RegOpMode.bits.LongRangeMode) {    // toggle implicit/explicit
            lora.setHeaderMode(!lora.getHeaderMode());
            lora.RegModemConfig.octet = radio.read_reg(REG_LR_MODEMCONFIG);
            lora_printHeaderMode();
            printf("\r\n");
        } else if (pcbuf[0] == 'a' && pcbuf[1] == 'g' && !radio.RegOpMode.bits.LongRangeMode) {
            fsk.RegRxConfig.bits.AgcAutoOn ^= 1;
            radio.write_reg(REG_FSK_RXCONFIG, fsk.RegRxConfig.octet);
            printf("AgcAutoOn:%d\r\n", fsk.RegRxConfig.bits.AgcAutoOn);            
        } else if (pcbuf[0] == 'a' && pcbuf[1] == 'l' && !radio.RegOpMode.bits.LongRangeMode) {
            fsk.RegAfcFei.bits.AfcAutoClearOn ^= 1;
            printf("AfcAutoClearOn: ");
            radio.write_reg(REG_FSK_AFCFEI, fsk.RegAfcFei.octet);
            if (fsk.RegAfcFei.bits.AfcAutoClearOn)
                printf("ON\r\n");
            else
                printf("off\r\n");
        } else if (pcbuf[0] == 'a' && pcbuf[1] == 'r' && !radio.RegOpMode.bits.LongRangeMode) {
            fsk.RegSyncConfig.bits.AutoRestartRxMode++;
            radio.write_reg(REG_FSK_SYNCCONFIG, fsk.RegSyncConfig.octet);
            fsk.RegSyncConfig.octet = radio.read_reg(REG_FSK_SYNCCONFIG);
            printf("AutoRestartRxMode:");
            switch (fsk.RegSyncConfig.bits.AutoRestartRxMode) {
                case 0: printf("off "); break;
                case 1: printf("no-pll-wait "); break;
                case 2: printf("pll-wait "); break;
                case 3: printf("3 "); break;
            }
            printf("\r\n");            
        } else if (pcbuf[0] == 'a' && pcbuf[1] == 'c' && !radio.RegOpMode.bits.LongRangeMode) {
            printf("clear afc: ");
            fsk.RegAfcFei.bits.AfcClear = 1;
            radio.write_reg(REG_FSK_AFCFEI, fsk.RegAfcFei.octet);
            fsk.RegAfcFei.bits.AfcClear = 0; 
            printf("%dHz\r\n", (int)(FREQ_STEP_HZ * radio.read_s16(REG_FSK_AFCMSB)));
        } else if (pcbuf[0] == 'b' && pcbuf[1] == 'g' && pcbuf[2] == 'r') {
            RegPdsTrim1_t pds_trim;
            pds_trim.octet = radio.read_reg(REG_PDSTRIM1);
            if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                sscanf(&pcbuf[3], "%d", &i);
                pds_trim.bits.prog_txdac = i;
            }
            radio.write_reg(REG_PDSTRIM1, pds_trim.octet);
            printf("prog_txdac:%.1fuA\r\n", 2.5 + (pds_trim.bits.prog_txdac * 0.625));
        } else if (pcbuf[0] == 'b' && pcbuf[1] == 'r' && !radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(&pcbuf[2], "%d", &i);
                fsk.set_bitrate(i);
            }
            printf("%dbps\r\n", fsk.get_bitrate());             
        } else if (pcbuf[0] == 'b' && pcbuf[1] == 'w') {
            if (radio.RegOpMode.bits.LongRangeMode) {
                if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                    radio.set_opmode(RF_OPMODE_STANDBY);
                    sscanf(&pcbuf[2], "%d", &i);
                    lora.setBw(i);
                } else
                    lora_printAllBw();
                lora.RegModemConfig.octet = radio.read_reg(REG_LR_MODEMCONFIG);
                printf("current ");
                lora_printBw();
                printf("\r\n");
            } else { // FSK:
                if (pcbuf[2] == 'a') {
                    if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                        radio.set_opmode(RF_OPMODE_STANDBY);
                        sscanf(&pcbuf[3], "%d", &i);
                        fsk.set_rx_dcc_bw_hz(i, 1);
                    }
                    printf("afcbw:%dHz\r\n", fsk.get_rx_bw_hz(REG_FSK_AFCBW));
                } else {
                    if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                        radio.set_opmode(RF_OPMODE_STANDBY);
                        sscanf(&pcbuf[2], "%d", &i);
                        fsk.set_rx_dcc_bw_hz(i, 0);
                    }
                    printf("rxbw:%dHz\r\n", fsk.get_rx_bw_hz(REG_FSK_RXBW));
                }
            }
        } else if (pcbuf[0] == 'v' && pcbuf[1] == 'h') {
            lora.poll_vh ^= 1;
            printf("poll_vh:%d\r\n", lora.poll_vh);
        } else if (pcbuf[0] == 'S' && !radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[1] == '0') {
                sscanf(pcbuf+1, "%x", &ui);
                if (ui < 0x100) {
                    fsk.RegSyncConfig.bits.SyncSize = 0;
                    radio.write_reg(REG_FSK_SYNCVALUE1, ui);
                } else if (ui < 0x10000) {
                    fsk.RegSyncConfig.bits.SyncSize = 1;
                    radio.write_reg(REG_FSK_SYNCVALUE2, ui & 0xff);
                    radio.write_reg(REG_FSK_SYNCVALUE1, ui >> 8);
                } else if (ui < 0x1000000) {
                    fsk.RegSyncConfig.bits.SyncSize = 2;
                    radio.write_reg(REG_FSK_SYNCVALUE3, ui & 0xff);
                    radio.write_reg(REG_FSK_SYNCVALUE2, (ui >> 8) & 0xff);
                    radio.write_reg(REG_FSK_SYNCVALUE1, ui >> 16);                              
                } else {
                    fsk.RegSyncConfig.bits.SyncSize = 3;
                    radio.write_reg(REG_FSK_SYNCVALUE4, ui & 0xff);
                    radio.write_reg(REG_FSK_SYNCVALUE3, (ui >> 8) & 0xff);
                    radio.write_reg(REG_FSK_SYNCVALUE2, (ui >> 16) & 0xff);
                    radio.write_reg(REG_FSK_SYNCVALUE1, ui >> 24);                       
                }
                radio.write_reg(REG_FSK_SYNCCONFIG, fsk.RegSyncConfig.octet);
            }
            fsk.RegSyncConfig.octet = radio.read_reg(REG_FSK_SYNCCONFIG);
            printf("%d: ", fsk.RegSyncConfig.bits.SyncSize);
            for (i = 0; i <= fsk.RegSyncConfig.bits.SyncSize; i++)
                printf("%02x ", radio.read_reg(REG_FSK_SYNCVALUE1+i));
            printf("\r\n");
        } else if (pcbuf[0] == 's' && pcbuf[1] == 'e' && pcbuf[2] == 'r') {
            service_en ^= 1;
            printf("service_en:%d\r\n", service_en);
        } else if (pcbuf[0] == 's' && pcbuf[1] == 's') {
            if (radio.RegOpMode.bits.LongRangeMode) { // sweep test on lora sync
                if (pcbuf[2] == 'h') { // sweep high nibble
                    lora_sync_sweep_hi = 1;
                } else if (pcbuf[2] == 'l') { // sweep low nibble
                    lora_sync_sweep_hi = 0;
                }
                lora_sync_byte = radio.read_reg(REG_LR_SYNC_BYTE);
                hop_timeout.attach(&lora_sync_sweep, 0.1);
            } else {
                if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                    sscanf(pcbuf+2, "%d", &i);
                    fsk.RegSyncConfig.bits.SyncSize = i;
                    radio.write_reg(REG_FSK_SYNCCONFIG, fsk.RegSyncConfig.octet);
                }
                fsk.RegSyncConfig.octet = radio.read_reg(REG_FSK_SYNCCONFIG);
                printf("SyncSize:%d\r\n", fsk.RegSyncConfig.bits.SyncSize);
            }
        } else if (pcbuf[0] == 's' && pcbuf[1] == 'f' && radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%d", &i);
                lora.setSf(i);
                if (i == 6 && !lora.getHeaderMode()) {
                    printf("SF6: to implicit header mode\r\n");
                    lora.setHeaderMode(true);
                }
            }
            lora.RegModemConfig2.octet = radio.read_reg(REG_LR_MODEMCONFIG2);
            lora_printSf();
            printf("\r\n");
        } else if (pcbuf[0] == 'f' && pcbuf[1] == 'i' && !radio.RegOpMode.bits.LongRangeMode) {
            fsk.init();
            printf("fsk.init\r\n");
        } else if (pcbuf[0] == 'f' && pcbuf[1] == 'd' && pcbuf[2] == 'e' && !radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[4] >= '0' && pcbuf[4] <= '9') {
                sscanf(pcbuf+4, "%d", &i);
                fsk.set_tx_fdev_hz(i);
            }
            printf("fdev:%dHz\r\n", fsk.get_tx_fdev_hz());
        } else if (pcbuf[0] == 'f' && pcbuf[1] == 'r' && pcbuf[2] == 'f') {
            if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                float MHz;
                sscanf(pcbuf+3, "%f", &MHz);
                //printf("MHz:%f\r\n", MHz);
                radio.set_frf_MHz(MHz);
            }
            printf("%fMHz\r\n", radio.get_frf_MHz());
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 'i' && pcbuf[2] == 'd') {
            if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                sscanf(pcbuf+3, "%d", &per_id);
            }
            printf("PER device ID:%d\r\n", per_id);
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 'i' && pcbuf[2] == 'n') {
            if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                sscanf(pcbuf+2, "%f", &per_tx_delay);
            }
            printf("per_tx_delay:%f\r\n", per_tx_delay);
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 'e' && pcbuf[2] == 'r') {
            per_en ^= 1;
            printf("per_en:%d\r\n", per_en);
            if (per_en && radio.RegOpMode.bits.LongRangeMode) {
                if (radio.type == SX1272) {
                    lora.RegModemConfig.sx1272bits.LowDataRateOptimize = 1;
                    radio.write_reg(REG_LR_MODEMCONFIG, lora.RegModemConfig.octet);
                } else if (radio.type == SX1276) {
                    lora.RegModemConfig3.sx1276bits.LowDataRateOptimize = 1;
                    radio.write_reg(REG_LR_MODEMCONFIG3, lora.RegModemConfig3.octet);
                }
                lora.RegPayloadLength = 9;
                radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
                radio.RegDioMapping1.bits.Dio3Mapping = 1;  // to ValidHeader
                radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);                                  
            }
            PacketRxSequencePrev = -1;
            //PacketRxSequence = 0;
            PacketPerKoCnt = 0;
            PacketPerOkCnt = 0;
            PacketNormalCnt = 0;
            if (!per_en) {
                per_timeout.detach();
            }
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 'r' && pcbuf[2] == 'e') {
            if (radio.RegOpMode.bits.LongRangeMode) {
                if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                    sscanf(pcbuf+3, "%d", &i);
                    radio.write_u16(REG_LR_PREAMBLEMSB, i);
                }
                lora.RegPreamble = radio.read_u16(REG_LR_PREAMBLEMSB);
                printf("lora PreambleLength:%d\r\n", lora.RegPreamble);                
            } else {
                if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                    sscanf(pcbuf+3, "%d", &i);
                    radio.write_u16(REG_FSK_PREAMBLEMSB, i);
                }
                printf("FSK TX PreambleSize:%d\r\n", radio.read_u16(REG_FSK_PREAMBLEMSB));
            }
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 't' && !radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%d", &i);
                fsk.RegPreambleDetect.bits.PreambleDetectorTol = i;
                radio.write_reg(REG_FSK_PREAMBLEDETECT, fsk.RegPreambleDetect.octet);
            }
            fsk.RegPreambleDetect.octet = radio.read_reg(REG_FSK_PREAMBLEDETECT);
            printf("PreambleDetectorTol:%d\r\n", fsk.RegPreambleDetect.bits.PreambleDetectorTol);
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 'd' && pcbuf[2] == '2') {
            if (pd2.read())
                pd2 = 0;
            else 
                pd2 = 1;
            printf("pd2:%d\r\n", pd2.read());
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 'd' && !radio.RegOpMode.bits.LongRangeMode) {
            fsk.RegPreambleDetect.bits.PreambleDetectorOn ^= 1;
            radio.write_reg(REG_FSK_PREAMBLEDETECT, fsk.RegPreambleDetect.octet);
            printf("PreambleDetector:");
            if (fsk.RegPreambleDetect.bits.PreambleDetectorOn)
                printf("On\r\n");
            else
                printf("OFF\r\n");            
        } else if (pcbuf[0] == 'p' && pcbuf[1] == 'l' && radio.RegOpMode.bits.LongRangeMode) {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%d", &i);
                lora.RegPayloadLength = i;
                radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);             
            }
            lora.RegPayloadLength = radio.read_reg(REG_LR_PAYLOADLENGTH);
            printf("PayloadLength:%d\r\n", lora.RegPayloadLength);
        } else if (pcbuf[0] == 'l' && pcbuf[1] == 'd' && radio.RegOpMode.bits.LongRangeMode) {
            if (radio.type == SX1272) {
                lora.RegModemConfig.octet = radio.read_reg(REG_LR_MODEMCONFIG);
                lora.RegModemConfig.sx1272bits.LowDataRateOptimize ^= 1;
                printf("LowDataRateOptimize:%d\r\n", lora.RegModemConfig.sx1272bits.LowDataRateOptimize);
                radio.write_reg(REG_LR_MODEMCONFIG, lora.RegModemConfig.octet);
            } else if (radio.type == SX1276) {
                lora.RegModemConfig3.octet = radio.read_reg(REG_LR_MODEMCONFIG3);
                lora.RegModemConfig3.sx1276bits.LowDataRateOptimize ^= 1;
                printf("LowDataRateOptimize:%d\r\n", lora.RegModemConfig3.sx1276bits.LowDataRateOptimize); 
                radio.write_reg(REG_LR_MODEMCONFIG3, lora.RegModemConfig3.octet);
            }   
        } else if (pcbuf[0] == 't' && pcbuf[1] == 'i' && pcbuf[2] == 'n' && radio.RegOpMode.bits.LongRangeMode) {                     
            lora.invert_tx(lora.RegTest33.bits.chirp_invert_tx);
            printf("chirp_invert_tx :%d\r\n", lora.RegTest33.bits.chirp_invert_tx);          
        } else if (pcbuf[0] == 'r' && pcbuf[1] == 'i' && pcbuf[2] == 'n' && radio.RegOpMode.bits.LongRangeMode) {
            lora.invert_rx(!lora.RegTest33.bits.invert_i_q);
            printf("rx invert_i_q:%d\r\n", lora.RegTest33.bits.invert_i_q);   
        } else if (pcbuf[0] == 'd' && pcbuf[1] == 'i' && pcbuf[2] == 'o') {
            if (radio.RegOpMode.bits.LongRangeMode)
                lora_print_dio();
            else
                fsk_print_dio();            
            printf("dio0:%d, dio1:%d\r\n", radio.dio0.read(), radio.dio0.read());
            lora.RegIrqFlags.octet = radio.read_reg(REG_LR_IRQFLAGS);
            printLoraIrqs_(false);            
        } else if (pcbuf[0] == 'd' && pcbuf[1] >= '0' && pcbuf[1] <= '5') {
            switch (pcbuf[1]) {
                case '0':
                    radio.RegDioMapping1.bits.Dio0Mapping++;
                    radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
                    break;
                case '1':
                    radio.RegDioMapping1.bits.Dio1Mapping++;
                    radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
                    break;    
                case '2':
                    radio.RegDioMapping1.bits.Dio2Mapping++;
                    radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
                    break;
                case '3':
                    radio.RegDioMapping1.bits.Dio3Mapping++;
                    radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
                    break;
                case '4':
                    radio.RegDioMapping2.bits.Dio4Mapping++;
                    radio.write_reg(REG_DIOMAPPING2, radio.RegDioMapping2.octet);
                    break; 
                case '5':
                    radio.RegDioMapping2.bits.Dio5Mapping++;
                    radio.write_reg(REG_DIOMAPPING2, radio.RegDioMapping2.octet);
                    break;                                                                                             
            } // ...switch (pcbuf[1])
            if (radio.RegOpMode.bits.LongRangeMode)
                lora_print_dio();
            else
                fsk_print_dio();
        } else if (pcbuf[0] == 'g') {   /******* GPS... **********/
            if (pcbuf[1] == 'e') {
                //gps.enable(!gps.enabled());
                if (gps.enabled()) {
                    gps.enable(false);
                    printf("gps disabled\r\n");
                } else {
                    gps.enable(true);
                    printf("GPS enabled\r\n");
                }
            } else if (pcbuf[1] == 'v') {
                gps.verbose ^= 1;
                printf("gps.verbose:%d\r\n", gps.verbose);
            }
        } else if (pcbuf[0] == 'G') {   // gpio registers.  G<portABCD><MO><pin0-f>[0123]
            GPIO_TypeDef *gpiox;
            char bp[2];
            bp[0] = pcbuf[3];
            bp[1] = 0;
            sscanf(bp, "%x", &i);
            switch (pcbuf[1]) {
                case 'A': gpiox = GPIOA; break;
                case 'B': gpiox = GPIOB; break;
                case 'C': gpiox = GPIOC; break;
                case 'D': gpiox = GPIOD; break;
                default: gpiox = NULL; break;
            }
            if (gpiox != NULL) {
                if (pcbuf[2] == 'M') {  // GPIOx->MODER
                    if (pcbuf[4] >= '0' && pcbuf[4] <= '3') {
                        a = i << 1;
                        ui = 3 << a;
                        d = pcbuf[4] - '0';
                        //gpiox->MODER &= ~ui;
                        gpiox->MODER &= ~ui;
                        gpiox->MODER |= d << a;;
                        printf("ui:%08x, d<<a:%08x\r\n", ui, d<<a);
                    }
                    for (len = 0; len < 16; len++) {
                        i = len << 1;
                        ui = gpiox->MODER >> i;
                        printf("%02d:", len);
                        switch (ui & 3) {
                            case 0: printf("input\r\n"); break;
                            case 1: printf("output\r\n"); break;
                            case 2: printf("AF\r\n"); break;
                            case 3: printf("analog\r\n"); break;
                        }
                    }
                    printf("MODER:%08x\r\n", gpiox->MODER);
                } else if (pcbuf[2] == 'O') {   // GPIOx->BSRR
                    ui = 1 << i;
                    if (pcbuf[4] == '0') {
                        gpiox->BSRR = ui << 16;
                    } else if (pcbuf[4] == '1') {
                        gpiox->BSRR = ui;
                    }
                    printf("ODR:%08x\r\n", gpiox->ODR);
                }
            }
                        
        } else if (pcbuf[0] == 's' && pcbuf[1] == 't' && pcbuf[2] == 'b') {
            radio.set_opmode(RF_OPMODE_STANDBY);
            //green_led = LED_OFF;
            red_led = LED_OFF;
        } else if (pcbuf[0] == 's' && pcbuf[1] == 'l' && pcbuf[2] == 'e') {
            radio.set_opmode(RF_OPMODE_SLEEP);
            //green_led = LED_OFF;
            red_led = LED_OFF;
        } else if (pcbuf[0] == 'c' && pcbuf[1] == 'h' && pcbuf[2] == 'a') {
            app = APP_CHAT;
            lora.start_rx();
            printf("chat start\r\n");
        }           
    }
    printf("> ");
    fflush(stdout);
        
}

int main()
{  
    set_time(0);    // start RTC
    rtc_alarm_init();
    
    pc.baud(57600);
    gps.init();

    printf("\nreset\r\n");    
    //green_led = LED_OFF;
    red_led = LED_OFF;
    
    radio.rf_switch.attach(rfsw_callback);
    
    if (radio.RegOpMode.bits.LongRangeMode)
        last_RxCurrentAddr = radio.read_reg(REG_LR_FIFORXCURRENTADDR);
    
    gps.enable(false);
    
    sx9500.RegProxCtrl0.bits.sensor_en = 3; // only CS0 and CS1 connected
    sx9500.write(SX9500_REG_PROXCTRL0, sx9500.RegProxCtrl0.octet);
    
    // set PROXTHRESH to 80 because CS1 has 48 showing always on PROXDIFF
    sx9500.write(SX9500_REG_PROXCTRL6, 0x04);
    
    mma8451q.set_active(0);
    mpl3115a2.SetModeStandby();
    
    GPIOA->MODER |= 0x01415500;     // unused pins as outputs: PA4, PA5, PA6, PA7, PA8, (PA11,PA12 USB)
    printf("GPIOA->MODER:%08x\r\n", GPIOA->MODER);

    GPIOB->MODER |= 0x00000401;     // unused pins as outputs: PB0(HDR_DIO1), PB5 (PB10 pulled hi by LED), PB3-T_SWO
    printf("GPIOB->MODER:%08x\r\n", GPIOB->MODER);

    GPIOC->MODER |= 0x00000041;    // unused pins as outputs: PC0(HDR_FEM_CSD), PC3(SPI3_enable)
    printf("GPIOC->MODER:%08x\r\n", GPIOC->MODER);

    

    while(1) {
        switch (app) {
            case APP_NONE:
                console();
                break;
            case APP_CHAT:
                console_chat();
                break;
        } // ...switch (app)
    } // ...while(1)
}