UART console application for testing SX1272/SX1276

Dependencies:   SX127x

/media/uploads/dudmuck/lora.png

This is a UART console test application for using SX127x library driver for SX1272/SX1276 radio transceivers. Serial console is provided at 57600bps. Refer to Serial Communication with a PC for information about using the serial port with your PC.

Using this command interface, you can exercise the functionality of radio chip without needing specialized software application for your PC.

Commands which can be used include ? to list available commands, or . to query status from radio chip, for example. The serial console allows you to configure the radio chip, such as setting spreading factor, bandwidth, operating frequency, etc.

A simple chat application is provided to try communications between two boards. The SX127x library object is instantiated with pin assignments generic arduino headers, but can be easily reassigned for any mbed board.

The same driver library can operate for both SX1272 and SX1276. Upon starting, the driver auto-detects whether SX1272 or SX1276 transceiver chip is connected by attempting to change the LowFrequencyModeOn bit in RegOpMode register. If this bit can be changed, then the radio device is SX1276. This bit is not implemented in SX1272. A few of the radio driver functions select behavior based on this detection. The differences between these two devices is small, from a software perspective.

Using with SX1276MB1xAS Shield

This component plugs into any board with arduino uno headers.

There are two different version of this shield. European version (MAS), and North American (LAS). The LAS shield uses PA_BOOST transmit pin to permit +20dBm power. The MAS version uses RFO transmit pin in Europe. This software reads RF switch pin (A4 pin) pulling resistor to determine which type of shield is installed.


Using with your own production board

This software is useful for validating RF performance your own LoRa board design, because only two external pins needs to be provided to PC (UART TX/RX). You can select an mbed platform which matches the CPU on your own board. If the memory size doesnt match exactly, you can export the program to an offline toolchain and edit the target type or linker file there.

Transmitter Test Guidelines

FSK mode is used for transmitter testing, because an unmodulated carrier can be sent, permitting easy measurement of TX power and frequency error.

commands used for transmitter testing:

  • frf915.0 change to your desired RF center frequency (in this case 915MHz)
  • L to toggle the radio chip into FSK mode.
  • fdev0 to configure TX frequency deviation to zero, to put the transmitted carrier on the center frequency.
  • pas to select which TX pin is connected to antenna matching (RFO vs PA_BOOST).
  • op<dBm> to configure TX power.
  • If you desire to test higher power PA_BOOST, use ocp<mA>
  • w 01 03 put radio chip into transmit mode (skips writing to FIFO). This will cause radio to transmit preamble, because the FIFO is empty in TX mode. Since Fdev is zero, an unmodulated carrier is sent.
  • Spectrum analyzer can now be used to to observe TX power, harmonics, power consumption, or frequency error.
  • stby to end transmission, or use h to reset radio chip to default condition.
  • Use period . command at any time to review current radio configuration.

LoRa transmitter testing

  • use L command to toggle radio into LoRa, if necessary.
  • Normally the tx command is used to manually send single packets.
  • txc will toggle TxContinuousMode in LoRa modem to send continuous modulated transmission.
  • Useful for checking adjacent channel power.
  • enter txc again to end transmission.

Receiver Test Guidelines

FSK mode is used for receiver sensitivity testing, allowing the use of a BERT signal generator (such as R/S SMIQ03B). Using this method provides real-time indication of receiver sensitivity, useful for tuning and impedance matching. The radio chip outputs DCLK and DATA digital signals which are connected back to BERT signal generator.

commands used for receiver testing:

  • L to toggle the radio chip into FSK mode.
  • datam to toggle FSK modem into continuous mode. This disables packet engine and gives direct access to demodulator.
  • configure DIO1 pin to DCLK function, and DIO2 pin to DATA function:
    • dio command to list current DIO pin asignments
    • d1 to cycle DIO1 function until Dclk is selected
    • d2 for DIO2, only Data function is available in FSK continuous mode
  • frf915.0 change to your desired RF center frequency (in this case 915MHz)
  • rx to start receiver
  • stby to disable receiver

