#include "lorawan.h"
#include "tim.h"
#include "commands.h"
#define __STDC_FORMAT_MACROS
#include <inttypes.h>

#define TYPE_ABZ
RawSerial pc(USBTX, USBRX);
Timer timer;

//#if defined(TARGET_DISCO_L072CZ_LRWAN1)
#if defined(TARGET_NUCLEO_L073RZ) && defined(TYPE_ABZ)
    SPI spi(PA_7, PA_6, PB_3); // mosi, miso, sclk
    //           dio0, dio1,  nss,  spi,  rst
    SX127x radio(PB_4, PB_1, PA_15, spi, PC_0); // sx1276 arduino shield
    
    #define CRF1    PA_1
    #define CRF2    PC_2
    #define CRF3    PC_1
    DigitalOut Vctl1(CRF1);
    DigitalOut Vctl2(CRF2);
    DigitalOut Vctl3(CRF3);
    
    void rfsw_callback()
    {
        if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER) {
            Vctl1 = 0;        
            if (radio.RegPaConfig.bits.PaSelect) {
                Vctl2 = 0;
                Vctl3 = 1;                        
            } else {
                Vctl2 = 1;
                Vctl3 = 0;            
            }
        } else {
            if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER || radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER_SINGLE)
                Vctl1 = 1;
            else
                Vctl1 = 0;
            
            Vctl2 = 0;
            Vctl3 = 0;        
        }
    }    
#else
    SPI spi(D11, D12, D13); // mosi, miso, sclk
    //           dio0, dio1, nss, spi, rst
    SX127x radio(  D2,   D3, D10, spi, A0); // sx1276 arduino shield
    
    DigitalInOut rfsw(A4);
    
    void rfsw_callback()
    {
        if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER)
            rfsw = 1;
        else
            rfsw = 0;
    }    
#endif

char pcbuf[64];
int pcbuf_len;
uint8_t beacon_payload[4];
uint32_t tx_ms;

unsigned int skip_beacon_cnt;

SX127x_lora lora(radio);

float starting_bg_rssi;

#define N_SAMPLES       64
void channel_scan()
{
    int min_ch, ch;
    uint32_t hz = LORAMAC_FIRST_CHANNEL;
    int acc[LORA_MAX_NB_CHANNELS];

    radio.set_opmode(RF_OPMODE_STANDBY);
    
    for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
        int i;
        float MHz = (float)hz / 1e6;
        radio.set_frf_MHz(MHz);
        radio.set_opmode(RF_OPMODE_RECEIVER);
        acc[ch] = 0;
        for (i = 0; i < N_SAMPLES; i++) {
            int rssi = lora.get_current_rssi();
            acc[ch] += rssi;
            wait(0.01);
        }
        radio.set_opmode(RF_OPMODE_STANDBY);
        printf("ch%u: %f\r\n", ch, acc[ch] / (float)N_SAMPLES);
        hz += LORAMAC_STEPWIDTH_CHANNEL;
        radio.set_frf_MHz((float)hz/1e6);
    }

    int min = 0x7fffffff;
    min_ch = 0;
    for (ch = 0; ch < LORA_MAX_NB_CHANNELS; ch++) {
        if (acc[ch] < min) {
            min = acc[ch];
            min_ch = ch;
        }
    }
    hz = LORAMAC_FIRST_CHANNEL + (min_ch * LORAMAC_STEPWIDTH_CHANNEL);
    printf("using ch%u, %luhz\r\n", min_ch, hz);
    radio.set_frf_MHz((float)hz/1e6);
    
    starting_bg_rssi = acc[min_ch] / (float)N_SAMPLES;
}

