Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
main.cpp
- Committer:
- dudmuck
- Date:
- 2021-08-24
- Revision:
- 13:8ce61a1897ab
- Parent:
- 9:295e37c38fb3
- Child:
- 14:14b9e1c08bfc
File content as of revision 13:8ce61a1897ab:
#include "mbed.h" #include "radio.h" //#define MENU_DEBUG BufferedSerial *_pc; namespace mbed { FileHandle *mbed_override_console(int fd) { static BufferedSerial console(USBTX, USBRX, MBED_CONF_PLATFORM_STDIO_BAUD_RATE); _pc = &console; return &console; } } menuState_t menuState; typedef enum { MSG_TYPE_PACKET = 0, MSG_TYPE_PER, MSG_TYPE_PINGPONG } msgType_e; // [row][column] const menu_t* menu_table[MAX_MENU_ROWS][MAX_MENU_COLUMNS]; int8_t StopMenuCols[MAX_MENU_ROWS]; #define SCROLLING_ROWS 20 struct _cp_ { uint8_t row; uint8_t tableCol; } curpos; uint8_t entry_buf_idx; char entry_buf[64]; msgType_e msg_type; const uint8_t PingMsg[] = "PING"; const uint8_t PongMsg[] = "PONG"; const uint8_t PerMsg[] = "PER"; volatile struct _flags_ { uint8_t ping_master : 1; // 0 uint8_t send_ping : 1; // 1 uint8_t send_pong : 1; // 2 uint8_t pingpongEnable : 1; // 3 uint8_t do_next_tx : 1; // 4 } flags; uint8_t botRow; int regAddr = -1; struct { float start_MHz; float step_MHz; float stop_MHz; unsigned dwell_ms; float MHz; Ticker ticker; bool ticking; bool tick; } cf_sweep; void Radio::cadDone(bool det) { log_printf("cadDone "); if (det) printf("CadDetected"); printf("\r\n"); } unsigned lfsr; #define LFSR_INIT 0x1ff uint8_t get_pn9_byte() { uint8_t ret = 0; int xor_out; xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf); // four bits at a time lfsr = (lfsr >> 4) | (xor_out << 5); // four bits at a time ret |= (lfsr >> 5) & 0x0f; xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf); // four bits at a time lfsr = (lfsr >> 4) | (xor_out << 5); // four bits at a time ret |= ((lfsr >> 1) & 0xf0); return ret; } void hw_reset_push() { Radio::hw_reset(); Radio::readChip(); menuState.mode = MENUMODE_REINIT_MENU; } const button_item_t hw_reset_item = { _ITEM_BUTTON, "hw_reset", hw_reset_push }; void clearIrqs_push() { Radio::clearIrqFlags(); } const button_item_t clearirqs_item = { _ITEM_BUTTON, "clearIrqs", clearIrqs_push }; const dropdown_item_t pktType_item = { _ITEM_DROPDOWN, Radio::pktType_strs, Radio::pktType_strs, Radio::pktType_read, Radio::pktType_write}; unsigned msgType_read(bool fw) { return msg_type; } menuMode_e msgType_write(unsigned widx) { msg_type = (msgType_e)widx; if (msg_type == MSG_TYPE_PER) { flags.pingpongEnable = 0; if (Radio::get_payload_length() < 7) Radio::set_payload_length(7); } else if (msg_type == MSG_TYPE_PINGPONG) { if (Radio::get_payload_length() != 12) Radio::set_payload_length(12); } else if (msg_type == MSG_TYPE_PACKET) { flags.pingpongEnable = 0; } return MENUMODE_REINIT_MENU; } const char* const msgType_strs[] = { "PACKET ", "PER ", "PINGPONG", NULL }; const dropdown_item_t msgType_item = { _ITEM_DROPDOWN, msgType_strs, msgType_strs, msgType_read, msgType_write}; void sweep_start_print() { printf("%.3fMHz", cf_sweep.start_MHz); } bool sweep_start_write(const char* valStr) { sscanf(valStr, "%f", &cf_sweep.start_MHz); return false; } void sweep_step_print() { printf("%.3fMHz", cf_sweep.step_MHz); } bool sweep_step_write(const char* valStr) { sscanf(valStr, "%f", &cf_sweep.step_MHz); return false; } void sweep_stop_print() { printf("%.3fMHz", cf_sweep.stop_MHz); } bool sweep_stop_write(const char* valStr) { sscanf(valStr, "%f", &cf_sweep.stop_MHz); return false; } void sweep_dwell_print() { printf("%ums", cf_sweep.dwell_ms); } bool sweep_dwell_write(const char* valStr) { sscanf(valStr, "%u", &cf_sweep.dwell_ms); return false; } const value_item_t tx_payload_length_item = { _ITEM_VALUE, 5, Radio::tx_payload_length_print, Radio::tx_payload_length_write}; void pn9_push() { uint8_t i, len = Radio::get_payload_length(); for (i = 0; i < len; i++) Radio::radio.tx_buf[i] = get_pn9_byte(); } const button_item_t tx_payload_pn9_item = { _ITEM_BUTTON, "PN9", pn9_push }; void regAddr_print() { if (regAddr != -1) printf("%x", regAddr); } bool regAddr_write(const char* txt) { sscanf(txt, "%x", ®Addr); return false; } const value_item_t regAddr_item = { _ITEM_VALUE, 5, regAddr_print, regAddr_write}; void regValue_print() { if (regAddr != -1) { printf("%x", Radio::read_register(regAddr)); } } bool regValue_write(const char* txt) { unsigned val; sscanf(txt, "%x", &val); if (regAddr != -1) { Radio::write_register(regAddr, val); } return false; } const value_item_t regValue_item = { _ITEM_VALUE, 5, regValue_print, regValue_write}; void tx_payload_print() { uint8_t i, len = Radio::get_payload_length(); for (i = 0; i < len; i++) printf("%02x ", Radio::radio.tx_buf[i]); } bool tx_payload_write(const char* txt) { unsigned o, i = 0, pktidx = 0; while (i < entry_buf_idx) { sscanf(entry_buf+i, "%02x", &o); printf("%02x ", o); i += 2; Radio::radio.tx_buf[pktidx++] = o; while (entry_buf[i] == ' ' && i < entry_buf_idx) i++; } log_printf("set payload len %u\r\n", pktidx); Radio::set_payload_length(pktidx); return true; } const value_item_t tx_payload_item = { _ITEM_VALUE, 80, tx_payload_print, tx_payload_write}; void txpkt_push() { Radio::txPkt(); } const button_item_t tx_pkt_item = { _ITEM_BUTTON, "TXPKT", txpkt_push }; void rxpkt_push() { Radio::Rx(); //yyy menuState.mode = MENUMODE_REDRAW; } const button_item_t rx_pkt_item = { _ITEM_BUTTON, "RXPKT", rxpkt_push }; const dropdown_item_t opmode_item = { _ITEM_DROPDOWN, Radio::opmode_status_strs, Radio::opmode_select_strs, Radio::opmode_read, Radio::opmode_write }; void tx_carrier_push() { Radio::tx_carrier(); } const button_item_t tx_carrier_item = { _ITEM_BUTTON, "TX_CARRIER", tx_carrier_push }; void tx_preamble_push() { Radio::tx_preamble(); } const button_item_t tx_preamble_item = { _ITEM_BUTTON, "TX_PREAMBLE", tx_preamble_push }; const value_item_t sweep_start_item = { _ITEM_VALUE, 5, sweep_start_print, sweep_start_write}; const value_item_t sweep_step_item = { _ITEM_VALUE, 5, sweep_step_print, sweep_step_write}; const value_item_t sweep_stop_item = { _ITEM_VALUE, 5, sweep_stop_print, sweep_stop_write}; const value_item_t sweep_dwell_item = { _ITEM_VALUE, 5, sweep_dwell_print, sweep_dwell_write}; const menu_t msg_pkt_menu[] = { { {LAST_CHIP_MENU_ROW+1, 1}, "tx payload length:", &tx_payload_length_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+1, 25}, NULL, &tx_payload_pn9_item, FLAG_MSGTYPE_PKT, &tx_payload_item }, { {LAST_CHIP_MENU_ROW+1, 40}, "regAddr:", ®Addr_item, FLAG_MSGTYPE_PKT, ®Value_item }, { {LAST_CHIP_MENU_ROW+1, 53}, "regValue:", ®Value_item, FLAG_MSGTYPE_PKT, }, { {LAST_CHIP_MENU_ROW+2, 1}, NULL, &tx_payload_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+3, 1}, NULL, &tx_pkt_item, FLAG_MSGTYPE_PKT, &opmode_item }, { {LAST_CHIP_MENU_ROW+3, 10}, NULL, &rx_pkt_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+3, 20}, NULL, &tx_carrier_item, FLAG_MSGTYPE_PKT, &opmode_item }, { {LAST_CHIP_MENU_ROW+3, 45}, NULL, &tx_preamble_item, FLAG_MSGTYPE_PKT, &opmode_item }, { {LAST_CHIP_MENU_ROW+4, 1}, "cf sweep start:", &sweep_start_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+4, 28}, "step:", &sweep_step_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+4, 45}, "stop:", &sweep_stop_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+4, 62}, "dwell_ms:", &sweep_dwell_item, FLAG_MSGTYPE_PKT }, { {0, 0}, NULL, NULL } }; unsigned tx_ipd_ms; Timeout mbedTimeout; unsigned MaxNumPacket; unsigned CntPacketTx; unsigned PacketRxSequencePrev; unsigned CntPacketRxKO; unsigned CntPacketRxOK; unsigned RxTimeOutCount; unsigned receivedCntPacket; void do_next_tx () { Radio::radio.tx_buf[0] = CntPacketTx >> 24; Radio::radio.tx_buf[1] = CntPacketTx >> 16; Radio::radio.tx_buf[2] = CntPacketTx >> 8; Radio::radio.tx_buf[3] = CntPacketTx; Radio::radio.tx_buf[4] = PerMsg[0]; Radio::radio.tx_buf[5] = PerMsg[1]; Radio::radio.tx_buf[6] = PerMsg[2]; Radio::txPkt(); } void next_tx_callback() { flags.do_next_tx = 1; } void pertx_push() { CntPacketTx = 1; // PacketRxSequencePrev initialized to 0 on receiver log_printf("do perTx\r\n"); next_tx_callback(); } const button_item_t pertx_item = { _ITEM_BUTTON, "PERTX", pertx_push }; void tx_ipd_print() { printf("%u", tx_ipd_ms); } bool tx_ipd_write(const char* valStr) { sscanf(valStr, "%u", &tx_ipd_ms); return false; } value_item_t per_ipd_item = { _ITEM_VALUE, 4, tx_ipd_print, tx_ipd_write }; void numpkts_print() { printf("%u", MaxNumPacket); } bool numpkts_write(const char* valStr) { sscanf(valStr, "%u", &MaxNumPacket); return false; } value_item_t per_numpkts_item = { _ITEM_VALUE, 6, numpkts_print, numpkts_write }; void perrx_push() { PacketRxSequencePrev = 0; CntPacketRxKO = 0; CntPacketRxOK = 0; RxTimeOutCount = 0; Radio::Rx(); } const button_item_t perrx_item = { _ITEM_BUTTON, "PERRX", perrx_push }; void per_reset_push() { PacketRxSequencePrev = 0; CntPacketRxKO = 0; CntPacketRxOK = 0; RxTimeOutCount = 0; } const button_item_t per_clear_item = { _ITEM_BUTTON, "resetCount", per_reset_push }; const menu_t msg_per_menu[] = { { {LAST_CHIP_MENU_ROW+3, 1}, NULL, &pertx_item, FLAG_MSGTYPE_PER }, { {LAST_CHIP_MENU_ROW+3, 10}, "TX IPD ms:", &per_ipd_item, FLAG_MSGTYPE_PER }, { {LAST_CHIP_MENU_ROW+3, 26}, "numPkts:", &per_numpkts_item, FLAG_MSGTYPE_PER }, { {LAST_CHIP_MENU_ROW+3, 40}, NULL, &perrx_item, FLAG_MSGTYPE_PER, &opmode_item }, { {LAST_CHIP_MENU_ROW+3, 50}, NULL, &per_clear_item, FLAG_MSGTYPE_PER, &opmode_item }, { {0, 0}, NULL, NULL } }; void SendPong() { unsigned failCnt = CntPacketRxKO + RxTimeOutCount; /* ping slave tx */ log_printf("ACK PKT%u\r\n", receivedCntPacket); Radio::radio.tx_buf[ 0] = receivedCntPacket >> 24; Radio::radio.tx_buf[ 1] = receivedCntPacket >> 16; Radio::radio.tx_buf[ 2] = receivedCntPacket >> 8; Radio::radio.tx_buf[ 3] = receivedCntPacket; Radio::radio.tx_buf[ 4] = failCnt >> 24; Radio::radio.tx_buf[ 5] = failCnt >> 16; Radio::radio.tx_buf[ 6] = failCnt >> 8; Radio::radio.tx_buf[ 7] = failCnt; Radio::radio.tx_buf[ 8] = PongMsg[0]; Radio::radio.tx_buf[ 9] = PongMsg[1]; Radio::radio.tx_buf[10] = PongMsg[2]; Radio::radio.tx_buf[11] = PongMsg[3]; Radio::txPkt(); } void SendPing() { /* ping master tx */ log_printf("MASTER > PING PKT%u\r\n", CntPacketTx); Radio::radio.tx_buf[0] = CntPacketTx >> 24; Radio::radio.tx_buf[1] = CntPacketTx >> 16; Radio::radio.tx_buf[2] = CntPacketTx >> 8; Radio::radio.tx_buf[3] = CntPacketTx; Radio::radio.tx_buf[4] = PingMsg[0]; Radio::radio.tx_buf[5] = PingMsg[1]; Radio::radio.tx_buf[6] = PingMsg[2]; Radio::radio.tx_buf[7] = PingMsg[3]; Radio::txPkt(); } void pingpong_start_push() { CntPacketTx = 1; CntPacketRxKO = 0; CntPacketRxOK = 0; RxTimeOutCount = 0; receivedCntPacket = 0; flags.send_ping = 1; flags.ping_master = 1; flags.pingpongEnable = 1; log_printf("ping start\r\n"); } const button_item_t pingpong_start_item = { _ITEM_BUTTON, "START", pingpong_start_push }; void pingpong_stop_push () { flags.pingpongEnable = 0; } const button_item_t pingpong_stop_item = { _ITEM_BUTTON, "STOP", pingpong_stop_push }; const menu_t msg_pingpong_menu[] = { { {LAST_CHIP_MENU_ROW+3, 1}, NULL, &pingpong_start_item, FLAG_MSGTYPE_PING }, { {LAST_CHIP_MENU_ROW+3, 15}, NULL, &pingpong_stop_item, FLAG_MSGTYPE_PING }, { {0, 0}, NULL, NULL } }; const menu_t* get_msg_menu() { switch (msg_type) { case MSG_TYPE_PACKET: return msg_pkt_menu; case MSG_TYPE_PER: return msg_per_menu; case MSG_TYPE_PINGPONG: return msg_pingpong_menu; } return NULL; } void frf_print() { float MHz; #ifdef SX127x_H MHz = Radio::radio.get_frf_MHz(); #else MHz = Radio::radio.getMHz(); #endif printf("%.3fMHz", MHz); } bool frf_write(const char* valStr) { float MHz; sscanf(valStr, "%f", &MHz); #ifdef SX127x_H Radio::radio.set_frf_MHz(MHz); #else Radio::radio.setMHz(MHz); #endif return false; } const value_item_t frf_item = { _ITEM_VALUE, 14, frf_print, frf_write }; const value_item_t Radio::tx_dbm_item = { _ITEM_VALUE, 7, Radio::tx_dbm_print, Radio::tx_dbm_write }; const dropdown_item_t tx_ramp_item = { _ITEM_DROPDOWN, Radio::tx_ramp_strs, Radio::tx_ramp_strs, Radio::tx_ramp_read, Radio::tx_ramp_write }; const button_item_t chipNum_item = { _ITEM_BUTTON, Radio::chipNum_str, NULL}; void botrow_print() { printf("%u", botRow); } bool botrow_write(const char* str) { unsigned n; sscanf(str, "%u", &n); botRow = n; printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region return false; } const value_item_t bottomRow_item = { _ITEM_VALUE, 4, botrow_print, botrow_write }; const menu_t common_menu[] = { { {1, 1}, NULL, &hw_reset_item, FLAG_MSGTYPE_ALL }, { {1, 13}, "packetType:", &pktType_item, FLAG_MSGTYPE_ALL }, { {1, 33}, NULL, &msgType_item, FLAG_MSGTYPE_ALL }, { {1, 43}, NULL, &chipNum_item, FLAG_MSGTYPE_ALL }, { {1, 61}, "bottomRow:", &bottomRow_item, FLAG_MSGTYPE_ALL }, { {2, 1}, NULL, &frf_item, FLAG_MSGTYPE_ALL }, { {2, 15}, "TX dBm:", &Radio::tx_dbm_item, FLAG_MSGTYPE_ALL }, { {2, 30}, "ramp us:", &tx_ramp_item, FLAG_MSGTYPE_ALL }, { {2, 45}, NULL, &clearirqs_item, FLAG_MSGTYPE_ALL }, { {2, 55}, "opmode:", &opmode_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; bool is_menu_item_changable(uint8_t table_row, uint8_t table_col) { const menu_t* m = menu_table[table_row][table_col]; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; switch (msg_type) { case MSG_TYPE_PACKET: if (m->flags & FLAG_MSGTYPE_PKT) break; else return false; case MSG_TYPE_PER: if (m->flags & FLAG_MSGTYPE_PER) break; else return false; case MSG_TYPE_PINGPONG: if (m->flags & FLAG_MSGTYPE_PING) break; else return false; } if (di->itemType == _ITEM_DROPDOWN) { if (di->selectable_strs == NULL || di->selectable_strs[0] == NULL) { log_printf("NULLstrs%u,%u\r\n", table_row, table_col); return false; } } else if (di->itemType == _ITEM_VALUE) { const value_item_t* vi = (const value_item_t*)m->itemPtr; if (vi->write == NULL) return false; } else if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->push == NULL) return false; } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t * ti = (const toggle_item_t *)m->itemPtr; if (ti->push == NULL) return false; } return true; } // ..is_menu_item_changable() void read_menu_item(const menu_t* m, bool selected) { uint8_t valCol; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; switch (msg_type) { case MSG_TYPE_PACKET: if (m->flags & FLAG_MSGTYPE_PKT) break; else return; case MSG_TYPE_PER: if (m->flags & FLAG_MSGTYPE_PER) break; else return; case MSG_TYPE_PINGPONG: if (m->flags & FLAG_MSGTYPE_PING) break; else return; } printf("\e[%u;%uf", m->pos.row, m->pos.col); // set (force) cursor to row;column valCol = m->pos.col; if (m->label) { printf("%s", m->label); valCol += strlen(m->label); } if (di->itemType == _ITEM_DROPDOWN) { if (di->read && di->printed_strs) { uint8_t ridx = di->read(false); if (selected) printf("\e[7m"); printf("%s", di->printed_strs[ridx]); if (selected) printf("\e[0m"); } else if (di->printed_strs) { printf("%s", di->printed_strs[0]); } } else if (di->itemType == _ITEM_VALUE) { const value_item_t* vi = (const value_item_t*)m->itemPtr; if (vi->print) { for (unsigned n = 0; n < vi->width; n++) printf(" "); printf("\e[%u;%uf", m->pos.row, valCol); // set (force) cursor to row;column if (selected) printf("\e[7m"); vi->print(); if (selected) printf("\e[0m"); } } else if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->label) { if (selected) printf("\e[7m%s\e[0m", bi->label); else printf("%s", bi->label); } } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t* ti = (const toggle_item_t *)m->itemPtr; bool on = ti->read(); if (ti->label1) { const char* const cptr = on ? ti->label1 : ti->label0; if (selected) printf("\e[7m%s\e[0m", cptr); else printf("%s", cptr); } else { if (on) printf("\e[1m"); if (selected) printf("\e[7m"); printf("%s", ti->label0); if (selected || on) printf("\e[0m"); } } } // ..read_menu_item() void draw_menu() { unsigned table_row; for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) { int table_col; for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) { read_menu_item(menu_table[table_row][table_col], false); } // ..table column iterator } // ..table row iterator read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } // ..draw_menu() typedef struct { int row; int col; } tablexy_t; void menu_init_(const menu_t* in, tablexy_t* tc) { unsigned n; for (n = 0; in[n].pos.row > 0; n++) { const menu_t* m = &in[n]; if (tc->row != m->pos.row - 1) { tc->row = m->pos.row - 1; tc->col = 0; } else tc->col++; menu_table[tc->row][tc->col] = m; #ifdef MENU_DEBUG printf("table:%u,%u ", tc->row, tc->col); printf(" %d<%d? ", StopMenuCols[tc->row], tc->col); #endif /* MENU_DEBUG */ if (StopMenuCols[tc->row] < tc->col) StopMenuCols[tc->row] = tc->col; #ifdef MENU_DEBUG printf("{%u %u}", tc->row, tc->col); printf("in:%p[%u] screen:%u,%u ", in, n, m->pos.row, m->pos.col); printf("pos@%p ", &m->pos); //printf(" loc:%p ", &in[n].itemPtr); if (in[n].itemPtr) { const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; printf(" itemPtr:%p type:%02x ", di, di->itemType); } printf("stopMenuCols[%u]: %d ", tc->row, StopMenuCols[tc->row]); if (m->label) printf("label:%s", m->label); else printf("noLabel"); printf("\r\n"); #endif /* MENU_DEBUG */ } #ifdef MENU_DEBUG printf("hit key:"); _pc->getc(); #endif /* MENU_DEBUG */ } void navigate_dropdown(uint8_t ch) { unsigned n; const menu_t* m = menuState.sm; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; switch (ch) { case 'A': // cursor UP if (menuState.sel_idx > 0) { menuState.sel_idx--; } break; case 'B': // cursor DOWN if (di->selectable_strs[menuState.sel_idx+1] != NULL) menuState.sel_idx++; break; } // ..switch (ch) for (n = 0; di->selectable_strs[n] != NULL; n++) { printf("\e[%u;%uf", m->pos.row+n, menuState.dropdown_col); if (n == menuState.sel_idx) printf("\e[7m"); printf("%s", di->selectable_strs[n]); if (n == menuState.sel_idx) printf("\e[0m"); } printf("\e[%u;%uf", m->pos.row + menuState.sel_idx, menuState.dropdown_col + strlen(di->selectable_strs[menuState.sel_idx])); } bool is_item_selectable(const menu_t* m) { const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->push == NULL) return false; } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr; if (ti->push == NULL) return false; } return true; } void navigate_menu(uint8_t ch) { read_menu_item(menu_table[curpos.row][curpos.tableCol], false); switch (ch) { case 'A': // cursor UP if (curpos.row == 0) break; { // find previous row up with column int8_t row; for (row = curpos.row - 1; row >= 0; row--) { if (StopMenuCols[row] > -1) { curpos.row = row; break; } } if (row == 0 && StopMenuCols[0] < 0) break; // nothing found } if (curpos.tableCol >= StopMenuCols[curpos.row]) { curpos.tableCol = StopMenuCols[curpos.row]-1; } break; case 'B': // cursor DOWN if (curpos.row >= MAX_MENU_ROWS) break; { // find next row down with column uint8_t row; for (row = curpos.row + 1; row < MAX_MENU_ROWS; row++) { if (StopMenuCols[row] != -1) { curpos.row = row; break; } } if (row == MAX_MENU_ROWS-1 && StopMenuCols[row] == -1) break; // nothing found } if (curpos.tableCol >= StopMenuCols[curpos.row]) { curpos.tableCol = StopMenuCols[curpos.row]-1; } break; case 'C': // cursor LEFT if (curpos.tableCol >= StopMenuCols[curpos.row]-1) break; { // find next row left with editable uint8_t tcol; for (tcol = curpos.tableCol + 1; tcol < StopMenuCols[curpos.row]; tcol++) { if (is_menu_item_changable(curpos.row, tcol)) { curpos.tableCol = tcol; break; } } } break; case 'D': // cursor RIGHT if (curpos.tableCol == 0) break; { int8_t tcol; for (tcol = curpos.tableCol - 1; tcol >= 0; tcol--) { if (is_menu_item_changable(curpos.row, tcol)) { curpos.tableCol = tcol; break; } } } break; default: //printf("unhancled-csi:%02x\eE", ch); break; } // ..switch (ch) if (!is_item_selectable(menu_table[curpos.row][curpos.tableCol])) { int c; for (c = 0; c < StopMenuCols[curpos.row]; c++) { if (is_item_selectable(menu_table[curpos.row][c])) { curpos.tableCol = c; break; } } if (c == StopMenuCols[curpos.row]) return; } #ifdef MENU_DEBUG log_printf("table:%u,%u screen:%u,%u \r\n", curpos.row, curpos.tableCol, menu_table[curpos.row][curpos.tableCol]->pos.row, menu_table[curpos.row][curpos.tableCol]->pos.col ); #endif /* MENU_DEBUG */ read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } // ..navigate_menu bool is_sweep_menu_item(const void* const itemPtr) { if (itemPtr == &sweep_start_item || itemPtr == &sweep_stop_item || itemPtr == &sweep_dwell_item || itemPtr == &tx_carrier_item || itemPtr == &tx_preamble_item ) return true; else return false; } void commit_menu_item_change() { const menu_t* m = menu_table[curpos.row][curpos.tableCol]; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; if (di->itemType == _ITEM_DROPDOWN) { menuState.mode = di->write(menuState.sel_idx); printf("\e[%u;%uf", m->pos.row, m->pos.col-2); } else if (di->itemType == _ITEM_VALUE) { const value_item_t* vi = (const value_item_t*)m->itemPtr; /* commit value entry */ if (vi->write) { if (vi->write(entry_buf)) menuState.mode = MENUMODE_REDRAW; else menuState.mode = MENUMODE_NONE; } else menuState.mode = MENUMODE_NONE; if (menuState.mode == MENUMODE_NONE) { read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } } } // ..commit_menu_item_change() void refresh_item_in_table(const void* item) { unsigned table_row; if (item == NULL) return; for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) { int table_col; for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) { //log_printf("%u %u %p\r\n", table_row, table_col, menu_table[table_row][table_col]->itemPtr); if (item == menu_table[table_row][table_col]->itemPtr) { read_menu_item(menu_table[table_row][table_col], false); return; } } } } void start_value_entry(const menu_t* m) { const value_item_t* vi = (const value_item_t*)m->itemPtr; uint8_t col = m->pos.col; if (m->label) col += strlen(m->label); printf("\e[%u;%uf", m->pos.row, col); for (unsigned i = 0; i < vi->width; i++) printf(" "); // clear displayed value for user entry printf("\e[%u;%uf", m->pos.row, col); menuState.mode = MENUMODE_ENTRY; entry_buf_idx = 0; } void start_menu_item_change() { const menu_t* m = menu_table[curpos.row][curpos.tableCol]; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; bool checkRefresh = false; if (di->itemType == _ITEM_DROPDOWN && di->selectable_strs) { menuState.dropdown_col = m->pos.col; unsigned n, sidx = 0; /* start dropdown */ if (di->read) sidx = di->read(true); if (m->label) menuState.dropdown_col += strlen(m->label); for (n = 0; di->selectable_strs[n] != NULL; n++) { uint8_t col = menuState.dropdown_col; bool leftPad = false; if (col > 3 && n > 0) { // dropdown left side padding col -= 2; leftPad = true; } printf("\e[%u;%uf", m->pos.row+n, col); if (leftPad ) { printf(" "); } if (n == sidx) printf("\e[7m"); printf("%s", di->selectable_strs[n]); if (n == sidx) printf("\e[0m"); printf(" "); // right side padding } printf("\e[%u;%uf", m->pos.row, menuState.dropdown_col-2); menuState.mode = MENUMODE_DROPDOWN; menuState.sel_idx = sidx; menuState.sm = m; } else if (di->itemType == _ITEM_VALUE) { /* start value entry */ start_value_entry(m); } else if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->push) { bi->push(); checkRefresh = true; } } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr; if (ti->push) { bool on = ti->push(); uint8_t col = m->pos.col; if (m->label) col += strlen(m->label); printf("\e[%u;%uf", m->pos.row, col); if (ti->label1) { printf("\e[7m%s\e[0m", on ? ti->label1 : ti->label0); } else { if (on) printf("\e[1;7m%s\e[0m", ti->label0); else printf("\e[7m%s\e[0m", ti->label0); } checkRefresh = true; } } if (checkRefresh) { if (m->refreshReadItem) { refresh_item_in_table(m->refreshReadItem); // read associated read_menu_item(m, true); // restore cursor } } } // ..start_menu_item_change() void full_menu_init() { unsigned n; const menu_t *m; tablexy_t txy; txy.row = INT_MAX; txy.col = 0; for (n = 0; n < MAX_MENU_ROWS; n++) { StopMenuCols[n] = -1; } menu_init_(common_menu, &txy); menu_init_(Radio::common_menu, &txy); m = Radio::get_modem_menu(); if (m == NULL) { log_printf("NULL-modemMenu\r\n"); for (;;) asm("nop"); } #ifdef MENU_DEBUG printf("modemmenuInit\r\n"); #endif menu_init_(m, &txy); m = Radio::get_modem_sub_menu(); if (m) { #ifdef MENU_DEBUG printf("modemsubmenuInit\r\n"); #endif menu_init_(m, &txy); } #ifdef MENU_DEBUG else printf("no-modemsubmenu\r\n"); #endif m = get_msg_menu(); if (m == NULL) { log_printf("NULL-msgMenu\r\n"); for (;;) asm("nop"); } menu_init_(m, &txy); for (n = 0; n < MAX_MENU_ROWS; n++) { if (StopMenuCols[n] != -1) StopMenuCols[n]++; } } bool ishexchar(char ch) { if (((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) || (ch >= '0' && ch <= '9')) return true; else return false; } enum _urx_ { URX_STATE_NONE = 0, URX_STATE_ESCAPE, URX_STATE_CSI, } uart_rx_state; Timeout uartRxTimeout; void uart_rx_timeout() { /* escape by itself: abort change on item */ menuState.mode = MENUMODE_REDRAW; uart_rx_state = URX_STATE_NONE; } void sweeper() { if (cf_sweep.ticking) { cf_sweep.tick = true; } } void sweep_service() { const menu_t* m = menu_table[curpos.row][curpos.tableCol]; if (is_sweep_menu_item(m->itemPtr)) { if (cf_sweep.start_MHz != 0 && cf_sweep.stop_MHz > cf_sweep.start_MHz && cf_sweep.dwell_ms > 0) { std::chrono::microseconds us = std::chrono::microseconds(cf_sweep.dwell_ms * 1000); cf_sweep.MHz = cf_sweep.start_MHz; cf_sweep.ticker.attach(sweeper, us); cf_sweep.ticking = true; } } else { if (cf_sweep.ticking) { cf_sweep.ticker.detach(); cf_sweep.ticking = false; } } } void serial_callback() { char serial_write_buf[4]; unsigned char serial_write_buf_idx = 0; //char ch = pc.getc(); char ch; _pc->read(&ch, 1); switch (uart_rx_state) { case URX_STATE_NONE: if (ch == 0x1b) { if (menuState.mode == MENUMODE_ENTRY) { /* abort entry mode */ menuState.mode = MENUMODE_NONE; read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } else { uart_rx_state = URX_STATE_ESCAPE; if (menuState.mode != MENUMODE_NONE) { /* is this escape by itself, user wants to abort? */ uartRxTimeout.attach(uart_rx_timeout, 30ms); } } } else if (ch == 2) { // ctrl-B log_printf("--------------\r\n"); } else if (ch == '\r') { if (menuState.mode == MENUMODE_NONE) { start_menu_item_change(); } else { entry_buf[entry_buf_idx] = 0; commit_menu_item_change(); } sweep_service(); } else if (menuState.mode == MENUMODE_ENTRY) { if (ch == 8) { if (entry_buf_idx > 0) { serial_write_buf[serial_write_buf_idx++] = 8; serial_write_buf[serial_write_buf_idx++] = ' '; serial_write_buf[serial_write_buf_idx++] = 8; entry_buf_idx--; } } else if (ch == 3) { // ctrl-C menuState.mode = MENUMODE_NONE; } else if (entry_buf_idx < sizeof(entry_buf)) { entry_buf[entry_buf_idx++] = ch; serial_write_buf[serial_write_buf_idx++] = ch; } } else if (menuState.mode == MENUMODE_NONE) { if (ishexchar(ch) || ch == '-') { // characters which start entry const value_item_t* vi = (const value_item_t*)menu_table[curpos.row][curpos.tableCol]->itemPtr; if (vi->itemType == _ITEM_VALUE) { start_value_entry(menu_table[curpos.row][curpos.tableCol]); entry_buf[entry_buf_idx++] = ch; serial_write_buf[serial_write_buf_idx++] = ch; } } else if (ch == 'r') { menuState.mode = MENUMODE_REDRAW; } else if (ch == '.') { Radio::test(); } } break; case URX_STATE_ESCAPE: uartRxTimeout.detach(); if (ch == '[') uart_rx_state = URX_STATE_CSI; else { #ifdef MENU_DEBUG log_printf("unhancled-esc:%02x\r\n", ch); #endif /* MENU_DEBUG */ uart_rx_state = URX_STATE_NONE; } break; case URX_STATE_CSI: if (menuState.mode == MENUMODE_NONE) navigate_menu(ch); else if (menuState.mode == MENUMODE_DROPDOWN) navigate_dropdown(ch); uart_rx_state = URX_STATE_NONE; //printf("\e[18;1f"); // set (force) cursor to row;column break; } // ..switch (uart_rx_state) if (serial_write_buf_idx > 0) { _pc->write(serial_write_buf, serial_write_buf_idx); } } void txDone() { if (msg_type == MSG_TYPE_PER) { log_printf("CntPacketTx%u, max:%u ipd%u\r\n", CntPacketTx, MaxNumPacket, tx_ipd_ms); if (++CntPacketTx <= MaxNumPacket) { std::chrono::microseconds us = std::chrono::microseconds(tx_ipd_ms * 1000); mbedTimeout.attach(next_tx_callback, us); } } else if (msg_type == MSG_TYPE_PINGPONG) { if (flags.ping_master) { ++CntPacketTx; } Radio::Rx(); } } void printRxPkt(uint8_t size) { uint8_t col; char str[80]; char *ptr, *endPtr; unsigned n = 0; endPtr = str + sizeof(str); ptr = str; col = 0; while (ptr < endPtr) { //sprintf(ptr, "%02x%c", Radio::radio.rx_buf[n], n == Radio::radio.rx_buf_offset ? '_' : ' '); // LR1110 sprintf(ptr, "%02x ", Radio::radio.rx_buf[n]); ptr += 3; col += 3; if (col >= 77) { log_printf("%s\r\n", str); ptr = str; col = 0; } if (++n >= size) break; } log_printf("%s\r\n", str); } void rxDone(uint8_t size, float rssi, float snr) { log_printf("rxDone %u, %.1fdBm %.1fdB\r\n", size, rssi, snr); if (msg_type == MSG_TYPE_PACKET) { printRxPkt(size); } else if (msg_type == MSG_TYPE_PER) { if (memcmp(Radio::radio.rx_buf+4, PerMsg, 3) == 0) { unsigned i, PacketRxSequence = Radio::radio.rx_buf[0]; PacketRxSequence <<= 8; PacketRxSequence += Radio::radio.rx_buf[1]; PacketRxSequence <<= 8; PacketRxSequence += Radio::radio.rx_buf[2]; PacketRxSequence <<= 8; PacketRxSequence += Radio::radio.rx_buf[3]; CntPacketRxOK++; if (PacketRxSequence <= PacketRxSequencePrev || PacketRxSequencePrev == 0) i = 0; // sequence reset to resync, dont count missed packets this time else i = PacketRxSequence - PacketRxSequencePrev - 1; CntPacketRxKO += i; RxTimeOutCount = 0; log_printf("PER rx%u ok%u ko%u\r\n", PacketRxSequence , CntPacketRxOK, CntPacketRxKO); PacketRxSequencePrev = PacketRxSequence; } // ..if PerMsg else { log_printf("per?\r\n"); printRxPkt(size); } } else if (msg_type == MSG_TYPE_PINGPONG) { if (memcmp(Radio::radio.rx_buf+4, PingMsg, 4) == 0) { /* ping slave rx */ Radio::setFS(); receivedCntPacket = Radio::radio.rx_buf[0]; receivedCntPacket <<= 8; receivedCntPacket += Radio::radio.rx_buf[1]; receivedCntPacket <<= 8; receivedCntPacket += Radio::radio.rx_buf[2]; receivedCntPacket <<= 8; receivedCntPacket += Radio::radio.rx_buf[3]; log_printf("%u rxPing->txPong\r\n", receivedCntPacket); flags.ping_master = 0; flags.send_pong = 1; } else if (memcmp(Radio::radio.rx_buf+8, PongMsg, 4) == 0) { unsigned cnt; /* ping master rx */ Radio::setFS(); cnt = Radio::radio.rx_buf[0]; cnt <<= 8; cnt += Radio::radio.rx_buf[1]; cnt <<= 8; cnt += Radio::radio.rx_buf[2]; cnt <<= 8; cnt += Radio::radio.rx_buf[3]; log_printf("%u rxPong->txPing\r\n", cnt); flags.send_ping = 1; } else { log_printf("pingpong?\r\n"); printRxPkt(size); } } else { /*for (unsigned n = 0; n < size; n++) log_printf("%02x\r\n", Radio::radio.rx_buf[n]);*/ log_printf("msg_type %u\r\n", msg_type); } } const RadioEvents_t rev = { txDone, rxDone }; volatile int foo; int main() { lfsr = LFSR_INIT; msg_type = MSG_TYPE_PACKET; uart_rx_state = URX_STATE_NONE; Radio::boardInit(&rev); { unsigned n; for (n = 0; n < MAX_MENU_ROWS; n++) StopMenuCols[n] = -1; } botRow = MAX_MENU_ROWS + SCROLLING_ROWS; printf("\e[7h"); // enable line wrapping printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region printf("\e[2J"); // erase entire screen full_menu_init(); printf("\e[2J"); // erase entire screen menuState.mode = MENUMODE_NONE; draw_menu(); curpos.row = 0; curpos.tableCol = 0; tx_ipd_ms = 100; for (;;) { if (_pc->readable()) { serial_callback(); } if (flags.send_ping) { if (flags.pingpongEnable) SendPing(); flags.send_ping = 0; } if (flags.send_pong) { if (flags.pingpongEnable) SendPong(); flags.send_pong = 0; } if (flags.do_next_tx) { do_next_tx(); flags.do_next_tx = 0; } if (menuState.mode == MENUMODE_REINIT_MENU) { full_menu_init(); menuState.mode = MENUMODE_REDRAW; } if (menuState.mode == MENUMODE_REDRAW) { // erase entire screen, some dropdowns extend to scrolling area printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region, if terminal started after printf("\e[2J"); //printf("\e[%u;1f\e[1J", MAX_MENU_ROWS); // erase menu area menuState.mode = MENUMODE_NONE; draw_menu(); } if (Radio::service(menuState.mode == MENUMODE_NONE ? LAST_CHIP_MENU_ROW : -1)) { read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } if (cf_sweep.tick) { cf_sweep.tick = false; #ifdef SX127x_H Radio::radio.set_frf_MHz(cf_sweep.MHz); #else Radio::radio.setMHz(cf_sweep.MHz); #endif cf_sweep.MHz += cf_sweep.step_MHz; if (cf_sweep.MHz >= cf_sweep.stop_MHz) cf_sweep.MHz = cf_sweep.start_MHz; } } // ..for (;;) } char strbuf[255]; void log_printf(const char* format, ...) { va_list arglist; // put cursor at last scrolling-area line printf("\e[%u;1f", botRow); va_start(arglist, format); vsprintf(strbuf, format, arglist); va_end(arglist); printf("%s", strbuf); }