Full command list

Arguments shown in square brackets [] indicate required. <> are optional, where leaving off the argument usually causes a read of the item, and providing the value causes a write operation. You should always have the radio chip datasheet on-hand when using these commands.

Hitting <enter> key by itself will repeat last command.
<Ctrl-C> will cancel an operation in progress.

command list: common commands (both LoRa and FSK)

commanddescription
. (period)print current radio status
?list available commands
Ltoggle active radio modem (LoRa vs FSK)
hhardware reset, put radio into default power-on condition
frf<MHz>get/set RF operating frequency
rxstart radio receiver (any received packets are printed onto your serial terminal)
rssiread instantaneous RSSI (level read at the time command is issued)
tx<%d>transmit test packet. Packet length value can be provided as argument, or uses last value if not provided
payl<%d>get/set payload length
bw<KHz>get/set bandwidth. In LoRa mode, both receive and transmit bandwidth are changed. For FSK, only receive bandwidth is affected. bwa accesses AFC bandwidth in FSK
pastoggle RFO / PA_BOOST transmit pin output selection
op<dBm>get/set TX output power. Value is provided in dBm. Special case is value of 20dBm (on PA_BOOST), which causes increase in TX DAC voltage
ocp<mA>get/set TX current limit, in milliamps. Necessary adjustment when +20dBm is used
dioshow DIO pin assignments
d<0-5>change DIO pin assignment, the pin number is given as arguement. Each pin has up to 4 possible functions
pres<%d>set preamble length. LoRa: number of symbols. FSK: number of bytes
crcontoggle crcOn
lnabcycle LNA-boost setting (receiver performance adjustment)
Rread all radio registers (use only while reading chip datasheet)
r[%x]read single radio register (use only while reading chip datasheet)
w[%x %x]write single radio register (use only while reading chip datasheet)
pllbwchange PLL bandwidth
stbyset chip mode to standby
sleepset chip mode to sleep
fstxset chip mode to fstx
fsrxset chip mode to fsrx
Eiger range test commandsdescription
pid<%d>get set ID number in range test payload
pertx<%d>start Eiger PER transmit. The count of packets to send is provided as arguement
perrxstart Eiger PER receive
txpd<%d>get/set tx delay between PER packets transmitted

command list: LoRa modem commands

LoRa commandLoRa description
iqinvtoggle RX IQ invert
cintoggle TX IQ invert
lhp<%d>(RX) get/set hop period
sync<%x>get/set sync (post-preamble gap, single byte)
cr<1-4>get/set codingRate
lhmtoggle explicit/implicit (explicit mode sends payload length with each packet)
sf<%d>get/set spreadingFactor (SF7 to SF12)
ldrtoggle LowDataRateOptimize (changes payload encoding, for long packets)
txctoggle TxContinuousMode
rxt<%d>get/set SymbTimeout
rxsstart RX_SINGLE (receives only for SymbTimeout symbols)
cad<%d num tries>run channel activity detection

command list: FSK modem commands