void measure_ambient()
{
    int i, acc = 0;
    float bg_rssi;
    float diff;
    static unsigned cnt = 0;
    
    for (i = 0; i < N_SAMPLES; i++) {
        int rssi = lora.get_current_rssi();
        acc += rssi;
        wait(0.01);
    }
    bg_rssi = acc / (float)N_SAMPLES;
    diff = bg_rssi - starting_bg_rssi;
    printf("bg_rssi:%.1fdBm vs %1.fdBm, diff:%.1f, %d\r\n", bg_rssi, starting_bg_rssi, diff, cnt);
    if (diff > 10) {
        if (++cnt > 3) {
            /* find better channel */
            channel_scan();
            lora.start_rx(RF_OPMODE_RECEIVER);
            cnt = 0;
        }
    } else
        cnt = 0;
}

void init_radio()
{
    radio.set_opmode(RF_OPMODE_STANDBY);

    radio.RegPaConfig.bits.OutputPower = 15;
    radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
    lora.enable();
    lora.setBw_KHz(BANDWIDTH_KHZ);
    lora.setSf(LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);
    printf("using sf%u\r\n", LoRaWan::Datarates[LORAMAC_DEFAULT_DATARATE]);

    channel_scan();

    radio.write_reg(REG_LR_SYNC_BYTE, LORA_MAC_PUBLIC_SYNCWORD);
    radio.write_reg(REG_LR_RX_MAX_PAYLOADLENGTH, 255);
}

