Files
chargeflow/components/evse/evse_pilot.c
2025-07-22 00:09:58 +01:00

183 lines
5.5 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "driver/ledc.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "evse_pilot.h"
#include "adc121s021_dma.h"
#include "board_config.h"
#define PILOT_PWM_TIMER LEDC_TIMER_0
#define PILOT_PWM_CHANNEL LEDC_CHANNEL_0
#define PILOT_PWM_SPEED_MODE LEDC_LOW_SPEED_MODE
#define PILOT_PWM_DUTY_RES LEDC_TIMER_10_BIT
#define PILOT_PWM_MAX_DUTY 1023
#define NUM_PILOT_SAMPLES 100
#define MAX_SAMPLE_ATTEMPTS 1000
#define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior
// ADC121S021 setup
#define ADC121_VREF_MV 3300 // AJUSTE conforme Vref do seu hardware!
#define ADC121_MAX 4095 // 12 bits
static const char *TAG = "evse_pilot";
// Memoização de estado para evitar comandos/logs desnecessários
static int last_pilot_level = -1;
static uint32_t last_pwm_duty = 0;
// Função para converter leitura bruta do ADC para mV
static int adc_raw_to_mv(uint16_t raw) {
return (raw * ADC121_VREF_MV) / ADC121_MAX;
}
void pilot_init(void)
{
// PWM (LEDC) configuração
ledc_timer_config_t ledc_timer = {
.speed_mode = PILOT_PWM_SPEED_MODE,
.timer_num = PILOT_PWM_TIMER,
.duty_resolution = PILOT_PWM_DUTY_RES,
.freq_hz = 1000,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = {
.speed_mode = PILOT_PWM_SPEED_MODE,
.channel = PILOT_PWM_CHANNEL,
.timer_sel = PILOT_PWM_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = board_config.pilot_pwm_gpio,
.duty = 0,
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, 0));
ESP_ERROR_CHECK(ledc_fade_func_install(0));
// Inicializa ADC121S021 externo
adc121s021_dma_init();
}
void pilot_set_level(bool level)
{
if (last_pilot_level == level) return; // só muda se necessário
last_pilot_level = level;
ESP_LOGI(TAG, "Set level %d", level);
ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0);
last_pwm_duty = 0; // PWM parado
}
void pilot_set_amps(uint16_t amps)
{
if (amps < 6 || amps > 80) {
ESP_LOGE(TAG, "Invalid ampere value: %d A (valid: 680 A)", amps);
return;
}
uint32_t duty_percent;
if (amps <= 51) {
duty_percent = (amps * 10) / 6; // Duty (%) = Amps / 0.6
} else {
duty_percent = (amps * 10) / 25 + 64; // Duty (%) = (Amps / 2.5) + 64
}
if (duty_percent > 100) duty_percent = 100;
uint32_t duty = (PILOT_PWM_MAX_DUTY * duty_percent) / 100;
if (last_pilot_level == 0 && last_pwm_duty == duty) return;
last_pilot_level = 0;
last_pwm_duty = duty;
ESP_LOGI(TAG, "Pilot set: %d A → %d/%d (≈ %d%% duty)",
amps, (int)duty, PILOT_PWM_MAX_DUTY, (int)duty_percent);
ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty);
ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL);
}
static int compare_int(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
static int select_low_median_qsort(int *src, int n, int percent) {
int k = (n * percent) / 100;
if (k == 0) k = 1;
int *copy = alloca(n * sizeof(int));
memcpy(copy, src, n * sizeof(int));
qsort(copy, n, sizeof(int), compare_int);
return copy[k / 2];
}
static int select_high_median_qsort(int *src, int n, int percent) {
int k = (n * percent) / 100;
if (k == 0) k = 1;
int *copy = alloca(n * sizeof(int));
memcpy(copy, src, n * sizeof(int));
qsort(copy, n, sizeof(int), compare_int);
return copy[n - k + (k / 2)];
}
void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12)
{
ESP_LOGD(TAG, "pilot_measure");
int samples[NUM_PILOT_SAMPLES];
int collected = 0, attempts = 0;
uint16_t adc_sample = 0;
// Lê samples usando ADC121S021 externo
while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) {
adc_sample = 0;
if (adc121s021_dma_get_sample(&adc_sample)) {
samples[collected++] = adc_sample;
esp_rom_delay_us(10);
} else {
esp_rom_delay_us(100);
attempts++;
}
}
if (collected < NUM_PILOT_SAMPLES) {
ESP_LOGW(TAG, "Timeout on sample read (%d/%d)", collected, NUM_PILOT_SAMPLES);
*up_voltage = PILOT_VOLTAGE_1;
*down_voltage_n12 = false;
return;
}
int high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
int low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT);
int high_mv = adc_raw_to_mv(high_raw);
int low_mv = adc_raw_to_mv(low_raw);
// Aplica thresholds definidos em board_config (em mV)
if (high_mv >= board_config.pilot_down_threshold_12)
*up_voltage = PILOT_VOLTAGE_12;
else if (high_mv >= board_config.pilot_down_threshold_9)
*up_voltage = PILOT_VOLTAGE_9;
else if (high_mv >= board_config.pilot_down_threshold_6)
*up_voltage = PILOT_VOLTAGE_6;
else if (high_mv >= board_config.pilot_down_threshold_3)
*up_voltage = PILOT_VOLTAGE_3;
else
*up_voltage = PILOT_VOLTAGE_1;
*down_voltage_n12 = (low_mv <= board_config.pilot_down_threshold_n12);
ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d", *up_voltage, *down_voltage_n12);
}