wayne roberts / Mbed OS utility_sx12xx
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "radio.h"
00003 
00004 //#define MENU_DEBUG
00005 
00006 #ifdef TARGET_NUCLEO_L073RZ
00007 BufferedSerial logpc(PC_10, PC_11, 115200);
00008 #else
00009     //#error log_uart_for_target
00010 #endif
00011 void logpc_printf(const char* format, ...);
00012 
00013 static BufferedSerial pc(USBTX, USBRX, MBED_CONF_PLATFORM_STDIO_BAUD_RATE);
00014 namespace mbed {
00015     FileHandle *mbed_override_console(int fd) {
00016         return &pc;
00017     }
00018 }
00019 
00020 menuState_t menuState;
00021 
00022 typedef enum {
00023     MSG_TYPE_PACKET = 0,
00024     MSG_TYPE_PER,
00025     MSG_TYPE_PINGPONG
00026 } msgType_e;
00027 
00028 // [row][column]
00029 const menu_t* menu_table[MAX_MENU_ROWS][MAX_MENU_COLUMNS];
00030 int8_t StopMenuCols[MAX_MENU_ROWS];
00031 
00032 #define SCROLLING_ROWS      20
00033 
00034 struct _cp_ {
00035     uint8_t row;
00036     uint8_t tableCol;
00037 } curpos;
00038 
00039 uint8_t entry_buf_idx;
00040 char entry_buf[64];
00041 
00042 msgType_e msg_type;
00043 
00044 const uint8_t PingMsg[] = "PING";
00045 const uint8_t PongMsg[] = "PONG";
00046 const uint8_t PerMsg[]  = "PER";
00047 
00048 volatile struct _flags_ {
00049     uint8_t ping_master     : 1; // 0
00050     uint8_t send_ping       : 1; // 1
00051     uint8_t send_pong       : 1; // 2
00052     uint8_t pingpongEnable  : 1; // 3
00053     uint8_t do_next_tx      : 1; // 4
00054 } flags;
00055 
00056 uint8_t botRow;
00057 int regAddr = -1;
00058 
00059 struct {
00060     float start_MHz;
00061     float step_MHz;
00062     float stop_MHz;
00063     unsigned dwell_ms;
00064     float MHz;
00065     Ticker ticker;
00066     bool ticking;
00067     bool tick;
00068 } cf_sweep;
00069 
00070 
00071 void Radio::cadDone(bool det)
00072 {
00073     log_printf("cadDone ");
00074     if (det)
00075         printf("CadDetected");
00076 
00077     printf("\r\n");
00078 }
00079 
00080 unsigned lfsr;
00081 #define LFSR_INIT       0x1ff
00082 
00083 uint8_t get_pn9_byte()
00084 {
00085     uint8_t ret = 0;
00086     int xor_out;
00087 
00088     xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf);   // four bits at a time
00089     lfsr = (lfsr >> 4) | (xor_out << 5);    // four bits at a time
00090 
00091     ret |= (lfsr >> 5) & 0x0f;
00092 
00093     xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf);   // four bits at a time
00094     lfsr = (lfsr >> 4) | (xor_out << 5);    // four bits at a time
00095 
00096     ret |= ((lfsr >> 1) & 0xf0);
00097 
00098     return ret;
00099 }
00100 
00101 void hw_reset_push()
00102 {
00103     Radio::hw_reset();
00104 
00105     Radio::readChip();
00106     menuState.mode = MENUMODE_REINIT_MENU;
00107 }
00108 
00109 const button_item_t hw_reset_item = { _ITEM_BUTTON, "hw_reset", hw_reset_push };
00110 
00111 void clearIrqs_push()
00112 {
00113     Radio::clearIrqFlags();
00114 }
00115 
00116 const button_item_t clearirqs_item = { _ITEM_BUTTON, "clearIrqs", clearIrqs_push };
00117 
00118 const dropdown_item_t pktType_item = { _ITEM_DROPDOWN, Radio::pktType_strs, Radio::pktType_strs, Radio::pktType_read, Radio::pktType_write};
00119 
00120 
00121 unsigned msgType_read(bool fw)
00122 {
00123     return msg_type;
00124 }
00125 
00126 menuMode_e msgType_write(unsigned widx)
00127 {
00128     msg_type = (msgType_e)widx;
00129 
00130     if (msg_type == MSG_TYPE_PER) {
00131         flags.pingpongEnable = 0;
00132         if (Radio::get_payload_length() < 7)
00133             Radio::set_payload_length(7);
00134     } else if (msg_type == MSG_TYPE_PINGPONG) {
00135         if (Radio::get_payload_length() != 12)
00136             Radio::set_payload_length(12);
00137     } else if (msg_type == MSG_TYPE_PACKET) {
00138         flags.pingpongEnable = 0;
00139     }
00140 
00141     return MENUMODE_REINIT_MENU;
00142 }
00143 
00144 const char* const msgType_strs[] = {
00145     "PACKET ",
00146     "PER    ",
00147     "PINGPONG",
00148     NULL
00149 };
00150 
00151 const dropdown_item_t msgType_item = { _ITEM_DROPDOWN, msgType_strs, msgType_strs, msgType_read, msgType_write};
00152 
00153 void sweep_start_print()
00154 {
00155     printf("%.3fMHz", cf_sweep.start_MHz);
00156 }
00157 
00158 bool sweep_start_write(const char* valStr)
00159 {
00160     sscanf(valStr, "%f", &cf_sweep.start_MHz);
00161     return false;
00162 }
00163 
00164 void sweep_step_print()
00165 {
00166     printf("%.3fMHz", cf_sweep.step_MHz);
00167 }
00168 
00169 bool sweep_step_write(const char* valStr)
00170 {
00171     sscanf(valStr, "%f", &cf_sweep.step_MHz);
00172     return false;
00173 }
00174 
00175 void sweep_stop_print()
00176 {
00177     printf("%.3fMHz", cf_sweep.stop_MHz);
00178 }
00179 
00180 bool sweep_stop_write(const char* valStr)
00181 {
00182     sscanf(valStr, "%f", &cf_sweep.stop_MHz);
00183     return false;
00184 }
00185 
00186 void sweep_dwell_print()
00187 {
00188     printf("%ums", cf_sweep.dwell_ms);
00189 }
00190 
00191 bool sweep_dwell_write(const char* valStr)
00192 {
00193     sscanf(valStr, "%u", &cf_sweep.dwell_ms);
00194     return false;
00195 }
00196 
00197 const value_item_t tx_payload_length_item = { _ITEM_VALUE, 5, Radio::tx_payload_length_print, Radio::tx_payload_length_write};
00198 
00199 void pn9_push()
00200 {
00201     uint8_t i, len = Radio::get_payload_length();
00202     for (i = 0; i < len; i++)
00203         Radio::radio.tx_buf[i] = get_pn9_byte();
00204 }
00205 
00206 const button_item_t tx_payload_pn9_item  = { _ITEM_BUTTON, "PN9", pn9_push };
00207 
00208 void regAddr_print()
00209 {
00210     if (regAddr != -1)
00211         printf("%x", regAddr);
00212 }
00213 
00214 bool regAddr_write(const char* txt)
00215 {
00216     sscanf(txt, "%x", &regAddr);
00217     return false;
00218 }
00219 
00220 const value_item_t regAddr_item = { _ITEM_VALUE, 5, regAddr_print, regAddr_write};
00221 
00222 void regValue_print()
00223 {
00224     if (regAddr != -1) {
00225         printf("%x", Radio::read_register(regAddr));
00226     }
00227 }
00228 
00229 bool regValue_write(const char* txt)
00230 {
00231     unsigned val;
00232     sscanf(txt, "%x", &val);
00233     if (regAddr != -1) {
00234         Radio::write_register(regAddr, val);
00235     }
00236     return false;
00237 }
00238 
00239 const value_item_t regValue_item = { _ITEM_VALUE, 5, regValue_print, regValue_write};
00240 
00241 void tx_payload_print()
00242 {
00243     uint8_t i, len = Radio::get_payload_length();
00244     for (i = 0; i < len; i++)
00245         printf("%02x ", Radio::radio.tx_buf[i]);
00246 }
00247 
00248 bool tx_payload_write(const char* txt)
00249 {
00250     unsigned o, i = 0, pktidx = 0;
00251     while (i < entry_buf_idx) {
00252         sscanf(entry_buf+i, "%02x", &o);
00253         printf("%02x ", o);
00254         i += 2;
00255         Radio::radio.tx_buf[pktidx++] = o;
00256         while (entry_buf[i] == ' ' && i < entry_buf_idx)
00257             i++;
00258     }
00259     log_printf("set payload len %u\r\n", pktidx);
00260     Radio::set_payload_length(pktidx);
00261     return true;
00262 }
00263 
00264 const value_item_t tx_payload_item = { _ITEM_VALUE, 80, tx_payload_print, tx_payload_write};
00265 
00266 void txpkt_push()
00267 {
00268     Radio::txPkt();
00269 }
00270 
00271 const button_item_t tx_pkt_item  = { _ITEM_BUTTON, "TXPKT", txpkt_push };
00272 
00273 void rxpkt_push()
00274 {
00275     Radio::Rx();
00276     //yyy menuState.mode = MENUMODE_REDRAW;
00277 }
00278 const button_item_t rx_pkt_item  = { _ITEM_BUTTON, "RXPKT", rxpkt_push };
00279 
00280 const dropdown_item_t opmode_item = { _ITEM_DROPDOWN, Radio::opmode_status_strs, Radio::opmode_select_strs, Radio::opmode_read, Radio::opmode_write };
00281 
00282 void tx_carrier_push()
00283 {
00284     Radio::tx_carrier();
00285 }
00286 const button_item_t tx_carrier_item = { _ITEM_BUTTON, "TX_CARRIER", tx_carrier_push };
00287 
00288 void tx_preamble_push()
00289 {
00290     Radio::tx_preamble();
00291 }
00292 const button_item_t tx_preamble_item = { _ITEM_BUTTON, "TX_PREAMBLE", tx_preamble_push };
00293 
00294 void get_rssi_push()
00295 {
00296     Radio::get_rssi();
00297 }
00298 const button_item_t get_rssi_item = { _ITEM_BUTTON, "GET_RSSI", get_rssi_push };
00299 
00300 const value_item_t sweep_start_item = { _ITEM_VALUE, 5, sweep_start_print, sweep_start_write};
00301 const value_item_t sweep_step_item = { _ITEM_VALUE, 5, sweep_step_print, sweep_step_write};
00302 const value_item_t sweep_stop_item = { _ITEM_VALUE, 5, sweep_stop_print, sweep_stop_write};
00303 const value_item_t sweep_dwell_item = { _ITEM_VALUE, 5, sweep_dwell_print, sweep_dwell_write};
00304 
00305 const menu_t msg_pkt_menu[] = {
00306     { {LAST_CHIP_MENU_ROW+1,  1}, "tx payload length:", &tx_payload_length_item, FLAG_MSGTYPE_PKT },
00307     { {LAST_CHIP_MENU_ROW+1, 25},                 NULL,    &tx_payload_pn9_item, FLAG_MSGTYPE_PKT, &tx_payload_item },
00308     { {LAST_CHIP_MENU_ROW+1, 40},           "regAddr:",           &regAddr_item, FLAG_MSGTYPE_PKT, &regValue_item },
00309     { {LAST_CHIP_MENU_ROW+1, 53},          "regValue:",          &regValue_item, FLAG_MSGTYPE_PKT, },
00310     { {LAST_CHIP_MENU_ROW+2,  1},                 NULL,        &tx_payload_item, FLAG_MSGTYPE_PKT },
00311     { {LAST_CHIP_MENU_ROW+3,  1},                 NULL,            &tx_pkt_item, FLAG_MSGTYPE_PKT, &opmode_item },
00312     { {LAST_CHIP_MENU_ROW+3, 10},                 NULL,            &rx_pkt_item, FLAG_MSGTYPE_PKT },
00313     { {LAST_CHIP_MENU_ROW+3, 20},                 NULL,        &tx_carrier_item, FLAG_MSGTYPE_PKT, &opmode_item },
00314     { {LAST_CHIP_MENU_ROW+3, 45},                 NULL,       &tx_preamble_item, FLAG_MSGTYPE_PKT, &opmode_item },
00315     { {LAST_CHIP_MENU_ROW+3, 58},                 NULL,          &get_rssi_item, FLAG_MSGTYPE_PKT, &opmode_item },
00316     { {LAST_CHIP_MENU_ROW+4,  1},    "cf sweep start:",       &sweep_start_item, FLAG_MSGTYPE_PKT },
00317     { {LAST_CHIP_MENU_ROW+4, 28},              "step:",        &sweep_step_item, FLAG_MSGTYPE_PKT },
00318     { {LAST_CHIP_MENU_ROW+4, 45},              "stop:",        &sweep_stop_item, FLAG_MSGTYPE_PKT },
00319     { {LAST_CHIP_MENU_ROW+4, 62},          "dwell_ms:",       &sweep_dwell_item, FLAG_MSGTYPE_PKT },
00320 
00321     { {0, 0}, NULL, NULL }
00322 };
00323 
00324 unsigned tx_ipd_ms;
00325 Timeout mbedTimeout;
00326 unsigned MaxNumPacket;
00327 unsigned CntPacketTx;
00328 unsigned PacketRxSequencePrev;
00329 unsigned CntPacketRxKO;
00330 unsigned CntPacketRxOK;
00331 unsigned RxTimeOutCount;
00332 unsigned receivedCntPacket;
00333 
00334 void do_next_tx ()
00335 {
00336     Radio::radio.tx_buf[0] = CntPacketTx >> 24;
00337     Radio::radio.tx_buf[1] = CntPacketTx >> 16;
00338     Radio::radio.tx_buf[2] = CntPacketTx >> 8;
00339     Radio::radio.tx_buf[3] = CntPacketTx;
00340 
00341     Radio::radio.tx_buf[4] = PerMsg[0];
00342     Radio::radio.tx_buf[5] = PerMsg[1];
00343     Radio::radio.tx_buf[6] = PerMsg[2];
00344 
00345     Radio::txPkt();
00346 }
00347 
00348 void next_tx_callback()
00349 {
00350     flags.do_next_tx = 1;
00351 }
00352 
00353 
00354 void pertx_push()
00355 {
00356     CntPacketTx = 1;    // PacketRxSequencePrev initialized to 0 on receiver
00357 
00358     log_printf("do perTx\r\n");
00359 
00360     next_tx_callback();
00361 }
00362 
00363 
00364 const button_item_t pertx_item = { _ITEM_BUTTON, "PERTX", pertx_push };
00365 
00366 void tx_ipd_print()
00367 {
00368     printf("%u", tx_ipd_ms);
00369 }
00370 
00371 bool tx_ipd_write(const char* valStr)
00372 {
00373     sscanf(valStr, "%u", &tx_ipd_ms);
00374     return false;
00375 }
00376 
00377 value_item_t per_ipd_item = { _ITEM_VALUE, 4, tx_ipd_print, tx_ipd_write };
00378 
00379 void numpkts_print()
00380 {
00381     printf("%u", MaxNumPacket);
00382 }
00383 
00384 bool numpkts_write(const char* valStr)
00385 {
00386     sscanf(valStr, "%u", &MaxNumPacket);
00387     return false;
00388 }
00389 
00390 value_item_t per_numpkts_item = { _ITEM_VALUE, 6, numpkts_print, numpkts_write };
00391 
00392 void perrx_push()
00393 {
00394     PacketRxSequencePrev = 0;
00395     CntPacketRxKO = 0;
00396     CntPacketRxOK = 0;
00397     RxTimeOutCount = 0;
00398 
00399     Radio::Rx();
00400 }
00401 
00402 const button_item_t perrx_item = { _ITEM_BUTTON, "PERRX", perrx_push };
00403 
00404 void per_reset_push()
00405 {
00406     PacketRxSequencePrev = 0;
00407     CntPacketRxKO = 0;
00408     CntPacketRxOK = 0;
00409     RxTimeOutCount = 0;
00410 }
00411 
00412 const button_item_t per_clear_item = { _ITEM_BUTTON, "resetCount", per_reset_push };
00413 
00414 const menu_t msg_per_menu[] = {
00415     { {LAST_CHIP_MENU_ROW+3,  1},          NULL,       &pertx_item, FLAG_MSGTYPE_PER },
00416     { {LAST_CHIP_MENU_ROW+3,  10}, "TX IPD ms:",     &per_ipd_item, FLAG_MSGTYPE_PER },
00417     { {LAST_CHIP_MENU_ROW+3,  26},   "numPkts:", &per_numpkts_item, FLAG_MSGTYPE_PER },
00418     { {LAST_CHIP_MENU_ROW+3,  40},         NULL,       &perrx_item, FLAG_MSGTYPE_PER, &opmode_item },
00419     { {LAST_CHIP_MENU_ROW+3,  50},         NULL,   &per_clear_item, FLAG_MSGTYPE_PER, &opmode_item },
00420     { {0, 0}, NULL, NULL }
00421 };
00422 
00423 void SendPong()
00424 {
00425     unsigned failCnt = CntPacketRxKO + RxTimeOutCount;
00426 
00427     /* ping slave tx */
00428     log_printf("ACK PKT%u\r\n", receivedCntPacket);
00429 
00430     Radio::radio.tx_buf[ 0] = receivedCntPacket >> 24;
00431     Radio::radio.tx_buf[ 1] = receivedCntPacket >> 16;
00432     Radio::radio.tx_buf[ 2] = receivedCntPacket >> 8;
00433     Radio::radio.tx_buf[ 3] = receivedCntPacket;
00434     Radio::radio.tx_buf[ 4] = failCnt >> 24;
00435     Radio::radio.tx_buf[ 5] = failCnt >> 16;
00436     Radio::radio.tx_buf[ 6] = failCnt >> 8;
00437     Radio::radio.tx_buf[ 7] = failCnt;
00438     Radio::radio.tx_buf[ 8] = PongMsg[0];
00439     Radio::radio.tx_buf[ 9] = PongMsg[1];
00440     Radio::radio.tx_buf[10] = PongMsg[2];
00441     Radio::radio.tx_buf[11] = PongMsg[3];
00442 
00443     Radio::txPkt();
00444 }
00445 
00446 void SendPing()
00447 {
00448     /* ping master tx */
00449 
00450     log_printf("MASTER > PING PKT%u\r\n", CntPacketTx);
00451     Radio::radio.tx_buf[0] = CntPacketTx >> 24;
00452     Radio::radio.tx_buf[1] = CntPacketTx >> 16;
00453     Radio::radio.tx_buf[2] = CntPacketTx >> 8;
00454     Radio::radio.tx_buf[3] = CntPacketTx;
00455     Radio::radio.tx_buf[4] = PingMsg[0];
00456     Radio::radio.tx_buf[5] = PingMsg[1];
00457     Radio::radio.tx_buf[6] = PingMsg[2];
00458     Radio::radio.tx_buf[7] = PingMsg[3];
00459 
00460     Radio::txPkt();
00461 }
00462 
00463 void
00464 pingpong_start_push()
00465 {
00466     CntPacketTx = 1;
00467     CntPacketRxKO = 0;
00468     CntPacketRxOK = 0;
00469     RxTimeOutCount = 0;
00470     receivedCntPacket = 0;
00471 
00472     flags.send_ping = 1;
00473 
00474     flags.ping_master = 1;
00475 
00476     flags.pingpongEnable = 1;
00477     log_printf("ping start\r\n");
00478 }
00479 
00480 const button_item_t pingpong_start_item = { _ITEM_BUTTON, "START", pingpong_start_push };
00481 
00482 void
00483 pingpong_stop_push ()
00484 {
00485     flags.pingpongEnable = 0;
00486 }
00487 
00488 const button_item_t pingpong_stop_item = { _ITEM_BUTTON, "STOP", pingpong_stop_push };
00489 
00490 const menu_t msg_pingpong_menu[] = {
00491     { {LAST_CHIP_MENU_ROW+3,  1}, NULL, &pingpong_start_item, FLAG_MSGTYPE_PING },
00492     { {LAST_CHIP_MENU_ROW+3, 15}, NULL,  &pingpong_stop_item, FLAG_MSGTYPE_PING },
00493     { {0, 0}, NULL, NULL }
00494 };
00495 
00496 const menu_t* get_msg_menu()
00497 {
00498     switch (msg_type) {
00499         case MSG_TYPE_PACKET:
00500             return msg_pkt_menu;
00501         case MSG_TYPE_PER:
00502             return msg_per_menu;
00503         case MSG_TYPE_PINGPONG:
00504             return msg_pingpong_menu;
00505     }
00506     return NULL;
00507 }
00508 
00509 void frf_print()
00510 {
00511     float MHz;
00512 #ifdef SX127x_H
00513     MHz = Radio::radio.get_frf_MHz();
00514 #else
00515     MHz = Radio::radio.getMHz();
00516 #endif
00517     printf("%.3fMHz", MHz);
00518 }
00519 
00520 bool frf_write(const char* valStr)
00521 {
00522     float MHz;
00523     sscanf(valStr, "%f", &MHz);
00524 #ifdef SX127x_H
00525     Radio::radio.set_frf_MHz(MHz);
00526 #else
00527     Radio::radio.setMHz(MHz);
00528 #endif
00529     return false;
00530 }
00531 
00532 const value_item_t frf_item = { _ITEM_VALUE, 14, frf_print, frf_write };
00533 
00534 const value_item_t Radio::tx_dbm_item = { _ITEM_VALUE, 7, Radio::tx_dbm_print, Radio::tx_dbm_write };
00535 
00536 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 };
00537 
00538 const button_item_t chipNum_item = { _ITEM_BUTTON, Radio::chipNum_str, NULL};
00539 
00540 void botrow_print()
00541 {
00542     printf("%u", botRow);
00543 }
00544 
00545 bool botrow_write(const char* str)
00546 {
00547     unsigned n;
00548     sscanf(str, "%u", &n);
00549     botRow = n;
00550 
00551     printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region
00552 
00553     return false;
00554 }
00555 
00556 const value_item_t bottomRow_item = { _ITEM_VALUE, 4, botrow_print, botrow_write };
00557 
00558 const menu_t common_menu[] = {
00559     {  {1, 1},          NULL,      &hw_reset_item, FLAG_MSGTYPE_ALL },
00560     { {1, 13}, "packetType:",       &pktType_item, FLAG_MSGTYPE_ALL },
00561     { {1, 33},          NULL,       &msgType_item, FLAG_MSGTYPE_ALL },
00562     { {1, 43},          NULL,       &chipNum_item, FLAG_MSGTYPE_ALL },
00563     { {1, 61},  "bottomRow:",     &bottomRow_item, FLAG_MSGTYPE_ALL },
00564     { {2, 1},           NULL,           &frf_item, FLAG_MSGTYPE_ALL },
00565     { {2, 15},     "TX dBm:", &Radio::tx_dbm_item, FLAG_MSGTYPE_ALL },
00566     { {2, 30},    "ramp us:",       &tx_ramp_item, FLAG_MSGTYPE_ALL },
00567     { {2, 45},          NULL,     &clearirqs_item, FLAG_MSGTYPE_ALL },
00568     { {2, 55},     "opmode:",        &opmode_item, FLAG_MSGTYPE_ALL },
00569 
00570 
00571     { {0, 0}, NULL, NULL }
00572 };
00573 
00574 bool
00575 is_menu_item_changable(uint8_t table_row, uint8_t table_col)
00576 {
00577     const menu_t* m = menu_table[table_row][table_col];
00578     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00579 
00580     switch (msg_type) {
00581         case MSG_TYPE_PACKET:
00582             if (m->flags & FLAG_MSGTYPE_PKT)
00583                 break;
00584             else
00585                 return false;
00586         case MSG_TYPE_PER:
00587             if (m->flags & FLAG_MSGTYPE_PER)
00588                 break;
00589             else
00590                 return false;
00591         case MSG_TYPE_PINGPONG:
00592             if (m->flags & FLAG_MSGTYPE_PING)
00593                 break;
00594             else
00595                 return false;
00596     }
00597 
00598     if (di->itemType == _ITEM_DROPDOWN) {
00599         if (di->selectable_strs == NULL || di->selectable_strs[0] == NULL) {
00600             log_printf("NULLstrs%u,%u\r\n", table_row, table_col);
00601             return false;
00602         }
00603     } else if (di->itemType == _ITEM_VALUE) {
00604         const value_item_t* vi = (const value_item_t*)m->itemPtr;
00605         if (vi->write == NULL)
00606             return false;
00607     } else if (di->itemType == _ITEM_BUTTON) {
00608         const button_item_t* bi = (const button_item_t*)m->itemPtr;
00609         if (bi->push == NULL)
00610             return false;
00611     } else if (di->itemType == _ITEM_TOGGLE) {
00612         const toggle_item_t * ti = (const toggle_item_t *)m->itemPtr;
00613         if (ti->push == NULL)
00614             return false;
00615     }
00616 
00617     return true;
00618 } // ..is_menu_item_changable()
00619 
00620 void read_menu_item(const menu_t* m, bool selected)
00621 {
00622     uint8_t valCol;
00623     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00624 
00625     switch (msg_type) {
00626         case MSG_TYPE_PACKET:
00627             if (m->flags & FLAG_MSGTYPE_PKT)
00628                 break;
00629             else
00630                 return;
00631         case MSG_TYPE_PER:
00632             if (m->flags & FLAG_MSGTYPE_PER)
00633                 break;
00634             else
00635                 return;
00636         case MSG_TYPE_PINGPONG:
00637             if (m->flags & FLAG_MSGTYPE_PING)
00638                 break;
00639             else
00640                 return;
00641     }
00642 
00643     printf("\e[%u;%uf", m->pos.row, m->pos.col);  // set (force) cursor to row;column
00644     valCol = m->pos.col;
00645     if (m->label) {
00646         printf("%s", m->label);
00647         valCol += strlen(m->label);
00648     }
00649     if (di->itemType == _ITEM_DROPDOWN) {
00650         if (di->read && di->printed_strs) {
00651             uint8_t ridx = di->read(false);
00652             if (selected)
00653                 printf("\e[7m");
00654             printf("%s", di->printed_strs[ridx]);
00655             if (selected)
00656                 printf("\e[0m");
00657         } else if (di->printed_strs) {
00658             printf("%s", di->printed_strs[0]);
00659         }
00660     } else if (di->itemType == _ITEM_VALUE) {
00661         const value_item_t* vi = (const value_item_t*)m->itemPtr;
00662         if (vi->print) {
00663             for (unsigned n = 0; n < vi->width; n++)
00664                 printf(" ");
00665 
00666             printf("\e[%u;%uf", m->pos.row, valCol);  // set (force) cursor to row;column
00667             if (selected)
00668                 printf("\e[7m");
00669             vi->print();
00670             if (selected)
00671                 printf("\e[0m");
00672         }
00673     } else if (di->itemType == _ITEM_BUTTON) {
00674         const button_item_t* bi = (const button_item_t*)m->itemPtr;
00675         if (bi->label) {
00676             if (selected)
00677                 printf("\e[7m%s\e[0m", bi->label);
00678             else
00679                 printf("%s", bi->label);
00680         }
00681     } else if (di->itemType == _ITEM_TOGGLE) {
00682         const toggle_item_t* ti = (const toggle_item_t *)m->itemPtr;
00683         bool on = ti->read();
00684         if (ti->label1) {
00685             const char* const cptr = on ? ti->label1 : ti->label0;
00686             if (selected)
00687                 printf("\e[7m%s\e[0m", cptr);
00688             else
00689                 printf("%s", cptr);
00690         } else {
00691             if (on)
00692                 printf("\e[1m");
00693             if (selected)
00694                 printf("\e[7m");
00695 
00696             printf("%s", ti->label0);
00697 
00698             if (selected || on)
00699                 printf("\e[0m");
00700         }
00701     }
00702     
00703 } // ..read_menu_item()
00704 
00705 void draw_menu()
00706 {
00707     unsigned table_row;
00708 
00709     for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) {
00710         int table_col;
00711         for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) {
00712             read_menu_item(menu_table[table_row][table_col], false);
00713         } // ..table column iterator
00714     } // ..table row iterator
00715 
00716     read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
00717     
00718     fflush(stdout);
00719     pc.sync();    
00720 } // ..draw_menu()
00721 
00722 typedef struct {
00723     int row;
00724     int col;
00725 } tablexy_t;
00726 
00727 void
00728 menu_init_(const menu_t* in, tablexy_t* tc)
00729 {
00730     unsigned n;
00731 
00732     for (n = 0; in[n].pos.row > 0; n++) {
00733         const menu_t* m = &in[n];
00734         if (tc->row != m->pos.row - 1) {
00735             tc->row = m->pos.row - 1;
00736             tc->col = 0;
00737         } else
00738             tc->col++;
00739 
00740         menu_table[tc->row][tc->col] = m;
00741 #ifdef MENU_DEBUG
00742         printf("table:%u,%u ", tc->row, tc->col);
00743         printf(" %d<%d? ", StopMenuCols[tc->row], tc->col);
00744 #endif /* MENU_DEBUG */
00745         if (StopMenuCols[tc->row] < tc->col)
00746             StopMenuCols[tc->row] = tc->col;
00747 #ifdef MENU_DEBUG
00748         printf("{%u %u}", tc->row, tc->col);
00749         printf("in:%p[%u] screen:%u,%u ", in, n, m->pos.row, m->pos.col);
00750         printf("pos@%p ", &m->pos);
00751         //printf(" loc:%p ", &in[n].itemPtr);
00752         if (in[n].itemPtr) {
00753             const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00754             printf(" itemPtr:%p type:%02x ", di, di->itemType);
00755         }
00756         printf("stopMenuCols[%u]: %d ", tc->row, StopMenuCols[tc->row]);
00757         if (m->label)
00758             printf("label:%s", m->label);
00759         else
00760             printf("noLabel");
00761         printf("\r\n");
00762 #endif /* MENU_DEBUG */
00763     }
00764 #ifdef MENU_DEBUG
00765     char ch;
00766     printf("hit key:");
00767     pc.read(&ch, 1);
00768 #endif /* MENU_DEBUG */
00769 
00770 }
00771 
00772 void navigate_dropdown(uint8_t ch)
00773 {
00774     unsigned n;
00775     const menu_t* m = menuState.sm;
00776     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00777 
00778     switch (ch) {
00779         case 'A':   // cursor UP
00780             if (menuState.sel_idx > 0) {
00781                 menuState.sel_idx--;
00782             }
00783             break;
00784         case 'B':   // cursor DOWN
00785             if (di->selectable_strs[menuState.sel_idx+1] != NULL)
00786                 menuState.sel_idx++;
00787             break;
00788     } // ..switch (ch)
00789 
00790     for (n = 0; di->selectable_strs[n] != NULL; n++) {
00791         printf("\e[%u;%uf", m->pos.row+n, menuState.dropdown_col);
00792         if (n == menuState.sel_idx)
00793             printf("\e[7m");
00794         printf("%s", di->selectable_strs[n]);
00795         if (n == menuState.sel_idx)
00796             printf("\e[0m");
00797     }
00798     printf("\e[%u;%uf", m->pos.row + menuState.sel_idx, menuState.dropdown_col + strlen(di->selectable_strs[menuState.sel_idx]));
00799 }
00800 
00801 bool is_item_selectable(const menu_t* m)
00802 {
00803     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00804 
00805     if (di->itemType == _ITEM_BUTTON) {
00806         const button_item_t* bi = (const button_item_t*)m->itemPtr;
00807         if (bi->push == NULL)
00808             return false;
00809     } else if (di->itemType == _ITEM_TOGGLE) {
00810         const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr;
00811         if (ti->push == NULL)
00812             return false;
00813     }
00814 
00815     return true;
00816 }
00817 
00818 void navigate_menu(uint8_t ch)
00819 {
00820     //logpc_printf("navigate_menu %c\r\n", ch);
00821     read_menu_item(menu_table[curpos.row][curpos.tableCol], false);
00822 
00823     switch (ch) {
00824         case 'A':   // cursor UP
00825             if (curpos.row == 0)
00826                 break;
00827 
00828             {   // find previous row up with column
00829                 int8_t row;
00830                 for (row = curpos.row - 1; row >= 0; row--) {
00831                     if (StopMenuCols[row] > -1) {
00832                         curpos.row = row;
00833                         break;
00834                     }
00835                 }
00836                 if (row == 0 && StopMenuCols[0] < 0)
00837                     break;  // nothing found
00838             }
00839 
00840             if (curpos.tableCol >= StopMenuCols[curpos.row]) {
00841                 curpos.tableCol = StopMenuCols[curpos.row]-1;
00842             }
00843 
00844             break;
00845         case 'B':   // cursor DOWN
00846             if (curpos.row >= MAX_MENU_ROWS)
00847                 break;
00848 
00849             {   // find next row down with column
00850                 uint8_t row;
00851                 for (row = curpos.row + 1; row < MAX_MENU_ROWS; row++) {
00852                     if (StopMenuCols[row] != -1) {
00853                         curpos.row = row;
00854                         break;
00855                     }
00856                 }
00857                 if (row == MAX_MENU_ROWS-1 && StopMenuCols[row] == -1)
00858                     break;  // nothing found
00859             }
00860 
00861             if (curpos.tableCol >= StopMenuCols[curpos.row]) {
00862                 curpos.tableCol = StopMenuCols[curpos.row]-1;
00863             }
00864 
00865 
00866             break;
00867         case 'C':    // cursor LEFT
00868             if (curpos.tableCol >= StopMenuCols[curpos.row]-1)
00869                 break;
00870 
00871             { // find next row left with editable
00872                 uint8_t tcol;
00873                 for (tcol = curpos.tableCol + 1; tcol < StopMenuCols[curpos.row]; tcol++) {
00874                     if (is_menu_item_changable(curpos.row, tcol)) {
00875                         curpos.tableCol = tcol;
00876                         break;
00877                     }
00878                 }
00879             }
00880 
00881             break;
00882         case 'D':   // cursor RIGHT
00883             if (curpos.tableCol == 0)
00884                 break;
00885 
00886             {
00887                 int8_t tcol;
00888                 for (tcol = curpos.tableCol - 1;  tcol >= 0; tcol--) {
00889                     if (is_menu_item_changable(curpos.row, tcol)) {
00890                         curpos.tableCol = tcol;
00891                         break;
00892                     }
00893                 }
00894             }
00895 
00896             break;
00897         default:
00898             //printf("unhancled-csi:%02x\eE", ch);
00899             break;
00900     } // ..switch (ch)
00901 
00902     if (!is_item_selectable(menu_table[curpos.row][curpos.tableCol])) {
00903         int c;
00904         for (c = 0; c < StopMenuCols[curpos.row]; c++) {
00905             if (is_item_selectable(menu_table[curpos.row][c])) {
00906                 curpos.tableCol = c;
00907                 break;
00908             }
00909         }
00910         if (c == StopMenuCols[curpos.row])
00911             return;
00912     }
00913 
00914 #ifdef MENU_DEBUG
00915     log_printf("table:%u,%u screen:%u,%u      \r\n", curpos.row, curpos.tableCol, 
00916         menu_table[curpos.row][curpos.tableCol]->pos.row,
00917         menu_table[curpos.row][curpos.tableCol]->pos.col
00918     );
00919 #endif /* MENU_DEBUG */
00920 
00921     read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
00922 } // ..navigate_menu
00923 
00924 
00925 bool is_sweep_menu_item(const void* const itemPtr)
00926 {
00927     if (itemPtr == &sweep_start_item ||
00928         itemPtr == &sweep_stop_item ||
00929         itemPtr == &sweep_dwell_item ||
00930         itemPtr == &tx_carrier_item ||
00931         itemPtr == &tx_preamble_item
00932     )
00933         return true;
00934     else
00935         return false;
00936 }
00937 
00938 void commit_menu_item_change()
00939 {
00940     const menu_t* m = menu_table[curpos.row][curpos.tableCol];
00941     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00942 
00943     if (di->itemType == _ITEM_DROPDOWN) {
00944         menuState.mode = di->write(menuState.sel_idx);
00945 
00946         printf("\e[%u;%uf", m->pos.row, m->pos.col-2);
00947     } else if (di->itemType == _ITEM_VALUE) {
00948         const value_item_t* vi = (const value_item_t*)m->itemPtr;
00949         /* commit value entry */
00950         if (vi->write) {
00951             if (vi->write(entry_buf))
00952                 menuState.mode = MENUMODE_REDRAW;
00953             else
00954                 menuState.mode = MENUMODE_NONE;
00955         } else
00956             menuState.mode = MENUMODE_NONE;
00957 
00958         if (menuState.mode == MENUMODE_NONE) {
00959             read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
00960         }
00961     }
00962 
00963 } // ..commit_menu_item_change()
00964 
00965 void refresh_item_in_table(const void* item)
00966 {
00967     unsigned table_row;
00968 
00969     if (item == NULL)
00970         return;
00971 
00972     for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) {
00973         int table_col;
00974         for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) {
00975             //log_printf("%u %u %p\r\n", table_row, table_col, menu_table[table_row][table_col]->itemPtr);
00976             if (item == menu_table[table_row][table_col]->itemPtr) {
00977                 read_menu_item(menu_table[table_row][table_col], false);
00978                 return;
00979             }
00980         }
00981     }
00982 }
00983 
00984 void
00985 start_value_entry(const menu_t* m)
00986 {
00987     const value_item_t* vi = (const value_item_t*)m->itemPtr;
00988     uint8_t col = m->pos.col;
00989 
00990     if (m->label)
00991         col += strlen(m->label);
00992 
00993     printf("\e[%u;%uf", m->pos.row, col);
00994     for (unsigned i = 0; i < vi->width; i++)
00995         printf(" ");   // clear displayed value for user entry
00996 
00997     printf("\e[%u;%uf", m->pos.row, col);
00998     menuState.mode = MENUMODE_ENTRY;
00999     entry_buf_idx = 0;
01000 }
01001 
01002 void start_menu_item_change()
01003 {
01004     const menu_t* m = menu_table[curpos.row][curpos.tableCol];
01005     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
01006     bool checkRefresh = false;
01007 
01008     if (di->itemType == _ITEM_DROPDOWN && di->selectable_strs) {
01009         menuState.dropdown_col = m->pos.col;
01010         unsigned n, sidx = 0;
01011         /* start dropdown */
01012         if (di->read)
01013             sidx = di->read(true);
01014 
01015         if (m->label)
01016             menuState.dropdown_col += strlen(m->label);
01017 
01018         for (n = 0; di->selectable_strs[n] != NULL; n++) {
01019             uint8_t col = menuState.dropdown_col;
01020             bool leftPad = false;
01021             if (col > 3 && n > 0) { // dropdown left side padding
01022                 col -= 2;
01023                 leftPad = true;
01024             }
01025             printf("\e[%u;%uf", m->pos.row+n, col);
01026             if (leftPad ) {
01027                 printf("  ");
01028             }
01029             if (n == sidx)
01030                 printf("\e[7m");
01031             printf("%s", di->selectable_strs[n]);
01032             if (n == sidx)
01033                 printf("\e[0m");
01034             printf("  ");   // right side padding
01035         }
01036         printf("\e[%u;%uf", m->pos.row, menuState.dropdown_col-2);
01037 
01038         menuState.mode = MENUMODE_DROPDOWN;
01039         menuState.sel_idx = sidx;
01040         menuState.sm = m;
01041     } else if (di->itemType == _ITEM_VALUE) {
01042         /* start value entry */
01043         start_value_entry(m);
01044     } else if (di->itemType == _ITEM_BUTTON) {
01045         const button_item_t* bi = (const button_item_t*)m->itemPtr;
01046         if (bi->push) {
01047             bi->push();
01048             checkRefresh = true;
01049         }
01050     } else if (di->itemType == _ITEM_TOGGLE) {
01051         const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr;
01052         if (ti->push) {
01053             bool on = ti->push();
01054             uint8_t col = m->pos.col;
01055 
01056             if (m->label)
01057                 col += strlen(m->label);
01058 
01059             printf("\e[%u;%uf", m->pos.row, col);
01060             if (ti->label1) {
01061                 printf("\e[7m%s\e[0m", on ? ti->label1 : ti->label0);
01062             } else {
01063                 if (on)
01064                     printf("\e[1;7m%s\e[0m", ti->label0);
01065                 else
01066                     printf("\e[7m%s\e[0m", ti->label0);
01067             }
01068             checkRefresh = true;
01069         }
01070     }
01071 
01072     if (checkRefresh) {
01073         if (m->refreshReadItem) {
01074             refresh_item_in_table(m->refreshReadItem);  // read associated
01075             read_menu_item(m, true);   // restore cursor
01076         }
01077     }
01078 
01079 } // ..start_menu_item_change()
01080 
01081 void full_menu_init()
01082 {
01083     unsigned n;
01084     const menu_t *m;
01085     tablexy_t txy;
01086 
01087     txy.row = INT_MAX;
01088     txy.col = 0;
01089 
01090     for (n = 0; n < MAX_MENU_ROWS; n++) {
01091         StopMenuCols[n] = -1;
01092     }
01093 
01094     menu_init_(common_menu, &txy);
01095 
01096     menu_init_(Radio::common_menu, &txy);
01097 
01098     m = Radio::get_modem_menu();
01099     if (m == NULL) {
01100         log_printf("NULL-modemMenu\r\n");
01101         for (;;) asm("nop");
01102     }
01103 #ifdef MENU_DEBUG
01104     printf("modemmenuInit\r\n");
01105 #endif
01106     menu_init_(m, &txy);
01107 
01108     m = Radio::get_modem_sub_menu();
01109     if (m) {
01110 #ifdef MENU_DEBUG
01111         printf("modemsubmenuInit\r\n");
01112 #endif
01113         menu_init_(m, &txy);
01114     }
01115 #ifdef MENU_DEBUG
01116     else
01117         printf("no-modemsubmenu\r\n");
01118 #endif
01119 
01120     m = get_msg_menu();
01121     if (m == NULL) {
01122         log_printf("NULL-msgMenu\r\n");
01123         for (;;) asm("nop");
01124     }
01125     menu_init_(m, &txy);
01126 
01127     for (n = 0; n < MAX_MENU_ROWS; n++) {
01128         if (StopMenuCols[n] != -1)
01129             StopMenuCols[n]++;
01130     }
01131 }
01132 
01133 bool ishexchar(char ch)
01134 {
01135     if (((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) || (ch >= '0' && ch <= '9'))
01136         return true;
01137     else
01138         return false;
01139 }
01140 
01141 enum _urx_ {
01142     URX_STATE_NONE = 0,
01143     URX_STATE_ESCAPE,
01144     URX_STATE_CSI,
01145 } uart_rx_state;
01146 
01147 Timeout uartRxTimeout;
01148 
01149 void uart_rx_timeout()
01150 {
01151     /* escape by itself: abort change on item */
01152     menuState.mode = MENUMODE_REDRAW;
01153 
01154     uart_rx_state = URX_STATE_NONE;
01155 }
01156 
01157 void sweeper()
01158 {
01159     if (cf_sweep.ticking) {
01160         cf_sweep.tick = true;
01161     }
01162 }
01163 
01164 void sweep_service()
01165 {
01166     const menu_t* m = menu_table[curpos.row][curpos.tableCol];
01167 
01168     if (is_sweep_menu_item(m->itemPtr)) {
01169         if (cf_sweep.start_MHz != 0 && cf_sweep.stop_MHz > cf_sweep.start_MHz && cf_sweep.dwell_ms > 0) {
01170             std::chrono::microseconds us = std::chrono::microseconds(cf_sweep.dwell_ms * 1000);
01171             cf_sweep.MHz = cf_sweep.start_MHz;
01172             cf_sweep.ticker.attach(sweeper, us);
01173             cf_sweep.ticking = true;
01174         }
01175     } else {
01176         if (cf_sweep.ticking) {
01177             cf_sweep.ticker.detach();
01178             cf_sweep.ticking = false;
01179         }
01180     }
01181 }
01182 
01183 void serial_callback()
01184 {
01185     char serial_write_buf[4];
01186     unsigned char serial_write_buf_idx = 0;
01187     char ch;
01188     pc.read(&ch, 1);
01189     
01190     switch (uart_rx_state) {
01191         case URX_STATE_NONE:
01192             if (ch == 0x1b) {
01193                 if (menuState.mode == MENUMODE_ENTRY) {
01194                     /* abort entry mode */
01195                     menuState.mode = MENUMODE_NONE;
01196                     read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
01197                 } else {
01198                     uart_rx_state = URX_STATE_ESCAPE;
01199                     if (menuState.mode != MENUMODE_NONE) {
01200                         /* is this escape by itself, user wants to abort? */
01201                         uartRxTimeout.attach(uart_rx_timeout, 30ms);
01202                     }
01203                 }
01204             } else if (ch == 2) { // ctrl-B
01205                 log_printf("--------------\r\n");
01206             } else if (ch == '\r') {
01207                 if (menuState.mode == MENUMODE_NONE) {
01208                     start_menu_item_change();
01209                 } else {
01210                     entry_buf[entry_buf_idx] = 0;
01211                     commit_menu_item_change();
01212                 }
01213                 sweep_service();
01214             } else if (menuState.mode == MENUMODE_ENTRY) {
01215                 if (ch == 8) {
01216                     if (entry_buf_idx > 0) {
01217                         serial_write_buf[serial_write_buf_idx++] = 8;
01218                         serial_write_buf[serial_write_buf_idx++] = ' ';
01219                         serial_write_buf[serial_write_buf_idx++] = 8;
01220                         entry_buf_idx--;
01221                     }
01222                 } else if (ch == 3) {    // ctrl-C
01223                     menuState.mode = MENUMODE_NONE;
01224                 } else if (entry_buf_idx < sizeof(entry_buf)) {
01225                     entry_buf[entry_buf_idx++] = ch;
01226                     serial_write_buf[serial_write_buf_idx++] = ch;
01227                 }
01228             } else if (menuState.mode == MENUMODE_NONE) {
01229                 if (ishexchar(ch) || ch == '-') {   // characters which start entry
01230                     const value_item_t* vi = (const value_item_t*)menu_table[curpos.row][curpos.tableCol]->itemPtr;
01231                     if (vi->itemType == _ITEM_VALUE) {
01232                         start_value_entry(menu_table[curpos.row][curpos.tableCol]);
01233                         entry_buf[entry_buf_idx++] = ch;
01234                         serial_write_buf[serial_write_buf_idx++] = ch;
01235                     }
01236                 } else if (ch == 'r') {
01237                     menuState.mode = MENUMODE_REDRAW;
01238                 } else if (ch == '.') {
01239                     Radio::test();
01240                 }
01241 
01242             }
01243             break;
01244         case URX_STATE_ESCAPE:
01245             uartRxTimeout.detach();
01246             if (ch == '[')
01247                 uart_rx_state = URX_STATE_CSI;
01248             else {
01249 #ifdef MENU_DEBUG
01250                 log_printf("unhancled-esc:%02x\r\n", ch);
01251 #endif /* MENU_DEBUG */
01252                 uart_rx_state = URX_STATE_NONE;
01253             }
01254             break;
01255         case URX_STATE_CSI:
01256             if (menuState.mode == MENUMODE_NONE)
01257                 navigate_menu(ch);
01258             else if (menuState.mode == MENUMODE_DROPDOWN)
01259                 navigate_dropdown(ch);
01260 
01261             uart_rx_state = URX_STATE_NONE;
01262             //printf("\e[18;1f");  // set (force) cursor to row;column
01263             break;
01264     } // ..switch (uart_rx_state)
01265     
01266     fflush(stdout);
01267 
01268     if (serial_write_buf_idx > 0) {
01269         pc.write(serial_write_buf, serial_write_buf_idx);
01270     }
01271     
01272     pc.sync();
01273 }
01274 
01275 void txDone()
01276 {
01277 
01278     if (msg_type == MSG_TYPE_PER) {
01279         log_printf("CntPacketTx%u, max:%u  ipd%u\r\n", CntPacketTx, MaxNumPacket, tx_ipd_ms);
01280         if (++CntPacketTx <= MaxNumPacket) {
01281             std::chrono::microseconds us = std::chrono::microseconds(tx_ipd_ms * 1000);
01282             mbedTimeout.attach(next_tx_callback, us);
01283         }
01284     } else if (msg_type == MSG_TYPE_PINGPONG) {
01285         if (flags.ping_master) {
01286             ++CntPacketTx;
01287         }
01288 
01289         Radio::Rx();
01290     }
01291 }
01292 
01293 void
01294 printRxPkt(uint8_t size)
01295 {
01296     uint8_t col;
01297     char str[80];
01298     char *ptr, *endPtr;
01299     unsigned n = 0;
01300     endPtr = str + sizeof(str);
01301     ptr = str;
01302     col = 0;
01303     while (ptr < endPtr) {
01304         //sprintf(ptr, "%02x%c", Radio::radio.rx_buf[n], n == Radio::radio.rx_buf_offset ? '_' : ' '); // LR1110
01305         sprintf(ptr, "%02x ", Radio::radio.rx_buf[n]);
01306         ptr += 3;
01307         col += 3;
01308         if (col >= 77) {
01309             log_printf("%s\r\n", str);
01310             ptr = str;
01311             col = 0;
01312         }
01313         if (++n >= size)
01314             break;
01315     }
01316     log_printf("%s\r\n", str);
01317 }
01318 
01319 void rxDone(uint8_t size, float rssi, float snr)
01320 {
01321     log_printf("rxDone %u, %.1fdBm  %.1fdB\r\n", size, rssi, snr);
01322     if (msg_type == MSG_TYPE_PACKET) {
01323         printRxPkt(size);
01324     } else if (msg_type == MSG_TYPE_PER) {
01325         if (memcmp(Radio::radio.rx_buf+4, PerMsg, 3) == 0) {
01326             unsigned i, PacketRxSequence = Radio::radio.rx_buf[0];
01327             PacketRxSequence <<= 8;
01328             PacketRxSequence += Radio::radio.rx_buf[1];
01329             PacketRxSequence <<= 8;
01330             PacketRxSequence += Radio::radio.rx_buf[2];
01331             PacketRxSequence <<= 8;
01332             PacketRxSequence += Radio::radio.rx_buf[3];
01333 
01334             CntPacketRxOK++;
01335 
01336             if (PacketRxSequence <= PacketRxSequencePrev || PacketRxSequencePrev == 0)
01337                 i = 0;  // sequence reset to resync, dont count missed packets this time
01338             else
01339                 i = PacketRxSequence - PacketRxSequencePrev - 1;
01340 
01341 
01342             CntPacketRxKO += i;
01343             RxTimeOutCount = 0;
01344             log_printf("PER rx%u  ok%u   ko%u\r\n", PacketRxSequence , CntPacketRxOK, CntPacketRxKO);
01345 
01346             PacketRxSequencePrev = PacketRxSequence;
01347         } // ..if PerMsg
01348         else {
01349             log_printf("per?\r\n");
01350             printRxPkt(size);
01351         }
01352     } else if (msg_type == MSG_TYPE_PINGPONG) {
01353         if (memcmp(Radio::radio.rx_buf+4, PingMsg, 4) == 0) {
01354             /* ping slave rx */
01355             Radio::setFS();
01356             receivedCntPacket = Radio::radio.rx_buf[0];
01357             receivedCntPacket <<= 8;
01358             receivedCntPacket += Radio::radio.rx_buf[1];
01359             receivedCntPacket <<= 8;
01360             receivedCntPacket += Radio::radio.rx_buf[2];
01361             receivedCntPacket <<= 8;
01362             receivedCntPacket += Radio::radio.rx_buf[3];
01363             log_printf("%u rxPing->txPong\r\n", receivedCntPacket);
01364 
01365             flags.ping_master = 0;
01366             flags.send_pong = 1;
01367 
01368         } else if (memcmp(Radio::radio.rx_buf+8, PongMsg, 4) == 0) {
01369             unsigned cnt;
01370             /* ping master rx */
01371             Radio::setFS();
01372             cnt = Radio::radio.rx_buf[0];
01373             cnt <<= 8;
01374             cnt += Radio::radio.rx_buf[1];
01375             cnt <<= 8;
01376             cnt += Radio::radio.rx_buf[2];
01377             cnt <<= 8;
01378             cnt += Radio::radio.rx_buf[3];
01379             log_printf("%u rxPong->txPing\r\n", cnt);
01380             flags.send_ping = 1;
01381         } else {
01382             log_printf("pingpong?\r\n");
01383             printRxPkt(size);
01384         }
01385     } else {
01386         /*for (unsigned n = 0; n < size; n++)
01387             log_printf("%02x\r\n", Radio::radio.rx_buf[n]);*/
01388         log_printf("msg_type %u\r\n", msg_type);
01389     }
01390 
01391 }
01392 
01393 const RadioEvents_t rev = {
01394     txDone,
01395     rxDone
01396 };
01397 
01398 volatile int foo;
01399 
01400 int main()
01401 {
01402     lfsr = LFSR_INIT;
01403     msg_type = MSG_TYPE_PACKET;
01404 
01405     uart_rx_state = URX_STATE_NONE;
01406 
01407     Radio::boardInit(&rev);
01408 
01409     {
01410         unsigned n;
01411         for (n = 0; n < MAX_MENU_ROWS; n++)
01412             StopMenuCols[n] = -1;
01413     }
01414 
01415     botRow = MAX_MENU_ROWS + SCROLLING_ROWS;
01416 
01417     printf("\e[7h"); // enable line wrapping
01418     printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region
01419     printf("\e[2J"); // erase entire screen
01420 
01421     full_menu_init();
01422 
01423     printf("\e[2J"); // erase entire screen
01424 
01425     menuState.mode = MENUMODE_NONE;
01426 
01427     draw_menu();
01428 
01429     curpos.row = 0;
01430     curpos.tableCol = 0;
01431 
01432     tx_ipd_ms = 100;
01433 
01434     logpc_printf("\r\nRESET\r\n");
01435     
01436     for (;;) {
01437         if (pc.readable()) {
01438             serial_callback();
01439         }
01440 
01441         if (flags.send_ping) {
01442             if (flags.pingpongEnable)
01443                 SendPing();
01444             flags.send_ping = 0;
01445         }
01446 
01447         if (flags.send_pong) {
01448             if (flags.pingpongEnable)
01449                 SendPong();
01450             flags.send_pong = 0;
01451         }
01452 
01453         if (flags.do_next_tx) {
01454             do_next_tx();
01455             flags.do_next_tx = 0;
01456         }
01457 
01458         if (menuState.mode == MENUMODE_REINIT_MENU) {
01459             full_menu_init();
01460             menuState.mode = MENUMODE_REDRAW;
01461         }
01462 
01463         if (menuState.mode == MENUMODE_REDRAW) {
01464             // erase entire screen, some dropdowns extend to scrolling area
01465             printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region, if terminal started after
01466             printf("\e[2J");
01467             //printf("\e[%u;1f\e[1J", MAX_MENU_ROWS);  // erase menu area
01468 
01469             menuState.mode = MENUMODE_NONE;
01470             draw_menu();
01471         }
01472 
01473         if (Radio::service(menuState.mode == MENUMODE_NONE ? LAST_CHIP_MENU_ROW : -1)) {
01474             read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
01475         }
01476 
01477         if (cf_sweep.tick) {
01478             cf_sweep.tick = false;
01479 #ifdef SX127x_H
01480             Radio::radio.set_frf_MHz(cf_sweep.MHz);
01481 #else
01482             Radio::radio.setMHz(cf_sweep.MHz);
01483 #endif
01484             cf_sweep.MHz += cf_sweep.step_MHz;
01485             if (cf_sweep.MHz >= cf_sweep.stop_MHz)
01486                 cf_sweep.MHz = cf_sweep.start_MHz;
01487         }
01488 
01489     } // ..for (;;)
01490 }
01491 
01492 char strbuf[255];
01493 
01494 void log_printf(const char* format, ...)
01495 {
01496     va_list arglist;
01497 
01498     // put cursor at last scrolling-area line
01499     printf("\e[%u;1f", botRow);
01500     va_start(arglist, format);
01501     vsprintf(strbuf, format, arglist);
01502     va_end(arglist);
01503 
01504     printf("%s", strbuf);
01505     
01506     fflush(stdout);
01507     pc.sync();
01508 }
01509 
01510 void logpc_printf(const char* format, ...)  /* printing on 2nd uart */
01511 {
01512     int len;
01513     va_list arglist;
01514 
01515     va_start(arglist, format);
01516     len = vsprintf(strbuf, format, arglist);
01517     va_end(arglist);
01518 
01519     //logpc.write(strbuf, len);
01520     
01521     fflush(stdout);
01522     pc.sync();
01523 }