void printLoraIrqs(bool clear)
{
    printf("\r\nIrqFlags:");
    if (lora.RegIrqFlags.bits.CadDetected)
        printf("CadDetected ");
    if (lora.RegIrqFlags.bits.FhssChangeChannel) {
        printf("FhssChangeChannel:%d ", lora.RegHopChannel.bits.FhssPresentChannel);
    }
    if (lora.RegIrqFlags.bits.CadDone)
        printf("CadDone ");
    if (lora.RegIrqFlags.bits.TxDone)
        printf("TxDone-dio0:%d ", radio.dio0.read());
    if (lora.RegIrqFlags.bits.ValidHeader)
        printf("[42mValidHeader[0m ");
    if (lora.RegIrqFlags.bits.PayloadCrcError)
        printf("[41mPayloadCrcError[0m ");
    if (lora.RegIrqFlags.bits.RxDone)
        printf("[42mRxDone[0m ");  
    if (lora.RegIrqFlags.bits.RxTimeout)
        printf("RxTimeout ");

    printf("\r\n");

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

void printOpMode()
{
    radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
    switch (radio.RegOpMode.bits.Mode) {
        case RF_OPMODE_SLEEP: printf("[7msleep[0m"); break;
        case RF_OPMODE_STANDBY: printf("[7mstby[0m"); break;
        case RF_OPMODE_SYNTHESIZER_TX: printf("[33mfstx[0m"); break;
        case RF_OPMODE_TRANSMITTER: printf("[31mtx[0m"); break;
        case RF_OPMODE_SYNTHESIZER_RX: printf("[33mfsrx[0m"); break;
        case RF_OPMODE_RECEIVER: printf("[32mrx[0m"); break;
        case 6:
            if (radio.RegOpMode.bits.LongRangeMode)
                printf("[42mrxs[0m");
            else
                printf("-6-");
            break;  // todo: different lora/fsk
        case 7:
            if (radio.RegOpMode.bits.LongRangeMode)
                printf("[45mcad[0m");
            else
                printf("-7-");
            break;  // todo: different lora/fsk
    }
}

#define GPO_IDX     6
void decrypted_uplink(uint8_t* buf, uint8_t buflen, uint8_t port)
{
    if (port == SENSOR_PORT) {
        uint16_t a_1, a_3, lum;
        a_1 = buf[0] << 8;
        a_1 += buf[1];
        a_3 = buf[2] << 8;
        a_3 += buf[3];
        lum = buf[4] << 8;
        lum += buf[5];
        printf("SENSOR gpi:%u, gpo:%u, a1:%u, a3:%u, lum:%u\r\n", (buf[GPO_IDX] & 2) >> 1,  buf[GPO_IDX] & 1, a_1, a_3, lum);
    } else if (port == TEXT_PORT) {
        buf[buflen] = 0;
        printf("TEXT %s\r\n", buf);
    } else {
        int i;
        printf("port%u: ", port);
        for (i = 0; i < buflen; i++)
            printf("%02x ", buf[i]);
        printf("\r\n");
    }
}

EventQueue queue;

volatile bool get_tx_done;
volatile bool beacon_guard;
bool restore_tx_invert;
bool restore_header_mode;

void send_downlink()
{
    if (!beacon_guard && LoRaWan::do_downlink) {            
        radio.set_opmode(RF_OPMODE_STANDBY);
        radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
        lora.invert_tx(true);
        restore_tx_invert = true;
        lora.setRxPayloadCrcOn(false);
        lora.start_tx(lora.RegPayloadLength);
        LoRaWan::do_downlink = false;
        get_tx_done = true;
        tx_ms = timer.read_ms();
    }
}


void
service_radio()
{
    service_action_e act = lora.service();

    switch (act) {
        case SERVICE_ERROR:
            printf("SERVICE_ERROR\r\n");
        case SERVICE_TX_DONE:
        case SERVICE_NONE:
            break;
        case SERVICE_READ_FIFO:
            LoRaWan::rx_slot = tim_get_current_slot();
            LoRaWan::rx_ms = timer.read_ms();
            printf("%lu, %.1fdB, %ddBm, ", time(NULL), lora.RegPktSnrValue / 4.0, lora.get_pkt_rssi());
            radio.set_opmode(RF_OPMODE_STANDBY);                       
            LoRaWan::parse_receive();
            if (!LoRaWan::do_downlink) {
                /* if not sending downlink, start receiver now */
                lora.start_rx(RF_OPMODE_RECEIVER);
            }
            
            break;
    } // ..switch (act)
}

volatile float prev_beacon_send_at;
volatile float beacon_send_at;
volatile float beacon_loaded_at;

volatile bool beacon_loaded;
void
send_beacon()
{
    prev_beacon_send_at = beacon_send_at;
    beacon_send_at = timer.read();

    if (!beacon_loaded)
        return;

    radio.set_opmode(RF_OPMODE_TRANSMITTER);
    beacon_loaded = false;
    get_tx_done = true;
    tx_ms = timer.read_ms();
}

static uint16_t beacon_crc( uint8_t *buffer, uint16_t length )
{
    // The CRC calculation follows CCITT
    const uint16_t polynom = 0x1021;
    // CRC initial value
    uint16_t crc = 0x0000;

    if( buffer == NULL )
    {
        return 0;
    }

    for( uint16_t i = 0; i < length; ++i )
    {
        crc ^= ( uint16_t ) buffer[i] << 8;
        for( uint16_t j = 0; j < 8; ++j )
        {
            crc = ( crc & 0x8000 ) ? ( crc << 1 ) ^ polynom : ( crc << 1 );
        }
    }

    return crc;
}

void
_load_beacon()
{
    uint16_t crc;
    radio.set_opmode(RF_OPMODE_STANDBY);
    // clear any flags which might have been set during beacon_guard
    radio.write_reg(REG_LR_IRQFLAGS, 0xff);
    lora.RegPayloadLength = BEACON_SIZE;
    radio.write_reg(REG_LR_PAYLOADLENGTH, lora.RegPayloadLength);
    lora.setHeaderMode(true);
    restore_header_mode = true;

    if (skip_beacon_cnt > 0) {
        //printf("skip_beacon_cnt:%d\r\n", skip_beacon_cnt);
        lora.invert_tx(true);
        restore_tx_invert = true;
        skip_beacon_cnt--;
    }

    radio.tx_buf[0] = beacon_payload[0];
    radio.tx_buf[1] = beacon_payload[1];
    radio.tx_buf[2] = beacon_payload[2];
    radio.tx_buf[3] = beacon_payload[3];
    beacon_payload[0] = CMD_NONE;

    crc = beacon_crc(radio.tx_buf, 4);
    radio.tx_buf[4] = crc & 0xff;
    radio.tx_buf[5] = crc >> 8;

    // DIO0 to TxDone
    radio.RegDioMapping1.bits.Dio0Mapping = 1;
    radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);
    
    // set FifoPtrAddr to FifoTxPtrBase
    radio.write_reg(REG_LR_FIFOADDRPTR, radio.read_reg(REG_LR_FIFOTXBASEADDR));
    
    // write PayloadLength bytes to fifo
    lora.write_fifo(lora.RegPayloadLength);

    // prepare for tx to occur in send_beacon()
    radio.set_opmode(RF_OPMODE_SYNTHESIZER_TX);
    beacon_loaded = true;

    beacon_loaded_at = timer.read();
}

