#include #include #include #include "configuration.h" #include "driver/adc_types_legacy.h" #include "driver/gpio.h" #include "esp_adc/adc_cali.h" #include "esp_adc/adc_continuous.h" #include "esp_adc/adc_oneshot.h" #include "esp_intr_alloc.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/portmacro.h" #include "hal/adc_types.h" #include "hal/gpio_types.h" #include "nvs_flash.h" #include "power_profiler.h" #include "resistor_ranges.h" #include "measure.h" #include "ble.h" #define TAG "main" // mV value that we consider the threshold for activating the next higher resistance #define UNDERRANGE_MV 10 // source for the comparison used for the range switching #define UNDERRANGE_SRC X10 // number of consecutive times the input has to be below the threshold before switching #define UNDERRANGE_NUM 3 // default source for the fused current measure #define DEFAULT_SRC X10 // valeur pour laquelle on passe a la source de haute precision (mV) #define DEFAULT_SRC_SW_MIN 10 // source de plus haute precision #define HIGH_PRECISION_SRC X100 // valeur pour laquelle on passe a la source de plus basse precision (mV) #define DEFAULT_SRC_SW_MAX 700 // source de plus basse precision #define LOW_PRECISION_SRC X1 resistor_range ranges[] = { [R1] = { .pin = GPIO_NUM_6, .resistance = 1000, }, [R10] = { .pin = GPIO_NUM_7, .resistance = 10000, }, [R100] = { .pin = GPIO_NUM_NC, .resistance = 100000, }, }; measurement_input inputs[] = { [X1] = { .channel = ADC1_CHANNEL_3, .gain = 1, .real_gain = 1114, }, [X10] = { .channel = ADC1_CHANNEL_0, .gain = 10, .real_gain = 10850, }, [X100] = { .channel = ADC1_CHANNEL_1, .gain = 100, .real_gain = 100000, }, }; static spinlock_t adc_res_mutex; volatile uint32_t meas_res[INPUTS_NUM]; volatile uint32_t meas_nb[INPUTS_NUM]; static bool IRAM_ATTR on_conv_done(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data){ adc_digi_output_data_t* res = (adc_digi_output_data_t*)edata->conv_frame_buffer; int index = get_input_index_from_channel(res->type2.channel); if(index == -1){ return false; } meas_res[index] += res->type2.data; meas_nb[index]++; return false; } configuration main_conf = { .refresh_delay = 1000, .range = R100, .zero_cali_nsamp = 10000, .auto_range = 1, .ranges = ranges, .ranges_num = R_NUM, }; uint8_t notify_range_f = 0; static void IRAM_ATTR overrange_handler(void* arg) { if(!main_conf.auto_range) return; activate_range(ranges, 0, R_NUM - 1); main_conf.range = 0; notify_range_f = 1; for(int i = 0; i < INPUTS_NUM; i++){ // dump the now invalid values (TODO: divide by the previous scale?) meas_res[i] = 0; meas_nb[i] = 0; } } void app_main(void){ gpio_config_t overrange_input = { .intr_type = GPIO_INTR_POSEDGE, .mode = GPIO_MODE_INPUT, .pin_bit_mask = 1ULL << OVERRANGE_PIN, .pull_down_en = 0, .pull_up_en = 0, }; gpio_config(&overrange_input); gpio_install_isr_service(0); gpio_isr_handler_add(OVERRANGE_PIN, overrange_handler, NULL); set_resistor_gpio(ranges, R100); activate_range(ranges, R100, R100); adc_continuous_handle_t adc_handle = init_measurement_inputs(inputs, INPUTS_NUM); spinlock_initialize(&adc_res_mutex); adc_cali_handle_t adc_conv_h[INPUTS_NUM]; init_conv_driver(inputs, INPUTS_NUM, adc_conv_h); adc_continuous_evt_cbs_t adc_event = { .on_conv_done = &on_conv_done, .on_pool_ovf = NULL, }; ESP_ERROR_CHECK(adc_continuous_register_event_callbacks(adc_handle, &adc_event, NULL)); ESP_ERROR_CHECK(adc_continuous_start(adc_handle)); ESP_ERROR_CHECK(nvs_flash_init()); uint32_t offsets[INPUTS_NUM] = {0}; main_conf.offsets = offsets; init_configuration_from_nvs(&main_conf); uint32_t meas_volts[INPUTS_NUM]; uint32_t meas_amp[INPUTS_NUM]; measurements meas = { .raw_volt = meas_volts, .amperes = meas_amp, }; measurements_ctxt meas_ctxt = { .handle = adc_handle, .inputs = inputs, .nb_inputs = INPUTS_NUM, }; initBLE(&main_conf, &meas, &meas_ctxt); int underrange_counter = 0; while(1){ uint32_t meas_res_buff[INPUTS_NUM]; uint32_t meas_nb_buff[INPUTS_NUM]; portENTER_CRITICAL(&adc_res_mutex); for(int i = 0; i < INPUTS_NUM; i++){ meas_res_buff[i] = meas_res[i]; meas_res[i] = 0; meas_nb_buff[i] = meas_nb[i]; meas_nb[i] = 0; } portEXIT_CRITICAL(&adc_res_mutex); for(int i = 0; i < INPUTS_NUM; i++){ int mv; uint32_t val = meas_res_buff[i]/meas_nb_buff[i]; val = (val >= offsets[i])?(val-offsets[i]):0; ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc_conv_h[i], val, &mv)); ESP_LOGI(TAG, "IN %d : %d mV (%lu / %lu = %lu)", i, mv, meas_res_buff[i], meas_nb_buff[i], meas_res_buff[i]/meas_nb_buff[i]); meas_volts[i] = mv; uint32_t buff = (uint64_t)mv * 1000000000L / ranges[main_conf.range].resistance / inputs[i].real_gain; meas_amp[i] = buff; } if(meas_volts[DEFAULT_SRC] < DEFAULT_SRC_SW_MIN) meas.amperes_f = meas_amp[HIGH_PRECISION_SRC]; else if(meas_volts[DEFAULT_SRC] > DEFAULT_SRC_SW_MAX) meas.amperes_f = meas_amp[LOW_PRECISION_SRC]; else meas.amperes_f = meas_amp[DEFAULT_SRC]; if(meas_volts[UNDERRANGE_SRC] < UNDERRANGE_MV){ underrange_counter++; if(underrange_counter > UNDERRANGE_NUM && main_conf.auto_range){ if(main_conf.range < R_NUM - 1){ main_conf.range++; activate_range(ranges, main_conf.range, R_NUM - 1); notify_range_f = 1; } underrange_counter = 0; } } else{ underrange_counter = 0; } notify_update(&meas); if(notify_range_f){ notify_range_f = 0; notify_range(&main_conf); } vTaskDelay(main_conf.refresh_delay / portTICK_PERIOD_MS); } }