FSK commandFSK description
c<%d>get/set test cases. Several FSK bitrates/bandwidths pre-configured to give optimal performance.
fdev<kHz>(TX) get/set frequency deviation
mods(TX) increment modulation shaping
par(TX) increment paRamp
datamtoggle DataMode (packet/continuous)
fifottoggle TxStartCondition (FifoThreshold level vs FifoNotEmpty)
br<%f kbps>get/set bitrate
dcfincrement DcFree (manchester / whitening)
pktftoggle PacketFormat fixed/variable length
syncontoggle SyncOn (frame sync, SFD enable)
bitsynctoggle BitSyncOn (continuous mode only)
syncw<hex bytes>get/set syncword. Sync bytes are provided by hex octects separated by spaces.
fei(RX) read FEI
rxt(RX) increment RxTrigger (RX start on rssi vs. preamble detect)
rssit<-dBm>(RX) get/set rssi threshold (trigger level for RSSI interrupt)
rssis<%d>(RX) get/set rssi smoothing
rssio<%d>(RX) get/set rssi offset
agcauto(RX) toggle AgcAutoOn (true = LNA gain set automatically)
afcauto(RX) toggle AfcAutoOn
ac(RX) AfcClear
ar(RX) increment AutoRestartRxMode
alc(RX) toggle AfcAutoClearOn (only if AfcAutoOn is set)
prep(RX) toggle PreamblePolarity (0xAA vs 0x55)
pde(RX) toggle PreambleDetectorOn
pds<%d>(RX) get/set PreambleDetectorSize
pdt<%d>(RX) get/set PreambleDetectorTol
mp(RX) toggle MapPreambleDetect (DIO function RSSI vs PreambleDetect)
thr<%d>get/set FifoThreshold (triggers FifoLevel interrupt)
polltoggle poll_irq_en. Radio events read from DIO pins vs polling of IrqFlags register
Eempty out FIFO
clkoutincrement ClkOut divider
ookenter OOK mode
ooktincrement OokThreshType
ooksincrement OokPeakTheshStep
sqlch<%d>get/set OokFixedThresh

main.cpp

Committer:
dudmuck
Date:
2015-07-21
Revision:
8:227605e4a760
Parent:
7:c3c54f222ced
Child:
9:2f13a9ef27b4

File content as of revision 8:227605e4a760:

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

//#define LORA_WAN_ENABLE

//DigitalOut green(LED_GREEN);

Serial pc(USBTX, USBRX);

uint8_t tx_cnt;
char pcbuf[64];
int pcbuf_len;

typedef enum {
    APP_NONE = 0,
    APP_CHAT
} app_e;

app_e app = APP_NONE;

#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

/***************************** eiger per: *************************************************/

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

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

//  pin:       3     8     1      7    10    12     5
//           mosi, miso, sclk,   cs,  rst,  dio0, dio1
SX127x radio(D11,   D12, D13,    D10,  A0,   D2,   D3); // sx1276 arduino shield
SX127x_fsk fsk(radio);
SX127x_lora lora(radio);

#ifdef TARGET_LPC11U6X
DigitalOut rfsw(P0_23);
#else
DigitalOut rfsw(A4);    // for SX1276 arduino shield
#endif

void rfsw_callback()
{
    if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER)
        rfsw = 1;
    else
        rfsw = 0;
    //printf("rfsw:%d\r\n", rfsw.read());
}

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();
    if (radio.type == SX1272) {
        printf(" LowDataRateOptimize:%d\r\n", lora.RegModemConfig.sx1272bits.LowDataRateOptimize);
    }

    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);

    if (radio.type == SX1272) {
        lora.RegModemConfig.octet = radio.read_reg(REG_LR_MODEMCONFIG);
        printf("LowDataRateOptimize:%d ", lora.RegModemConfig.sx1272bits.LowDataRateOptimize);
    } else if (radio.type == SX1276) {
        lora.RegModemConfig3.octet = radio.read_reg(REG_LR_MODEMCONFIG3);
        printf("LowDataRateOptimize:%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);
    
    printf("\r\n");
    //printf("A %02x\r\n", radio.RegModemConfig2.octet);
}

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) {
        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\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\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()
{
    radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
    if (radio.RegPaConfig.bits.PaSelect) {
        float output_dBm = 17 - (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("version:0x%02x %.3fMHz ", 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);
    }    

}

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] = {
        0x3d, 0xfd, 0xf3, 0x80, 0x45, 0x0e, 0x8b, 0x8d, 0x3e, 0xd5, 0x89, 0x25, 0xaa, 0xd4, 0x23, 0x53
};
// 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(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 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];

    lora.start_tx(lora.RegPayloadLength);
}