void load_beacon()
{
    beacon_guard = true;
    queue.call_in(200, _load_beacon);
}

void get_time_till_beacon()
{
    uint16_t slots = tim_get_current_slot();
    printf("slots:%u\r\n", slots);
}

void rx_isr()
{
    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;
        } else {
            pcbuf[pcbuf_idx] = 0;   // null terminate
            prev_len = pcbuf_idx;
            pcbuf_idx = 0;
            pcbuf_len = prev_len;
        }
    } else if (pcbuf_idx < sizeof(pcbuf)) {
        pcbuf[pcbuf_idx++] = c;
        pc.putc(c);
    }
}

void cmd_skip_beacon(uint8_t idx)
{
    if (pcbuf[idx] >= '0' && pcbuf[idx] <= '9') {
        sscanf(pcbuf+idx, "%u", &skip_beacon_cnt);
    }
    printf("skip_beacon_cnt:%u\r\n", skip_beacon_cnt);
}

void cmd_list_motes(uint8_t idx)
{
    int i;
    for (i = 0; i < N_MOTES; i++) {
        if (motes[i].dev_addr != DEVADDR_NONE) {
            LoRaWan::print_octets_rev("", motes[i].dev_eui, LORA_EUI_LENGTH);
            printf("    %" PRIx32 "\r\n", motes[i].dev_addr);
        }
    }
}

void
cmd_beacon_payload(uint8_t idx)
{
    uint32_t i;
    uint32_t* ptr;
    sscanf(pcbuf+idx, "%" PRIx32, &i);
    printf("beacon_payload:%08" PRIx32 "\r\n", i);
    ptr = (uint32_t*)beacon_payload;
    *ptr = i;
}

void
cmd_send_downlink(uint8_t idx)
{
    ota_mote_t* mote = NULL;
    int i;
    unsigned int dev_addr;
    sscanf(pcbuf+idx, "%x", &dev_addr);
    for (i = 0; i < N_MOTES; i++) {
        if (motes[i].dev_addr == dev_addr) {
            break;
        }
    }
    if (i == N_MOTES) {
        printf("mote %x not found\r\n", dev_addr);
        return;
    }
    mote = &motes[i];

    while (pcbuf[idx] != ' ') {
        if (pcbuf[++idx] == 0) {
            printf("hit end\r\n");
            return;
        }
    }
    idx++;    // step past space

    mote->user_downlink_length = 0;
    while (pcbuf[idx] > ' ') {
        int o;
        sscanf(pcbuf+idx, "%02x", &o);
        LoRaWan::user_downlink[mote->user_downlink_length++] = o;
        idx += 2;
    }

    printf("%u bytes scheduled for %" PRIx32 "\r\n", mote->user_downlink_length, mote->dev_addr);
}

void cmd_rx_restart(uint8_t idx)
{
    /*radio.set_opmode(RF_OPMODE_STANDBY);
    printf("standby\r\n");*/
    radio.set_opmode(RF_OPMODE_SLEEP);
    printf("sleep\r\n");
    wait(0.05);
    radio.set_opmode(RF_OPMODE_RECEIVER);
    printf("receive\r\n");
}

void cmd_beacon_gpo(uint8_t idx)
{
    int gpo;
    sscanf(pcbuf+idx, "%d", &gpo);
    beacon_payload[0] = CMD_GPIO_OUT;
    beacon_payload[1] = gpo;   
    printf("beacon gpo: %d\r\n", gpo);
}

