Text menu driven ANSI/VT100 console test utility for LoRa transceivers
radio chip selection
Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
if you're using LR1110, then import LR1110 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.
If you're using Type1SJ select target DISCO_L072CZ_LRWAN1
and import sx126x driver into your program.
This is VT100 text-based menu driven test program for SX12xx transceiver devices.
Serial console is divided into horizontally into top half and bottom half.
The bottom half serves as scrolling area to log activity.
The top half serves as menu, to configure the radio.
For all devices, the serial console operates at 115200 8N1, and requires terminal with ANSI-VT100 capability, such as putty/teraterm/minicom etc.
Use program only with keyboard up/down/left/right keys. Enter to change an item, or number for value item. Some items are single bit, requiring only enter key to toggle. Others with fixed choices give a drop-down menu.
radio_sx126x.cpp
- Committer:
- Wayne Roberts
- Date:
- 2018-08-20
- Revision:
- 1:0817a150122b
- Child:
- 2:ea9245bb1c53
File content as of revision 1:0817a150122b:
#include "radio.h" #ifdef SX126x_H #ifdef TARGET_FF_ARDUINO SPI spi(D11, D12, D13); // mosi, miso, sclk //spi, nss, busy, dio1 SX126x Radio::radio(spi, D7, D3, D5); DigitalOut antswPower(D8); AnalogIn xtalSel(A3); DigitalIn chipType(A2); #define CHIP_TYPE_SX1262 0 #define CHIP_TYPE_SX1261 1 #define PINNAME_NRST A0 #define LED_ON 1 #define LED_OFF 0 DigitalOut tx_led(A4); DigitalOut rx_led(A5); void Radio::chipModeChange() { if (radio.chipMode == CHIPMODE_NONE) { tx_led = LED_OFF; rx_led = LED_OFF; } else if (radio.chipMode == CHIPMODE_TX) { tx_led = LED_ON; rx_led = LED_OFF; } else if (radio.chipMode == CHIPMODE_RX) { tx_led = LED_OFF; rx_led = LED_ON; } } #endif /* TARGET_FF_ARDUINO */ const char* const Radio::chipNum_str = "SX126x"; ModulationParams_t Radio::mpFSK, Radio::mpLORA; PacketParams_t Radio::ppFSK, Radio::ppLORA; const RadioEvents_t* Radio::RadioEvents; LowPowerTimer Radio::lpt; uint8_t Radio::pktType; uint8_t Radio::bw_idx; const char* opModes[] = { "SLEEP ", // 0 "STBY_RC ", // 1 "STBY_XOSC", // 2 "FS ", // 3 "RX ", // 4 "TX " // 5 }; void Radio::readChip() { bwSel_t bwSel; shapeCfg_t shapeCfg; unsigned d = radio.readReg(REG_ADDR_BITRATE, 3); pc.printf("%06x:%u->", d ,d); pc.printf("bitrate %ubps\r\n", (32 * XTAL_FREQ_HZ) / d); mpFSK.gfsk.bitrateHi = d >> 16; mpFSK.gfsk.bitrateMid = d >> 8; mpFSK.gfsk.bitrateLo = d; d = radio.readReg(REG_ADDR_FREQDEV, 3); pc.printf("fdev %fKHz\r\n", d / KHZ_TO_FRF); mpFSK.gfsk.fdevHi = d >> 16; mpFSK.gfsk.fdevMid = d >> 8; mpFSK.gfsk.fdevLo = d; shapeCfg.octet = radio.readReg(REG_ADDR_SHAPECFG, 1); mpFSK.gfsk.PulseShape = shapeCfg.octet; bwSel.octet = radio.readReg(REG_ADDR_BWSEL, 1); // GFSK_RX_BW_* pc.printf("bwsSel:%02x\r\n", bwSel.octet); mpFSK.gfsk.bandwidth = bwSel.octet; { unsigned n = radio.readReg(REG_ADDR_FSK_PREAMBLE_TXLEN , 2); ppFSK.gfsk.PreambleLengthHi = n << 8; // param1 ppFSK.gfsk.PreambleLengthLo = n;// param2 } { pktCtrl1_t pktCtrl1; pktCtrl1.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL1, 1); ppFSK.gfsk.PreambleDetectorLength = pktCtrl1.octet & 0x07; // param3 } ppFSK.gfsk.SyncWordLength = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1);// param4 ppFSK.gfsk.AddrComp = radio.readReg(REG_ADDR_NODEADDRCOMP, 1);// param5 { pktCtrl0_t pktCtrl0; pktCtrl0.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL0, 1); ppFSK.gfsk.PacketType = pktCtrl0.bits.pkt_len_format; // param6 } ppFSK.gfsk.PayloadLength = radio.readReg(REG_ADDR_FSK_PAYLOAD_LEN, 1);// param7 { pktCtrl2_t pktCtrl2; pktCtrl2.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL2, 1); ppFSK.gfsk.CRCType = pktCtrl2.octet & 0x7; // param8 ppFSK.gfsk.Whitening = pktCtrl2.bits.whit_enable; // param9 } /*******************************/ { loraConfig0_t conf0; conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1); pc.printf("LoRa bw%u sf%u ", conf0.bits.modem_bw, conf0.bits.modem_sf); mpLORA.lora.spreadingFactor = conf0.bits.modem_sf; mpLORA.lora.bandwidth = conf0.bits.modem_bw; } { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); mpLORA.lora.LowDatarateOptimize = conf1.bits.ppm_offset; ppLORA.lora.HeaderType = conf1.bits.implicit_header; ppLORA.lora.InvertIQ = conf1.bits.rx_invert_iq; mpLORA.lora.codingRate = conf1.bits.tx_coding_rate; } { loraConfig2_t conf2; conf2.octet = radio.readReg(REG_ADDR_LORA_CONFIG2, 1); ppLORA.lora.CRCType = conf2.bits.tx_payload_crc16_en; } { uint32_t val = radio.readReg(REG_ADDR_LORA_PREAMBLE_SYMBNB, 2); ppLORA.lora.PreambleLengthHi = val >> 8; ppLORA.lora.PreambleLengthLo = val; } { AnaCtrl6_t AnaCtrl6; AnaCtrl7_t AnaCtrl7; PaCtrl1b_t PaCtrl1b; AnaCtrl6.octet = radio.readReg(REG_ADDR_ANACTRL6, 1); pa_config_buf[0] = AnaCtrl6.bits.pa_dctrim_select_ana; // paDutyCycle AnaCtrl7.octet = radio.readReg(REG_ADDR_ANACTRL7, 1); pa_config_buf[1] = AnaCtrl7.bits.pa_hp_sel_ana; // hpMax PaCtrl1b.octet = radio.readReg(REG_ADDR_PA_CTRL1B, 1); pa_config_buf[2] = PaCtrl1b.bits.tx_mode_bat; // deviceSel pa_config_buf[3] = 1; // paLut } } void Radio::hw_reset() { radio.hw_reset(PINNAME_NRST); } void Radio::clearIrqFlags() { uint8_t buf[2]; buf[0] = 0x03; buf[1] = 0xff; radio.xfer(OPCODE_CLEAR_IRQ_STATUS, 2, 0, buf); } uint8_t Radio::get_payload_length() { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_GFSK) { ppFSK.gfsk.PayloadLength = radio.readReg(REG_ADDR_FSK_PAYLOAD_LEN, 1); return ppFSK.gfsk.PayloadLength; } else if (pktType == PACKET_TYPE_LORA) { ppLORA.lora.PayloadLength = radio.readReg(REG_ADDR_LORA_TXPKTLEN, 1); return ppLORA.lora.PayloadLength; } else return 0; } const char* const Radio::opmode_status_strs[] = { "<0> ", // 0 "RFU ", // 1 "STBY_RC ", // 2 "STBY_XOSC", // 3 "FS ", // 4 "RX ", // 5 "TX ", // 6 "<7> ", // 7 NULL }; const char* const Radio::opmode_select_strs[] = { "SLEEP ", // 0 "STDBY_RC ", // 1 "STDBY_XOSC", // 2 "FS ", // 3 "RX ", // 4 "TX ", // 5 NULL }; unsigned Radio::opmode_read_cb(bool forWriting) { status_t status; radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet); if (forWriting) { /* translate opmode_status_strs to opmode_select_strs */ switch (status.bits.chipMode) { case 2: return 1; // STBY_RC case 3: return 2; // STBY_XOSC case 4: return 3; // FS case 5: return 4; // RX case 6: return 5; // TX default: return 0; } } else return status.bits.chipMode; } menuMode_e Radio::opmode_write_cb(unsigned sel) { switch (sel) { case 0: antswPower = 0; radio.setSleep(true, false); break; case 1: antswPower = 0; radio.setStandby(STBY_RC); break; case 2: antswPower = 0; radio.setStandby(STBY_XOSC); break; case 3: antswPower = 0; radio.setFS(); break; case 4: antswPower = 1; radio.start_rx(0); break; case 5: antswPower = 1; { uint8_t buf[3]; buf[0] = 0; buf[0] = 0; buf[1] = 0; radio.xfer(OPCODE_SET_TX, 3, 0, buf); } break; } return MENUMODE_REDRAW; } void Radio::setFS() { radio.setFS(); } const char* const Radio::pktType_strs[] = { "GFSK ", "LORA ", NULL }; unsigned Radio::pktType_read_cb(bool fw) { return radio.getPacketType(); } menuMode_e Radio::pktType_write_cb(unsigned idx) { radio.setPacketType(idx); return MENUMODE_REINIT_MENU; } void Radio::tx_carrier() { radio.xfer(OPCODE_SET_TX_CARRIER, 0, 0, NULL); } void Radio::tx_preamble() { radio.xfer(OPCODE_SET_TX_PREAMBLE, 0, 0, NULL); } void Radio::txPkt() { uint8_t txlen = get_payload_length(); radio.setBufferBase(0, 0); { uint8_t buf[8]; IrqFlags_t irqEnable; irqEnable.word = 0; irqEnable.bits.TxDone = 1; irqEnable.bits.Timeout = 1; buf[0] = irqEnable.word >> 8; // enable bits buf[1] = irqEnable.word; // enable bits buf[2] = irqEnable.word >> 8; // dio1 buf[3] = irqEnable.word; // dio1 buf[4] = 0; // dio2 buf[5] = 0; // dio2 buf[6] = 0; // dio3 buf[7] = 0; // dio3 radio.xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, 0, buf); } radio.start_tx(txlen); } uint8_t Radio::tx_param_buf[2]; uint8_t Radio::pa_config_buf[4]; void Radio::tx_dbm_print() { PwrCtrl_t PwrCtrl; PaCtrl1b_t PaCtrl1b; PwrCtrl.octet = radio.readReg(REG_ADDR_PWR_CTRL, 1); PaCtrl1b.octet = radio.readReg(REG_ADDR_PA_CTRL1B, 1); pa_config_buf[2] = PaCtrl1b.bits.tx_mode_bat; // deviceSel if (PaCtrl1b.bits.tx_mode_bat) pc.printf("%d", PwrCtrl.bits.tx_pwr - 17); else pc.printf("%d", PwrCtrl.bits.tx_pwr - 9); } bool Radio::tx_dbm_write(const char* str) { int dbm; sscanf(str, "%d", &dbm); tx_param_buf[0] = dbm; radio.xfer(OPCODE_SET_TX_PARAMS, 2, 0, tx_param_buf); return false; } const char* Radio::tx_ramp_strs[] = { "10 ", // 0 "20 ", // 1 "80 ", // 2 "80 ", // 3 "200 ", // 4 "800 ", // 5 "1700", // 6 "3400", // 7 NULL }; unsigned Radio::tx_ramp_read_cb(bool fw) { PwrCtrl_t PwrCtrl; PwrCtrl.octet = radio.readReg(REG_ADDR_PWR_CTRL, 1); tx_param_buf[1] = PwrCtrl.octet >> 5; return PwrCtrl.bits.ramp_time; } menuMode_e Radio::tx_ramp_write_cb(unsigned sidx) { tx_param_buf[1] = sidx; radio.xfer(OPCODE_SET_TX_PARAMS, 2, 0, tx_param_buf); return MENUMODE_REDRAW; } void Radio::set_payload_length(uint8_t len) { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_GFSK) { ppFSK.gfsk.PayloadLength = len; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); } else if (pktType == PACKET_TYPE_LORA) { ppLORA.lora.PayloadLength = len; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); } } void Radio::tx_payload_length_print() { pc.printf("%u", get_payload_length()); } bool Radio::tx_payload_length_write(const char* txt) { unsigned len; sscanf(txt, "%u", &len); set_payload_length(len); return false; } bool Radio::service(int8_t statusRow) { static uint8_t prevRxStatus; static uint8_t prevPktCtrl0; uint8_t buf[4]; static IrqFlags_t prevIrqFlags; IrqFlags_t irqFlags; bool ret = false; static us_timestamp_t prev_now; us_timestamp_t now = lpt.read_us(); radio.service(); if (statusRow > 0 && now-prev_now > 50000) { uint8_t rxStatus, pktCtrl0; bool chg = false; radio.xfer(OPCODE_GET_IRQ_STATUS, 0, 3, buf); irqFlags.word = buf[1] << 8; irqFlags.word |= buf[2]; rxStatus = radio.readReg(0x6c9, 1); if (rxStatus != prevRxStatus) { chg = true; prevRxStatus = rxStatus; } pktCtrl0 = radio.readReg(0x6b3, 1); if (pktCtrl0 != prevPktCtrl0) { chg = true; prevPktCtrl0 = pktCtrl0; } if (irqFlags.word != prevIrqFlags.word && chg) { pc.printf("\e[%u;1f", statusRow); // set (force) cursor to row;column pc.printf("%02x ", rxStatus); pc.printf("%02x ", pktCtrl0); if (irqFlags.bits.TxDone) pc.printf("TxDone "); if (irqFlags.bits.RxDone) pc.printf("RxDone "); if (irqFlags.bits.PreambleDetected) pc.printf("PreambleDetected "); if (irqFlags.bits.SyncWordValid) pc.printf("SyncWordValid "); if (irqFlags.bits.HeaderValid) pc.printf("HeaderValid "); if (irqFlags.bits.HeaderErr) pc.printf("HeaderErr "); if (irqFlags.bits.CrCerr) pc.printf("CrCerr "); if (irqFlags.bits.CadDone) pc.printf("CadDone "); if (irqFlags.bits.CadDetected) pc.printf("CadDetected "); if (irqFlags.bits.Timeout) pc.printf("Timeout "); pc.printf("\e[K"); ret = true; prevIrqFlags.word = irqFlags.word; } prev_now = now; } return ret; } void Radio::Rx() { antswPower = 1; { uint8_t buf[8]; IrqFlags_t irqEnable; irqEnable.word = 0; irqEnable.bits.RxDone = 1; irqEnable.bits.Timeout = 1; buf[0] = 3;//irqEnable.word >> 8; // enable bits buf[1] = 0xff;//irqEnable.word; // enable bits buf[2] = irqEnable.word >> 8; // dio1 buf[3] = irqEnable.word; // dio1 buf[4] = 0; // dio2 buf[5] = 0; // dio2 buf[6] = 0; // dio3 buf[7] = 0; // dio3 radio.xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, 0, buf); } radio.start_rx(RX_TIMEOUT_CONTINUOUS); } void Radio::rxDone(uint8_t size, float rssi, float snr) { if (pktType == PACKET_TYPE_GFSK) { int16_t cfo = radio.readReg(REG_ADDR_FSK_DEMOD_CFO, 2); // justify 12bit to 16bit signed if (cfo & 0x0800) cfo |= 0xf000; log_printf("cfo:%d\r\n", cfo); } else if (pktType == PACKET_TYPE_LORA) { const float bwkhzs[] = { 7.81, 10.42, 15.63, 20.83, 31.25, 41.67, 62.5, 125, 250, 500 }; int hz; int32_t fei; loraStatus1_t loraStatus1; loraStatus1.dword = radio.readReg(REG_ADDR_LORA_STATUS, 3); if (loraStatus1.bits.est_freq_error & 0x80000) fei = 0xfff00000 | loraStatus1.bits.est_freq_error; else fei = loraStatus1.bits.est_freq_error; //hz = fei * HZ_TO_FRF * bwkhzs[bw_idx]/500; hz = fei * -HZ_TO_FRF * bwkhzs[bw_idx]/1000; log_printf("hz:%d\r\n", hz); } RadioEvents->RxDone(size, rssi, snr); } void Radio::txDoneBottom() { if (RadioEvents->TxDone_botHalf) RadioEvents->TxDone_botHalf(); } uint8_t ana_regs[128]; void Radio::boardInit(const RadioEvents_t* e) { hw_reset(); radio.txDone = txDoneBottom; radio.rxDone = rxDone; radio.chipModeChange = chipModeChange; readChip(); RadioEvents = e; lpt.start(); } bool Radio::deviceSel_read() { PaCtrl1b_t PaCtrl1b; PaCtrl1b.octet = radio.readReg(REG_ADDR_PA_CTRL1B, 1); pa_config_buf[2] = PaCtrl1b.bits.tx_mode_bat; // deviceSel return PaCtrl1b.bits.tx_mode_bat; } bool Radio::deviceSel_push() { if (pa_config_buf[2]) pa_config_buf[2] = 0; else pa_config_buf[2] = 1; radio.xfer(OPCODE_SET_PA_CONFIG, 4, 0, pa_config_buf); return pa_config_buf[2]; } const toggle_item_t Radio::deviceSel_item = { _ITEM_TOGGLE, "SX1262", "SX1261", deviceSel_read, deviceSel_push}; static const char* paDutyCycles[] = { "0", "1", "2", "3", "4", "5", "6", "7", NULL }; unsigned Radio::paDutyCycle_read(bool forWriting) { AnaCtrl6_t AnaCtrl6; AnaCtrl6.octet = radio.readReg(REG_ADDR_ANACTRL6, 1); pa_config_buf[0] = AnaCtrl6.bits.pa_dctrim_select_ana; return AnaCtrl6.bits.pa_dctrim_select_ana; } menuMode_e Radio::paDutyCycle_write(unsigned sidx) { pa_config_buf[0] = sidx; radio.xfer(OPCODE_SET_TX_PARAMS, 2, 0, tx_param_buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::paDutyCycle_item = { _ITEM_DROPDOWN, paDutyCycles, paDutyCycles, paDutyCycle_read, paDutyCycle_write}; static const char* hpMaxs[] = { "0", "1", "2", "3", "4", "5", "6", "7", NULL }; unsigned Radio::hpMax_read(bool forWriting) { AnaCtrl7_t AnaCtrl7; AnaCtrl7.octet = radio.readReg(REG_ADDR_ANACTRL7, 1); pa_config_buf[1] = AnaCtrl7.bits.pa_hp_sel_ana; return AnaCtrl7.bits.pa_hp_sel_ana; } menuMode_e Radio::hpMax_write(unsigned sidx) { pa_config_buf[1] = sidx; radio.xfer(OPCODE_SET_TX_PARAMS, 2, 0, tx_param_buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::hpMax_item = { _ITEM_DROPDOWN, hpMaxs, hpMaxs, hpMax_read, hpMax_write}; void Radio::ocp_print() { uint8_t ocp = radio.readReg(REG_ADDR_OCP, 1); pc.printf("%.1f", ocp * 2.5); } bool Radio::ocp_write(const char* txt) { float mA; if (sscanf(txt, "%f", &mA) == 1) radio.writeReg(REG_ADDR_OCP, mA / 2.5, 1); return false; } const value_item_t Radio::ocp_item = { _ITEM_VALUE, 7, ocp_print, ocp_write}; const menu_t Radio::common_menu[] = { { {FIRST_CHIP_MENU_ROW, 1}, "deviceSel:", &deviceSel_item, FLAG_MSGTYPE_ALL, &tx_dbm_item }, { {FIRST_CHIP_MENU_ROW, 18}, "paDutyCycle:", &paDutyCycle_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW, 36}, "hpMax:", &hpMax_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW, 45}, "ocp mA:", &ocp_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; const uint8_t loraBWs[] = { LORA_BW_7, LORA_BW_10, LORA_BW_15, LORA_BW_20, LORA_BW_31, LORA_BW_41, LORA_BW_62, LORA_BW_125, LORA_BW_250, LORA_BW_500 }; static const char* lora_bwstrs[] = { " 7.81KHz", "10.42KHz", "15.63KHz", "20.83KHz", "31.25KHz", "41.67KHz", " 62.5KHz", " 125KHz", " 250KHz", " 500KHz", NULL }; unsigned Radio::lora_bw_read(bool forWriting) { unsigned n; loraConfig0_t conf0; conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1); mpLORA.lora.bandwidth = conf0.bits.modem_bw; for (n = 0; n < sizeof(loraBWs); n++) { if (conf0.bits.modem_bw == loraBWs[n]) { bw_idx = n; return n; } } return sizeof(loraBWs); } menuMode_e Radio::lora_bw_write(unsigned sidx) { mpLORA.lora.bandwidth = loraBWs[sidx]; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); bw_idx = sidx; return MENUMODE_REDRAW; } const dropdown_item_t Radio::lora_bw_item = { _ITEM_DROPDOWN, lora_bwstrs, lora_bwstrs, lora_bw_read, lora_bw_write}; void Radio::lora_sf_print() { loraConfig0_t conf0; conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1); mpLORA.lora.spreadingFactor = conf0.bits.modem_sf; pc.printf("%u", conf0.bits.modem_sf); } bool Radio::lora_sf_write(const char* str) { unsigned sf; if (sscanf(str, "%u", &sf) == 1) { mpLORA.lora.spreadingFactor = sf; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); } return false; } const value_item_t Radio::lora_sf_item = { _ITEM_VALUE, 3, lora_sf_print, lora_sf_write }; static const char* lora_crs[] = { "4/5 ", "4/6 ", "4/7 ", "4/8 ", NULL }; unsigned Radio::lora_cr_read(bool forWriting) { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); mpLORA.lora.codingRate = conf1.bits.tx_coding_rate; return conf1.bits.tx_coding_rate; } menuMode_e Radio::lora_cr_write(unsigned sidx) { mpLORA.lora.codingRate = sidx; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::lora_cr_item = { _ITEM_DROPDOWN, lora_crs, lora_crs, lora_cr_read, lora_cr_write}; bool Radio::ppmOffset_read() { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); mpLORA.lora.LowDatarateOptimize = conf1.bits.ppm_offset; return conf1.bits.ppm_offset; } bool Radio::ppmOffset_push() { if (mpLORA.lora.LowDatarateOptimize) mpLORA.lora.LowDatarateOptimize = 0; else mpLORA.lora.LowDatarateOptimize = 1; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); return mpLORA.lora.LowDatarateOptimize; } const toggle_item_t Radio::lora_ppmOffset_item = { _ITEM_TOGGLE, "LowDatarateOptimize", NULL, ppmOffset_read, ppmOffset_push}; void Radio::lora_pblLen_print() { uint32_t val = radio.readReg(REG_ADDR_LORA_PREAMBLE_SYMBNB, 2); ppLORA.lora.PreambleLengthHi = val >> 8; ppLORA.lora.PreambleLengthLo = val; pc.printf("%u", val); } bool Radio::lora_pblLen_write(const char* txt) { unsigned n; if (sscanf(txt, "%u", &n) == 1) { ppLORA.lora.PreambleLengthHi = n >> 8; ppLORA.lora.PreambleLengthLo = n; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); } return false; } const value_item_t Radio::lora_pblLen_item = { _ITEM_VALUE, 5, lora_pblLen_print, lora_pblLen_write}; bool Radio::lora_headerType_read() { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); ppLORA.lora.HeaderType = conf1.bits.implicit_header; return conf1.bits.implicit_header; } bool Radio::lora_headerType_push() { if (ppLORA.lora.HeaderType) ppLORA.lora.HeaderType = 0; else ppLORA.lora.HeaderType = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); return ppLORA.lora.HeaderType; } const toggle_item_t Radio::lora_headerType_item = { _ITEM_TOGGLE, "EXPLICIT", "IMPLICIT", lora_headerType_read, lora_headerType_push}; bool Radio::lora_crcon_read() { loraConfig2_t conf2; conf2.octet = radio.readReg(REG_ADDR_LORA_CONFIG2, 1); ppLORA.lora.CRCType = conf2.bits.tx_payload_crc16_en; return conf2.bits.tx_payload_crc16_en; } bool Radio::lora_crcon_push() { if (ppLORA.lora.CRCType) ppLORA.lora.CRCType = 0; else ppLORA.lora.CRCType = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); return ppLORA.lora.CRCType; } const toggle_item_t Radio::lora_crcon_item = { _ITEM_TOGGLE, "CrcOn", NULL, lora_crcon_read, lora_crcon_push}; bool Radio::lora_inviq_read() { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); ppLORA.lora.InvertIQ = conf1.bits.rx_invert_iq; return conf1.bits.rx_invert_iq; } bool Radio::lora_inviq_push() { if (ppLORA.lora.InvertIQ) ppLORA.lora.InvertIQ = 0; else ppLORA.lora.InvertIQ = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); return ppLORA.lora.InvertIQ; } const toggle_item_t Radio::lora_inviq_item = { _ITEM_TOGGLE, "InvertIQ", NULL, lora_inviq_read, lora_inviq_push}; const menu_t Radio::lora_menu[] = { { {FIRST_CHIP_MENU_ROW+1, 1}, NULL, &lora_bw_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 12}, "sf:", &lora_sf_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 20}, "cr:", &lora_cr_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 30}, NULL, &lora_ppmOffset_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 1}, "PreambleLength:", &lora_pblLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 22}, NULL, &lora_headerType_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 32}, NULL, &lora_crcon_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 39}, NULL, &lora_inviq_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; void Radio::test() { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_GFSK) { } else if (pktType == PACKET_TYPE_LORA) { } } void Radio::gfsk_bitrate_print() { unsigned d = radio.readReg(REG_ADDR_BITRATE, 3); float f = d / 32.0; pc.printf("%u", (unsigned)(XTAL_FREQ_HZ / f)); mpFSK.gfsk.bitrateHi = d >> 16; mpFSK.gfsk.bitrateMid = d >> 8; mpFSK.gfsk.bitrateLo = d; } bool Radio::gfsk_bitrate_write(const char* txt) { unsigned bps, br; if (sscanf(txt, "%u", &bps) == 1) { br = 32 * (XTAL_FREQ_HZ / (float)bps); mpFSK.gfsk.bitrateHi = br >> 16; mpFSK.gfsk.bitrateMid = br >> 8; mpFSK.gfsk.bitrateLo = br; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); } return false; } const value_item_t Radio::gfsk_bitrate_item = { _ITEM_VALUE, 8, gfsk_bitrate_print, gfsk_bitrate_write}; static const char* gfsk_bts[] = { "off", // 0 "0.3", // 1 "0.5", // 2 "0.7", // 3 "1.0", // 4 NULL }; unsigned Radio::gfsk_bt_read(bool forWriting) { shapeCfg_t shapeCfg; shapeCfg.octet = radio.readReg(REG_ADDR_SHAPECFG, 1); mpFSK.gfsk.PulseShape = shapeCfg.octet; if (shapeCfg.bits.pulse_shape) return shapeCfg.bits.bt + 1; else return 0; } menuMode_e Radio::gfsk_bt_write(unsigned sidx) { switch (sidx) { case 0: mpFSK.gfsk.PulseShape = GFSK_SHAPE_NONE; break; case 1: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT0_3; break; case 2: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT0_5; break; case 3: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT0_7; break; case 4: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT1_0; break; } radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_bt_item = { _ITEM_DROPDOWN, gfsk_bts, gfsk_bts, gfsk_bt_read, gfsk_bt_write}; static const uint8_t rx_bws[] = { GFSK_RX_BW_4800, GFSK_RX_BW_5800, GFSK_RX_BW_7300, GFSK_RX_BW_9700, GFSK_RX_BW_11700, GFSK_RX_BW_14600, GFSK_RX_BW_19500, GFSK_RX_BW_23400, GFSK_RX_BW_29300, GFSK_RX_BW_39000, GFSK_RX_BW_46900, GFSK_RX_BW_58600, GFSK_RX_BW_78200, GFSK_RX_BW_93800, GFSK_RX_BW_117300, GFSK_RX_BW_156200, GFSK_RX_BW_187200, GFSK_RX_BW_234300, GFSK_RX_BW_312000, GFSK_RX_BW_373600, GFSK_RX_BW_467000 }; static const char* rxbw_str[] = { " 4.8KHz", " 5.8KHz", " 7.3KHz", " 9.7KHz", " 11.7KHz", " 14.6KHz", " 19.5KHz", " 23.4KHz", " 29.3KHz", " 39.0KHz", " 46.9KHz", " 58.6KHz", " 78.2KHz", " 93.8KHz", "117.3KHz", "156.2KHz", "187.2KHz", "234.3KHz", "312.0KHz", "373.6KHz", "467.0KHz", NULL }; unsigned Radio::gfsk_rxbw_read(bool forWriting) { unsigned n; bwSel_t bwSel; bwSel.octet = radio.readReg(REG_ADDR_BWSEL, 1); mpFSK.gfsk.bandwidth = bwSel.octet; for (n = 0; n < sizeof(rx_bws); n++) { if (bwSel.octet == rx_bws[n]) return n; } return sizeof(rx_bws); } menuMode_e Radio::gfsk_rxbw_write(unsigned sidx) { mpFSK.gfsk.bandwidth = rx_bws[sidx]; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_rxbw_item = { _ITEM_DROPDOWN, rxbw_str, rxbw_str, gfsk_rxbw_read, gfsk_rxbw_write}; void Radio::gfsk_fdev_print() { unsigned d = radio.readReg(REG_ADDR_FREQDEV, 3); pc.printf("%u", (unsigned)(d * FREQ_STEP)); } bool Radio::gfsk_fdev_write(const char* txt) { unsigned hz, fdev; if (sscanf(txt, "%u", &hz) == 1) { fdev = hz / FREQ_STEP; mpFSK.gfsk.fdevHi = fdev >> 16; mpFSK.gfsk.fdevMid = fdev >> 8; mpFSK.gfsk.fdevLo = fdev; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); } return false; } const value_item_t Radio::gfsk_fdev_item = { _ITEM_VALUE, 8, gfsk_fdev_print, gfsk_fdev_write}; void Radio::gfsk_pblLen_print() { unsigned n = radio.readReg(REG_ADDR_FSK_PREAMBLE_TXLEN , 2); ppFSK.gfsk.PreambleLengthHi = n << 8; // param1 ppFSK.gfsk.PreambleLengthLo = n;// param2 pc.printf("%u", n); } bool Radio::gfsk_pblLen_write(const char* txt) { unsigned n; if (sscanf(txt, "%u", &n) == 1) { ppFSK.gfsk.PreambleLengthHi = n << 8; // param1 ppFSK.gfsk.PreambleLengthLo = n;// param2 radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); } return false; } const value_item_t Radio::gfsk_pblLen_item = { _ITEM_VALUE, 5, gfsk_pblLen_print, gfsk_pblLen_write}; static const char* fsk_detlens[] = { " off ", " 8bits", "16bits", "24bits", "32bits", NULL }; unsigned Radio::gfsk_pblDetLen_read(bool forWriting) { pktCtrl1_t pktCtrl1; pktCtrl1.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL1, 1); ppFSK.gfsk.PreambleDetectorLength = pktCtrl1.octet & 0x07; // param3 if (pktCtrl1.bits.preamble_det_on) return pktCtrl1.bits.preamble_len_rx + 1; else return 0; } menuMode_e Radio::gfsk_pblDetLen_write(unsigned sidx) { if (sidx == 0) ppFSK.gfsk.PreambleDetectorLength = 0; else ppFSK.gfsk.PreambleDetectorLength = sidx + 3; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_pblDetLen_item = { _ITEM_DROPDOWN, fsk_detlens, fsk_detlens, gfsk_pblDetLen_read, gfsk_pblDetLen_write}; void Radio::gfsk_swl_print() { ppFSK.gfsk.SyncWordLength = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1);// param4 pc.printf("%u", ppFSK.gfsk.SyncWordLength); } bool Radio::gfsk_swl_write(const char* txt) { unsigned n; unsigned r; r = sscanf(txt, "%u", &n); if (r == 1) { ppFSK.gfsk.SyncWordLength = n; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); } return false; } const value_item_t Radio::gfsk_swl_item = { _ITEM_VALUE, 3, gfsk_swl_print, gfsk_swl_write}; void Radio::gfsk_syncword_print() { unsigned addr = REG_ADDR_SYNCADDR; uint8_t swl_bits = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1); if (swl_bits & 7) { swl_bits |= 7; swl_bits++; } while (swl_bits > 0) { pc.printf("%02x", radio.readReg(addr++, 1)); swl_bits -= 8; } } bool Radio::gfsk_syncword_write(const char* txt) { const char* ptr = txt; unsigned addr = REG_ADDR_SYNCADDR; int8_t swl_bits = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1); if (swl_bits & 7) { swl_bits |= 7; swl_bits++; } while (swl_bits > 0) { char buf[3]; unsigned n; buf[0] = ptr[0]; buf[1] = ptr[1]; buf[2] = 0; sscanf(buf, "%x", &n); radio.writeReg(addr++, n, 1); ptr += 2; swl_bits -= 8; } return false; } const value_item_t Radio::gfsk_syncword_item = { _ITEM_VALUE, 17, gfsk_syncword_print, gfsk_syncword_write}; bool Radio::gfsk_fixLen_read() { pktCtrl0_t pktCtrl0; pktCtrl0.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL0, 1); ppFSK.gfsk.PacketType = pktCtrl0.bits.pkt_len_format; // param6 return pktCtrl0.bits.pkt_len_format; } bool Radio::gfsk_fixLen_push() { if (ppFSK.gfsk.PacketType) ppFSK.gfsk.PacketType = 0; else ppFSK.gfsk.PacketType = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return ppFSK.gfsk.PacketType; } const toggle_item_t Radio::gfsk_fixLen_item = { _ITEM_TOGGLE, "fixed ", "variable", gfsk_fixLen_read, gfsk_fixLen_push }; static const char* addrcomps[] = { " off ", "NodeAddress ", "NodeAddress+broadcast", NULL }; unsigned Radio::gfsk_addrcomp_read(bool forWriting) { ppFSK.gfsk.AddrComp = radio.readReg(REG_ADDR_NODEADDRCOMP, 1);// param5 return ppFSK.gfsk.AddrComp; } menuMode_e Radio::gfsk_addrcomp_write(unsigned sidx) { ppFSK.gfsk.AddrComp = sidx; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_addrcomp_item = { _ITEM_DROPDOWN, addrcomps, addrcomps, gfsk_addrcomp_read, gfsk_addrcomp_write}; void Radio::gfsk_nodeadrs_print() { pc.printf("%02x", radio.readReg(REG_ADDR_NODEADDR, 1)); } bool Radio::gfsk_nodeadrs_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_NODEADDR, v, 1); return false; } const value_item_t Radio::gfsk_nodeadrs_item = { _ITEM_VALUE, 3, gfsk_nodeadrs_print, gfsk_nodeadrs_write}; void Radio::gfsk_broadcast_print() { pc.printf("%02x", radio.readReg(REG_ADDR_BROADCAST, 1)); } bool Radio::gfsk_broadcast_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_BROADCAST, v, 1); return false; } const value_item_t Radio::gfsk_broadcast_item = { _ITEM_VALUE, 3, gfsk_broadcast_print, gfsk_broadcast_write}; static const char* crctypes[] = { " off ", // 0 "1 Byte ", // 1 "2 Byte ", // 2 "1 Byte inv", // 3 "2 Byte inv", // 4 NULL }; unsigned Radio::gfsk_crctype_read(bool forWriting) { pktCtrl2_t pktCtrl2; pktCtrl2.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL2, 1); ppFSK.gfsk.CRCType = pktCtrl2.octet & 0x7; // param8 switch (ppFSK.gfsk.CRCType) { case GFSK_CRC_OFF: return 0; case GFSK_CRC_1_BYTE: return 1; case GFSK_CRC_2_BYTE: return 2; case GFSK_CRC_1_BYTE_INV: return 3; case GFSK_CRC_2_BYTE_INV: return 4; default: return 5; } } menuMode_e Radio::gfsk_crctype_write(unsigned sidx) { switch (sidx) { case 0: ppFSK.gfsk.CRCType = GFSK_CRC_OFF; break; case 1: ppFSK.gfsk.CRCType = GFSK_CRC_1_BYTE; break; case 2: ppFSK.gfsk.CRCType = GFSK_CRC_2_BYTE; break; case 3: ppFSK.gfsk.CRCType = GFSK_CRC_1_BYTE_INV; break; case 4: ppFSK.gfsk.CRCType = GFSK_CRC_2_BYTE_INV; break; } radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_crctype_item = { _ITEM_DROPDOWN, crctypes, crctypes, gfsk_crctype_read, gfsk_crctype_write}; bool Radio::gfsk_white_read() { pktCtrl2_t pktCtrl2; pktCtrl2.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL2, 1); ppFSK.gfsk.Whitening = pktCtrl2.bits.whit_enable; // param9 return pktCtrl2.bits.whit_enable; } bool Radio::gfsk_white_push() { if (ppFSK.gfsk.Whitening) ppFSK.gfsk.Whitening = 0; else ppFSK.gfsk.Whitening = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return ppFSK.gfsk.Whitening; } const toggle_item_t Radio::gfsk_white_item = { _ITEM_TOGGLE, "Whitening", NULL, gfsk_white_read, gfsk_white_push}; void Radio::gfsk_crcinit_print() { pc.printf("%04x", radio.readReg(REG_ADDR_FSK_CRCINIT, 2)); } bool Radio::gfsk_crcinit_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_FSK_CRCINIT, v, 2); return false; } const value_item_t Radio::gfsk_crcinit_item = { _ITEM_VALUE, 5, gfsk_crcinit_print, gfsk_crcinit_write}; void Radio::gfsk_crcpoly_print() { pc.printf("%04x", radio.readReg(REG_ADDR_FSK_CRCPOLY, 2)); } bool Radio::gfsk_crcpoly_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_FSK_CRCPOLY, v, 2); return false; } const value_item_t Radio::gfsk_crcpoly_item = { _ITEM_VALUE, 5, gfsk_crcpoly_print, gfsk_crcpoly_write}; void Radio::gfsk_whiteInit_print() { PktCtrl1a_t PktCtrl1a; PktCtrl1a.word = radio.readReg(REG_ADDR_FSK_PKTCTRL1A, 2); pc.printf("%x", PktCtrl1a.bits.whit_init_val); } bool Radio::gfsk_whiteInit_write(const char* txt) { unsigned n; PktCtrl1a_t PktCtrl1a; PktCtrl1a.word = radio.readReg(REG_ADDR_FSK_PKTCTRL1A, 2); if (sscanf(txt, "%x", &n) == 1) { PktCtrl1a.bits.whit_init_val = n; radio.writeReg(REG_ADDR_FSK_PKTCTRL1A, PktCtrl1a.word, 2); } return false; } const value_item_t Radio::gfsk_whiteInit_item = { _ITEM_VALUE, 5, gfsk_whiteInit_print, gfsk_whiteInit_write}; const menu_t Radio::gfsk_menu[] = { { {FIRST_CHIP_MENU_ROW+1, 1}, "bps:", &gfsk_bitrate_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 15}, "bt:", &gfsk_bt_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 23}, "rxbw:", &gfsk_rxbw_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 39}, "fdev:", &gfsk_fdev_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 53}, NULL, &gfsk_fixLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 1}, "PreambleLength:", &gfsk_pblLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 21}, "PreambleDetectorLength:", &gfsk_pblDetLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 51}, "SyncWordLength bits:", &gfsk_swl_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 1}, "SyncWord:", &gfsk_syncword_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 1}, "AddrComp:", &gfsk_addrcomp_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 33}, "NodeAdrs:", &gfsk_nodeadrs_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 47}, "broadcast:", &gfsk_broadcast_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+5, 1}, "crcType:", &gfsk_crctype_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+5, 21}, "crcInit:", &gfsk_crcinit_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+5, 34}, "crcPoly:", &gfsk_crcpoly_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+6, 1}, NULL, &gfsk_white_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+6, 12}, "lfsr init:", &gfsk_whiteInit_item, FLAG_MSGTYPE_ALL }, //12345678901234567890123456789012 { {0, 0}, NULL, NULL } }; const menu_t* Radio::get_modem_sub_menu() { return NULL; } const menu_t* Radio::get_modem_menu() { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_LORA) { return lora_menu; } else if (pktType == PACKET_TYPE_GFSK) { return gfsk_menu; } return NULL; } #endif /* ..SX126x_H */