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
Diff: main.cpp
- Revision:
- 0:e1e70da93044
- Child:
- 2:972a5704f152
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Wed Apr 24 10:11:06 2019 -0700 @@ -0,0 +1,1699 @@ +#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 +}; + +#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; + +const uint8_t PingMsg[] = "PING"; +const uint8_t PongMsg[] = "PONG"; +const uint8_t PerMsg[] = "PER"; + +volatile struct _flags_ { + uint8_t ping_master : 1; // 0 + uint8_t send_ping : 1; // 1 + uint8_t send_pong : 1; // 2 + uint8_t pingpongEnable : 1; // 3 + uint8_t do_next_tx : 1; // 4 +} flags; + +uint8_t botRow; +int regAddr = -1; + +/* + * 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 +#define PHOTOS_PIN +*/ +#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) { + log_printf("waiting %u %u\r\n", uart_rx_buf_in, foo); + } + + 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 }, +}; + + +#ifdef PHOTOS_PIN +void photos_push() +{ + uint16_t val = photos.read_u16(); + log_printf("photos %u\r\n", val); +} + +void tx_photos_push() +{ + uint16_t 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)); + Radio::txPkt(); +} + +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 }, +}; +#endif /* PHOTOS_PIN */ + +void temperature_push() +{ + displayFloatToInt_t val; + demo_sample_temp(&val); +} + +void pressure_push() +{ + displayFloatToInt_t val; + demo_sample_pressure(&val); +} + +void tx_temp_push() +{ + 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)); + + Radio::txPkt(); +} + +void tx_pressure_push() +{ + 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)); + + Radio::txPkt(); +} + + +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 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 }, +}; + +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); + menu_init_(lps22hh_menu, &txy); + + m = get_msg_menu(); + if (m == NULL) { + log_printf("NULL-msgMenu\r\n"); + for (;;) asm("nop"); + } + menu_init_(m, &txy); + + for (n = 0; n < MAX_MENU_ROWS; n++) { + if (StopMenuCols[n] != -1) + StopMenuCols[n]++; + } +} + +bool ishexchar(char ch) +{ + if (((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) || (ch >= '0' && ch <= '9')) + return true; + else + return false; +} + +enum _urx_ { + URX_STATE_NONE = 0, + URX_STATE_ESCAPE, + URX_STATE_CSI, +} uart_rx_state; + +Timeout uartRxTimeout; + +void uart_rx_timeout() +{ + /* escape by itself: abort change on item */ + menuState.mode = MENUMODE_REDRAW; + + uart_rx_state = URX_STATE_NONE; +} + +void 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(); + } +} + +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); + + //wait_ms(1000); + + 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(); + + 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 { + } + + } // ..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); +} +