void cmd_beacon_rgb(uint8_t idx)
{
    int r, g ,b;
    sscanf(pcbuf+idx, "%d %d %d", &r, &g, &b);
    beacon_payload[0] = CMD_LED_RGB;
    beacon_payload[1] = r;
    beacon_payload[2] = g;
    beacon_payload[3] = b;
    printf("beacon rgb: %d %d %d\r\n", r, g, b);
}

void cmd_downlink_rgb(uint8_t idx)
{
    ota_mote_t* mote = NULL;
    int i, r, g ,b;
    unsigned int dev_addr;
    sscanf(pcbuf+idx, "%x %d %d %d", &dev_addr, &r, &g, &b);
    for (i = 0; i < N_MOTES; i++) {
        if (motes[i].dev_addr == dev_addr) {
            break;
        }
    }
    if (i == N_MOTES) {
        printf("mote %x not found\r\n", dev_addr);
        return;
    }
    mote = &motes[i];

    mote->user_downlink_length = 0;
    LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_LED_RGB;
    LoRaWan::user_downlink[mote->user_downlink_length++] = r;
    LoRaWan::user_downlink[mote->user_downlink_length++] = g;
    LoRaWan::user_downlink[mote->user_downlink_length++] = b;
    
    printf("rgb %d %d %d to mote %" PRIx32 "\r\n", r, g, b, mote->dev_addr);
}

void cmd_downlink_gpo(uint8_t idx)
{
    ota_mote_t* mote = NULL;
    int i, gpo;
    unsigned int dev_addr;
    sscanf(pcbuf+idx, "%x %d", &dev_addr, &gpo);
    for (i = 0; i < N_MOTES; i++) {
        if (motes[i].dev_addr == dev_addr) {
            break;
        }
    }
    if (i == N_MOTES) {
        printf("mote %x not found\r\n", dev_addr);
        return;
    }
    mote = &motes[i];

    mote->user_downlink_length = 0;
    LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_GPIO_OUT;
    LoRaWan::user_downlink[mote->user_downlink_length++] = gpo;
    
    printf("gpo %d to mote %" PRIx32 "\r\n", gpo, mote->dev_addr);    
}

void cmd_status(uint8_t idx)
{
    radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
    printf("%.3fMHz sf%ubw%u ", radio.get_frf_MHz(), lora.getSf(), lora.getBw());
    printOpMode();
    if (!radio.RegOpMode.bits.LongRangeMode) {
        printf("FSK\r\n");
        return;
    }

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

    printf(" do_downlink:%u get_tx_done:%u, ", LoRaWan::do_downlink, get_tx_done);
    lora.RegTest33.octet = radio.read_reg(REG_LR_TEST33);     // invert_i_q
    lora.RegDriftInvert.octet = radio.read_reg(REG_LR_DRIFT_INVERT);
    printf("modemstat:%02x, rxinv:%x,%x\r\n", radio.read_reg(REG_LR_MODEMSTAT), lora.RegTest33.octet, lora.RegDriftInvert.octet);
    radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
    printf("\r\nskip_beacon_cnt:%u, currently:%u dio0map:%u\r\n", skip_beacon_cnt, tim_get_current_slot(), radio.RegDioMapping1.bits.Dio0Mapping);
    printf("FIfoAddrPtr:%02x RxBase:%02x\r\n", radio.read_reg(REG_LR_FIFOADDRPTR), radio.read_reg(REG_LR_FIFORXBASEADDR));
}

