test sending sensor results over lora radio. Accelerometer and temp/pressure.

Dependencies:   SX127x

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 #include "demo.h"
00004 
00005 bool svc_lis2dh12;
00006 //#define MENU_DEBUG
00007 
00008 RawSerial pc(USBTX, USBRX);
00009 
00010 menuState_t menuState;
00011 
00012 typedef enum {
00013     MSG_TYPE_PACKET = 0,
00014     MSG_TYPE_PER,
00015     MSG_TYPE_PINGPONG
00016 } msgType_e;
00017 
00018 enum {
00019     CMD_TEMP,
00020     CMD_PRES,
00021     CMD_ACCEL,
00022     CMD_PHOTOS,
00023     CMD_NONE
00024 };
00025 
00026 #define MAX_MENU_ROWS       16
00027 // [row][column]
00028 const menu_t* menu_table[MAX_MENU_ROWS][MAX_MENU_COLUMNS];
00029 int8_t StopMenuCols[MAX_MENU_ROWS];
00030 
00031 //#define SCROLLING_ROWS      20
00032 #define SCROLLING_ROWS      30
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 = MSG_TYPE_PACKET;
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     uint8_t txBusy          : 1; // 5
00055     uint8_t measure_tx      : 1; // 5
00056 } flags;
00057 
00058 uint8_t botRow;
00059 int regAddr = -1;
00060 
00061 #define PHOTOS_PIN          PA_0
00062 /*
00063  * available pins PA_0, PA_1, PA_2, PA_3, PA_4, PA_5, PA_6, PA_7, PB_0, PB_1, PC_0, PC_1, PC_2
00064 */
00065 #ifdef PHOTOS_PIN
00066 AnalogIn photos(PHOTOS_PIN);
00067 #endif /* PHOTOS_PIN */
00068 
00069 #define UART_RX_BUF_LEN     8
00070 uint8_t uart_rx_buf[UART_RX_BUF_LEN];
00071 volatile uint8_t uart_rx_buf_in;
00072 volatile uint8_t uart_rx_buf_out;
00073 bool accel_tx_en;
00074 
00075 #ifdef MENU_DEBUG
00076 char wait_uart_rx()
00077 {
00078     char ret;
00079     unsigned foo = uart_rx_buf_in;
00080     while (uart_rx_buf_in == foo) {
00081         asm("nop");
00082     }
00083 
00084     ret = uart_rx_buf[uart_rx_buf_out++];
00085     if (uart_rx_buf_out == UART_RX_BUF_LEN)
00086         uart_rx_buf_out = 0;
00087 
00088     return ret;
00089 }
00090 #endif /* MENU_DEBUG */
00091 
00092 unsigned lfsr;
00093 #define LFSR_INIT       0x1ff
00094 
00095 uint8_t get_pn9_byte()
00096 {
00097     uint8_t ret = 0;
00098     int xor_out;
00099 
00100     xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf);   // four bits at a time
00101     lfsr = (lfsr >> 4) | (xor_out << 5);    // four bits at a time
00102 
00103     ret |= (lfsr >> 5) & 0x0f;
00104 
00105     xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf);   // four bits at a time
00106     lfsr = (lfsr >> 4) | (xor_out << 5);    // four bits at a time
00107 
00108     ret |= ((lfsr >> 1) & 0xf0);
00109 
00110     return ret;
00111 }
00112 
00113 void hw_reset_push()
00114 {
00115     Radio::hw_reset();
00116 
00117     Radio::readChip();
00118     menuState.mode = MENUMODE_REINIT_MENU;
00119 }
00120 
00121 const button_item_t hw_reset_item = { _ITEM_BUTTON, "hw_reset", hw_reset_push };
00122 
00123 void clearIrqs_push()
00124 {
00125     Radio::clearIrqFlags();
00126 }
00127 
00128 const button_item_t clearirqs_item = { _ITEM_BUTTON, "clearIrqs", clearIrqs_push };
00129 
00130 const dropdown_item_t pktType_item = { _ITEM_DROPDOWN, Radio::pktType_strs, Radio::pktType_strs, Radio::pktType_read, Radio::pktType_write};
00131 
00132 
00133 unsigned msgType_read(bool fw)
00134 {
00135     return msg_type;
00136 }
00137 
00138 menuMode_e msgType_write(unsigned widx)
00139 {
00140     msg_type = (msgType_e)widx;
00141 
00142     if (msg_type == MSG_TYPE_PER) {
00143         flags.pingpongEnable = 0;
00144         if (Radio::get_payload_length() < 7)
00145             Radio::set_payload_length(7);
00146     } else if (msg_type == MSG_TYPE_PINGPONG) {
00147         if (Radio::get_payload_length() != 12)
00148             Radio::set_payload_length(12);
00149     } else if (msg_type == MSG_TYPE_PACKET) {
00150         flags.pingpongEnable = 0;
00151     }
00152 
00153     return MENUMODE_REINIT_MENU;
00154 }
00155 
00156 const char* const msgType_strs[] = {
00157     "PACKET ",
00158     "PER    ",
00159     "PINGPONG",
00160     NULL
00161 };
00162 
00163 const dropdown_item_t msgType_item = { _ITEM_DROPDOWN, msgType_strs, msgType_strs, msgType_read, msgType_write};
00164 
00165 #define LAST_CHIP_MENU_ROW          (MAX_MENU_ROWS-5)
00166 
00167 const value_item_t tx_payload_length_item = { _ITEM_VALUE, 5, Radio::tx_payload_length_print, Radio::tx_payload_length_write};
00168 
00169 void pn9_push()
00170 {
00171     uint8_t i, len = Radio::get_payload_length();
00172     for (i = 0; i < len; i++)
00173         Radio::radio.tx_buf[i] = get_pn9_byte();
00174 }
00175 
00176 const button_item_t tx_payload_pn9_item  = { _ITEM_BUTTON, "PN9", pn9_push };
00177 
00178 void regAddr_print()
00179 {
00180     if (regAddr != -1)
00181         pc.printf("%x", regAddr);
00182 }
00183 
00184 bool regAddr_write(const char* txt)
00185 {
00186     sscanf(txt, "%x", &regAddr);
00187     return false;
00188 }
00189 
00190 const value_item_t regAddr_item = { _ITEM_VALUE, 5, regAddr_print, regAddr_write};
00191 
00192 void regValue_print()
00193 {
00194     if (regAddr != -1) {
00195         pc.printf("%x", Radio::read_register(regAddr));
00196     }
00197 }
00198 
00199 bool regValue_write(const char* txt)
00200 {
00201     unsigned val;
00202     sscanf(txt, "%x", &val);
00203     if (regAddr != -1) {
00204         Radio::write_register(regAddr, val);
00205     }
00206     return false;
00207 }
00208 
00209 const value_item_t regValue_item = { _ITEM_VALUE, 5, regValue_print, regValue_write};
00210 
00211 void tx_payload_print()
00212 {
00213     uint8_t i, len = Radio::get_payload_length();
00214     for (i = 0; i < len; i++)
00215         pc.printf("%02x ", Radio::radio.tx_buf[i]);
00216 }
00217 
00218 bool tx_payload_write(const char* txt)
00219 {
00220     unsigned o, i = 0, pktidx = 0;
00221     while (i < entry_buf_idx) {
00222         sscanf(entry_buf+i, "%02x", &o);
00223         pc.printf("%02x ", o);
00224         i += 2;
00225         Radio::radio.tx_buf[pktidx++] = o;
00226         while (entry_buf[i] == ' ' && i < entry_buf_idx)
00227             i++;
00228     }
00229     log_printf("set payload len %u\r\n", pktidx);
00230     Radio::set_payload_length(pktidx);
00231     return true;
00232 }
00233 
00234 const value_item_t tx_payload_item = { _ITEM_VALUE, 80, tx_payload_print, tx_payload_write};
00235 
00236 void txpkt_push()
00237 {
00238     Radio::txPkt();
00239 }
00240 
00241 const button_item_t tx_pkt_item  = { _ITEM_BUTTON, "TXPKT", txpkt_push };
00242 
00243 void rxpkt_push()
00244 {
00245     Radio::Rx();
00246     menuState.mode = MENUMODE_REDRAW;
00247 }
00248 const button_item_t rx_pkt_item  = { _ITEM_BUTTON, "RXPKT", rxpkt_push };
00249 
00250 const dropdown_item_t opmode_item = { _ITEM_DROPDOWN, Radio::opmode_status_strs, Radio::opmode_select_strs, Radio::opmode_read, Radio::opmode_write };
00251 
00252 void tx_carrier_push()
00253 {
00254     Radio::tx_carrier();
00255 }
00256 const button_item_t tx_carrier_item = { _ITEM_BUTTON, "TX_CARRIER", tx_carrier_push };
00257 
00258 void tx_preamble_push()
00259 {
00260     Radio::tx_preamble();
00261 }
00262 const button_item_t tx_preamble_item = { _ITEM_BUTTON, "TX_PREAMBLE", tx_preamble_push };
00263 
00264 bool accel_en_read()
00265 {
00266     uint8_t status;
00267     accel_is_enabled(&status);
00268     return status;
00269 }
00270 
00271 bool accel_tx_en_read()
00272 {
00273     uint8_t status;
00274     accel_is_enabled(&status);
00275     return status && accel_tx_en;
00276 }
00277 
00278 
00279 bool accel_en_push()
00280 {
00281     uint8_t status;
00282     accel_is_enabled(&status);
00283 
00284     accel_tx_en = false;
00285     if (status)
00286         accel_enable(false);
00287     else
00288         accel_enable(true);
00289 
00290     accel_is_enabled(&status);
00291     return status;
00292 }
00293 
00294 bool accel_tx_en_push()
00295 {
00296     uint8_t status;
00297     accel_is_enabled(&status);
00298 
00299     accel_tx_en = true;
00300     if (status)
00301         accel_enable(false);
00302     else
00303         accel_enable(true);
00304 
00305     accel_is_enabled(&status);
00306     return status;
00307 }
00308 
00309 const toggle_item_t accel_enable_item = { _ITEM_TOGGLE, "enable", NULL, accel_en_read, accel_en_push};
00310 const toggle_item_t accel_tx_enable_item = { _ITEM_TOGGLE, "tx_enable", NULL, accel_tx_en_read, accel_tx_en_push};
00311 
00312 const menu_t lis12dh12_menu[] = {
00313     { {LAST_CHIP_MENU_ROW-1,  1}, "LIS12DH12 ", &accel_enable_item, FLAG_MSGTYPE_ALL },
00314     { {LAST_CHIP_MENU_ROW-1,  24}, NULL, &accel_tx_enable_item, FLAG_MSGTYPE_ALL },
00315     { {0, 0}, NULL, NULL }
00316 };
00317 
00318 
00319 #ifdef PHOTOS_PIN
00320 void photos_push()
00321 {
00322     uint16_t val = photos.read_u16();
00323     log_printf("photos %u\r\n", val);
00324 }
00325 
00326 void photos_tx()
00327 {
00328     uint16_t val;
00329     val = photos.read_u16();
00330     log_printf("Tx photos %u\r\n", val);
00331     Radio::radio.tx_buf[0] = CMD_PHOTOS;
00332     Radio::set_payload_length(sizeof(uint16_t)+1);
00333     memcpy(&Radio::radio.tx_buf[1], &val, sizeof(uint16_t));
00334 
00335     flags.txBusy = true;
00336     Radio::txPkt();
00337 }
00338 
00339 unsigned pollTickerRate;
00340 Ticker pollTicker;
00341 uint8_t enabledSensor = CMD_NONE;
00342 
00343 void tx_photos_push()
00344 {
00345     if (pollTickerRate > 0) {
00346         if (enabledSensor != CMD_PHOTOS)
00347             enabledSensor = CMD_PHOTOS;
00348         else {
00349             enabledSensor = CMD_NONE;
00350         }
00351         return;
00352     }
00353 
00354     photos_tx();
00355 }
00356 
00357 const button_item_t photos_item = { _ITEM_BUTTON, "photos", photos_push };
00358 const button_item_t tx_photos_item = { _ITEM_BUTTON, "tx_photos", tx_photos_push };
00359 
00360 const menu_t photos_menu[] = {
00361     { {(LAST_CHIP_MENU_ROW-2),  1}, "PHOTOS ", &photos_item, FLAG_MSGTYPE_ALL },
00362     { {(LAST_CHIP_MENU_ROW-2),  24}, NULL, &tx_photos_item, FLAG_MSGTYPE_ALL },
00363     { {0, 0}, NULL, NULL }
00364 };
00365 #endif /* PHOTOS_PIN */
00366 
00367 void temperature_push()
00368 {
00369     displayFloatToInt_t val;
00370     demo_sample_temp(&val);
00371 }
00372 
00373 void pressure_push()
00374 {
00375     displayFloatToInt_t val;
00376     demo_sample_pressure(&val);
00377 }
00378 
00379 void temp_tx()
00380 {
00381     displayFloatToInt_t val;
00382     demo_sample_temp(&val);
00383 
00384     Radio::set_payload_length(sizeof(displayFloatToInt_t)+1);
00385 
00386     Radio::radio.tx_buf[0] = CMD_TEMP;
00387     memcpy(&Radio::radio.tx_buf[1], &val, sizeof(displayFloatToInt_t));
00388 
00389     flags.txBusy = true;
00390     Radio::txPkt();
00391 }
00392 
00393 void tx_temp_push()
00394 {
00395     if (pollTickerRate > 0) {
00396         if (enabledSensor != CMD_TEMP)
00397             enabledSensor = CMD_TEMP;
00398         else {
00399             enabledSensor = CMD_NONE;
00400         }
00401         return;
00402     }
00403 
00404     temp_tx();
00405 }
00406 
00407 void pres_tx()
00408 {
00409     displayFloatToInt_t val;
00410     demo_sample_pressure(&val);
00411 
00412     Radio::set_payload_length(sizeof(displayFloatToInt_t)+1);
00413 
00414     Radio::radio.tx_buf[0] = CMD_PRES;
00415     memcpy(&Radio::radio.tx_buf[1], &val, sizeof(displayFloatToInt_t));
00416 
00417     flags.txBusy = true;
00418     Radio::txPkt();
00419 }
00420 
00421 void tx_pressure_push()
00422 {
00423     if (pollTickerRate > 0) {
00424         if (enabledSensor != CMD_PRES)
00425             enabledSensor = CMD_PRES;
00426         else {
00427             enabledSensor = CMD_NONE;
00428         }
00429         return;
00430     }
00431 
00432     pres_tx();
00433 }
00434 
00435 void sensorPoll()
00436 {
00437     if (!flags.txBusy && menuState.mode == MENUMODE_NONE)
00438         flags.measure_tx = 1;
00439 }
00440 
00441 void pollRate_print()
00442 {
00443     pc.printf("%ums", pollTickerRate / 1000);
00444 }
00445 
00446 bool pollRate_write(const char* str)
00447 {
00448     sscanf(str, "%u", &pollTickerRate);
00449 
00450     if (pollTickerRate > 0) {
00451         pollTickerRate *= 1000;
00452         pollTicker.attach_us(sensorPoll, pollTickerRate);
00453     } else
00454         pollTicker.detach();
00455 
00456     log_printf("pollTickerRate %uus\r\n", pollTickerRate);
00457 
00458     return false;
00459 }
00460 
00461 const button_item_t temperature_item = { _ITEM_BUTTON, "temperature", temperature_push };
00462 const button_item_t pressure_item = { _ITEM_BUTTON, "pressure", pressure_push };
00463 const button_item_t tx_temp_item = { _ITEM_BUTTON, "tx_temp", tx_temp_push };
00464 const button_item_t tx_pressure_item = { _ITEM_BUTTON, "tx_pressure", tx_pressure_push };
00465 const value_item_t pollRate_item = { _ITEM_VALUE, 8, pollRate_print, pollRate_write };
00466 
00467 const menu_t lps22hh_menu[] = {
00468     { {LAST_CHIP_MENU_ROW,  1}, "LPS22HH ", &temperature_item, FLAG_MSGTYPE_ALL },
00469     { {LAST_CHIP_MENU_ROW, 22}, NULL, &pressure_item, FLAG_MSGTYPE_ALL },
00470     { {LAST_CHIP_MENU_ROW, 31}, NULL, &tx_temp_item, FLAG_MSGTYPE_ALL },
00471     { {LAST_CHIP_MENU_ROW, 40}, NULL, &tx_pressure_item, FLAG_MSGTYPE_ALL },
00472     { {LAST_CHIP_MENU_ROW, 53}, "poll rate:", &pollRate_item, FLAG_MSGTYPE_ALL },
00473     { {0, 0}, NULL, NULL }
00474 };
00475 
00476 const menu_t msg_pkt_menu[] = {
00477     { {LAST_CHIP_MENU_ROW+1,  1}, "tx payload length:", &tx_payload_length_item, FLAG_MSGTYPE_PKT },
00478     { {LAST_CHIP_MENU_ROW+1, 25},                 NULL,    &tx_payload_pn9_item, FLAG_MSGTYPE_PKT, &tx_payload_item },
00479     { {LAST_CHIP_MENU_ROW+1, 40},           "regAddr:",           &regAddr_item, FLAG_MSGTYPE_PKT, &regValue_item },
00480     { {LAST_CHIP_MENU_ROW+1, 53},          "regValue:",          &regValue_item, FLAG_MSGTYPE_PKT, },
00481     { {LAST_CHIP_MENU_ROW+2,  1},                 NULL,        &tx_payload_item, FLAG_MSGTYPE_PKT },
00482     { {LAST_CHIP_MENU_ROW+3,  1},                 NULL,            &tx_pkt_item, FLAG_MSGTYPE_PKT, &opmode_item },
00483     { {LAST_CHIP_MENU_ROW+3, 10},                 NULL,            &rx_pkt_item, FLAG_MSGTYPE_PKT },
00484     { {LAST_CHIP_MENU_ROW+3, 20},                 NULL,        &tx_carrier_item, FLAG_MSGTYPE_PKT, &opmode_item },
00485     { {LAST_CHIP_MENU_ROW+3, 45},                 NULL,       &tx_preamble_item, FLAG_MSGTYPE_PKT, &opmode_item },
00486 
00487     { {0, 0}, NULL, NULL }
00488 };
00489 
00490 unsigned tx_ipd_ms;
00491 Timeout mbedTimeout;
00492 unsigned MaxNumPacket;
00493 unsigned CntPacketTx;
00494 unsigned PacketRxSequencePrev;
00495 unsigned CntPacketRxKO;
00496 unsigned CntPacketRxOK;
00497 unsigned RxTimeOutCount;
00498 unsigned receivedCntPacket;
00499 
00500 void do_next_tx ()
00501 {
00502     Radio::radio.tx_buf[0] = CntPacketTx >> 24;
00503     Radio::radio.tx_buf[1] = CntPacketTx >> 16;
00504     Radio::radio.tx_buf[2] = CntPacketTx >> 8;
00505     Radio::radio.tx_buf[3] = CntPacketTx;
00506 
00507     Radio::radio.tx_buf[4] = PerMsg[0];
00508     Radio::radio.tx_buf[5] = PerMsg[1];
00509     Radio::radio.tx_buf[6] = PerMsg[2];
00510 
00511     Radio::txPkt();
00512 }
00513 
00514 void next_tx_callback()
00515 {
00516     flags.do_next_tx = 1;
00517 }
00518 
00519 
00520 void pertx_push()
00521 {
00522     CntPacketTx = 1;    // PacketRxSequencePrev initialized to 0 on receiver
00523 
00524     log_printf("do perTx\r\n");
00525 
00526     next_tx_callback();
00527 }
00528 
00529 
00530 const button_item_t pertx_item = { _ITEM_BUTTON, "PERTX", pertx_push };
00531 
00532 void tx_ipd_print()
00533 {
00534     pc.printf("%u", tx_ipd_ms);
00535 }
00536 
00537 bool tx_ipd_write(const char* valStr)
00538 {
00539     sscanf(valStr, "%u", &tx_ipd_ms);
00540     return false;
00541 }
00542 
00543 value_item_t per_ipd_item = { _ITEM_VALUE, 4, tx_ipd_print, tx_ipd_write };
00544 
00545 void numpkts_print()
00546 {
00547     pc.printf("%u", MaxNumPacket);
00548 }
00549 
00550 bool numpkts_write(const char* valStr)
00551 {
00552     sscanf(valStr, "%u", &MaxNumPacket);
00553     return false;
00554 }
00555 
00556 value_item_t per_numpkts_item = { _ITEM_VALUE, 6, numpkts_print, numpkts_write };
00557 
00558 void perrx_push()
00559 {
00560     PacketRxSequencePrev = 0;
00561     CntPacketRxKO = 0;
00562     CntPacketRxOK = 0;
00563     RxTimeOutCount = 0;
00564 
00565     Radio::Rx();
00566 }
00567 
00568 const button_item_t perrx_item = { _ITEM_BUTTON, "PERRX", perrx_push };
00569 
00570 void per_reset_push()
00571 {
00572     PacketRxSequencePrev = 0;
00573     CntPacketRxKO = 0;
00574     CntPacketRxOK = 0;
00575     RxTimeOutCount = 0;
00576 }
00577 
00578 const button_item_t per_clear_item = { _ITEM_BUTTON, "resetCount", per_reset_push };
00579 
00580 const menu_t msg_per_menu[] = {
00581     { {LAST_CHIP_MENU_ROW+3,  1},          NULL,       &pertx_item, FLAG_MSGTYPE_PER },
00582     { {LAST_CHIP_MENU_ROW+3,  12}, "TX IPD ms:",     &per_ipd_item, FLAG_MSGTYPE_PER },
00583     { {LAST_CHIP_MENU_ROW+3,  26},   "numPkts:", &per_numpkts_item, FLAG_MSGTYPE_PER },
00584     { {LAST_CHIP_MENU_ROW+3,  40},         NULL,       &perrx_item, FLAG_MSGTYPE_PER, &opmode_item },
00585     { {LAST_CHIP_MENU_ROW+3,  50},         NULL,   &per_clear_item, FLAG_MSGTYPE_PER, &opmode_item },
00586     { {0, 0}, NULL, NULL }
00587 };
00588 
00589 void SendPong()
00590 {
00591     unsigned failCnt = CntPacketRxKO + RxTimeOutCount;
00592 
00593     /* ping slave tx */
00594     log_printf("ACK PKT%u\r\n", receivedCntPacket);
00595 
00596     Radio::radio.tx_buf[ 0] = receivedCntPacket >> 24;
00597     Radio::radio.tx_buf[ 1] = receivedCntPacket >> 16;
00598     Radio::radio.tx_buf[ 2] = receivedCntPacket >> 8;
00599     Radio::radio.tx_buf[ 3] = receivedCntPacket;
00600     Radio::radio.tx_buf[ 4] = failCnt >> 24;
00601     Radio::radio.tx_buf[ 5] = failCnt >> 16;
00602     Radio::radio.tx_buf[ 6] = failCnt >> 8;
00603     Radio::radio.tx_buf[ 7] = failCnt;
00604     Radio::radio.tx_buf[ 8] = PongMsg[0];
00605     Radio::radio.tx_buf[ 9] = PongMsg[1];
00606     Radio::radio.tx_buf[10] = PongMsg[2];
00607     Radio::radio.tx_buf[11] = PongMsg[3];
00608 
00609     Radio::txPkt();
00610 }
00611 
00612 void SendPing()
00613 {
00614     /* ping master tx */
00615 
00616     log_printf("MASTER > PING PKT%u\r\n", CntPacketTx);
00617     Radio::radio.tx_buf[0] = CntPacketTx >> 24;
00618     Radio::radio.tx_buf[1] = CntPacketTx >> 16;
00619     Radio::radio.tx_buf[2] = CntPacketTx >> 8;
00620     Radio::radio.tx_buf[3] = CntPacketTx;
00621     Radio::radio.tx_buf[4] = PingMsg[0];
00622     Radio::radio.tx_buf[5] = PingMsg[1];
00623     Radio::radio.tx_buf[6] = PingMsg[2];
00624     Radio::radio.tx_buf[7] = PingMsg[3];
00625 
00626     Radio::txPkt();
00627 }
00628 
00629 void
00630 pingpong_start_push()
00631 {
00632     CntPacketTx = 1;
00633     CntPacketRxKO = 0;
00634     CntPacketRxOK = 0;
00635     RxTimeOutCount = 0;
00636     receivedCntPacket = 0;
00637 
00638     flags.send_ping = 1;
00639 
00640     flags.ping_master = 1;
00641 
00642     flags.pingpongEnable = 1;
00643     log_printf("ping start\r\n");
00644 }
00645 
00646 const button_item_t pingpong_start_item = { _ITEM_BUTTON, "START", pingpong_start_push };
00647 
00648 void
00649 pingpong_stop_push ()
00650 {
00651     flags.pingpongEnable = 0;
00652 }
00653 
00654 const button_item_t pingpong_stop_item = { _ITEM_BUTTON, "STOP", pingpong_stop_push };
00655 
00656 const menu_t msg_pingpong_menu[] = {
00657     { {LAST_CHIP_MENU_ROW+3,  1}, NULL, &pingpong_start_item, FLAG_MSGTYPE_PING },
00658     { {LAST_CHIP_MENU_ROW+3, 15}, NULL,  &pingpong_stop_item, FLAG_MSGTYPE_PING },
00659     { {0, 0}, NULL, NULL }
00660 };
00661 
00662 const menu_t* get_msg_menu()
00663 {
00664     switch (msg_type) {
00665         case MSG_TYPE_PACKET:
00666             return msg_pkt_menu;
00667         case MSG_TYPE_PER:
00668             return msg_per_menu;
00669         case MSG_TYPE_PINGPONG:
00670             return msg_pingpong_menu;
00671     }
00672     return NULL;
00673 }
00674 
00675 void frf_print()
00676 {
00677     float MHz;
00678 #ifdef SX127x_H
00679     MHz = Radio::radio.get_frf_MHz();
00680 #else
00681     MHz = Radio::radio.getMHz();
00682 #endif
00683     pc.printf("%.3fMHz", MHz);
00684 }
00685 
00686 bool frf_write(const char* valStr)
00687 {
00688     float MHz;
00689     sscanf(valStr, "%f", &MHz);
00690 #ifdef SX127x_H
00691     Radio::radio.set_frf_MHz(MHz);
00692 #else
00693     Radio::radio.setMHz(MHz);
00694 #endif
00695     return false;
00696 }
00697 
00698 const value_item_t frf_item = { _ITEM_VALUE, 14, frf_print, frf_write };
00699 
00700 const value_item_t Radio::tx_dbm_item = { _ITEM_VALUE, 7, Radio::tx_dbm_print, Radio::tx_dbm_write };
00701 
00702 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 };
00703 
00704 const button_item_t chipNum_item = { _ITEM_BUTTON, Radio::chipNum_str, NULL};
00705 
00706 void botrow_print()
00707 {
00708     pc.printf("%u", botRow);
00709 }
00710 
00711 bool botrow_write(const char* str)
00712 {
00713     unsigned n;
00714     sscanf(str, "%u", &n);
00715     botRow = n;
00716 
00717     pc.printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region
00718 
00719     return false;
00720 }
00721 
00722 const value_item_t bottomRow_item = { _ITEM_VALUE, 4, botrow_print, botrow_write };
00723 
00724 const menu_t common_menu[] = {
00725     {  {1, 1},          NULL,      &hw_reset_item, FLAG_MSGTYPE_ALL },
00726     { {1, 15}, "packetType:",       &pktType_item, FLAG_MSGTYPE_ALL },
00727     { {1, 37},          NULL,       &msgType_item, FLAG_MSGTYPE_ALL },
00728     { {1, 50},          NULL,       &chipNum_item, FLAG_MSGTYPE_ALL },
00729     { {1, 60},  "bottomRow:",     &bottomRow_item, FLAG_MSGTYPE_ALL },
00730     { {2, 1},           NULL,           &frf_item, FLAG_MSGTYPE_ALL },
00731     { {2, 15},     "TX dBm:", &Radio::tx_dbm_item, FLAG_MSGTYPE_ALL },
00732     { {2, 30},    "ramp us:",       &tx_ramp_item, FLAG_MSGTYPE_ALL },
00733     { {2, 45},          NULL,     &clearirqs_item, FLAG_MSGTYPE_ALL },
00734     { {2, 55},     "opmode:",        &opmode_item, FLAG_MSGTYPE_ALL },
00735 
00736 
00737     { {0, 0}, NULL, NULL }
00738 };
00739 
00740 bool
00741 is_menu_item_changable(uint8_t table_row, uint8_t table_col)
00742 {
00743     const menu_t* m = menu_table[table_row][table_col];
00744     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00745 
00746     switch (msg_type) {
00747         case MSG_TYPE_PACKET:
00748             if (m->flags & FLAG_MSGTYPE_PKT)
00749                 break;
00750             else
00751                 return false;
00752         case MSG_TYPE_PER:
00753             if (m->flags & FLAG_MSGTYPE_PER)
00754                 break;
00755             else
00756                 return false;
00757         case MSG_TYPE_PINGPONG:
00758             if (m->flags & FLAG_MSGTYPE_PING)
00759                 break;
00760             else
00761                 return false;
00762     }
00763 
00764     if (di->itemType == _ITEM_DROPDOWN) {
00765         if (di->selectable_strs == NULL || di->selectable_strs[0] == NULL) {
00766             log_printf("NULLstrs%u,%u\r\n", table_row, table_col);
00767             return false;
00768         }
00769     } else if (di->itemType == _ITEM_VALUE) {
00770         const value_item_t* vi = (const value_item_t*)m->itemPtr;
00771         if (vi->write == NULL)
00772             return false;
00773     } else if (di->itemType == _ITEM_BUTTON) {
00774         const button_item_t* bi = (const button_item_t*)m->itemPtr;
00775         if (bi->push == NULL)
00776             return false;
00777     } else if (di->itemType == _ITEM_TOGGLE) {
00778         const toggle_item_t * ti = (const toggle_item_t *)m->itemPtr;
00779         if (ti->push == NULL)
00780             return false;
00781     }
00782 
00783     return true;
00784 } // ..is_menu_item_changable()
00785 
00786 void read_menu_item(const menu_t* m, bool selected)
00787 {
00788     uint8_t valCol;
00789     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00790 
00791     switch (msg_type) {
00792         case MSG_TYPE_PACKET:
00793             if (m->flags & FLAG_MSGTYPE_PKT)
00794                 break;
00795             else
00796                 return;
00797         case MSG_TYPE_PER:
00798             if (m->flags & FLAG_MSGTYPE_PER)
00799                 break;
00800             else
00801                 return;
00802         case MSG_TYPE_PINGPONG:
00803             if (m->flags & FLAG_MSGTYPE_PING)
00804                 break;
00805             else
00806                 return;
00807     }
00808 
00809     pc.printf("\e[%u;%uf", m->pos.row, m->pos.col);  // set (force) cursor to row;column
00810     valCol = m->pos.col;
00811     if (m->label) {
00812         pc.printf(m->label);
00813         valCol += strlen(m->label);
00814     }
00815     if (di->itemType == _ITEM_DROPDOWN) {
00816         if (di->read && di->printed_strs) {
00817             uint8_t ridx = di->read(false);
00818             if (selected)
00819                 pc.printf("\e[7m");
00820             pc.printf(di->printed_strs[ridx]);
00821             if (selected)
00822                 pc.printf("\e[0m");
00823         } else if (di->printed_strs) {
00824             pc.printf(di->printed_strs[0]);
00825         }
00826     } else if (di->itemType == _ITEM_VALUE) {
00827         const value_item_t* vi = (const value_item_t*)m->itemPtr;
00828         if (vi->print) {
00829             for (unsigned n = 0; n < vi->width; n++)
00830                 pc.putc(' ');
00831 
00832             pc.printf("\e[%u;%uf", m->pos.row, valCol);  // set (force) cursor to row;column
00833             if (selected)
00834                 pc.printf("\e[7m");
00835             vi->print();
00836             if (selected)
00837                 pc.printf("\e[0m");
00838         }
00839     } else if (di->itemType == _ITEM_BUTTON) {
00840         const button_item_t* bi = (const button_item_t*)m->itemPtr;
00841         if (bi->label) {
00842             if (selected)
00843                 pc.printf("\e[7m%s\e[0m", bi->label);
00844             else
00845                 pc.printf("%s", bi->label);
00846         }
00847     } else if (di->itemType == _ITEM_TOGGLE) {
00848         const toggle_item_t* ti = (const toggle_item_t *)m->itemPtr;
00849         bool on = ti->read();
00850         if (ti->label1) {
00851             const char* const cptr = on ? ti->label1 : ti->label0;
00852             if (selected)
00853                 pc.printf("\e[7m%s\e[0m", cptr);
00854             else
00855                 pc.printf("%s", cptr);
00856         } else {
00857             if (on)
00858                 pc.printf("\e[1m");
00859             if (selected)
00860                 pc.printf("\e[7m");
00861 
00862             pc.printf("%s", ti->label0);
00863 
00864             if (selected || on)
00865                 pc.printf("\e[0m");
00866         }
00867     }
00868 } // ..read_menu_item()
00869 
00870 void draw_menu()
00871 {
00872     unsigned table_row;
00873 
00874     for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) {
00875         int table_col;
00876         for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) {
00877             read_menu_item(menu_table[table_row][table_col], false);
00878         } // ..table column iterator
00879     } // ..table row iterator
00880 
00881     read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
00882 
00883 } // ..draw_menu()
00884 
00885 typedef struct {
00886     int row;
00887     int col;
00888 } tablexy_t;
00889 
00890 void
00891 menu_init_(const menu_t* in, tablexy_t* tc)
00892 {
00893     unsigned n;
00894 
00895     for (n = 0; in[n].pos.row > 0; n++) {
00896         const menu_t* m = &in[n];
00897         if (tc->row != m->pos.row - 1) {
00898             tc->row = m->pos.row - 1;
00899             tc->col = 0;
00900         } else
00901             tc->col++;
00902 
00903         menu_table[tc->row][tc->col] = m;
00904 #ifdef MENU_DEBUG
00905         pc.printf("table:%u,%u ", tc->row, tc->col);
00906         pc.printf(" %d<%d? ", StopMenuCols[tc->row], tc->col);
00907 #endif /* MENU_DEBUG */
00908         if (StopMenuCols[tc->row] < tc->col)
00909             StopMenuCols[tc->row] = tc->col;
00910 #ifdef MENU_DEBUG
00911         pc.printf("{%u %u}", tc->row, tc->col);
00912         pc.printf("in:%p[%u] screen:%u,%u ", in, n, m->pos.row, m->pos.col);
00913         //pc.printf(" loc:%p ", &in[n].itemPtr);
00914         if (in[n].itemPtr) {
00915             const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00916             pc.printf(" itemPtr:%p type:%02x ", di, di->itemType);
00917         }
00918         pc.printf("stopMenuCols[%u]: %d ", tc->row, StopMenuCols[tc->row]);
00919         if (m->label)
00920             pc.printf("label:%s", m->label);
00921         else
00922             pc.printf("noLabel");
00923         pc.printf("\r\n");
00924 #endif /* MENU_DEBUG */
00925 
00926 
00927     }
00928 #ifdef MENU_DEBUG
00929     pc.printf("hit key:");
00930     wait_uart_rx(); //pc.getc();
00931     
00932 #endif /* MENU_DEBUG */
00933 }
00934 
00935 void navigate_dropdown(uint8_t ch)
00936 {
00937     unsigned n;
00938     const menu_t* m = menuState.sm;
00939     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00940 
00941     switch (ch) {
00942         case 'A':   // cursor UP
00943             if (menuState.sel_idx > 0) {
00944                 menuState.sel_idx--;
00945             }
00946             break;
00947         case 'B':   // cursor DOWN
00948             if (di->selectable_strs[menuState.sel_idx+1] != NULL)
00949                 menuState.sel_idx++;
00950             break;
00951     } // ..switch (ch)
00952 
00953     for (n = 0; di->selectable_strs[n] != NULL; n++) {
00954         pc.printf("\e[%u;%uf", m->pos.row+n, menuState.dropdown_col);
00955         if (n == menuState.sel_idx)
00956             pc.printf("\e[7m");
00957         pc.printf(di->selectable_strs[n]);
00958         if (n == menuState.sel_idx)
00959             pc.printf("\e[0m");
00960     }
00961     pc.printf("\e[%u;%uf", m->pos.row + menuState.sel_idx, menuState.dropdown_col + strlen(di->selectable_strs[menuState.sel_idx]));
00962 }
00963 
00964 bool is_item_selectable(const menu_t* m)
00965 {
00966     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
00967 
00968     if (di->itemType == _ITEM_BUTTON) {
00969         const button_item_t* bi = (const button_item_t*)m->itemPtr;
00970         if (bi->push == NULL)
00971             return false;
00972     } else if (di->itemType == _ITEM_TOGGLE) {
00973         const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr;
00974         if (ti->push == NULL)
00975             return false;
00976     }
00977 
00978     return true;
00979 }
00980 
00981 void navigate_menu(uint8_t ch)
00982 {
00983     read_menu_item(menu_table[curpos.row][curpos.tableCol], false);
00984 
00985     switch (ch) {
00986         case 'A':   // cursor UP
00987             if (curpos.row == 0)
00988                 break;
00989 
00990             {   // find previous row up with column
00991                 int8_t row;
00992                 for (row = curpos.row - 1; row >= 0; row--) {
00993                     if (StopMenuCols[row] > -1) {
00994                         curpos.row = row;
00995                         break;
00996                     }
00997                 }
00998                 if (row == 0 && StopMenuCols[0] < 0)
00999                     break;  // nothing found
01000             }
01001 
01002             if (curpos.tableCol >= StopMenuCols[curpos.row]) {
01003                 curpos.tableCol = StopMenuCols[curpos.row]-1;
01004             }
01005 
01006             break;
01007         case 'B':   // cursor DOWN
01008             if (curpos.row >= MAX_MENU_ROWS)
01009                 break;
01010 
01011             {   // find next row down with column
01012                 uint8_t row;
01013                 for (row = curpos.row + 1; row < MAX_MENU_ROWS; row++) {
01014                     if (StopMenuCols[row] != -1) {
01015                         curpos.row = row;
01016                         break;
01017                     }
01018                 }
01019                 if (row == MAX_MENU_ROWS-1 && StopMenuCols[row] == -1)
01020                     break;  // nothing found
01021             }
01022 
01023             if (curpos.tableCol >= StopMenuCols[curpos.row]) {
01024                 curpos.tableCol = StopMenuCols[curpos.row]-1;
01025             }
01026 
01027 
01028             break;
01029         case 'C':    // cursor LEFT
01030             if (curpos.tableCol >= StopMenuCols[curpos.row]-1)
01031                 break;
01032 
01033             { // find next row left with editable
01034                 uint8_t tcol;
01035                 for (tcol = curpos.tableCol + 1; tcol < StopMenuCols[curpos.row]; tcol++) {
01036                     if (is_menu_item_changable(curpos.row, tcol)) {
01037                         curpos.tableCol = tcol;
01038                         break;
01039                     }
01040                 }
01041             }
01042 
01043             break;
01044         case 'D':   // cursor RIGHT
01045             if (curpos.tableCol == 0)
01046                 break;
01047 
01048             {
01049                 int8_t tcol;
01050                 for (tcol = curpos.tableCol - 1;  tcol >= 0; tcol--) {
01051                     if (is_menu_item_changable(curpos.row, tcol)) {
01052                         curpos.tableCol = tcol;
01053                         break;
01054                     }
01055                 }
01056             }
01057 
01058             break;
01059         default:
01060             //pc.printf("unhancled-csi:%02x\eE", ch);
01061             break;
01062     } // ..switch (ch)
01063 
01064     if (!is_item_selectable(menu_table[curpos.row][curpos.tableCol])) {
01065         int c;
01066         for (c = 0; c < StopMenuCols[curpos.row]; c++) {
01067             if (is_item_selectable(menu_table[curpos.row][c])) {
01068                 curpos.tableCol = c;
01069                 break;
01070             }
01071         }
01072         if (c == StopMenuCols[curpos.row])
01073             return;
01074     }
01075 
01076 #ifdef MENU_DEBUG
01077     log_printf("table:%u,%u screen:%u,%u      \r\n", curpos.row, curpos.tableCol, 
01078         menu_table[curpos.row][curpos.tableCol]->pos.row,
01079         menu_table[curpos.row][curpos.tableCol]->pos.col
01080     );
01081 #endif /* MENU_DEBUG */
01082 
01083     read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
01084 } // ..navigate_menu
01085 
01086 void commit_menu_item_change()
01087 {
01088     const menu_t* m = menu_table[curpos.row][curpos.tableCol];
01089     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
01090 
01091     if (di->itemType == _ITEM_DROPDOWN) {
01092         menuState.mode = di->write(menuState.sel_idx);
01093 
01094         pc.printf("\e[%u;%uf", m->pos.row, m->pos.col-2);
01095     } else if (di->itemType == _ITEM_VALUE) {
01096         const value_item_t* vi = (const value_item_t*)m->itemPtr;
01097         /* commit value entry */
01098         if (vi->write) {
01099             if (vi->write(entry_buf))
01100                 menuState.mode = MENUMODE_REDRAW;
01101             else
01102                 menuState.mode = MENUMODE_NONE;
01103         } else
01104             menuState.mode = MENUMODE_NONE;
01105 
01106         if (menuState.mode == MENUMODE_NONE) {
01107             read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
01108         }
01109     }
01110 } // ..commit_menu_item_change()
01111 
01112 void refresh_item_in_table(const void* item)
01113 {
01114     unsigned table_row;
01115 
01116     if (item == NULL)
01117         return;
01118 
01119     for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) {
01120         int table_col;
01121         for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) {
01122             //log_printf("%u %u %p\r\n", table_row, table_col, menu_table[table_row][table_col]->itemPtr);
01123             if (item == menu_table[table_row][table_col]->itemPtr) {
01124                 read_menu_item(menu_table[table_row][table_col], false);
01125                 return;
01126             }
01127         }
01128     }
01129 }
01130 
01131 void
01132 start_value_entry(const menu_t* m)
01133 {
01134     const value_item_t* vi = (const value_item_t*)m->itemPtr;
01135     uint8_t col = m->pos.col;
01136 
01137     if (m->label)
01138         col += strlen(m->label);
01139 
01140     pc.printf("\e[%u;%uf", m->pos.row, col);
01141     for (unsigned i = 0; i < vi->width; i++)
01142         pc.putc(' ');   // clear displayed value for user entry
01143 
01144     pc.printf("\e[%u;%uf", m->pos.row, col);
01145     menuState.mode = MENUMODE_ENTRY;
01146     entry_buf_idx = 0;
01147 }
01148 
01149 void start_menu_item_change()
01150 {
01151     const menu_t* m = menu_table[curpos.row][curpos.tableCol];
01152     const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr;
01153     bool checkRefresh = false;
01154 
01155     if (di->itemType == _ITEM_DROPDOWN && di->selectable_strs) {
01156         menuState.dropdown_col = m->pos.col;
01157         unsigned n, sidx = 0;
01158         /* start dropdown */
01159         if (di->read)
01160             sidx = di->read(true);
01161 
01162         if (m->label)
01163             menuState.dropdown_col += strlen(m->label);
01164 
01165         for (n = 0; di->selectable_strs[n] != NULL; n++) {
01166             uint8_t col = menuState.dropdown_col;
01167             bool leftPad = false;
01168             if (col > 3 && n > 0) { // dropdown left side padding
01169                 col -= 2;
01170                 leftPad = true;
01171             }
01172             pc.printf("\e[%u;%uf", m->pos.row+n, col);
01173             if (leftPad ) {
01174                 pc.putc(' ');
01175                 pc.putc(' ');
01176             }
01177             if (n == sidx)
01178                 pc.printf("\e[7m");
01179             pc.printf(di->selectable_strs[n]);
01180             if (n == sidx)
01181                 pc.printf("\e[0m");
01182             pc.putc(' ');   // right side padding
01183             pc.putc(' ');
01184         }
01185         pc.printf("\e[%u;%uf", m->pos.row, menuState.dropdown_col-2);
01186 
01187         menuState.mode = MENUMODE_DROPDOWN;
01188         menuState.sel_idx = sidx;
01189         menuState.sm = m;
01190     } else if (di->itemType == _ITEM_VALUE) {
01191         /* start value entry */
01192         start_value_entry(m);
01193     } else if (di->itemType == _ITEM_BUTTON) {
01194         const button_item_t* bi = (const button_item_t*)m->itemPtr;
01195         if (bi->push) {
01196             bi->push();
01197             checkRefresh = true;
01198         }
01199     } else if (di->itemType == _ITEM_TOGGLE) {
01200         const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr;
01201         if (ti->push) {
01202             bool on = ti->push();
01203             uint8_t col = m->pos.col;
01204 
01205             if (m->label)
01206                 col += strlen(m->label);
01207 
01208             pc.printf("\e[%u;%uf", m->pos.row, col);
01209             if (ti->label1) {
01210                 pc.printf("\e[7m%s\e[0m", on ? ti->label1 : ti->label0);
01211             } else {
01212                 if (on)
01213                     pc.printf("\e[1;7m%s\e[0m", ti->label0);
01214                 else
01215                     pc.printf("\e[7m%s\e[0m", ti->label0);
01216             }
01217             checkRefresh = true;
01218         }
01219     }
01220 
01221     if (checkRefresh) {
01222         if (m->refreshReadItem) {
01223             refresh_item_in_table(m->refreshReadItem);  // read associated
01224             read_menu_item(m, true);   // restore cursor
01225         }
01226     }
01227 } // ..start_menu_item_change()
01228 
01229 void full_menu_init()
01230 {
01231     unsigned n;
01232     const menu_t *m;
01233     tablexy_t txy;
01234 
01235     txy.row = INT_MAX;
01236     txy.col = 0;
01237 
01238     for (n = 0; n < MAX_MENU_ROWS; n++) {
01239         StopMenuCols[n] = -1;
01240     }
01241 
01242     menu_init_(common_menu, &txy);
01243 
01244     menu_init_(Radio::common_menu, &txy);
01245 
01246     m = Radio::get_modem_menu();
01247     if (m == NULL) {
01248         log_printf("NULL-modemMenu\r\n");
01249         for (;;) asm("nop");
01250     }
01251 #ifdef MENU_DEBUG
01252     pc.printf("modemmenuInit\r\n");
01253 #endif
01254     menu_init_(m, &txy);
01255 
01256     m = Radio::get_modem_sub_menu();
01257     if (m) {
01258 #ifdef MENU_DEBUG
01259         pc.printf("modemsubmenuInit\r\n");
01260 #endif
01261         menu_init_(m, &txy);
01262     }
01263 #ifdef MENU_DEBUG
01264     else
01265         pc.printf("no-modemsubmenu\r\n");
01266 #endif
01267 
01268 #ifdef PHOTOS_PIN
01269     menu_init_(photos_menu, &txy);
01270 #endif /* PHOTOS_PIN */
01271     menu_init_(lis12dh12_menu, &txy);   // accel
01272     menu_init_(lps22hh_menu, &txy);     // temp, pressure
01273 
01274     m = get_msg_menu();
01275     if (m == NULL) {
01276         log_printf("NULL-msgMenu %d\r\n", msg_type);
01277         for (;;) asm("nop");
01278     }
01279     menu_init_(m, &txy);
01280 
01281     for (n = 0; n < MAX_MENU_ROWS; n++) {
01282         if (StopMenuCols[n] != -1)
01283             StopMenuCols[n]++;
01284     }
01285 }
01286 
01287 bool ishexchar(char ch)
01288 {
01289     if (((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) || (ch >= '0' && ch <= '9'))
01290         return true;
01291     else
01292         return false;
01293 }
01294 
01295 enum _urx_ {
01296     URX_STATE_NONE = 0,
01297     URX_STATE_ESCAPE,
01298     URX_STATE_CSI,
01299 } uart_rx_state;
01300 
01301 Timeout uartRxTimeout;
01302 
01303 void uart_rx_timeout()
01304 {
01305     /* escape by itself: abort change on item */
01306     menuState.mode = MENUMODE_REDRAW;
01307 
01308     uart_rx_state = URX_STATE_NONE;
01309 }
01310 
01311 void serial_callback(char ch)
01312 {
01313     switch (uart_rx_state) {
01314         case URX_STATE_NONE:
01315             if (ch == 0x1b) {
01316                 if (menuState.mode == MENUMODE_ENTRY) {
01317                     /* abort entry mode */
01318                     menuState.mode = MENUMODE_NONE;
01319                     read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
01320                 } else {
01321                     uart_rx_state = URX_STATE_ESCAPE;
01322                     if (menuState.mode != MENUMODE_NONE) {
01323                         /* is this escape by itself, user wants to abort? */
01324                         uartRxTimeout.attach(uart_rx_timeout, 0.03);
01325                     }
01326                 }
01327             } else if (ch == 2) { // ctrl-B
01328                 log_printf("--------------\r\n");
01329             } else if (ch == '\r') {
01330                 if (menuState.mode == MENUMODE_NONE) {
01331                     start_menu_item_change();
01332                 } else {
01333                     entry_buf[entry_buf_idx] = 0;
01334                     commit_menu_item_change();
01335                 }
01336             } else if (menuState.mode == MENUMODE_ENTRY) {
01337                 if (ch == 8) {
01338                     if (entry_buf_idx > 0) {
01339                         pc.putc(8);
01340                         pc.putc(' ');
01341                         pc.putc(8);
01342                         entry_buf_idx--;
01343                     }
01344                 } else if (ch == 3) {    // ctrl-C
01345                     menuState.mode = MENUMODE_NONE;
01346                 } else if (entry_buf_idx < sizeof(entry_buf)) {
01347                     entry_buf[entry_buf_idx++] = ch;
01348                     pc.putc(ch);
01349                 }
01350             } else if (menuState.mode == MENUMODE_NONE) {
01351                 if (ishexchar(ch) || ch == '-') {   // characters which start entry
01352                     const value_item_t* vi = (const value_item_t*)menu_table[curpos.row][curpos.tableCol]->itemPtr;
01353                     if (vi->itemType == _ITEM_VALUE) {
01354                         start_value_entry(menu_table[curpos.row][curpos.tableCol]);
01355                         entry_buf[entry_buf_idx++] = ch;
01356                         pc.putc(ch);
01357                     }
01358                 } else if (ch == 'r') {
01359                     menuState.mode = MENUMODE_REDRAW;
01360                 } else if (ch == '.') {
01361                     Radio::test();
01362                 }
01363 
01364             }
01365             break;
01366         case URX_STATE_ESCAPE:
01367             uartRxTimeout.detach();
01368             if (ch == '[')
01369                 uart_rx_state = URX_STATE_CSI;
01370             else {
01371 #ifdef MENU_DEBUG
01372                 log_printf("unhancled-esc:%02x\r\n", ch);
01373 #endif /* MENU_DEBUG */
01374                 uart_rx_state = URX_STATE_NONE;
01375             }
01376             break;
01377         case URX_STATE_CSI:
01378             if (menuState.mode == MENUMODE_NONE)
01379                 navigate_menu(ch);
01380             else if (menuState.mode == MENUMODE_DROPDOWN)
01381                 navigate_dropdown(ch);
01382 
01383             uart_rx_state = URX_STATE_NONE;
01384             //pc.printf("\e[18;1f");  // set (force) cursor to row;column
01385             break;
01386     } // ..switch (uart_rx_state)
01387 }
01388 
01389 void ev_uart_rx()
01390 {
01391     log_printf("ev_uart_rx %u %u\r\n", uart_rx_buf_in, uart_rx_buf_out);
01392     while (uart_rx_buf_in != uart_rx_buf_out) {
01393         log_printf("X ev_uart_rx %u %u\r\n", uart_rx_buf_in, uart_rx_buf_out);
01394         serial_callback(uart_rx_buf[uart_rx_buf_out++]);
01395         if (uart_rx_buf_out == UART_RX_BUF_LEN)
01396             uart_rx_buf_out = 0;
01397     }
01398 }
01399 
01400 //volatile unsigned rxCnt = 0;
01401 void rx_isr()
01402 {
01403     //rxCnt++;
01404     uart_rx_buf[uart_rx_buf_in++] = pc.getc();
01405     if (uart_rx_buf_in == UART_RX_BUF_LEN)
01406         uart_rx_buf_in = 0;
01407 
01408     //queue.call(ev_uart_rx);
01409 }
01410 
01411 
01412 void txDone()
01413 {
01414     if (msg_type == MSG_TYPE_PER) {
01415         log_printf("CntPacketTx%u, max:%u  ipd%u\r\n", CntPacketTx, MaxNumPacket, tx_ipd_ms);
01416         if (++CntPacketTx <= MaxNumPacket)
01417             mbedTimeout.attach_us(next_tx_callback, tx_ipd_ms * 1000);
01418     } else if (msg_type == MSG_TYPE_PINGPONG) {
01419         if (flags.ping_master) {
01420             ++CntPacketTx;
01421         }
01422 
01423         Radio::Rx();
01424     }
01425 
01426     flags.txBusy = false;
01427 }
01428 
01429 static void
01430 printRxPkt(uint8_t size)
01431 {
01432     char str[80];
01433     char *ptr, *endPtr;
01434     unsigned n = 0;
01435     endPtr = str + sizeof(str);
01436     ptr = str;
01437     while (ptr < endPtr) {
01438         sprintf(ptr, "%02x ", Radio::radio.rx_buf[n]);
01439         ptr += 3;
01440         if (++n >= size)
01441             break;
01442     }
01443     log_printf("%s\r\n", str);
01444 }
01445 
01446 void rxDone(uint8_t size, float rssi, float snr)
01447 {
01448     log_printf("rxDone %u, %.1fdBm  %.1fdB\r\n", size, rssi, snr);
01449     if (msg_type == MSG_TYPE_PACKET) {
01450         switch (Radio::radio.rx_buf[0]) {
01451             displayFloatToInt_t val;
01452             case CMD_TEMP:
01453                 memcpy(&val, &Radio::radio.rx_buf[1], sizeof(displayFloatToInt_t));
01454                 log_printf("TEMP: %c%d.%02d\r\n", ((val.sign) ? '-' : '+'),
01455                     (int)val.out_int, (int)val.out_dec);
01456                 break;
01457             case CMD_PRES:
01458                 memcpy(&val, &Radio::radio.rx_buf[1], sizeof(displayFloatToInt_t));
01459                 log_printf("PRESS: %c%d.%02d\r\n", ((val.sign) ? '-' : '+'),
01460                     (int)val.out_int, (int)val.out_dec);
01461                 break;
01462             case CMD_ACCEL:
01463                 SensorAxes_t acc;
01464                 memcpy(&acc, &Radio::radio.rx_buf[1], sizeof(SensorAxes_t));
01465                 log_printf("ACC  %5ld  %5ld  %5ld\r\n", acc.AXIS_X, acc.AXIS_Y, acc.AXIS_Z);
01466                 break;
01467             case CMD_PHOTOS:
01468                 uint16_t pv;
01469                 memcpy(&pv, &Radio::radio.rx_buf[1], sizeof(uint16_t));
01470                 log_printf("photos: %u\r\n", pv);
01471                 break;
01472             default:
01473                 printRxPkt(size);
01474                 break;
01475         }
01476     } else if (msg_type == MSG_TYPE_PER) {
01477         if (memcmp(Radio::radio.rx_buf+4, PerMsg, 3) == 0) {
01478             unsigned i, PacketRxSequence = Radio::radio.rx_buf[0];
01479             PacketRxSequence <<= 8;
01480             PacketRxSequence += Radio::radio.rx_buf[1];
01481             PacketRxSequence <<= 8;
01482             PacketRxSequence += Radio::radio.rx_buf[2];
01483             PacketRxSequence <<= 8;
01484             PacketRxSequence += Radio::radio.rx_buf[3];
01485 
01486             CntPacketRxOK++;
01487 
01488             if (PacketRxSequence <= PacketRxSequencePrev || PacketRxSequencePrev == 0)
01489                 i = 0;  // sequence reset to resync, dont count missed packets this time
01490             else
01491                 i = PacketRxSequence - PacketRxSequencePrev - 1;
01492 
01493 
01494             CntPacketRxKO += i;
01495             RxTimeOutCount = 0;
01496             log_printf("PER rx%u  ok%u   ko%u\r\n", PacketRxSequence , CntPacketRxOK, CntPacketRxKO);
01497 
01498             PacketRxSequencePrev = PacketRxSequence;
01499         } // ..if PerMsg
01500         else {
01501             log_printf("per?\r\n");
01502             printRxPkt(size);
01503         }
01504     } else if (msg_type == MSG_TYPE_PINGPONG) {
01505         if (memcmp(Radio::radio.rx_buf+4, PingMsg, 4) == 0) {
01506             /* ping slave rx */
01507             Radio::setFS();
01508             receivedCntPacket = Radio::radio.rx_buf[0];
01509             receivedCntPacket <<= 8;
01510             receivedCntPacket += Radio::radio.rx_buf[1];
01511             receivedCntPacket <<= 8;
01512             receivedCntPacket += Radio::radio.rx_buf[2];
01513             receivedCntPacket <<= 8;
01514             receivedCntPacket += Radio::radio.rx_buf[3];
01515             log_printf("%u rxPing->txPong\r\n", receivedCntPacket);
01516 
01517             flags.ping_master = 0;
01518             flags.send_pong = 1;
01519 
01520         } else if (memcmp(Radio::radio.rx_buf+8, PongMsg, 4) == 0) {
01521             unsigned cnt;
01522             /* ping master rx */
01523             Radio::setFS();
01524             cnt = Radio::radio.rx_buf[0];
01525             cnt <<= 8;
01526             cnt += Radio::radio.rx_buf[1];
01527             cnt <<= 8;
01528             cnt += Radio::radio.rx_buf[2];
01529             cnt <<= 8;
01530             cnt += Radio::radio.rx_buf[3];
01531             log_printf("%u rxPong->txPing\r\n", cnt);
01532             flags.send_ping = 1;
01533         } else {
01534             log_printf("pingpong?\r\n");
01535             printRxPkt(size);
01536         }
01537     } else {
01538         /*for (unsigned n = 0; n < size; n++)
01539             log_printf("%02x\r\n", Radio::radio.rx_buf[n]);*/
01540         log_printf("msg_type %u\r\n", msg_type);
01541     }
01542 
01543 }
01544 
01545 const RadioEvents_t rev = {
01546     txDone,
01547     rxDone
01548 };
01549 
01550 static DrvStatusTypeDef LIS2DH12_Read_Single_FIFO_Data(uint16_t sampleIndex, SensorAxes_t* acceleration)
01551 {
01552   //SensorAxes_t acceleration;
01553 
01554   /* Read single FIFO data (acceleration in 3 axes) */
01555   if (lis2dh12_get_axes(acceleration) == COMPONENT_ERROR)
01556   {
01557     return COMPONENT_ERROR;
01558   }
01559 
01560   if (sampleIndex < SAMPLE_LIST_MAX)
01561   {
01562     log_printf("[DATA %02d]  %5ld  %5ld  %5ld\r\n", sampleIndex + 1, acceleration->AXIS_X,
01563              acceleration->AXIS_Y,
01564              acceleration->AXIS_Z);
01565   }
01566 
01567   return COMPONENT_OK;
01568 }
01569 
01570 void uart_rx_service()
01571 {
01572     while (uart_rx_buf_in != uart_rx_buf_out) {
01573         serial_callback(uart_rx_buf[uart_rx_buf_out++]);
01574         if (uart_rx_buf_out == UART_RX_BUF_LEN)
01575             uart_rx_buf_out = 0;
01576     }
01577 }
01578 
01579 static DrvStatusTypeDef LIS2DH12_Read_All_FIFO_Data(void)
01580 {
01581     uint16_t samplesToRead = accel_get_num_samples();
01582     SensorAxes_t acc;
01583 
01584     /* 'samplesToRead' actually contains number of words in FIFO but each FIFO sample (data set) consists of 3 words
01585     so the 'samplesToRead' has to be divided by 3 */
01586     samplesToRead /= 3;
01587 
01588     log_printf("\r\n%d samples in FIFO.\r\n\r\nStarted downloading data from FIFO ...\r\n", samplesToRead);
01589 
01590     log_printf("\r\n[DATA ##]  ACC_X  ACC_Y  ACC_Z  [mg]\r\n");
01591 
01592     for (int i = 0; i < samplesToRead; i++)
01593     {
01594         uart_rx_service();
01595 
01596         if (LIS2DH12_Read_Single_FIFO_Data(i, &acc) == COMPONENT_ERROR)
01597         {
01598             return COMPONENT_ERROR;
01599         } else {
01600         }
01601     }
01602 
01603     if (accel_tx_en) {
01604         Radio::radio.tx_buf[0] = CMD_ACCEL;
01605         Radio::set_payload_length(sizeof(SensorAxes_t)+1);
01606         memcpy(&Radio::radio.tx_buf[1], &acc, sizeof(SensorAxes_t));
01607         Radio::txPkt();
01608     }
01609 
01610     if (samplesToRead > SAMPLE_LIST_MAX)
01611     {
01612         log_printf("\r\nSample list limited to: %d\r\n", SAMPLE_LIST_MAX);
01613     }
01614 
01615     return COMPONENT_OK;
01616 }
01617 
01618 
01619 int main()
01620 {
01621 
01622     lfsr = LFSR_INIT;
01623     msg_type = MSG_TYPE_PACKET;
01624 
01625     uart_rx_state = URX_STATE_NONE;
01626     pc.attach(rx_isr);
01627 
01628     Radio::boardInit(&rev);
01629 
01630     {
01631         unsigned n;
01632         for (n = 0; n < MAX_MENU_ROWS; n++)
01633             StopMenuCols[n] = -1;
01634     }
01635 
01636     botRow = MAX_MENU_ROWS + SCROLLING_ROWS;
01637 
01638     pc.baud(115200);
01639     pc.printf("\e[7h"); // enable line wrapping
01640     pc.printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region
01641     pc.printf("\e[2J"); // erase entire screen
01642 
01643     full_menu_init();
01644     //wait_uart_rx(); //pc.getc();
01645 
01646     pc.printf("\e[2J"); // erase entire screen
01647 
01648     menuState.mode = MENUMODE_NONE;
01649 
01650     draw_menu();
01651 
01652     curpos.row = 0;
01653     curpos.tableCol = 0;
01654 
01655     tx_ipd_ms = 100;
01656 
01657     if (demo_start()) {
01658         log_printf("demo_start-Failed\r\n");
01659         for (;;) { asm("nop"); }
01660     } else
01661         log_printf("demo_start-OK\r\n");
01662 
01663 
01664     svc_lis2dh12 = true;
01665 
01666     for (;;) {
01667         int ret;
01668 
01669         uart_rx_service();
01670 
01671         if (flags.send_ping) {
01672             if (flags.pingpongEnable)
01673                 SendPing();
01674             flags.send_ping = 0;
01675         }
01676 
01677         if (flags.send_pong) {
01678             if (flags.pingpongEnable)
01679                 SendPong();
01680             flags.send_pong = 0;
01681         }
01682 
01683         if (flags.do_next_tx) {
01684             do_next_tx();
01685             flags.do_next_tx = 0;
01686         }
01687 
01688         if (menuState.mode == MENUMODE_REINIT_MENU) {
01689             full_menu_init();
01690             menuState.mode = MENUMODE_REDRAW;
01691         }
01692 
01693         if (menuState.mode == MENUMODE_REDRAW) {
01694             // erase entire screen, some dropdowns extend to scrolling area
01695             pc.printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region, if terminal started after
01696             pc.printf("\e[2J");
01697             //pc.printf("\e[%u;1f\e[1J", MAX_MENU_ROWS);  // erase menu area
01698 
01699             menuState.mode = MENUMODE_NONE;
01700             draw_menu();
01701         }
01702 
01703         if (Radio::service(menuState.mode == MENUMODE_NONE ? LAST_CHIP_MENU_ROW : -1)) {
01704             read_menu_item(menu_table[curpos.row][curpos.tableCol], true);
01705         }
01706 
01707         if (svc_lis2dh12) {
01708             ret = lis2dh_mainloop();
01709             switch (ret) {
01710                 case LIS2DH_FAIL_STATE:
01711                     log_printf("lis2dh-stateFail\r\n");
01712                     break;
01713                 case LIS2DH_FAIL:
01714                     log_printf("lis2dh-Fail\r\n");
01715                     break;
01716                 case LIS2DH_BSP_FAIL:
01717                     log_printf("lis2dh bsp-fail\r\n");
01718                     wait(0.05);
01719                     break;
01720                 case LIS2DH_MAIN_SLEEP:
01721                     lis2dh_set_fifo_mode();
01722 
01723                     log_printf("lis2dh fifo-mode-sleep\r\n");
01724                     svc_lis2dh12 = false;
01725                     break;
01726                 case LIS2DH_MAIN_READ_FIFO:
01727                     if (LIS2DH12_Read_All_FIFO_Data() == COMPONENT_ERROR)
01728                     {
01729                         return LIS2DH_FAIL;
01730                     }
01731 
01732                     /* Reset FIFO by setting FIFO mode to Bypass */
01733                     if (lis2dh_set_fifo_bypass() < 0) {
01734                         log_printf("fifo-bypass-fail\r\n");
01735                     }
01736                     break;
01737                 default:
01738                     break;
01739             }
01740         } // ..if (svc_lis2dh12)
01741         else {
01742         }
01743 
01744         if (flags.measure_tx) {
01745             switch (enabledSensor) {
01746                 case CMD_PHOTOS:
01747                     photos_tx();
01748                     break;
01749                 case CMD_PRES:
01750                     pres_tx();
01751                     break;
01752                 case CMD_TEMP:
01753                     temp_tx();
01754                     break;
01755             }
01756             flags.measure_tx = 0;
01757         }
01758 
01759     } // ..for (;;)
01760 }
01761 
01762 void lis2dh_int1()
01763 {
01764     log_printf("\r\nReceived FIFO Threshold Interrupt on INT1 pin ...\r\n"
01765                "\r\nNucleo processor is waking up ...\r\n"
01766     );
01767 
01768     svc_lis2dh12 = true;
01769 }
01770 
01771 char strbuf[255];
01772 
01773 void c_log_printf(const char* format, ...)
01774 {
01775     va_list arglist;
01776 
01777     // put cursor at last scrolling-area line
01778     pc.printf("\e[%u;1f", botRow);
01779     va_start(arglist, format);
01780     vsnprintf(strbuf, sizeof(strbuf), format, arglist);
01781     va_end(arglist);
01782 
01783     pc.printf(strbuf);
01784 }
01785 
01786 void log_printf(const char* format, ...)
01787 {
01788     va_list arglist;
01789 
01790     // put cursor at last scrolling-area line
01791     pc.printf("\e[%u;1f", botRow);
01792     va_start(arglist, format);
01793     vsnprintf(strbuf, sizeof(strbuf), format, arglist);
01794     va_end(arglist);
01795 
01796     pc.printf(strbuf);
01797 }
01798