void
service_radio()
{
    service_action_e act;
    
    if (radio.RegOpMode.bits.LongRangeMode) {

        act = lora.service();
    
        switch (act) {
            case SERVICE_READ_FIFO:
                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;

                            /* 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++;
                            
                            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;
                            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:
                if (app == APP_CHAT) {
                    lora.start_rx();
                } else if (per_en) {
                    per_timeout.attach(&per_cb, per_tx_delay); // start next TX
                }
                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)
    }
}

/*int get_kbd_str(char* buf, int size)
{
    char c;
    int i;
    static int prev_len;
    
    for (i = 0;;) {
        if (pc.readable()) {
            c = pc.getc();
            if (c == 8 && 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
                return -1;
            } else if (i < size) {
                buf[i++] = c;
                pc.putc(c);
            }
        } else {
            service_radio();
        }
    } // ...for()
}*/

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

void
console()
{
    //int len, i;
    int i, n;
    uint32_t ui;
    uint8_t a, d;
    static uint16_t fsk_tx_length;
    
    service_radio();
        
    //len = get_kbd_str(pcbuf, sizeof(pcbuf));
    if (pcbuf_len < 0) {
        printf("abort\r\n");
        per_en = false;
        pcbuf_len = 0;
        return;
    }
    if (pcbuf_len == 0)
        return;
    
    printf("\r\n");
    if (pcbuf_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", radio.RegModemConfig2.octet);
                    lora.RegModemConfig2.sx1276bits.TxContinuousMode ^= 1;   // same for sx1272 and sx1276
                    //printf("b %02x\r\n", radio.RegModemConfig2.octet);
                    radio.write_reg(REG_LR_MODEMCONFIG2, lora.RegModemConfig2.octet);
                    lora.RegModemConfig2.octet = radio.read_reg(REG_LR_MODEMCONFIG);
                    //printf("c %02x\r\n", radio.RegModemConfig2.octet);
                    lora_printTxContinuousMode();
                    printf("\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("L           toggle LongRangeMode/FSK\r\n");
                printf("i           radio_init\r\n");
                printf("h           hw_reset\r\n");
                printf("tx[%%d]    transmit (optional packet length)\r\n");
                printf("rx          receive\r\n");   
                printf("C           toggle crcOn\r\n");
                printf("op[%%d]    get/set output power\r\n");
                printf("bgr[%%d]        get/set prog_txdac BGR bias for TXDAC (7=+20dBm)\r\n"); 
                printf("ocp[%%d]     get/set over-current protection (mA)\r\n");
                printf("d[0-5]      change DIO pin assignment\r\n");
                printf("frf[%%f}   get/set operating frequency (MHz)\r\n");
                printf("r %%x        read radio register (addr)\r\n");
                printf("w %%x %%x     write radio register (addr data)\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");               
                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("pre[%%d}    FSK get-set TX preamble length\r\n");
                }
                break;
            case ',':
                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;
                    lora.start_tx(lora.RegPayloadLength);
                }
            } 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);
                }
            }
        } 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] == 'r' && pcbuf[1] == 'x') { // RX
            if (per_en) {
                PacketNormalCnt = 0;
                PacketRxSequencePrev = -1;
                PacketPerKoCnt = 0;
                PacketPerOkCnt = 0;                
                //dio3.rise(&dio3_cb);
            }        
            if (radio.RegOpMode.bits.LongRangeMode)
                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, &n);
            radio.write_reg(i, n);
            printf("%02x: %02x\r\n", i, radio.read_reg(i));
        }