void cmd_op(uint8_t idx)
{
    int i, dbm;
    RegPdsTrim1_t pds_trim;
    uint8_t adr;
    if (radio.type == SX1276)
        adr = REG_PDSTRIM1_SX1276;
    else
        adr = REG_PDSTRIM1_SX1272;
       
    pds_trim.octet = radio.read_reg(adr);   
                
    if (pcbuf[idx] >= '0' && (pcbuf[idx] <= '9' || pcbuf[idx] == '-')) {
        sscanf(pcbuf+idx, "%d", &i);
        if (radio.RegPaConfig.bits.PaSelect) {
            /* PABOOST used: +2dbm to +17, or +20 */
            if (i == 20) {
                printf("+20dBm PADAC bias\r\n");
                i -= 3;
                pds_trim.bits.prog_txdac = 7;
                radio.write_reg(adr, pds_trim.octet);
            }
            if (i > 1)
                    radio.RegPaConfig.bits.OutputPower = i - 2;
        } else {
            /* RFO used: -1 to +14dbm */
            if (i < 15)
                radio.RegPaConfig.bits.OutputPower = i + 1;
        }
        radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
    }
    radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
    if (radio.RegPaConfig.bits.PaSelect) {
        printf("PA_BOOST ");
        dbm = radio.RegPaConfig.bits.OutputPower + pds_trim.bits.prog_txdac - 2;
    } else {
        printf("RFO ");
        dbm = radio.RegPaConfig.bits.OutputPower - 1;
    }
    printf("OutputPower:%ddBm\r\n", dbm);
}

void cmd_set_time(uint8_t idx)
{
    set_time(0);
    printf("time:%" PRIu32 "\r\n", time(NULL));
}


void cmd_pwm(uint8_t idx)
{
    ota_mote_t* mote;
    int i;
    unsigned dev_addr, p, d;

    if (sscanf(pcbuf+idx, "%x %u %u", &dev_addr, &p, &d) != 3) {
        printf("parse fail\r\n");
        return;
    }
    for (i = 0; i < N_MOTES; i++) {
        if (motes[i].dev_addr == dev_addr) {
            break;
        }
    }
    if (i == N_MOTES) {
        printf("mote %x not found\r\n", dev_addr);
        return;
    }

    mote = &motes[i];

    mote->user_downlink_length = 0;
    LoRaWan::user_downlink[mote->user_downlink_length++] = CMD_PWM;
    LoRaWan::user_downlink[mote->user_downlink_length++] = p;
    LoRaWan::user_downlink[mote->user_downlink_length++] = d;
    
    printf("period:%u duty:%u to mote %" PRIx32 "\r\n", p, d, mote->dev_addr);
}

void cmd_beacon_pwm(uint8_t idx)
{
    unsigned p, d;
    if (sscanf(pcbuf+idx, "%u %u", &p, &d) != 2) {
        printf("parse fail\r\n");
        return;
    }

    beacon_payload[0] = CMD_PWM;
    beacon_payload[1] = p;
    beacon_payload[2] = d;
    
    printf("period:%u duty:%u\r\n", p, d);
}

void cmd_help(uint8_t);

typedef struct {
    const char* const cmd;
    void (*handler)(uint8_t args_at);
    const char* const arg_descr;
    const char* const description;
} menu_item_t;

const menu_item_t menu_items[] = 
{   /* after first character, command names must be [A-Za-z] */
    { "?", cmd_help, "","show available commands"}, 
    { ".", cmd_status, "","read status"}, 
    { "b", cmd_beacon_payload, "<%x>","set beacon payload"}, 
    { "sb", cmd_skip_beacon, "<%d>","skip beacons"}, 
    { "list", cmd_list_motes, "","list active motes"}, 
    { "dl", cmd_send_downlink, "[%x %s]","send downlink <mote-hex-dev-addr> <hex-payload>"}, 
    { "rxr", cmd_rx_restart, "", "restart RX"},
    { "brgb", cmd_beacon_rgb, "%u %u %u", "load RGB command into next beacon" },
    { "rgb", cmd_downlink_rgb, "%x %u %u %u", "load RGB command to mote"},
    { "bgpo", cmd_beacon_gpo, "%d", "load output pin command into next beacon"},
    { "gpo", cmd_downlink_gpo, "%x %d", "load output pin command to mote"},
    { "op", cmd_op, "<dBm>","(TX) get/set TX power"},  
    { "tz", cmd_set_time, "", "set seconds to zero"},   
    { "p", cmd_pwm, "%x %u %u", "send pwm period, duty to dev_addr"},
    { "bp", cmd_beacon_pwm, "%u %u", "send pwm period, duty on beacon"},
    { NULL, NULL, NULL, NULL }
};

