test sending sensor results over lora radio. Accelerometer and temp/pressure.
Serial terminal operates at 115200.
This project provides a text-based menu over serial port.
Operating the program only requires using the arrow keys, enter key to activate a control, or entering numbers.
Two sensors provided:
LIS12DH12
accelerometer operates in a continuous sampling mode. Enable control for accelerometer enables this continuous sampling, approx every 3 seconds.
LPS22HH temperature / pressure sensor operates as single shot, where pressing the control button on terminal causes single sample to be performed.
poll rate
control will enable repeated reading of pressure/temperature-sensor or photo-sensor when poll rate is greater than zero.
target must be: DISCO_L072CZ_LRWAN1
main.cpp
- Committer:
- Wayne Roberts
- Date:
- 2019-04-29
- Revision:
- 2:972a5704f152
- Parent:
- 0:e1e70da93044
File content as of revision 2:972a5704f152:
#include "mbed.h" #include "radio.h" #include "demo.h" bool svc_lis2dh12; //#define MENU_DEBUG RawSerial pc(USBTX, USBRX); menuState_t menuState; typedef enum { MSG_TYPE_PACKET = 0, MSG_TYPE_PER, MSG_TYPE_PINGPONG } msgType_e; enum { CMD_TEMP, CMD_PRES, CMD_ACCEL, CMD_PHOTOS, CMD_NONE }; #define MAX_MENU_ROWS 16 // [row][column] const menu_t* menu_table[MAX_MENU_ROWS][MAX_MENU_COLUMNS]; int8_t StopMenuCols[MAX_MENU_ROWS]; //#define SCROLLING_ROWS 20 #define SCROLLING_ROWS 30 struct _cp_ { uint8_t row; uint8_t tableCol; } curpos; uint8_t entry_buf_idx; char entry_buf[64]; msgType_e msg_type = MSG_TYPE_PACKET; const uint8_t PingMsg[] = "PING"; const uint8_t PongMsg[] = "PONG"; const uint8_t PerMsg[] = "PER"; volatile struct _flags_ { uint8_t ping_master : 1; // 0 uint8_t send_ping : 1; // 1 uint8_t send_pong : 1; // 2 uint8_t pingpongEnable : 1; // 3 uint8_t do_next_tx : 1; // 4 uint8_t txBusy : 1; // 5 uint8_t measure_tx : 1; // 5 } flags; uint8_t botRow; int regAddr = -1; #define PHOTOS_PIN PA_0 /* * 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 */ #ifdef PHOTOS_PIN AnalogIn photos(PHOTOS_PIN); #endif /* PHOTOS_PIN */ #define UART_RX_BUF_LEN 8 uint8_t uart_rx_buf[UART_RX_BUF_LEN]; volatile uint8_t uart_rx_buf_in; volatile uint8_t uart_rx_buf_out; bool accel_tx_en; #ifdef MENU_DEBUG char wait_uart_rx() { char ret; unsigned foo = uart_rx_buf_in; while (uart_rx_buf_in == foo) { asm("nop"); } ret = uart_rx_buf[uart_rx_buf_out++]; if (uart_rx_buf_out == UART_RX_BUF_LEN) uart_rx_buf_out = 0; return ret; } #endif /* MENU_DEBUG */ unsigned lfsr; #define LFSR_INIT 0x1ff uint8_t get_pn9_byte() { uint8_t ret = 0; int xor_out; xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf); // four bits at a time lfsr = (lfsr >> 4) | (xor_out << 5); // four bits at a time ret |= (lfsr >> 5) & 0x0f; xor_out = ((lfsr >> 5) & 0xf) ^ (lfsr & 0xf); // four bits at a time lfsr = (lfsr >> 4) | (xor_out << 5); // four bits at a time ret |= ((lfsr >> 1) & 0xf0); return ret; } void hw_reset_push() { Radio::hw_reset(); Radio::readChip(); menuState.mode = MENUMODE_REINIT_MENU; } const button_item_t hw_reset_item = { _ITEM_BUTTON, "hw_reset", hw_reset_push }; void clearIrqs_push() { Radio::clearIrqFlags(); } const button_item_t clearirqs_item = { _ITEM_BUTTON, "clearIrqs", clearIrqs_push }; const dropdown_item_t pktType_item = { _ITEM_DROPDOWN, Radio::pktType_strs, Radio::pktType_strs, Radio::pktType_read, Radio::pktType_write}; unsigned msgType_read(bool fw) { return msg_type; } menuMode_e msgType_write(unsigned widx) { msg_type = (msgType_e)widx; if (msg_type == MSG_TYPE_PER) { flags.pingpongEnable = 0; if (Radio::get_payload_length() < 7) Radio::set_payload_length(7); } else if (msg_type == MSG_TYPE_PINGPONG) { if (Radio::get_payload_length() != 12) Radio::set_payload_length(12); } else if (msg_type == MSG_TYPE_PACKET) { flags.pingpongEnable = 0; } return MENUMODE_REINIT_MENU; } const char* const msgType_strs[] = { "PACKET ", "PER ", "PINGPONG", NULL }; const dropdown_item_t msgType_item = { _ITEM_DROPDOWN, msgType_strs, msgType_strs, msgType_read, msgType_write}; #define LAST_CHIP_MENU_ROW (MAX_MENU_ROWS-5) const value_item_t tx_payload_length_item = { _ITEM_VALUE, 5, Radio::tx_payload_length_print, Radio::tx_payload_length_write}; void pn9_push() { uint8_t i, len = Radio::get_payload_length(); for (i = 0; i < len; i++) Radio::radio.tx_buf[i] = get_pn9_byte(); } const button_item_t tx_payload_pn9_item = { _ITEM_BUTTON, "PN9", pn9_push }; void regAddr_print() { if (regAddr != -1) pc.printf("%x", regAddr); } bool regAddr_write(const char* txt) { sscanf(txt, "%x", ®Addr); return false; } const value_item_t regAddr_item = { _ITEM_VALUE, 5, regAddr_print, regAddr_write}; void regValue_print() { if (regAddr != -1) { pc.printf("%x", Radio::read_register(regAddr)); } } bool regValue_write(const char* txt) { unsigned val; sscanf(txt, "%x", &val); if (regAddr != -1) { Radio::write_register(regAddr, val); } return false; } const value_item_t regValue_item = { _ITEM_VALUE, 5, regValue_print, regValue_write}; void tx_payload_print() { uint8_t i, len = Radio::get_payload_length(); for (i = 0; i < len; i++) pc.printf("%02x ", Radio::radio.tx_buf[i]); } bool tx_payload_write(const char* txt) { unsigned o, i = 0, pktidx = 0; while (i < entry_buf_idx) { sscanf(entry_buf+i, "%02x", &o); pc.printf("%02x ", o); i += 2; Radio::radio.tx_buf[pktidx++] = o; while (entry_buf[i] == ' ' && i < entry_buf_idx) i++; } log_printf("set payload len %u\r\n", pktidx); Radio::set_payload_length(pktidx); return true; } const value_item_t tx_payload_item = { _ITEM_VALUE, 80, tx_payload_print, tx_payload_write}; void txpkt_push() { Radio::txPkt(); } const button_item_t tx_pkt_item = { _ITEM_BUTTON, "TXPKT", txpkt_push }; void rxpkt_push() { Radio::Rx(); menuState.mode = MENUMODE_REDRAW; } const button_item_t rx_pkt_item = { _ITEM_BUTTON, "RXPKT", rxpkt_push }; const dropdown_item_t opmode_item = { _ITEM_DROPDOWN, Radio::opmode_status_strs, Radio::opmode_select_strs, Radio::opmode_read, Radio::opmode_write }; void tx_carrier_push() { Radio::tx_carrier(); } const button_item_t tx_carrier_item = { _ITEM_BUTTON, "TX_CARRIER", tx_carrier_push }; void tx_preamble_push() { Radio::tx_preamble(); } const button_item_t tx_preamble_item = { _ITEM_BUTTON, "TX_PREAMBLE", tx_preamble_push }; bool accel_en_read() { uint8_t status; accel_is_enabled(&status); return status; } bool accel_tx_en_read() { uint8_t status; accel_is_enabled(&status); return status && accel_tx_en; } bool accel_en_push() { uint8_t status; accel_is_enabled(&status); accel_tx_en = false; if (status) accel_enable(false); else accel_enable(true); accel_is_enabled(&status); return status; } bool accel_tx_en_push() { uint8_t status; accel_is_enabled(&status); accel_tx_en = true; if (status) accel_enable(false); else accel_enable(true); accel_is_enabled(&status); return status; } const toggle_item_t accel_enable_item = { _ITEM_TOGGLE, "enable", NULL, accel_en_read, accel_en_push}; const toggle_item_t accel_tx_enable_item = { _ITEM_TOGGLE, "tx_enable", NULL, accel_tx_en_read, accel_tx_en_push}; const menu_t lis12dh12_menu[] = { { {LAST_CHIP_MENU_ROW-1, 1}, "LIS12DH12 ", &accel_enable_item, FLAG_MSGTYPE_ALL }, { {LAST_CHIP_MENU_ROW-1, 24}, NULL, &accel_tx_enable_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; #ifdef PHOTOS_PIN void photos_push() { uint16_t val = photos.read_u16(); log_printf("photos %u\r\n", val); } void photos_tx() { uint16_t val; val = photos.read_u16(); log_printf("Tx photos %u\r\n", val); Radio::radio.tx_buf[0] = CMD_PHOTOS; Radio::set_payload_length(sizeof(uint16_t)+1); memcpy(&Radio::radio.tx_buf[1], &val, sizeof(uint16_t)); flags.txBusy = true; Radio::txPkt(); } unsigned pollTickerRate; Ticker pollTicker; uint8_t enabledSensor = CMD_NONE; void tx_photos_push() { if (pollTickerRate > 0) { if (enabledSensor != CMD_PHOTOS) enabledSensor = CMD_PHOTOS; else { enabledSensor = CMD_NONE; } return; } photos_tx(); } const button_item_t photos_item = { _ITEM_BUTTON, "photos", photos_push }; const button_item_t tx_photos_item = { _ITEM_BUTTON, "tx_photos", tx_photos_push }; const menu_t photos_menu[] = { { {(LAST_CHIP_MENU_ROW-2), 1}, "PHOTOS ", &photos_item, FLAG_MSGTYPE_ALL }, { {(LAST_CHIP_MENU_ROW-2), 24}, NULL, &tx_photos_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; #endif /* PHOTOS_PIN */ void temperature_push() { displayFloatToInt_t val; demo_sample_temp(&val); } void pressure_push() { displayFloatToInt_t val; demo_sample_pressure(&val); } void temp_tx() { displayFloatToInt_t val; demo_sample_temp(&val); Radio::set_payload_length(sizeof(displayFloatToInt_t)+1); Radio::radio.tx_buf[0] = CMD_TEMP; memcpy(&Radio::radio.tx_buf[1], &val, sizeof(displayFloatToInt_t)); flags.txBusy = true; Radio::txPkt(); } void tx_temp_push() { if (pollTickerRate > 0) { if (enabledSensor != CMD_TEMP) enabledSensor = CMD_TEMP; else { enabledSensor = CMD_NONE; } return; } temp_tx(); } void pres_tx() { displayFloatToInt_t val; demo_sample_pressure(&val); Radio::set_payload_length(sizeof(displayFloatToInt_t)+1); Radio::radio.tx_buf[0] = CMD_PRES; memcpy(&Radio::radio.tx_buf[1], &val, sizeof(displayFloatToInt_t)); flags.txBusy = true; Radio::txPkt(); } void tx_pressure_push() { if (pollTickerRate > 0) { if (enabledSensor != CMD_PRES) enabledSensor = CMD_PRES; else { enabledSensor = CMD_NONE; } return; } pres_tx(); } void sensorPoll() { if (!flags.txBusy && menuState.mode == MENUMODE_NONE) flags.measure_tx = 1; } void pollRate_print() { pc.printf("%ums", pollTickerRate / 1000); } bool pollRate_write(const char* str) { sscanf(str, "%u", &pollTickerRate); if (pollTickerRate > 0) { pollTickerRate *= 1000; pollTicker.attach_us(sensorPoll, pollTickerRate); } else pollTicker.detach(); log_printf("pollTickerRate %uus\r\n", pollTickerRate); return false; } const button_item_t temperature_item = { _ITEM_BUTTON, "temperature", temperature_push }; const button_item_t pressure_item = { _ITEM_BUTTON, "pressure", pressure_push }; const button_item_t tx_temp_item = { _ITEM_BUTTON, "tx_temp", tx_temp_push }; const button_item_t tx_pressure_item = { _ITEM_BUTTON, "tx_pressure", tx_pressure_push }; const value_item_t pollRate_item = { _ITEM_VALUE, 8, pollRate_print, pollRate_write }; const menu_t lps22hh_menu[] = { { {LAST_CHIP_MENU_ROW, 1}, "LPS22HH ", &temperature_item, FLAG_MSGTYPE_ALL }, { {LAST_CHIP_MENU_ROW, 22}, NULL, &pressure_item, FLAG_MSGTYPE_ALL }, { {LAST_CHIP_MENU_ROW, 31}, NULL, &tx_temp_item, FLAG_MSGTYPE_ALL }, { {LAST_CHIP_MENU_ROW, 40}, NULL, &tx_pressure_item, FLAG_MSGTYPE_ALL }, { {LAST_CHIP_MENU_ROW, 53}, "poll rate:", &pollRate_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; const menu_t msg_pkt_menu[] = { { {LAST_CHIP_MENU_ROW+1, 1}, "tx payload length:", &tx_payload_length_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+1, 25}, NULL, &tx_payload_pn9_item, FLAG_MSGTYPE_PKT, &tx_payload_item }, { {LAST_CHIP_MENU_ROW+1, 40}, "regAddr:", ®Addr_item, FLAG_MSGTYPE_PKT, ®Value_item }, { {LAST_CHIP_MENU_ROW+1, 53}, "regValue:", ®Value_item, FLAG_MSGTYPE_PKT, }, { {LAST_CHIP_MENU_ROW+2, 1}, NULL, &tx_payload_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+3, 1}, NULL, &tx_pkt_item, FLAG_MSGTYPE_PKT, &opmode_item }, { {LAST_CHIP_MENU_ROW+3, 10}, NULL, &rx_pkt_item, FLAG_MSGTYPE_PKT }, { {LAST_CHIP_MENU_ROW+3, 20}, NULL, &tx_carrier_item, FLAG_MSGTYPE_PKT, &opmode_item }, { {LAST_CHIP_MENU_ROW+3, 45}, NULL, &tx_preamble_item, FLAG_MSGTYPE_PKT, &opmode_item }, { {0, 0}, NULL, NULL } }; unsigned tx_ipd_ms; Timeout mbedTimeout; unsigned MaxNumPacket; unsigned CntPacketTx; unsigned PacketRxSequencePrev; unsigned CntPacketRxKO; unsigned CntPacketRxOK; unsigned RxTimeOutCount; unsigned receivedCntPacket; void do_next_tx () { Radio::radio.tx_buf[0] = CntPacketTx >> 24; Radio::radio.tx_buf[1] = CntPacketTx >> 16; Radio::radio.tx_buf[2] = CntPacketTx >> 8; Radio::radio.tx_buf[3] = CntPacketTx; Radio::radio.tx_buf[4] = PerMsg[0]; Radio::radio.tx_buf[5] = PerMsg[1]; Radio::radio.tx_buf[6] = PerMsg[2]; Radio::txPkt(); } void next_tx_callback() { flags.do_next_tx = 1; } void pertx_push() { CntPacketTx = 1; // PacketRxSequencePrev initialized to 0 on receiver log_printf("do perTx\r\n"); next_tx_callback(); } const button_item_t pertx_item = { _ITEM_BUTTON, "PERTX", pertx_push }; void tx_ipd_print() { pc.printf("%u", tx_ipd_ms); } bool tx_ipd_write(const char* valStr) { sscanf(valStr, "%u", &tx_ipd_ms); return false; } value_item_t per_ipd_item = { _ITEM_VALUE, 4, tx_ipd_print, tx_ipd_write }; void numpkts_print() { pc.printf("%u", MaxNumPacket); } bool numpkts_write(const char* valStr) { sscanf(valStr, "%u", &MaxNumPacket); return false; } value_item_t per_numpkts_item = { _ITEM_VALUE, 6, numpkts_print, numpkts_write }; void perrx_push() { PacketRxSequencePrev = 0; CntPacketRxKO = 0; CntPacketRxOK = 0; RxTimeOutCount = 0; Radio::Rx(); } const button_item_t perrx_item = { _ITEM_BUTTON, "PERRX", perrx_push }; void per_reset_push() { PacketRxSequencePrev = 0; CntPacketRxKO = 0; CntPacketRxOK = 0; RxTimeOutCount = 0; } const button_item_t per_clear_item = { _ITEM_BUTTON, "resetCount", per_reset_push }; const menu_t msg_per_menu[] = { { {LAST_CHIP_MENU_ROW+3, 1}, NULL, &pertx_item, FLAG_MSGTYPE_PER }, { {LAST_CHIP_MENU_ROW+3, 12}, "TX IPD ms:", &per_ipd_item, FLAG_MSGTYPE_PER }, { {LAST_CHIP_MENU_ROW+3, 26}, "numPkts:", &per_numpkts_item, FLAG_MSGTYPE_PER }, { {LAST_CHIP_MENU_ROW+3, 40}, NULL, &perrx_item, FLAG_MSGTYPE_PER, &opmode_item }, { {LAST_CHIP_MENU_ROW+3, 50}, NULL, &per_clear_item, FLAG_MSGTYPE_PER, &opmode_item }, { {0, 0}, NULL, NULL } }; void SendPong() { unsigned failCnt = CntPacketRxKO + RxTimeOutCount; /* ping slave tx */ log_printf("ACK PKT%u\r\n", receivedCntPacket); Radio::radio.tx_buf[ 0] = receivedCntPacket >> 24; Radio::radio.tx_buf[ 1] = receivedCntPacket >> 16; Radio::radio.tx_buf[ 2] = receivedCntPacket >> 8; Radio::radio.tx_buf[ 3] = receivedCntPacket; Radio::radio.tx_buf[ 4] = failCnt >> 24; Radio::radio.tx_buf[ 5] = failCnt >> 16; Radio::radio.tx_buf[ 6] = failCnt >> 8; Radio::radio.tx_buf[ 7] = failCnt; Radio::radio.tx_buf[ 8] = PongMsg[0]; Radio::radio.tx_buf[ 9] = PongMsg[1]; Radio::radio.tx_buf[10] = PongMsg[2]; Radio::radio.tx_buf[11] = PongMsg[3]; Radio::txPkt(); } void SendPing() { /* ping master tx */ log_printf("MASTER > PING PKT%u\r\n", CntPacketTx); Radio::radio.tx_buf[0] = CntPacketTx >> 24; Radio::radio.tx_buf[1] = CntPacketTx >> 16; Radio::radio.tx_buf[2] = CntPacketTx >> 8; Radio::radio.tx_buf[3] = CntPacketTx; Radio::radio.tx_buf[4] = PingMsg[0]; Radio::radio.tx_buf[5] = PingMsg[1]; Radio::radio.tx_buf[6] = PingMsg[2]; Radio::radio.tx_buf[7] = PingMsg[3]; Radio::txPkt(); } void pingpong_start_push() { CntPacketTx = 1; CntPacketRxKO = 0; CntPacketRxOK = 0; RxTimeOutCount = 0; receivedCntPacket = 0; flags.send_ping = 1; flags.ping_master = 1; flags.pingpongEnable = 1; log_printf("ping start\r\n"); } const button_item_t pingpong_start_item = { _ITEM_BUTTON, "START", pingpong_start_push }; void pingpong_stop_push () { flags.pingpongEnable = 0; } const button_item_t pingpong_stop_item = { _ITEM_BUTTON, "STOP", pingpong_stop_push }; const menu_t msg_pingpong_menu[] = { { {LAST_CHIP_MENU_ROW+3, 1}, NULL, &pingpong_start_item, FLAG_MSGTYPE_PING }, { {LAST_CHIP_MENU_ROW+3, 15}, NULL, &pingpong_stop_item, FLAG_MSGTYPE_PING }, { {0, 0}, NULL, NULL } }; const menu_t* get_msg_menu() { switch (msg_type) { case MSG_TYPE_PACKET: return msg_pkt_menu; case MSG_TYPE_PER: return msg_per_menu; case MSG_TYPE_PINGPONG: return msg_pingpong_menu; } return NULL; } void frf_print() { float MHz; #ifdef SX127x_H MHz = Radio::radio.get_frf_MHz(); #else MHz = Radio::radio.getMHz(); #endif pc.printf("%.3fMHz", MHz); } bool frf_write(const char* valStr) { float MHz; sscanf(valStr, "%f", &MHz); #ifdef SX127x_H Radio::radio.set_frf_MHz(MHz); #else Radio::radio.setMHz(MHz); #endif return false; } const value_item_t frf_item = { _ITEM_VALUE, 14, frf_print, frf_write }; const value_item_t Radio::tx_dbm_item = { _ITEM_VALUE, 7, Radio::tx_dbm_print, Radio::tx_dbm_write }; const dropdown_item_t tx_ramp_item = { _ITEM_DROPDOWN, Radio::tx_ramp_strs, Radio::tx_ramp_strs, Radio::tx_ramp_read, Radio::tx_ramp_write }; const button_item_t chipNum_item = { _ITEM_BUTTON, Radio::chipNum_str, NULL}; void botrow_print() { pc.printf("%u", botRow); } bool botrow_write(const char* str) { unsigned n; sscanf(str, "%u", &n); botRow = n; pc.printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region return false; } const value_item_t bottomRow_item = { _ITEM_VALUE, 4, botrow_print, botrow_write }; const menu_t common_menu[] = { { {1, 1}, NULL, &hw_reset_item, FLAG_MSGTYPE_ALL }, { {1, 15}, "packetType:", &pktType_item, FLAG_MSGTYPE_ALL }, { {1, 37}, NULL, &msgType_item, FLAG_MSGTYPE_ALL }, { {1, 50}, NULL, &chipNum_item, FLAG_MSGTYPE_ALL }, { {1, 60}, "bottomRow:", &bottomRow_item, FLAG_MSGTYPE_ALL }, { {2, 1}, NULL, &frf_item, FLAG_MSGTYPE_ALL }, { {2, 15}, "TX dBm:", &Radio::tx_dbm_item, FLAG_MSGTYPE_ALL }, { {2, 30}, "ramp us:", &tx_ramp_item, FLAG_MSGTYPE_ALL }, { {2, 45}, NULL, &clearirqs_item, FLAG_MSGTYPE_ALL }, { {2, 55}, "opmode:", &opmode_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; bool is_menu_item_changable(uint8_t table_row, uint8_t table_col) { const menu_t* m = menu_table[table_row][table_col]; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; switch (msg_type) { case MSG_TYPE_PACKET: if (m->flags & FLAG_MSGTYPE_PKT) break; else return false; case MSG_TYPE_PER: if (m->flags & FLAG_MSGTYPE_PER) break; else return false; case MSG_TYPE_PINGPONG: if (m->flags & FLAG_MSGTYPE_PING) break; else return false; } if (di->itemType == _ITEM_DROPDOWN) { if (di->selectable_strs == NULL || di->selectable_strs[0] == NULL) { log_printf("NULLstrs%u,%u\r\n", table_row, table_col); return false; } } else if (di->itemType == _ITEM_VALUE) { const value_item_t* vi = (const value_item_t*)m->itemPtr; if (vi->write == NULL) return false; } else if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->push == NULL) return false; } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t * ti = (const toggle_item_t *)m->itemPtr; if (ti->push == NULL) return false; } return true; } // ..is_menu_item_changable() void read_menu_item(const menu_t* m, bool selected) { uint8_t valCol; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; switch (msg_type) { case MSG_TYPE_PACKET: if (m->flags & FLAG_MSGTYPE_PKT) break; else return; case MSG_TYPE_PER: if (m->flags & FLAG_MSGTYPE_PER) break; else return; case MSG_TYPE_PINGPONG: if (m->flags & FLAG_MSGTYPE_PING) break; else return; } pc.printf("\e[%u;%uf", m->pos.row, m->pos.col); // set (force) cursor to row;column valCol = m->pos.col; if (m->label) { pc.printf(m->label); valCol += strlen(m->label); } if (di->itemType == _ITEM_DROPDOWN) { if (di->read && di->printed_strs) { uint8_t ridx = di->read(false); if (selected) pc.printf("\e[7m"); pc.printf(di->printed_strs[ridx]); if (selected) pc.printf("\e[0m"); } else if (di->printed_strs) { pc.printf(di->printed_strs[0]); } } else if (di->itemType == _ITEM_VALUE) { const value_item_t* vi = (const value_item_t*)m->itemPtr; if (vi->print) { for (unsigned n = 0; n < vi->width; n++) pc.putc(' '); pc.printf("\e[%u;%uf", m->pos.row, valCol); // set (force) cursor to row;column if (selected) pc.printf("\e[7m"); vi->print(); if (selected) pc.printf("\e[0m"); } } else if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->label) { if (selected) pc.printf("\e[7m%s\e[0m", bi->label); else pc.printf("%s", bi->label); } } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t* ti = (const toggle_item_t *)m->itemPtr; bool on = ti->read(); if (ti->label1) { const char* const cptr = on ? ti->label1 : ti->label0; if (selected) pc.printf("\e[7m%s\e[0m", cptr); else pc.printf("%s", cptr); } else { if (on) pc.printf("\e[1m"); if (selected) pc.printf("\e[7m"); pc.printf("%s", ti->label0); if (selected || on) pc.printf("\e[0m"); } } } // ..read_menu_item() void draw_menu() { unsigned table_row; for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) { int table_col; for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) { read_menu_item(menu_table[table_row][table_col], false); } // ..table column iterator } // ..table row iterator read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } // ..draw_menu() typedef struct { int row; int col; } tablexy_t; void menu_init_(const menu_t* in, tablexy_t* tc) { unsigned n; for (n = 0; in[n].pos.row > 0; n++) { const menu_t* m = &in[n]; if (tc->row != m->pos.row - 1) { tc->row = m->pos.row - 1; tc->col = 0; } else tc->col++; menu_table[tc->row][tc->col] = m; #ifdef MENU_DEBUG pc.printf("table:%u,%u ", tc->row, tc->col); pc.printf(" %d<%d? ", StopMenuCols[tc->row], tc->col); #endif /* MENU_DEBUG */ if (StopMenuCols[tc->row] < tc->col) StopMenuCols[tc->row] = tc->col; #ifdef MENU_DEBUG pc.printf("{%u %u}", tc->row, tc->col); pc.printf("in:%p[%u] screen:%u,%u ", in, n, m->pos.row, m->pos.col); //pc.printf(" loc:%p ", &in[n].itemPtr); if (in[n].itemPtr) { const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; pc.printf(" itemPtr:%p type:%02x ", di, di->itemType); } pc.printf("stopMenuCols[%u]: %d ", tc->row, StopMenuCols[tc->row]); if (m->label) pc.printf("label:%s", m->label); else pc.printf("noLabel"); pc.printf("\r\n"); #endif /* MENU_DEBUG */ } #ifdef MENU_DEBUG pc.printf("hit key:"); wait_uart_rx(); //pc.getc(); #endif /* MENU_DEBUG */ } void navigate_dropdown(uint8_t ch) { unsigned n; const menu_t* m = menuState.sm; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; switch (ch) { case 'A': // cursor UP if (menuState.sel_idx > 0) { menuState.sel_idx--; } break; case 'B': // cursor DOWN if (di->selectable_strs[menuState.sel_idx+1] != NULL) menuState.sel_idx++; break; } // ..switch (ch) for (n = 0; di->selectable_strs[n] != NULL; n++) { pc.printf("\e[%u;%uf", m->pos.row+n, menuState.dropdown_col); if (n == menuState.sel_idx) pc.printf("\e[7m"); pc.printf(di->selectable_strs[n]); if (n == menuState.sel_idx) pc.printf("\e[0m"); } pc.printf("\e[%u;%uf", m->pos.row + menuState.sel_idx, menuState.dropdown_col + strlen(di->selectable_strs[menuState.sel_idx])); } bool is_item_selectable(const menu_t* m) { const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->push == NULL) return false; } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr; if (ti->push == NULL) return false; } return true; } void navigate_menu(uint8_t ch) { read_menu_item(menu_table[curpos.row][curpos.tableCol], false); switch (ch) { case 'A': // cursor UP if (curpos.row == 0) break; { // find previous row up with column int8_t row; for (row = curpos.row - 1; row >= 0; row--) { if (StopMenuCols[row] > -1) { curpos.row = row; break; } } if (row == 0 && StopMenuCols[0] < 0) break; // nothing found } if (curpos.tableCol >= StopMenuCols[curpos.row]) { curpos.tableCol = StopMenuCols[curpos.row]-1; } break; case 'B': // cursor DOWN if (curpos.row >= MAX_MENU_ROWS) break; { // find next row down with column uint8_t row; for (row = curpos.row + 1; row < MAX_MENU_ROWS; row++) { if (StopMenuCols[row] != -1) { curpos.row = row; break; } } if (row == MAX_MENU_ROWS-1 && StopMenuCols[row] == -1) break; // nothing found } if (curpos.tableCol >= StopMenuCols[curpos.row]) { curpos.tableCol = StopMenuCols[curpos.row]-1; } break; case 'C': // cursor LEFT if (curpos.tableCol >= StopMenuCols[curpos.row]-1) break; { // find next row left with editable uint8_t tcol; for (tcol = curpos.tableCol + 1; tcol < StopMenuCols[curpos.row]; tcol++) { if (is_menu_item_changable(curpos.row, tcol)) { curpos.tableCol = tcol; break; } } } break; case 'D': // cursor RIGHT if (curpos.tableCol == 0) break; { int8_t tcol; for (tcol = curpos.tableCol - 1; tcol >= 0; tcol--) { if (is_menu_item_changable(curpos.row, tcol)) { curpos.tableCol = tcol; break; } } } break; default: //pc.printf("unhancled-csi:%02x\eE", ch); break; } // ..switch (ch) if (!is_item_selectable(menu_table[curpos.row][curpos.tableCol])) { int c; for (c = 0; c < StopMenuCols[curpos.row]; c++) { if (is_item_selectable(menu_table[curpos.row][c])) { curpos.tableCol = c; break; } } if (c == StopMenuCols[curpos.row]) return; } #ifdef MENU_DEBUG log_printf("table:%u,%u screen:%u,%u \r\n", curpos.row, curpos.tableCol, menu_table[curpos.row][curpos.tableCol]->pos.row, menu_table[curpos.row][curpos.tableCol]->pos.col ); #endif /* MENU_DEBUG */ read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } // ..navigate_menu void commit_menu_item_change() { const menu_t* m = menu_table[curpos.row][curpos.tableCol]; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; if (di->itemType == _ITEM_DROPDOWN) { menuState.mode = di->write(menuState.sel_idx); pc.printf("\e[%u;%uf", m->pos.row, m->pos.col-2); } else if (di->itemType == _ITEM_VALUE) { const value_item_t* vi = (const value_item_t*)m->itemPtr; /* commit value entry */ if (vi->write) { if (vi->write(entry_buf)) menuState.mode = MENUMODE_REDRAW; else menuState.mode = MENUMODE_NONE; } else menuState.mode = MENUMODE_NONE; if (menuState.mode == MENUMODE_NONE) { read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } } } // ..commit_menu_item_change() void refresh_item_in_table(const void* item) { unsigned table_row; if (item == NULL) return; for (table_row = 0; table_row < MAX_MENU_ROWS; table_row++) { int table_col; for (table_col = 0; table_col < StopMenuCols[table_row]; table_col++) { //log_printf("%u %u %p\r\n", table_row, table_col, menu_table[table_row][table_col]->itemPtr); if (item == menu_table[table_row][table_col]->itemPtr) { read_menu_item(menu_table[table_row][table_col], false); return; } } } } void start_value_entry(const menu_t* m) { const value_item_t* vi = (const value_item_t*)m->itemPtr; uint8_t col = m->pos.col; if (m->label) col += strlen(m->label); pc.printf("\e[%u;%uf", m->pos.row, col); for (unsigned i = 0; i < vi->width; i++) pc.putc(' '); // clear displayed value for user entry pc.printf("\e[%u;%uf", m->pos.row, col); menuState.mode = MENUMODE_ENTRY; entry_buf_idx = 0; } void start_menu_item_change() { const menu_t* m = menu_table[curpos.row][curpos.tableCol]; const dropdown_item_t* di = (const dropdown_item_t*)m->itemPtr; bool checkRefresh = false; if (di->itemType == _ITEM_DROPDOWN && di->selectable_strs) { menuState.dropdown_col = m->pos.col; unsigned n, sidx = 0; /* start dropdown */ if (di->read) sidx = di->read(true); if (m->label) menuState.dropdown_col += strlen(m->label); for (n = 0; di->selectable_strs[n] != NULL; n++) { uint8_t col = menuState.dropdown_col; bool leftPad = false; if (col > 3 && n > 0) { // dropdown left side padding col -= 2; leftPad = true; } pc.printf("\e[%u;%uf", m->pos.row+n, col); if (leftPad ) { pc.putc(' '); pc.putc(' '); } if (n == sidx) pc.printf("\e[7m"); pc.printf(di->selectable_strs[n]); if (n == sidx) pc.printf("\e[0m"); pc.putc(' '); // right side padding pc.putc(' '); } pc.printf("\e[%u;%uf", m->pos.row, menuState.dropdown_col-2); menuState.mode = MENUMODE_DROPDOWN; menuState.sel_idx = sidx; menuState.sm = m; } else if (di->itemType == _ITEM_VALUE) { /* start value entry */ start_value_entry(m); } else if (di->itemType == _ITEM_BUTTON) { const button_item_t* bi = (const button_item_t*)m->itemPtr; if (bi->push) { bi->push(); checkRefresh = true; } } else if (di->itemType == _ITEM_TOGGLE) { const toggle_item_t* ti = (const toggle_item_t*)m->itemPtr; if (ti->push) { bool on = ti->push(); uint8_t col = m->pos.col; if (m->label) col += strlen(m->label); pc.printf("\e[%u;%uf", m->pos.row, col); if (ti->label1) { pc.printf("\e[7m%s\e[0m", on ? ti->label1 : ti->label0); } else { if (on) pc.printf("\e[1;7m%s\e[0m", ti->label0); else pc.printf("\e[7m%s\e[0m", ti->label0); } checkRefresh = true; } } if (checkRefresh) { if (m->refreshReadItem) { refresh_item_in_table(m->refreshReadItem); // read associated read_menu_item(m, true); // restore cursor } } } // ..start_menu_item_change() void full_menu_init() { unsigned n; const menu_t *m; tablexy_t txy; txy.row = INT_MAX; txy.col = 0; for (n = 0; n < MAX_MENU_ROWS; n++) { StopMenuCols[n] = -1; } menu_init_(common_menu, &txy); menu_init_(Radio::common_menu, &txy); m = Radio::get_modem_menu(); if (m == NULL) { log_printf("NULL-modemMenu\r\n"); for (;;) asm("nop"); } #ifdef MENU_DEBUG pc.printf("modemmenuInit\r\n"); #endif menu_init_(m, &txy); m = Radio::get_modem_sub_menu(); if (m) { #ifdef MENU_DEBUG pc.printf("modemsubmenuInit\r\n"); #endif menu_init_(m, &txy); } #ifdef MENU_DEBUG else pc.printf("no-modemsubmenu\r\n"); #endif #ifdef PHOTOS_PIN menu_init_(photos_menu, &txy); #endif /* PHOTOS_PIN */ menu_init_(lis12dh12_menu, &txy); // accel menu_init_(lps22hh_menu, &txy); // temp, pressure m = get_msg_menu(); if (m == NULL) { log_printf("NULL-msgMenu %d\r\n", msg_type); for (;;) asm("nop"); } menu_init_(m, &txy); for (n = 0; n < MAX_MENU_ROWS; n++) { if (StopMenuCols[n] != -1) StopMenuCols[n]++; } } bool ishexchar(char ch) { if (((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) || (ch >= '0' && ch <= '9')) return true; else return false; } enum _urx_ { URX_STATE_NONE = 0, URX_STATE_ESCAPE, URX_STATE_CSI, } uart_rx_state; Timeout uartRxTimeout; void uart_rx_timeout() { /* escape by itself: abort change on item */ menuState.mode = MENUMODE_REDRAW; uart_rx_state = URX_STATE_NONE; } void serial_callback(char ch) { switch (uart_rx_state) { case URX_STATE_NONE: if (ch == 0x1b) { if (menuState.mode == MENUMODE_ENTRY) { /* abort entry mode */ menuState.mode = MENUMODE_NONE; read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } else { uart_rx_state = URX_STATE_ESCAPE; if (menuState.mode != MENUMODE_NONE) { /* is this escape by itself, user wants to abort? */ uartRxTimeout.attach(uart_rx_timeout, 0.03); } } } else if (ch == 2) { // ctrl-B log_printf("--------------\r\n"); } else if (ch == '\r') { if (menuState.mode == MENUMODE_NONE) { start_menu_item_change(); } else { entry_buf[entry_buf_idx] = 0; commit_menu_item_change(); } } else if (menuState.mode == MENUMODE_ENTRY) { if (ch == 8) { if (entry_buf_idx > 0) { pc.putc(8); pc.putc(' '); pc.putc(8); entry_buf_idx--; } } else if (ch == 3) { // ctrl-C menuState.mode = MENUMODE_NONE; } else if (entry_buf_idx < sizeof(entry_buf)) { entry_buf[entry_buf_idx++] = ch; pc.putc(ch); } } else if (menuState.mode == MENUMODE_NONE) { if (ishexchar(ch) || ch == '-') { // characters which start entry const value_item_t* vi = (const value_item_t*)menu_table[curpos.row][curpos.tableCol]->itemPtr; if (vi->itemType == _ITEM_VALUE) { start_value_entry(menu_table[curpos.row][curpos.tableCol]); entry_buf[entry_buf_idx++] = ch; pc.putc(ch); } } else if (ch == 'r') { menuState.mode = MENUMODE_REDRAW; } else if (ch == '.') { Radio::test(); } } break; case URX_STATE_ESCAPE: uartRxTimeout.detach(); if (ch == '[') uart_rx_state = URX_STATE_CSI; else { #ifdef MENU_DEBUG log_printf("unhancled-esc:%02x\r\n", ch); #endif /* MENU_DEBUG */ uart_rx_state = URX_STATE_NONE; } break; case URX_STATE_CSI: if (menuState.mode == MENUMODE_NONE) navigate_menu(ch); else if (menuState.mode == MENUMODE_DROPDOWN) navigate_dropdown(ch); uart_rx_state = URX_STATE_NONE; //pc.printf("\e[18;1f"); // set (force) cursor to row;column break; } // ..switch (uart_rx_state) } void ev_uart_rx() { log_printf("ev_uart_rx %u %u\r\n", uart_rx_buf_in, uart_rx_buf_out); while (uart_rx_buf_in != uart_rx_buf_out) { log_printf("X ev_uart_rx %u %u\r\n", uart_rx_buf_in, uart_rx_buf_out); serial_callback(uart_rx_buf[uart_rx_buf_out++]); if (uart_rx_buf_out == UART_RX_BUF_LEN) uart_rx_buf_out = 0; } } //volatile unsigned rxCnt = 0; void rx_isr() { //rxCnt++; uart_rx_buf[uart_rx_buf_in++] = pc.getc(); if (uart_rx_buf_in == UART_RX_BUF_LEN) uart_rx_buf_in = 0; //queue.call(ev_uart_rx); } void txDone() { if (msg_type == MSG_TYPE_PER) { log_printf("CntPacketTx%u, max:%u ipd%u\r\n", CntPacketTx, MaxNumPacket, tx_ipd_ms); if (++CntPacketTx <= MaxNumPacket) mbedTimeout.attach_us(next_tx_callback, tx_ipd_ms * 1000); } else if (msg_type == MSG_TYPE_PINGPONG) { if (flags.ping_master) { ++CntPacketTx; } Radio::Rx(); } flags.txBusy = false; } static void printRxPkt(uint8_t size) { char str[80]; char *ptr, *endPtr; unsigned n = 0; endPtr = str + sizeof(str); ptr = str; while (ptr < endPtr) { sprintf(ptr, "%02x ", Radio::radio.rx_buf[n]); ptr += 3; if (++n >= size) break; } log_printf("%s\r\n", str); } void rxDone(uint8_t size, float rssi, float snr) { log_printf("rxDone %u, %.1fdBm %.1fdB\r\n", size, rssi, snr); if (msg_type == MSG_TYPE_PACKET) { switch (Radio::radio.rx_buf[0]) { displayFloatToInt_t val; case CMD_TEMP: memcpy(&val, &Radio::radio.rx_buf[1], sizeof(displayFloatToInt_t)); log_printf("TEMP: %c%d.%02d\r\n", ((val.sign) ? '-' : '+'), (int)val.out_int, (int)val.out_dec); break; case CMD_PRES: memcpy(&val, &Radio::radio.rx_buf[1], sizeof(displayFloatToInt_t)); log_printf("PRESS: %c%d.%02d\r\n", ((val.sign) ? '-' : '+'), (int)val.out_int, (int)val.out_dec); break; case CMD_ACCEL: SensorAxes_t acc; memcpy(&acc, &Radio::radio.rx_buf[1], sizeof(SensorAxes_t)); log_printf("ACC %5ld %5ld %5ld\r\n", acc.AXIS_X, acc.AXIS_Y, acc.AXIS_Z); break; case CMD_PHOTOS: uint16_t pv; memcpy(&pv, &Radio::radio.rx_buf[1], sizeof(uint16_t)); log_printf("photos: %u\r\n", pv); break; default: printRxPkt(size); break; } } else if (msg_type == MSG_TYPE_PER) { if (memcmp(Radio::radio.rx_buf+4, PerMsg, 3) == 0) { unsigned i, PacketRxSequence = Radio::radio.rx_buf[0]; PacketRxSequence <<= 8; PacketRxSequence += Radio::radio.rx_buf[1]; PacketRxSequence <<= 8; PacketRxSequence += Radio::radio.rx_buf[2]; PacketRxSequence <<= 8; PacketRxSequence += Radio::radio.rx_buf[3]; CntPacketRxOK++; if (PacketRxSequence <= PacketRxSequencePrev || PacketRxSequencePrev == 0) i = 0; // sequence reset to resync, dont count missed packets this time else i = PacketRxSequence - PacketRxSequencePrev - 1; CntPacketRxKO += i; RxTimeOutCount = 0; log_printf("PER rx%u ok%u ko%u\r\n", PacketRxSequence , CntPacketRxOK, CntPacketRxKO); PacketRxSequencePrev = PacketRxSequence; } // ..if PerMsg else { log_printf("per?\r\n"); printRxPkt(size); } } else if (msg_type == MSG_TYPE_PINGPONG) { if (memcmp(Radio::radio.rx_buf+4, PingMsg, 4) == 0) { /* ping slave rx */ Radio::setFS(); receivedCntPacket = Radio::radio.rx_buf[0]; receivedCntPacket <<= 8; receivedCntPacket += Radio::radio.rx_buf[1]; receivedCntPacket <<= 8; receivedCntPacket += Radio::radio.rx_buf[2]; receivedCntPacket <<= 8; receivedCntPacket += Radio::radio.rx_buf[3]; log_printf("%u rxPing->txPong\r\n", receivedCntPacket); flags.ping_master = 0; flags.send_pong = 1; } else if (memcmp(Radio::radio.rx_buf+8, PongMsg, 4) == 0) { unsigned cnt; /* ping master rx */ Radio::setFS(); cnt = Radio::radio.rx_buf[0]; cnt <<= 8; cnt += Radio::radio.rx_buf[1]; cnt <<= 8; cnt += Radio::radio.rx_buf[2]; cnt <<= 8; cnt += Radio::radio.rx_buf[3]; log_printf("%u rxPong->txPing\r\n", cnt); flags.send_ping = 1; } else { log_printf("pingpong?\r\n"); printRxPkt(size); } } else { /*for (unsigned n = 0; n < size; n++) log_printf("%02x\r\n", Radio::radio.rx_buf[n]);*/ log_printf("msg_type %u\r\n", msg_type); } } const RadioEvents_t rev = { txDone, rxDone }; static DrvStatusTypeDef LIS2DH12_Read_Single_FIFO_Data(uint16_t sampleIndex, SensorAxes_t* acceleration) { //SensorAxes_t acceleration; /* Read single FIFO data (acceleration in 3 axes) */ if (lis2dh12_get_axes(acceleration) == COMPONENT_ERROR) { return COMPONENT_ERROR; } if (sampleIndex < SAMPLE_LIST_MAX) { log_printf("[DATA %02d] %5ld %5ld %5ld\r\n", sampleIndex + 1, acceleration->AXIS_X, acceleration->AXIS_Y, acceleration->AXIS_Z); } return COMPONENT_OK; } void uart_rx_service() { while (uart_rx_buf_in != uart_rx_buf_out) { serial_callback(uart_rx_buf[uart_rx_buf_out++]); if (uart_rx_buf_out == UART_RX_BUF_LEN) uart_rx_buf_out = 0; } } static DrvStatusTypeDef LIS2DH12_Read_All_FIFO_Data(void) { uint16_t samplesToRead = accel_get_num_samples(); SensorAxes_t acc; /* 'samplesToRead' actually contains number of words in FIFO but each FIFO sample (data set) consists of 3 words so the 'samplesToRead' has to be divided by 3 */ samplesToRead /= 3; log_printf("\r\n%d samples in FIFO.\r\n\r\nStarted downloading data from FIFO ...\r\n", samplesToRead); log_printf("\r\n[DATA ##] ACC_X ACC_Y ACC_Z [mg]\r\n"); for (int i = 0; i < samplesToRead; i++) { uart_rx_service(); if (LIS2DH12_Read_Single_FIFO_Data(i, &acc) == COMPONENT_ERROR) { return COMPONENT_ERROR; } else { } } if (accel_tx_en) { Radio::radio.tx_buf[0] = CMD_ACCEL; Radio::set_payload_length(sizeof(SensorAxes_t)+1); memcpy(&Radio::radio.tx_buf[1], &acc, sizeof(SensorAxes_t)); Radio::txPkt(); } if (samplesToRead > SAMPLE_LIST_MAX) { log_printf("\r\nSample list limited to: %d\r\n", SAMPLE_LIST_MAX); } return COMPONENT_OK; } int main() { lfsr = LFSR_INIT; msg_type = MSG_TYPE_PACKET; uart_rx_state = URX_STATE_NONE; pc.attach(rx_isr); Radio::boardInit(&rev); { unsigned n; for (n = 0; n < MAX_MENU_ROWS; n++) StopMenuCols[n] = -1; } botRow = MAX_MENU_ROWS + SCROLLING_ROWS; pc.baud(115200); pc.printf("\e[7h"); // enable line wrapping pc.printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region pc.printf("\e[2J"); // erase entire screen full_menu_init(); //wait_uart_rx(); //pc.getc(); pc.printf("\e[2J"); // erase entire screen menuState.mode = MENUMODE_NONE; draw_menu(); curpos.row = 0; curpos.tableCol = 0; tx_ipd_ms = 100; if (demo_start()) { log_printf("demo_start-Failed\r\n"); for (;;) { asm("nop"); } } else log_printf("demo_start-OK\r\n"); svc_lis2dh12 = true; for (;;) { int ret; uart_rx_service(); if (flags.send_ping) { if (flags.pingpongEnable) SendPing(); flags.send_ping = 0; } if (flags.send_pong) { if (flags.pingpongEnable) SendPong(); flags.send_pong = 0; } if (flags.do_next_tx) { do_next_tx(); flags.do_next_tx = 0; } if (menuState.mode == MENUMODE_REINIT_MENU) { full_menu_init(); menuState.mode = MENUMODE_REDRAW; } if (menuState.mode == MENUMODE_REDRAW) { // erase entire screen, some dropdowns extend to scrolling area pc.printf("\e[%u;%ur", MAX_MENU_ROWS, botRow); // set scrolling region, if terminal started after pc.printf("\e[2J"); //pc.printf("\e[%u;1f\e[1J", MAX_MENU_ROWS); // erase menu area menuState.mode = MENUMODE_NONE; draw_menu(); } if (Radio::service(menuState.mode == MENUMODE_NONE ? LAST_CHIP_MENU_ROW : -1)) { read_menu_item(menu_table[curpos.row][curpos.tableCol], true); } if (svc_lis2dh12) { ret = lis2dh_mainloop(); switch (ret) { case LIS2DH_FAIL_STATE: log_printf("lis2dh-stateFail\r\n"); break; case LIS2DH_FAIL: log_printf("lis2dh-Fail\r\n"); break; case LIS2DH_BSP_FAIL: log_printf("lis2dh bsp-fail\r\n"); wait(0.05); break; case LIS2DH_MAIN_SLEEP: lis2dh_set_fifo_mode(); log_printf("lis2dh fifo-mode-sleep\r\n"); svc_lis2dh12 = false; break; case LIS2DH_MAIN_READ_FIFO: if (LIS2DH12_Read_All_FIFO_Data() == COMPONENT_ERROR) { return LIS2DH_FAIL; } /* Reset FIFO by setting FIFO mode to Bypass */ if (lis2dh_set_fifo_bypass() < 0) { log_printf("fifo-bypass-fail\r\n"); } break; default: break; } } // ..if (svc_lis2dh12) else { } if (flags.measure_tx) { switch (enabledSensor) { case CMD_PHOTOS: photos_tx(); break; case CMD_PRES: pres_tx(); break; case CMD_TEMP: temp_tx(); break; } flags.measure_tx = 0; } } // ..for (;;) } void lis2dh_int1() { log_printf("\r\nReceived FIFO Threshold Interrupt on INT1 pin ...\r\n" "\r\nNucleo processor is waking up ...\r\n" ); svc_lis2dh12 = true; } char strbuf[255]; void c_log_printf(const char* format, ...) { va_list arglist; // put cursor at last scrolling-area line pc.printf("\e[%u;1f", botRow); va_start(arglist, format); vsnprintf(strbuf, sizeof(strbuf), format, arglist); va_end(arglist); pc.printf(strbuf); } void log_printf(const char* format, ...) { va_list arglist; // put cursor at last scrolling-area line pc.printf("\e[%u;1f", botRow); va_start(arglist, format); vsnprintf(strbuf, sizeof(strbuf), format, arglist); va_end(arglist); pc.printf(strbuf); }