#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] == '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));
            /* increase OCP threshold to allow more power */
            radio.RegOcp.octet = radio.read_reg(REG_OCP);
            if (radio.RegOcp.bits.OcpTrim < 16) {
                radio.RegOcp.bits.OcpTrim = 16;
                radio.write_reg(REG_OCP, radio.RegOcp.octet);
            }                    
        } else if (pcbuf[0] == 'o' && pcbuf[1] == 'c' && pcbuf[2] == 'p') {
            if (pcbuf[3] >= '0' && pcbuf[3] <= '9') {
                sscanf(pcbuf+3, "%d", &i);
                if (i < 130)
                    radio.RegOcp.bits.OcpTrim = (i - 45) / 5;
                else
                    radio.RegOcp.bits.OcpTrim = (i + 30) / 10;
                radio.write_reg(REG_OCP, radio.RegOcp.octet);
            }            
            radio.RegOcp.octet = radio.read_reg(REG_OCP);
            if (radio.RegOcp.bits.OcpTrim < 16)
                i = 45 + (5 * radio.RegOcp.bits.OcpTrim);
            else if (radio.RegOcp.bits.OcpTrim < 28)
                i = (10 * radio.RegOcp.bits.OcpTrim) - 30;
            else
                i = 240;
            printf("Ocp: %dmA\r\n", i);                  
        } 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] == '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] == '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] == 's' && !radio.RegOpMode.bits.LongRangeMode) {
            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] == '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+3, "%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' && !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') {
            if (pcbuf[2] >= '0' && pcbuf[2] <= '9') {
                sscanf(pcbuf+2, "%d", &i);
                if (radio.RegOpMode.bits.LongRangeMode) {
                    lora.RegPayloadLength = i;
                    radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
                } else {
                    fsk.RegPktConfig2.bits.PayloadLength = i;
                    radio.write_u16(REG_FSK_PACKETCONFIG2, fsk.RegPktConfig2.word);
                }
            }
            if (radio.RegOpMode.bits.LongRangeMode) {
                lora.RegPayloadLength = radio.read_reg(REG_LR_PAYLOADLENGTH);
                printf("PayloadLength:%d\r\n", lora.RegPayloadLength);
            } else {
                printf("PayloadLength:%d\r\n", fsk_get_PayloadLength());
            }
        } else if (pcbuf[0] == 'l' && pcbuf[1] == 'd') {
            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] >= '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] == 's' && pcbuf[1] == 't' && pcbuf[2] == 'b') {
            radio.set_opmode(RF_OPMODE_STANDBY);
        } else if (pcbuf[0] == 's' && pcbuf[1] == 'l' && pcbuf[2] == 'e') {
            radio.set_opmode(RF_OPMODE_SLEEP);
        } else if (pcbuf[0] == 'c' && pcbuf[1] == 'h' && pcbuf[2] == 'a') {
            app = APP_CHAT;
            lora.start_rx();
            printf("chat start\r\n");
        }           
    }
    
    pcbuf_len = 0;
    printf("> ");
    fflush(stdout);
        
}

void rx_callback()
{
    static uint8_t pcbuf_idx = 0;
    static uint8_t prev_len = 0;;
    char c = pc.getc();
    if (c == 8) {
        if (pcbuf_idx > 0) {
            pc.putc(8);
            pc.putc(' ');
            pc.putc(8);
            pcbuf_idx--;
        }
    } else if (c == 3) {    // ctrl-C
        pcbuf_len = -1;
    } else if (c == '\r') {
        if (pcbuf_idx == 0) {
            pcbuf_len = prev_len;
            //return prev_len;
        } else {
            pcbuf[pcbuf_idx] = 0;
            prev_len = pcbuf_idx;
            pcbuf_idx = 0;
            //return prev_len;
            pcbuf_len = prev_len;
        }
    } else if (pcbuf_idx < sizeof(pcbuf)) {
        pcbuf[pcbuf_idx++] = c;
        pc.putc(c);
    }
}

int main()
{    
#if defined(TARGET_NUCLEO_L152RE) && defined(USE_DEBUGGER)
    DBGMCU_Config(DBGMCU_SLEEP,   ENABLE);
    DBGMCU_Config(DBGMCU_STOP,    ENABLE);
    DBGMCU_Config(DBGMCU_STANDBY, ENABLE);
#endif

    pc.baud(57600);
    printf("\r\nmain()\r\n");
    
    pc.attach(rx_callback);
    
    radio.rf_switch.attach(rfsw_callback);
      

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

#if TARGET_NUCLEO_L152RE        
        //sleep();
#endif
    } // ...while(1)
}