power-profiler-soft/main/power_profiler.c
2023-06-16 13:23:33 +02:00

215 lines
5.1 KiB
C

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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
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,
},
[X10] = {
.channel = ADC1_CHANNEL_0,
.gain = 10,
},
[X100] = {
.channel = ADC1_CHANNEL_1,
.gain = 100,
},
};
static spinlock_t adc_res_mutex;
volatile uint32_t meas_res[INPUTS_NUM];
volatile uint32_t meas_nb[INPUTS_NUM];
int get_input_index_from_channel(adc_channel_t channel){
switch(channel){
case ADC1_CHANNEL_3:
return 0;
case ADC1_CHANNEL_0:
return 1;
case ADC1_CHANNEL_1:
return 2;
default:
return -1;
}
}
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);
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;
}
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)", i, mv, meas_res_buff[i], meas_nb_buff[i]);
meas_volts[i] = mv;
meas_amp[i] = mv * 1000000L / ranges[main_conf.range].resistance / inputs[i].gain;
}
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);
}
}