// // Created by MightyPork on 2018/03/31. // #include #include "debug.h" #include "msg_queue.h" #include "gex_gateway.h" #include "main.h" #include "minmax.h" #include "payload_parser.h" #include "nrf.h" #include "crc32.h" #include "payload_builder.h" enum MsgTypes { MSG_TYPE_NETWORK_ID = 1, MSG_TYPE_DATA = 2, }; struct msg_network_id { uint8_t msg_type; uint8_t bytes[4]; }; struct msg_data { uint8_t msg_type; uint8_t dev_addr; uint8_t length; uint8_t data[32]; }; static uint8_t gex_network[4]; // USB RX enum USB_RX_STATE { CMD_STATE_IDLE = 0, CMD_STATE_TXMSG = 1, }; #define MAX_FRAME_LEN 512 static enum USB_RX_STATE cmd_state = CMD_STATE_IDLE; static uint32_t txmsg_len = 0; // total len to be collected static uint32_t txmsg_collected = 0; // already collected len static uint8_t txmsg_payload[MAX_FRAME_LEN]; // equal buffer size in GEX static uint8_t txmsg_addr = 0; static uint8_t txmsg_cksum = 0; enum GW_CMD { CMD_GET_ID = 'i', // 105 - get network ID CMD_RESET = 'r', // reset the radio and network CMD_ADD_NODES = 'n', // add a node by address byte CMD_TXMSG = 'm', // 109 - send a message }; void respond_gw_id(void); void handle_cmd_addnodes(PayloadParser *pp); void handle_cmd_reset(void); void start_slave_cmd(uint8_t slave_addr, uint16_t frame_len, uint8_t cksum) { txmsg_len = frame_len; txmsg_collected = 0; txmsg_addr = slave_addr; txmsg_cksum = cksum; } #define MAX_RETRY 20 void handle_txframe_chunk(const uint8_t *buffer, uint16_t size) { uint32_t wanted = MIN(txmsg_len - txmsg_collected, size); memcpy(&txmsg_payload[txmsg_collected], buffer, wanted); txmsg_collected += wanted; if (wanted < size) { // this was the end - simple checksum to verify it's a valid frame uint8_t ck = 0; for (int i = 0; i < txmsg_len; i++) { ck ^= txmsg_payload[i]; } ck = ~ck; if (ck != txmsg_cksum) { dbg("Checksum from usb master mismatch!"); } else { dbg_nrf("Verified, sending a %d B frame to slave.", (int) txmsg_len); uint8_t pipe = NRF_Addr2PipeNum(txmsg_addr); if (pipe == 0xFF) { dbg("Bad slave num!"); } else { uint32_t remain = txmsg_len; for (int i = 0; i <= txmsg_len/32; i++) { uint8_t chunk = (uint8_t) MIN(remain, 32); bool suc = false; uint16_t delay = 1; // repeated tx attempts for (int j = 0; j < MAX_RETRY; j++) { suc = NRF_SendPacket(pipe, &txmsg_payload[i*32], chunk); // use the pipe to retrieve the address if (!suc) { dbg_nrf("Retry"); LL_mDelay(delay); delay += 2; // longer delay next time } else { break; } } // TODO it sometimes gets through after a retry but the communication stalls?? if(delay != 1 && suc) { dbg_nrf(" Succ after retry"); } remain -= chunk; if (!suc) { dbg("Sending failed, discard rest"); NRF_PowerDown(); NRF_FlushRx(); NRF_FlushTx(); NRF_ModeRX(); break; // skip rest of the frame } } if (remain == 0) { dbg_nrf("Sending completed. Is Rx ready? %d", (int)NRF_IsRxPacket()); } } } LL_GPIO_ResetOutputPin(LED_GPIO_Port, LEDTX_Pin); cmd_state = CMD_STATE_IDLE; } } void gw_handle_usb_out(uint8_t *buffer) { if (cmd_state == CMD_STATE_IDLE) { PayloadParser pp = pp_start(buffer, 64, NULL); // handle binary commands for the gateway switch (pp_u8(&pp)) { case CMD_GET_ID: respond_gw_id(); break; case CMD_RESET: handle_cmd_reset(); break; case CMD_ADD_NODES: // payload is: u8-count, u8[] node addresses handle_cmd_addnodes(&pp); break; case CMD_TXMSG:; // u8-slave-addr, u16-len, u8-checksum // the message is sent in the following frames. uint8_t slave_addr = pp_u8(&pp); uint16_t frame_len = pp_u16(&pp); uint8_t cksum = pp_u8(&pp); if (frame_len == 0 || frame_len > MAX_FRAME_LEN) { dbg("Frame too big!"); break; } LL_GPIO_SetOutputPin(LED_GPIO_Port, LEDTX_Pin); start_slave_cmd(slave_addr, frame_len, cksum); dbg_nrf("Collecting frame for slave %02x: %d bytes", (int)slave_addr, (int)frame_len); cmd_state = CMD_STATE_TXMSG; // handle the rest as payload uint32_t len; const uint8_t *tail = pp_tail(&pp, &len); handle_txframe_chunk(tail, (uint16_t) len); break; default: dbg("Bad cmd"); } } else if (cmd_state == CMD_STATE_TXMSG) { LL_GPIO_SetOutputPin(LED_GPIO_Port, LEDTX_Pin); handle_txframe_chunk(buffer, 64); } } void handle_cmd_reset(void) { // mq_reset(&usb_inq); // TODO this may be causing problems?? NRF_ResetPipes(); NRF_PowerDown(); NRF_FlushRx(); NRF_FlushTx(); NRF_ModeRX(); } void handle_cmd_addnodes(PayloadParser *pp) { uint8_t count = pp_u8(pp); for(int i = 0; i < count; i++) { uint8_t node = pp_u8(pp); uint8_t pipenum; bool suc = NRF_AddPipe(node, &pipenum); if (!suc) { dbg("Failed to add node."); } else { dbg_nrf("Bound node %02x to pipe %d", node, pipenum); } } } void respond_gw_id(void) { dbg_nrf("> respond_gw_id"); struct msg_network_id m = { .msg_type = MSG_TYPE_NETWORK_ID, .bytes = { gex_network[0], gex_network[1], gex_network[2], gex_network[3], } }; bool suc = mq_post(&usb_inq, (uint8_t *) &m, sizeof(m)); if (!suc) { dbg("IN que overflow!!"); } } /** * Compute the GEX network ID based on the gateway MCU unique ID. * The ID is a 4-byte code that must be entered in each node to join the network. */ static void compute_network_id(void) { uint8_t uid[12]; PayloadBuilder pb = pb_start(uid, 12, NULL); pb_u32(&pb, LL_GetUID_Word0()); pb_u32(&pb, LL_GetUID_Word1()); pb_u32(&pb, LL_GetUID_Word2()); uint32_t ck = CRC32_Start(); for (int i = 0; i < 12; i++) { ck = CRC32_Add(ck, uid[i]); } ck = CRC32_End(ck); pb = pb_start(gex_network, 4, NULL); pb_u32(&pb, ck); dbg("Gateway network ID: %02X-%02X-%02X-%02X", gex_network[0], gex_network[1], gex_network[2], gex_network[3]); } void gw_setup_radio(void) { dbg("Init NRF"); NRF_Init(NRF_SPEED_2M); compute_network_id(); NRF_SetBaseAddress(gex_network); NRF_ModeRX(); // base state is RX } void EXTI2_IRQHandler(void) { LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); LL_GPIO_SetOutputPin(LED_GPIO_Port, LEDRX_Pin); struct msg_data m; m.msg_type = MSG_TYPE_DATA; uint8_t pipenum; m.length = NRF_ReceivePacket(m.data, &pipenum); if (m.length == 0) { dbg("IRQ but no msg!"); } else { dbg_nrf("Msg RXd from nordic!"); m.dev_addr = NRF_PipeNum2Addr(pipenum); bool suc = mq_post(&usb_inq, (uint8_t *) &m, sizeof(m)); if (!suc) { dbg("IN que overflow!!"); } } }