void bootstrap() { DPRINT("Device booted at time: %d\n", timer_get_counter_value()); // TODO not printed for some reason, debug later id = hw_get_unique_id(); hw_radio_init(&alloc_new_packet, &release_packet); rx_cfg.channel_id = current_channel_id; tx_cfg.channel_id = current_channel_id; ubutton_register_callback(0, &userbutton_callback); ubutton_register_callback(1, &userbutton_callback); fifo_init(&uart_rx_fifo, uart_rx_buffer, sizeof(uart_rx_buffer)); uart_set_rx_interrupt_callback(&uart_rx_cb); uart_rx_interrupt_enable(true); sched_register_task(&start_rx); sched_register_task(&transmit_packet); sched_register_task(&start); sched_register_task(&process_uart_rx_fifo); current_state = STATE_CONFIG_DIRECTION; sched_post_task(&start); sched_post_task(&process_uart_rx_fifo); }
// TODO document state diagram static void switch_state(state_t new_state) { switch(new_state) { case D7ASP_STATE_MASTER: switch(d7asp_state) { case D7ASP_STATE_IDLE: d7asp_state = new_state; sched_post_task(&flush_fifos); DPRINT("Switching to state D7ASP_STATE_MASTER"); break; case D7ASP_STATE_SLAVE_PENDING_MASTER: d7asp_state = new_state; sched_post_task(&flush_fifos); DPRINT("Switching to state D7ASP_STATE_MASTER"); break; case D7ASP_STATE_SLAVE: d7asp_state = D7ASP_STATE_SLAVE_PENDING_MASTER; DPRINT("Switching to state D7ASP_STATE_SLAVE_PENDING_MASTER"); break; case D7ASP_STATE_MASTER: // new requests in fifo, reschedule for later flushing // TODO sched_post_task(&flush_fifos); break; default: assert(false); } break; case D7ASP_STATE_SLAVE: switch(d7asp_state) { case D7ASP_STATE_IDLE: d7asp_state = new_state; current_request_id = NO_ACTIVE_REQUEST_ID; DPRINT("Switching to state D7ASP_STATE_SLAVE"); break; default: assert(false); } break; case D7ASP_STATE_SLAVE_PENDING_MASTER: assert(d7asp_state == D7ASP_STATE_SLAVE); d7asp_state = D7ASP_STATE_SLAVE_PENDING_MASTER; DPRINT("Switching to state D7ASP_STATE_SLAVE_PENDING_MASTER"); break; case D7ASP_STATE_IDLE: d7asp_state = new_state; current_request_id = NO_ACTIVE_REQUEST_ID; DPRINT("Switching to state D7ASP_STATE_IDLE"); break; default: assert(false); } }
void bootstrap() { DPRINT("Device booted at time: %d\n", timer_get_counter_value()); #ifndef FRAMEWORK_LOG_BINARY console_print("\r\nPER TEST - commands:\r\n"); console_print(" CHANfffriii channel settings:\r\n"); console_print(" fff frequency : 433, 868, 915\r\n"); console_print(" r rate : L(ow) N(ormal) H(igh)\r\n"); console_print(" iii center_freq_index\r\n"); console_print(" TRANsss transmit a packet every sss seconds.\r\n"); console_print(" RECV receive packets\r\n"); console_print(" RSET reset module\r\n"); #endif id = hw_get_unique_id(); hw_radio_init(&alloc_new_packet, &release_packet); rx_cfg.channel_id = current_channel_id; tx_cfg.channel_id = current_channel_id; ubutton_register_callback(0, &userbutton_callback); ubutton_register_callback(1, &userbutton_callback); fifo_init(&uart_rx_fifo, uart_rx_buffer, sizeof(uart_rx_buffer)); console_set_rx_interrupt_callback(&uart_rx_cb); console_rx_interrupt_enable(); sched_register_task(&start_rx); sched_register_task(&transmit_packet); sched_register_task(&start); sched_register_task(&process_uart_rx_fifo); current_state = STATE_CONFIG_DIRECTION; sched_post_task(&start); sched_post_task(&process_uart_rx_fifo); #ifdef PLATFORM_EFM32GG_STK3700 #else char str[20]; channel_id_to_string(¤t_channel_id, str, sizeof(str)); lcd_write_line(6, str); #endif timer_post_task(&transmit_packet, TIMER_TICKS_PER_SEC * 1); }
static void process_uart_rx_fifo() { if(fifo_get_size(&uart_rx_fifo) >= COMMAND_SIZE) { uint8_t received_cmd[COMMAND_SIZE]; fifo_pop(&uart_rx_fifo, received_cmd, COMMAND_SIZE); if(strncmp(received_cmd, COMMAND_CHAN, COMMAND_SIZE) == 0) { process_command_chan(); } else if(strncmp(received_cmd, COMMAND_TRAN, COMMAND_SIZE) == 0) { while(fifo_get_size(&uart_rx_fifo) < COMMAND_TRAN_PARAM_SIZE); char param[COMMAND_TRAN_PARAM_SIZE]; fifo_pop(&uart_rx_fifo, param, COMMAND_TRAN_PARAM_SIZE); tx_packet_delay_s = atoi(param); DPRINT("performing TRAN command with %d tx_packet_delay_s\r\n", tx_packet_delay_s); stop(); is_mode_rx = false; current_state = STATE_RUNNING; sched_post_task(&start); } else if(strncmp(received_cmd, COMMAND_RECV, COMMAND_SIZE) == 0) { DPRINT("entering RECV mode\r\n"); stop(); is_mode_rx = true; current_state = STATE_RUNNING; sched_post_task(&start); } else if(strncmp(received_cmd, COMMAND_RSET, COMMAND_SIZE) == 0) { DPRINT("resetting...\r\n"); hw_reset(); } else { char err[40]; DPRINT("ERROR invalid command %.4s\n\r", received_cmd); } fifo_clear(&uart_rx_fifo); } timer_post_task_delay(&process_uart_rx_fifo, TIMER_TICKS_PER_SEC); }
void read_rssi() { timestamped_rssi_t rssi_measurement; rssi_measurement.tick = timer_get_counter_value(); char rssi_samples_str[5 * RSSI_SAMPLES_PER_MEASUREMENT] = ""; int16_t max_rssi_sample = -200; for(int i = 0; i < RSSI_SAMPLES_PER_MEASUREMENT; i++) { rssi_measurement.rssi[i] = hw_radio_get_rssi(); if(rssi_measurement.rssi[i] > max_rssi_sample) max_rssi_sample = rssi_measurement.rssi[i]; sprintf(rssi_samples_str + (i * 5), ",%04i", rssi_measurement.rssi[i]); // TODO delay? } char str[80]; char channel_str[8] = ""; channel_id_to_string(&rx_cfg.channel_id, channel_str, sizeof(channel_str)); lcd_write_string(channel_str); sprintf(str, "%7s,%i%s\n", channel_str, rssi_measurement.tick, rssi_samples_str); console_print(str); #ifdef PLATFORM_EFM32GG_STK3700 //lcd_all_on(); lcd_write_number(max_rssi_sample); #elif defined HAS_LCD sprintf(str, "%7s,%d\n", channel_str, max_rssi_sample); lcd_write_string(str); #endif if(!use_manual_channel_switching) { switch_next_channel(); sched_post_task(&start_rx); } else { sched_post_task(&process_uart_rx_fifo); // check for UART commands first uint16_t delay = rand() % 5000; timer_post_task_delay(&read_rssi, delay); } hw_watchdog_feed(); }
void button_task() { button_id_t button_id = NUM_USERBUTTONS; ubutton_callback_t callback = 0x0; start_atomic(); for(int i = 0; i < NUM_USERBUTTONS;i++) { for(;buttons[i].cur_callback_id < BUTTON_QUEUE_SIZE && buttons[i].callbacks[buttons[i].cur_callback_id] == 0x0; buttons[i].cur_callback_id++); if(buttons[i].cur_callback_id < BUTTON_QUEUE_SIZE) { callback = buttons[i].callbacks[buttons[i].cur_callback_id]; button_id = i; buttons[i].cur_callback_id++; break; } } end_atomic(); if(button_id < NUM_USERBUTTONS && callback != 0x0) { //reschedule the task to do the next callback (if needed) sched_post_task(&button_task); callback(button_id); } }
static void on_request_completed() { assert(d7asp_state == D7ASP_STATE_MASTER); if(!bitmap_get(current_master_session.progress_bitmap, current_request_id)) { current_request_retry_count++; // the request may be retransmitted, don't free yet (this will be done in flush_fifo() when failed) } else { // request completed, no retries needed so we can free the packet packet_queue_free_packet(current_request_packet); // terminate the dialog if all request handled // we need to switch to the state idle otherwise we may receive a new packet before the task flush_fifos is handled // in this case, we may assert since the state remains MASTER if (current_request_id == current_master_session.next_request_id - 1) { flush_completed(); return; } current_request_id = NO_ACTIVE_REQUEST_ID; } sched_post_task(&flush_fifos); // continue flushing until all request handled ... }
void bootstrap() { hw_radio_init(NULL, NULL); sched_register_task(&read_rssi); sched_register_task(&start_rx); sched_post_task(&start_rx); }
static void process_uart_rx_fifo() { if(fifo_get_size(&uart_rx_fifo) >= COMMAND_SIZE) { uint8_t received_cmd[COMMAND_SIZE]; fifo_pop(&uart_rx_fifo, received_cmd, COMMAND_SIZE); if(strncmp(received_cmd, COMMAND_CHAN, COMMAND_SIZE) == 0) { process_command_chan(); } else if(strncmp(received_cmd, COMMAND_TRAN, COMMAND_SIZE) == 0) { while(fifo_get_size(&uart_rx_fifo) < COMMAND_TRAN_PARAM_SIZE); char param[COMMAND_TRAN_PARAM_SIZE]; fifo_pop(&uart_rx_fifo, param, COMMAND_TRAN_PARAM_SIZE); tx_packet_delay_s = atoi(param); stop(); is_mode_rx = false; current_state = STATE_RUNNING; sched_post_task(&start); } else if(strncmp(received_cmd, COMMAND_RECV, COMMAND_SIZE) == 0) { stop(); is_mode_rx = true; current_state = STATE_RUNNING; sched_post_task(&start); } else if(strncmp(received_cmd, COMMAND_RSET, COMMAND_SIZE) == 0) { hw_reset(); } else { char err[40]; snprintf(err, sizeof(err), "ERROR invalid command %.4s\n", received_cmd); uart_transmit_string(err); } fifo_clear(&uart_rx_fifo); } timer_post_task_delay(&process_uart_rx_fifo, TIMER_TICKS_PER_SEC); }
static void packet_received(hw_radio_packet_t* packet) { uint16_t crc = __builtin_bswap16(crc_calculate(packet->data, packet->length - 2)); if(memcmp(&crc, packet->data + packet->length + 1 - 2, 2) != 0) { DPRINT("CRC error"); missed_packets_counter++; } else { #if HW_NUM_LEDS > 0 led_toggle(0); #endif uint16_t msg_counter = 0; uint64_t msg_id; memcpy(&msg_id, packet->data + 1, sizeof(msg_id)); memcpy(&msg_counter, packet->data + 1 + sizeof(msg_id), sizeof(msg_counter)); char chan[8]; channel_id_to_string(&(packet->rx_meta.rx_cfg.channel_id), chan, sizeof(chan)); console_printf("%7s,%i,%i,%lu,%lu,%i\n", chan, msg_counter, packet->rx_meta.rssi, (unsigned long)msg_id, (unsigned long)id, packet->rx_meta.timestamp); if(counter == 0) { // just start, assume received all previous counters to reset PER to 0% received_packets_counter = msg_counter - 1; counter = msg_counter - 1; } uint16_t expected_counter = counter + 1; if(msg_counter == expected_counter) { received_packets_counter++; counter++; } else if(msg_counter > expected_counter) { missed_packets_counter += msg_counter - expected_counter; counter = msg_counter; } else { sched_post_task(&start); } double per = 0; if(msg_counter > 0) per = 100.0 - ((double)received_packets_counter / (double)msg_counter) * 100.0; sprintf(lcd_msg, "%i %i", (int)per, packet->rx_meta.rssi); #ifdef PLATFORM_EFM32GG_STK3700 lcd_write_string(lcd_msg); #else lcd_write_line(4, lcd_msg); #endif } }
static void process_rx_fifo(void *arg) { if(!parsed_header) { // <sync byte (0xC0)><version (0x00)><length of ALP command (1 byte)><ALP command> // TODO CRC if(fifo_get_size(&rx_fifo) > SERIAL_ALP_FRAME_HEADER_SIZE) { uint8_t header[SERIAL_ALP_FRAME_HEADER_SIZE]; fifo_peek(&rx_fifo, header, 0, SERIAL_ALP_FRAME_HEADER_SIZE); DPRINT_DATA(header, 3); // TODO tmp if(header[0] != SERIAL_ALP_FRAME_SYNC_BYTE || header[1] != SERIAL_ALP_FRAME_VERSION) { fifo_skip(&rx_fifo, 1); DPRINT("skip"); parsed_header = false; payload_len = 0; if(fifo_get_size(&rx_fifo) > SERIAL_ALP_FRAME_HEADER_SIZE) sched_post_task(&process_rx_fifo); return; } parsed_header = true; fifo_skip(&rx_fifo, SERIAL_ALP_FRAME_HEADER_SIZE); payload_len = header[2]; DPRINT("found header, payload size = %i", payload_len); sched_post_task(&process_rx_fifo); } } else { if(fifo_get_size(&rx_fifo) < payload_len) { DPRINT("payload not complete yet"); return; } // payload complete, start parsing // rx_fifo can be bigger than the current serial packet, init a subview fifo // which is restricted to payload_len so we can't parse past this packet. fifo_t payload_fifo; fifo_init_subview(&payload_fifo, &rx_fifo, 0, payload_len); process_serial_frame(&payload_fifo); // pop parsed bytes from original fifo fifo_skip(&rx_fifo, payload_len - fifo_get_size(&payload_fifo)); parsed_header = false; } }
static void uart_rx_cb(uint8_t data) { if( echo ) { console_print_byte(data); if( data == '\r' ) { console_print_byte('\n'); } } error_t err; err = fifo_put(&cmd_fifo, &data, 1); assert(err == SUCCESS); if(!sched_is_scheduled(&process_cmd_fifo)) sched_post_task(&process_cmd_fifo); }
void userbutton_callback(button_id_t button_id) { // change channel and restart use_manual_channel_switching = true; switch(button_id) { case 0: switch_prev_channel(); break; case 1: switch_next_channel(); break; } sched_post_task(&start_rx); }
static void button_callback(pin_id_t pin_id, uint8_t event_mask) { for(int i = 0; i < NUM_USERBUTTONS;i++) { if(hw_gpio_pin_matches(buttons[i].button_id, pin_id)) { //set cur_callback_id to 0 to trigger all registered callbacks and post a task to do the actual callbacks buttons[i].cur_callback_id = 0; sched_post_task(&button_task); } } }
void bootstrap() { DPRINT("Device booted at time: %d\n", timer_get_counter_value()); // TODO not printed for some reason, debug later data[0] = PACKET_LENGTH-1; int i = 1; for (;i<PACKET_LENGTH;i++) data[i] = i; hw_radio_init(&alloc_new_packet, &release_packet); tx_packet->tx_meta.tx_cfg = tx_cfg; #ifdef RX_MODE sched_register_task(&start_rx); sched_post_task(&start_rx); #else sched_register_task(&transmit_packet); sched_post_task(&transmit_packet); #endif }
// TODO doc // ATx\r : shell command, where x is a char which maps to a command. // List of supported commands: // - R: reboot device // AT$<command handler id> : command to be handled by the command handler specified. The command handler id is a byte < 65 (non ASCII) // The handlers are passed the command fifo (including the header) and are responsible for pop()-ing the bytes which are processed by the handler. // When the fifo does not yet contain a full command which can be processed by the specific handler nothing should be popped and the handler will // called again later when more data is received. static void process_cmd_fifo() { if(fifo_get_size(&cmd_fifo) >= SHELL_CMD_HEADER_SIZE) { uint8_t cmd_header[SHELL_CMD_HEADER_SIZE]; fifo_peek(&cmd_fifo, cmd_header, 0, SHELL_CMD_HEADER_SIZE); if(cmd_header[0] != 'A' || cmd_header[1] != 'T') { // unexpected data, pop and return // TODO log? fifo_pop(&cmd_fifo, cmd_header, 1); sched_post_task(&process_cmd_fifo); return; } if(cmd_header[2] != '$') { process_shell_cmd(cmd_header[2]); fifo_pop(&cmd_fifo, cmd_header, SHELL_CMD_HEADER_SIZE); } else { get_cmd_handler_callback(cmd_header[3])(&cmd_fifo); } sched_post_task(&process_cmd_fifo); } else if(fifo_get_size(&cmd_fifo) >= 3) { // AT[\r|\n] uint8_t cmd_header[3]; fifo_peek(&cmd_fifo, cmd_header, 0, 3); if( cmd_header[0] == 'A' && cmd_header[1] == 'T' && ( cmd_header[2] == '\r' || cmd_header[2] == '\n' ) ) { console_print("OK\r\n"); fifo_pop(&cmd_fifo, cmd_header, 3); } } }
static void userbutton_callback(button_id_t button_id) { stop(); switch(button_id) { case 0: switch(current_state) { case STATE_CONFIG_DIRECTION: current_state = STATE_CONFIG_DATARATE; break; case STATE_CONFIG_DATARATE: current_state = STATE_RUNNING; break; case STATE_RUNNING: current_state = STATE_CONFIG_DIRECTION; break; } break; case 1: switch(current_state) { case STATE_CONFIG_DIRECTION: is_mode_rx = !is_mode_rx; break; case STATE_CONFIG_DATARATE: if(current_channel_id.channel_header.ch_class == PHY_CLASS_NORMAL_RATE) current_channel_id.channel_header.ch_class = PHY_CLASS_LO_RATE; else current_channel_id.channel_header.ch_class = PHY_CLASS_NORMAL_RATE; break; case STATE_RUNNING: increase_channel(); break; } break; } sched_post_task(&start); }
void userbutton_callback(button_id_t button_id) { // change channel and restart switch(button_id) { case 0: if(current_channel_indexes_index > 0) current_channel_indexes_index--; else current_channel_indexes_index = channel_count - 1; break; case 1: if(current_channel_indexes_index < channel_count - 1) current_channel_indexes_index++; else current_channel_indexes_index = 0; } sched_post_task(&start); }
void userbutton_callback(button_id_t button_id) { switch(button_id) { case 0: // TODO switch to values in dBm and use API to change instead of directly changing reg values // change ezr eirp and restart if(current_eirp_level < MAX_EIRP+1) current_eirp_level -= 0x05; else current_eirp_level = MAX_EIRP; //change eirp level change_eirp(); break; case 1: // change channel and restart if(current_channel_indexes_index < channel_count - 1) current_channel_indexes_index++; else current_channel_indexes_index = 0; sched_post_task(&start); } }
void d7asp_signal_transaction_response_period_elapsed() { if(d7asp_state == D7ASP_STATE_MASTER) on_request_completed(); else if((d7asp_state == D7ASP_STATE_SLAVE) || (d7asp_state == D7ASP_STATE_SLAVE_PENDING_MASTER)) { if (current_response_packet) { DPRINT("Discard the response since the response period is expired"); packet_queue_free_packet(current_response_packet); current_response_packet = NULL; } if (d7asp_state == D7ASP_STATE_SLAVE) switch_state(D7ASP_STATE_IDLE); else if(d7asp_state == D7ASP_STATE_SLAVE_PENDING_MASTER) { switch_state(D7ASP_STATE_MASTER); DPRINT("Schedule task to flush the fifo"); sched_post_task(&flush_fifos); } } }
static void execute_csma_ca() { // TODO generate random channel queue //hw_radio_set_rx(NULL, NULL, NULL); // put radio in RX but disable callbacks to make sure we don't receive packets when in this state // TODO use correct rx cfg + it might be interesting to switch to idle first depending on calculated offset uint16_t tx_duration = calculate_tx_duration(); timer_tick_t Tc = CONVERT_TO_TI(current_packet->d7atp_tc); switch (dll_state) { case DLL_STATE_CSMA_CA_STARTED: { dll_tca = Tc - tx_duration; dll_cca_started = timer_get_counter_value(); DPRINT("Tca= %i = %i - %i", dll_tca, Tc, tx_duration); #ifndef FRAMEWORK_TIMER_RESET_COUNTER // Adjust TCA value according the time already elapsed in the response period if (tc_starting_time) // TODO how do manage tc_starting_time? not set for now { dll_tca -= dll_cca_started - tc_starting_time; DPRINT("Adjusted Tca= %i = %i - %i", dll_tca, dll_cca_started, tc_starting_time); } #endif if (dll_tca <= 0) { DPRINT("Tca negative, CCA failed"); // Let the upper layer decide eventually to change the channel in order to get a chance a send this frame switch_state(DLL_STATE_IDLE); d7anp_signal_packet_csma_ca_insertion_completed(false); break; } uint16_t t_offset = 0; csma_ca_mode_t csma_ca_mode = current_access_profile->control_csma_ca_mode; // TODO overrule mode to UNC for subsequent requests by the requester, or a single response to a unicast request switch(csma_ca_mode) { case CSMA_CA_MODE_UNC: // no delay dll_slot_duration = 0; break; case CSMA_CA_MODE_AIND: // TODO implement AIND { dll_slot_duration = tx_duration; // no initial delay break; } case CSMA_CA_MODE_RAIND: // TODO implement RAIND { dll_slot_duration = tx_duration; uint16_t max_nr_slots = dll_tca / tx_duration; uint16_t slots_wait = get_rnd() % max_nr_slots; t_offset = slots_wait * tx_duration; break; } case CSMA_CA_MODE_RIGD: // TODO implement RAIND { dll_rigd_n = 0; dll_tca0 = dll_tca; dll_slot_duration = (uint16_t) ((double)dll_tca0) / (2 << (dll_rigd_n)); t_offset = get_rnd() % dll_slot_duration; break; } } DPRINT("slot duration: %i t_offset: %i csma ca mode: %i", dll_slot_duration, t_offset, csma_ca_mode); dll_to = dll_tca - t_offset; if (t_offset > 0) { switch_state(DLL_STATE_CCA1); timer_post_task_delay(&execute_cca, t_offset); } else { switch_state(DLL_STATE_CCA1); sched_post_task(&execute_cca); } break; } case DLL_STATE_CSMA_CA_RETRY: { int32_t cca_duration = timer_get_counter_value() - dll_cca_started; dll_to -= cca_duration; DPRINT("RETRY dll_to = %i < %i ", dll_to, t_g); if (dll_to < t_g) { switch_state(DLL_STATE_CCA_FAIL); sched_post_task(&execute_csma_ca); break; } dll_tca = dll_to; dll_cca_started = timer_get_counter_value(); uint16_t t_offset = 0; switch(current_access_profile->control_csma_ca_mode) { case CSMA_CA_MODE_AIND: case CSMA_CA_MODE_RAIND: { uint16_t max_nr_slots = dll_tca / tx_duration; uint16_t slots_wait = get_rnd() % max_nr_slots; t_offset = slots_wait * tx_duration; break; } case CSMA_CA_MODE_RIGD: { dll_rigd_n++; dll_slot_duration = (uint16_t) ((double)dll_tca0) / (2 << (dll_rigd_n+1)); if(dll_slot_duration != 0) // TODO can be 0, validate t_offset = get_rnd() % dll_slot_duration; else t_offset = 0; DPRINT("slot duration: %i", dll_slot_duration); break; } } DPRINT("t_offset: %i", t_offset); dll_to = dll_tca - t_offset; if (t_offset > 0) { timer_post_task_delay(&execute_csma_ca, t_offset); } else { switch_state(DLL_STATE_CCA1); sched_post_task(&execute_cca); } break; } case DLL_STATE_CCA_FAIL: { // TODO hw_radio_set_idle(); switch_state(DLL_STATE_IDLE); d7anp_signal_packet_csma_ca_insertion_completed(false); if (process_received_packets_after_tx) { sched_post_task(&process_received_packets); process_received_packets_after_tx = false; } if (resume_fg_scan) { switch_state(DLL_STATE_FOREGROUND_SCAN); hw_rx_cfg_t rx_cfg = (hw_rx_cfg_t){ .channel_id.channel_header = current_access_profile->subbands[0].channel_header, .channel_id.center_freq_index = current_access_profile->subbands[0].channel_index_start, .syncword_class = PHY_SYNCWORD_CLASS1, }; hw_radio_set_rx(&rx_cfg, &packet_received, NULL); resume_fg_scan = false; } break; } } } void dll_execute_scan_automation() { uint8_t scan_access_class = fs_read_dll_conf_active_access_class(); if(active_access_class != scan_access_class) { fs_read_access_class(scan_access_class, &scan_access_profile); active_access_class = scan_access_class; } current_access_profile = &scan_access_profile; if(current_access_profile->control_scan_type_is_foreground && current_access_profile->control_number_of_subbands > 0) // TODO background scan { assert(current_access_profile->control_number_of_subbands == 1); // TODO multiple not supported switch_state(DLL_STATE_SCAN_AUTOMATION); hw_rx_cfg_t rx_cfg = { .channel_id = { .channel_header = current_access_profile->subbands[0].channel_header, .center_freq_index = current_access_profile->subbands[0].channel_index_start }, .syncword_class = PHY_SYNCWORD_CLASS1 };
static void flush_fifos() { assert(d7asp_state == D7ASP_STATE_MASTER); DPRINT("Flushing FIFOs"); hw_watchdog_feed(); // TODO do here? if(current_request_id == NO_ACTIVE_REQUEST_ID) { // find first request which is not acked or dropped int8_t found_next_req_index = bitmap_search(current_master_session.progress_bitmap, false, MODULE_D7AP_FIFO_MAX_REQUESTS_COUNT); if(found_next_req_index == -1 || found_next_req_index == current_master_session.next_request_id) { // we handled all requests ... flush_completed(); // TODO move callback to ALP? // if(d7asp_init_args != NULL && d7asp_init_args->d7asp_fifo_flush_completed_cb != NULL) // d7asp_init_args->d7asp_fifo_flush_completed_cb(fifo.token, fifo.progress_bitmap, fifo.success_bitmap, REQUESTS_BITMAP_BYTE_COUNT); return; } current_request_id = found_next_req_index; current_request_retry_count = 0; current_request_packet = packet_queue_alloc_packet(); packet_queue_mark_processing(current_request_packet); current_request_packet->d7anp_addressee = &(current_master_session.config.addressee); // TODO explicitly pass addressee down the stack layers? memcpy(current_request_packet->payload, current_master_session.request_buffer + current_master_session.requests_indices[current_request_id], current_master_session.requests_lengths[current_request_id]); current_request_packet->payload_length = current_master_session.requests_lengths[current_request_id]; // TODO calculate Tl and Tc // Tc(NB, LEN, CH) = (SFC * NB + 1) * TTX(CH, LEN) + TG with NB the number of concurrent devices and SF the collision Avoidance Spreading Factor // Tl should correspond to the maximum time needed to send the remaining requests in the FIFO including the RETRY parameter // For now, set Tc and Tl according the transmission_timeout_period set in the access profile dae_access_profile_t active_addressee_access_profile; fs_read_access_class(current_request_packet->d7anp_addressee->ctrl.access_class, &active_addressee_access_profile); current_request_packet->d7atp_tc = active_addressee_access_profile.transmission_timeout_period; current_request_packet->d7anp_listen_timeout = active_addressee_access_profile.transmission_timeout_period; } else { // retrying request ... DPRINT("Current request retry count: %i", current_request_retry_count); if(current_request_retry_count == single_request_retry_limit) { // mark request as failed and pop mark_current_request_done(); DPRINT("Request reached single request retry limit (%i), skipping request", single_request_retry_limit); packet_queue_free_packet(current_request_packet); current_request_id = NO_ACTIVE_REQUEST_ID; sched_post_task(&flush_fifos); // continue flushing until all request handled ... return; } // TODO stop on error } // TODO calculate D7ANP timeout (and update during transaction lifetime) (based on Tc, channel, cs, payload size, # msgs, # retries) d7atp_start_dialog(current_master_session.token, current_request_id, (current_request_id == current_master_session.next_request_id - 1), current_request_packet, ¤t_master_session.config.qos); }
void rssi_valid(int16_t cur_rssi) { sched_post_task(&read_rssi); }
static void packet_transmitted(hw_radio_packet_t* hw_radio_packet) { assert(dll_state == DLL_STATE_TX_FOREGROUND); switch_state(DLL_STATE_TX_FOREGROUND_COMPLETED); DPRINT("Transmitted packet with length = %i", hw_radio_packet->length); packet_t* packet = packet_queue_find_packet(hw_radio_packet); d7anp_signal_packet_transmitted(packet); if(process_received_packets_after_tx) { sched_post_task(&process_received_packets); process_received_packets_after_tx = false; } #ifdef RESPONDER_USE_FG_SCAN_OUTSIDE_TRANSACTION // TODO validate if still needed, if yes: needs to be tested /* * Resume the FG scan only after an unicast packet, otherwise, wait the * response period expiration. */ if (resume_fg_scan && packet->dll_header.control_target_address_set) { switch_state(DLL_STATE_FOREGROUND_SCAN); hw_rx_cfg_t rx_cfg = (hw_rx_cfg_t){ .channel_id.channel_header = current_access_profile->subbands[0].channel_header, .channel_id.center_freq_index = current_access_profile->subbands[0].channel_index_start, .syncword_class = PHY_SYNCWORD_CLASS1, }; hw_radio_set_rx(&rx_cfg, &packet_received, NULL); resume_fg_scan = false; } #else if (resume_fg_scan) { switch_state(DLL_STATE_FOREGROUND_SCAN); hw_rx_cfg_t rx_cfg = (hw_rx_cfg_t){ .channel_id.channel_header = current_access_profile->subbands[0].channel_header, .channel_id.center_freq_index = current_access_profile->subbands[0].channel_index_start, .syncword_class = PHY_SYNCWORD_CLASS1, }; hw_radio_set_rx(&rx_cfg, &packet_received, NULL); resume_fg_scan = false; } #endif } static void cca_rssi_valid(int16_t cur_rssi) { // When the radio goes back to Rx state, the rssi_valid callback may be still set. Skip it in this case if (dll_state != DLL_STATE_CCA1 && dll_state != DLL_STATE_CCA2) return; if (cur_rssi <= E_CCA) { if(dll_state == DLL_STATE_CCA1) { DPRINT("CCA1 RSSI: %d", cur_rssi); switch_state(DLL_STATE_CCA2); timer_post_task_delay(&execute_cca, 5); return; } else if(dll_state == DLL_STATE_CCA2) { // OK, send packet DPRINT("CCA2 RSSI: %d", cur_rssi); DPRINT("CCA2 succeeded, transmitting ..."); // log_print_data(current_packet->hw_radio_packet.data, current_packet->hw_radio_packet.length + 1); // TODO tmp switch_state(DLL_STATE_TX_FOREGROUND); d7anp_signal_packet_csma_ca_insertion_completed(true); error_t err = hw_radio_send_packet(¤t_packet->hw_radio_packet, &packet_transmitted); assert(err == SUCCESS); if (!resume_fg_scan) hw_radio_set_idle(); // ensure radio goes back to IDLE after transmission instead of to RX return; } } else { DPRINT("Channel not clear, RSSI: %i", cur_rssi); switch_state(DLL_STATE_CSMA_CA_RETRY); execute_csma_ca(); //switch_state(DLL_STATE_CCA_FAIL); //d7atp_signal_packet_csma_ca_insertion_completed(false); } } static void execute_cca() { assert(dll_state == DLL_STATE_CCA1 || dll_state == DLL_STATE_CCA2); hw_rx_cfg_t rx_cfg =(hw_rx_cfg_t){ .channel_id.channel_header = current_access_profile->subbands[0].channel_header, .channel_id.center_freq_index = current_access_profile->subbands[0].channel_index_start, .syncword_class = PHY_SYNCWORD_CLASS1, }; hw_radio_set_rx(&rx_cfg, NULL, &cca_rssi_valid); } static uint16_t calculate_tx_duration() { int data_rate = 6; // Normal rate: 6.9 bytes/tick // TODO select correct subband switch (current_access_profile->subbands[0].channel_header.ch_class) { case PHY_CLASS_LO_RATE: data_rate = 1; // Lo Rate: 1.2 bytes/tick break; case PHY_CLASS_HI_RATE: data_rate = 20; // High rate: 20.83 byte/tick } uint16_t duration = (current_packet->hw_radio_packet.length / data_rate) + 1; return duration; }
void process_command_chan() { while(fifo_get_size(&uart_rx_fifo) < COMMAND_CHAN_PARAM_SIZE); char param[COMMAND_CHAN_PARAM_SIZE]; fifo_pop(&uart_rx_fifo, param, COMMAND_CHAN_PARAM_SIZE); channel_id_t new_channel = { .channel_header.ch_coding = PHY_CODING_PN9 }; if(strncmp(param, "433", 3) == 0) new_channel.channel_header.ch_freq_band = PHY_BAND_433; else if(strncmp(param, "868", 3) == 0) new_channel.channel_header.ch_freq_band = PHY_BAND_868; else if(strncmp(param, "915", 3) == 0) new_channel.channel_header.ch_freq_band = PHY_BAND_915; else goto error; char channel_class = param[3]; if(channel_class == 'L') new_channel.channel_header.ch_class = PHY_CLASS_LO_RATE; else if(channel_class == 'N') new_channel.channel_header.ch_class = PHY_CLASS_NORMAL_RATE; else if(channel_class == 'H') new_channel.channel_header.ch_class = PHY_CLASS_HI_RATE; else goto error; uint16_t center_freq_index = atoi((const char*)(param + 4)); new_channel.center_freq_index = center_freq_index; // validate if(new_channel.channel_header.ch_freq_band == PHY_BAND_433) { if(new_channel.channel_header.ch_class == PHY_CLASS_NORMAL_RATE && (new_channel.center_freq_index % 8 != 0 || new_channel.center_freq_index > 56)) goto error; else if(new_channel.channel_header.ch_class == PHY_CLASS_LO_RATE && new_channel.center_freq_index > 68) goto error; else if(new_channel.channel_header.ch_class == PHY_CLASS_HI_RATE) goto error; } // TODO validate PHY_BAND_868 // valid band, apply ... rx_cfg.channel_id = new_channel; // change channel and restart prepare_channel_indexes(); current_channel_indexes_index = 0; sched_post_task(&start_rx); return; error: console_print("Error parsing CHAN command. Expected format example: '433L001'\n"); fifo_clear(&uart_rx_fifo); } void process_command_loop() { use_manual_channel_switching = false; sched_post_task(&start_rx); }