void cmd_help(uint8_t args_at)
{
    int i;
    
    for (i = 0; menu_items[i].cmd != NULL ; i++) {
        printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description);
    }
    
}

void
console()
{
    int i;
    uint8_t user_cmd_len;
    
    if (pcbuf_len < 0) {    // ctrl-C
        //printf("abort\r\n");
        return;
    }
    if (pcbuf_len == 0)
        return;
        
    printf("\r\n");
        
    /* get end of user-entered command */
    user_cmd_len = 1;   // first character can be any character
    for (i = 1; i <= pcbuf_len; i++) {
        if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') {
            user_cmd_len = i;
            break;
        }
    }

    for (i = 0; menu_items[i].cmd != NULL ; i++) {
        int mi_len = strlen(menu_items[i].cmd);

        if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) {
            while (pcbuf[mi_len] == ' ')   // skip past spaces
                mi_len++;
            menu_items[i].handler(mi_len);
            break;
        }
    }
   
    pcbuf_len = 0;
    printf("> ");
    fflush(stdout); 
}

int main()
{
    Thread eventThread;
    pc.baud(38400);
    printf("\r\nreset %f\r\n", radio.get_frf_MHz());
    set_time(0);

    radio.hw_reset();
    printf("%fMHz\r\n", radio.get_frf_MHz());

//#ifndef TARGET_DISCO_L072CZ_LRWAN1
#ifndef TYPE_ABZ
    rfsw.input();
    if (rfsw.read()) {
        printf("LAS\r\n");
        /* LAS HF=PA_BOOST  LF=RFO */
        radio.RegPaConfig.bits.PaSelect = 1;
    } else {
        printf("MAS\r\n");
        radio.RegPaConfig.bits.PaSelect = 0;
    }
    rfsw.output();
#endif /* !TARGET_DISCO_L072CZ_LRWAN1 */

    radio.rf_switch = rfsw_callback; 

    init_radio();

    lora.start_rx(RF_OPMODE_RECEIVER);

    eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
    LoRaWan::init();

    timer.start();
    tim_init();
    
    pc.attach(&rx_isr);


    for (;;) {
        console();
            
        if (radio.dio0) {
            if (get_tx_done) {
                get_tx_done = false;
                lora.RegIrqFlags.octet = 0;
                lora.RegIrqFlags.bits.TxDone = 1;
                radio.write_reg(REG_LR_IRQFLAGS, lora.RegIrqFlags.octet);        
            
                if (restore_tx_invert) {
                    lora.invert_tx(false);
                    restore_tx_invert = false;
                }

                if (restore_header_mode) {
                    lora.setHeaderMode(false);
                    restore_header_mode = false;
                }
                
                lora.start_rx(RF_OPMODE_RECEIVER);

                if (beacon_guard) {   // beacon done transmitting
                    measure_ambient();
                    beacon_guard = false;
                }
            } else {
                 if (!beacon_guard)
                    service_radio();
            }
        } else if (get_tx_done) {
            /* dio0 not yet asserted */
            uint32_t since = timer.read_ms() - tx_ms;
            if (since > 4000) {
                radio.RegOpMode.octet = radio.read_reg(REG_OPMODE);
                if (radio.RegOpMode.bits.Mode == RF_OPMODE_STANDBY) {
                    /* done transmitting, but dio0 not asserted */
                    printf("txDone but no dio0\r\n");
                    radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1);
                    if (radio.RegDioMapping1.bits.Dio0Mapping != 1) {
                        printf("dio0mapping-fail\r\n");
                        radio.RegDioMapping1.bits.Dio0Mapping = 1;
                        radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet);                        
                    }                   
                }
            }
        }
        
        if (LoRaWan::do_downlink && !beacon_guard) {  
            uint32_t since = timer.read_ms() - LoRaWan::rx_ms;
            if (since > 110)
                printf("since-tx:%lu\r\n", since);
            if (since > 500) {
                LoRaWan::do_downlink = false;
                printf("stalled-tx\r\n");
                cmd_rx_restart(0);
            }
        }
        
    } // ..for(;;)
}
