void bootstrap()
{
#ifdef HAS_LCD
    lcd_write_string("NOISE");
#endif

#if NUM_USERBUTTONS > 1
    ubutton_register_callback(0, &userbutton_callback);
    ubutton_register_callback(1, &userbutton_callback);
#endif

    prepare_channel_indexes();

    hw_radio_init(NULL, NULL);

    fifo_init(&uart_rx_fifo, uart_rx_buffer, sizeof(uart_rx_buffer));

    console_set_rx_interrupt_callback(&uart_rx_cb);
    console_rx_interrupt_enable(true);

    sched_register_task(&read_rssi);
    sched_register_task(&start_rx);
    sched_register_task(&process_uart_rx_fifo);
    timer_post_task_delay(&start_rx, TIMER_TICKS_PER_SEC * 3);

    sched_register_task((&execute_sensor_measurement));
    timer_post_task_delay(&execute_sensor_measurement, TEMPERATURE_PERIOD);

    measureTemperature();
}
void led_on_callback()
{
	led_on(0);
	timer_post_task_delay(&led_on_callback, TIMER_TICKS_PER_SEC);
	timer_post_task_delay(&led_off_callback, TIMER_TICKS_PER_SEC*0.050);
	log_print_string("Toggled on %d", 0);

	hw_watchdog_feed();

}
void bootstrap()
{
	led_on(0);
	led_on(1);

	log_print_string("Device booted at time: %d\n", timer_get_counter_value());
	console_print("Device Booted\r\n");

    sched_register_task(&led_on_callback);
    sched_register_task(&led_off_callback);
    sched_register_task(&timer1_callback);

    timer_post_task_delay(&led_on_callback, TIMER_TICKS_PER_SEC);
    //timer_post_task_delay(&timer1_callback, 0x0000FFFF + (uint32_t)100);

#if PLATFORM_NUM_BUTTONS > 0
    int i= 0;
    for (i=0;i<PLATFORM_NUM_BUTTONS;i++)
	{
    	ubutton_register_callback(i, &userbutton_callback);
	}
#endif

    led_off(0);
    led_off(1);
}
static void start_rx(hw_rx_cfg_t const* rx_cfg)
{
	DPRINT("start_rx");

	if (current_state == HW_RADIO_STATE_OFF)
		ezradio_hal_DeassertShutdown();

    current_state = HW_RADIO_STATE_RX;

    configure_channel(&(rx_cfg->channel_id));
    configure_syncword_class(rx_cfg->syncword_class, rx_cfg->channel_id.channel_header.ch_coding);

    rx_fifo_data_lenght = 0;
    if (rx_cfg->channel_id.channel_header.ch_coding == PHY_CODING_FEC_PN9)
    {
    	ezradioStartRx(ez_channel_id, false);
    } else {

    	ezradioStartRx(ez_channel_id, true);
    }

    DEBUG_RX_START();


    if(rssi_valid_callback != 0)
    {
    	timer_post_task_delay(&report_rssi, TIMER_TICKS_PER_SEC / 5000);
    }
}
void timer1_callback()
{
	led_toggle(1);
	timer_post_task_delay(&timer1_callback, 0x0000FFFF + (uint32_t)100);
	log_print_string("Toggled led %d", 1);
	console_print("Toggle led 1\r\n");
}
void bootstrap()
{
    log_print_string("Device booted at time: %d\n", timer_get_counter_value());

    hw_radio_init(p_alloc, p_free);
    hw_radio_set_rx(&rx_cfg, rx_callback, rssi_valid);


    NG(tx_buffer).radio_packet.length = sizeof(packet_struct_t) - sizeof(hw_radio_packet_t);
    NG(tx_buffer).radio_packet.tx_meta.tx_cfg.channel_id = rx_cfg.channel_id;
    NG(tx_buffer).radio_packet.tx_meta.tx_cfg.syncword_class = rx_cfg.syncword_class;
    NG(tx_buffer).radio_packet.tx_meta.tx_cfg.eirp = 0;

	NG(tx_buffer).src_node = hw_get_unique_id();
    NG(tx_buffer).dst_node = 0xFFFF;
    NG(tx_buffer).counter = 0;


    sched_register_task(&send_packet);
    timer_post_task_delay(&send_packet, TIMER_TICKS_PER_SEC + (get_rnd() %TIMER_TICKS_PER_SEC));

//    NG(status).is_rx = false;
//    NG(status).is_sleep = true;
//    NG(status).tx_busy = false;
//    NG(status).rx_busy = false;
//    sched_register_task(&poll_status);
//    sched_post_task(poll_status);

}
void send_packet()
{
	hw_radio_send_packet((hw_radio_packet_t*)(&NG(tx_buffer)), tx_callback);
	log_print_string("Sending packet with counter %u", NG(tx_buffer).counter);
	NG(tx_buffer).counter++;
	timer_post_task_delay(send_packet, TIMER_TICKS_PER_SEC + (get_rnd() %TIMER_TICKS_PER_SEC));
}
void execute_sensor_measurement() {
#if HW_NUM_LEDS >= 1
    led_toggle(0);
#endif
    // use the counter value for now instead of 'real' sensor
    uint32_t val = timer_get_counter_value();
    // file 0x40 is configured to use D7AActP trigger an ALP action which 
    // broadcasts this file data on Access Class 0
    fs_write_file(0x40, 0, (uint8_t*)&val, 4);
    timer_post_task_delay(&execute_sensor_measurement, REPORTING_INTERVAL_TICKS);
}
void led_on_callback()
{
  led_toggle(0);
  timer_post_task_delay(&led_on_callback, TIMER_TICKS_PER_SEC * 70);
  //timer_post_task_delay(&led_off_callback, TIMER_TICKS_PER_SEC*0.050);
	log_print_string("Toggled on %d", 0);
	console_print("Toggle led 0\r\n");

	hw_watchdog_feed();

}
void read_rssi()
{
    timestamped_rssi_t rssi_measurement;
    rssi_measurement.tick = timer_get_counter_value();
    rssi_measurement.rssi = hw_radio_get_rssi();
    rssi_measurements[rssi_measurements_index] = rssi_measurement;
    rssi_measurements_index++;
    if(rssi_measurements_index < 1000)
        timer_post_task_delay(&read_rssi, 1); // TODO delay
    else
        log_print_string("done");
}
Esempio n. 11
0
void execute_sensor_measurement()
{
#ifdef PLATFORM_EFM32GG_STK3700
  float internal_temp = hw_get_internal_temperature();
  lcd_write_temperature(internal_temp*10, 1);

  uint32_t vdd = hw_get_battery();


  fs_write_file(SENSOR_FILE_ID, 0, (uint8_t*)&internal_temp, sizeof(internal_temp)); // File 0x40 is configured to use D7AActP trigger an ALP action which broadcasts this file data on Access Class 0
#endif

#if (defined PLATFORM_EFM32HG_STK3400  || defined PLATFORM_EZR32LG_WSTK6200A)
  char str[30];

  float internal_temp = hw_get_internal_temperature();
  sprintf(str, "Int T: %2d.%d C", (int)internal_temp, (int)(internal_temp*10)%10);
  lcd_write_line(2,str);
  log_print_string(str);

  uint32_t rhData;
  uint32_t tData;
  getHumidityAndTemperature(&rhData, &tData);

  sprintf(str, "Ext T: %d.%d C", (tData/1000), (tData%1000)/100);
  lcd_write_line(3,str);
  log_print_string(str);

  sprintf(str, "Ext H: %d.%d", (rhData/1000), (rhData%1000)/100);
  lcd_write_line(4,str);
  log_print_string(str);

  uint32_t vdd = hw_get_battery();

  sprintf(str, "Batt %d mV", vdd);
  lcd_write_line(5,str);
  log_print_string(str);

  //TODO: put sensor values in array

  uint8_t sensor_values[8];
  uint16_t *pointer =  (uint16_t*) sensor_values;
  *pointer++ = (uint16_t) (internal_temp * 10);
  *pointer++ = (uint16_t) (tData /100);
  *pointer++ = (uint16_t) (rhData /100);
  *pointer++ = (uint16_t) (vdd /10);

  fs_write_file(SENSOR_FILE_ID, 0, (uint8_t*)&sensor_values,8);
#endif

  timer_post_task_delay(&execute_sensor_measurement, SENSOR_UPDATE);
}
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 bootstrap()
{
	led_on(0);
	led_on(1);

	log_print_string("Device booted at time: %d\n", timer_get_counter_value());

    sched_register_task(&led_on_callback);
    sched_register_task(&led_off_callback);
    sched_register_task(&timer1_callback);

    timer_post_task_delay(&led_on_callback, TIMER_TICKS_PER_SEC);
    timer_post_task_delay(&timer1_callback, 0x0000FFFF + (uint32_t)100);

#if NUM_USERBUTTONS > 0
    ubutton_register_callback(0, &userbutton_callback);
    ubutton_register_callback(1, &userbutton_callback);
#endif

    led_off(0);
    led_off(1);


}
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();
}
Esempio n. 15
0
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);
}
void bootstrap()
{
    DPRINT("Device booted at time: %d\n", timer_get_counter_value()); // TODO not printed for some reason, debug later

#ifdef HAS_LCD
    lcd_write_string("cont tx");
#endif

    switch(current_channel_class)
    {
        // TODO only 433 for now
        case PHY_CLASS_NORMAL_RATE:
        	channel_count = NORMAL_RATE_CHANNEL_COUNT;
            realloc(channel_indexes, channel_count);
            for(int i = 0; i < channel_count; i++)
                channel_indexes[i] = i * 8;

            break;
        case PHY_CLASS_LO_RATE:
        	channel_count = LO_RATE_CHANNEL_COUNT;
            realloc(channel_indexes, channel_count);
            for(int i = 0; i < channel_count; i++)
                channel_indexes[i] = i;

            break;
    }

#if NUM_USERBUTTONS > 1
    ubutton_register_callback(0, &userbutton_callback);
    ubutton_register_callback(1, &userbutton_callback);
#endif

    hw_radio_init(NULL, NULL);

    sched_register_task(&start);
    timer_post_task_delay(&start, TIMER_TICKS_PER_SEC * 5);
}
void bootstrap()
{
    DPRINT("Device booted at time: %d\n", timer_get_counter_value()); // TODO not printed for some reason, debug later

#ifdef HAS_LCD
    lcd_write_string("cont TX \n");
#endif

    switch(current_channel_class)
    {
        case PHY_CLASS_NORMAL_RATE:
          channel_count = NORMAL_RATE_CHANNEL_COUNT;
            realloc(channel_indexes, channel_count);
            for(int i = 0; i < channel_count; i++)
                channel_indexes[i] = i * 8;

            break;
        case PHY_CLASS_LO_RATE:
          channel_count = LO_RATE_CHANNEL_COUNT;
            realloc(channel_indexes, channel_count);
            for(int i = 0; i < channel_count; i++)
                channel_indexes[i] = i;

            break;
    }

#if PLATFORM_NUM_BUTTONS > 1
    ubutton_register_callback(0, &userbutton_callback);
    ubutton_register_callback(1, &userbutton_callback);
#endif

    hw_radio_init(&alloc_packet_callback, &release_packet_callback);

    sched_register_task(&start);
    timer_post_task_delay(&start, 500);
}
static void tx_callback(hw_radio_packet_t* tx_packet)
{
	log_print_string("Sent packet with counter %u", NG(tx_buffer).counter);
	timer_post_task_delay(send_packet, TIMER_TICKS_PER_SEC + (get_rnd() %TIMER_TICKS_PER_SEC));
}
void rssi_valid(int16_t cur_rssi)
{
    timer_post_task_delay(&read_rssi, 1); // TODO delay
}
void execute_sensor_measurement()
{
	timer_post_task_delay(&execute_sensor_measurement, TEMPERATURE_PERIOD);
	measureTemperature();
}
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(&current_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;
}
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
        };