Skip to content

Commit

Permalink
Move pin configuration to YAML
Browse files Browse the repository at this point in the history
This allows you to use different boards without having to modify the cpp
code.

The structure with all the setters in the climate class feels
suboptimal. I've browsed through the esphome code and this seems to be
the way to do it.

Based on the work in #6, thanks @unregist!
  • Loading branch information
hberntsen committed Dec 6, 2024
2 parents 43fc938 + 641eea5 commit c0253ac
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 27 deletions.
31 changes: 16 additions & 15 deletions esphome/components/MhiAcCtrl/MHI-AC-Ctrl-core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ static StaticSemaphore_t miso_semaphore_buffer;

static int ready = 0;
static bool active_mode = false;

static gpio_num_t gpio_cs_out;

static SemaphoreHandle_t snapshot_semaphore_handle;
static StaticSemaphore_t snapshot_semaphore_buffer;
Expand All @@ -57,12 +57,12 @@ static bool IRAM_ATTR timer_group_isr_callback(void *args)
esp_err_t err;
BaseType_t xHigherPriorityTaskWoken;
// Trigger Chip Select
gpio_set_level(GPIO_CS_OUT, 1);
gpio_set_level(gpio_cs_out, 1);
if(ready) {
spi_slave_transaction_t *t = (spi_slave_transaction_t *) args;
err = spi_slave_queue_trans(RCV_HOST, t, 0);
}
gpio_set_level(GPIO_CS_OUT, 0);
gpio_set_level(gpio_cs_out, 0);
return false;
}

Expand Down Expand Up @@ -572,25 +572,26 @@ static void mhi_poll_task(void *arg)
}
}

void mhi_ac_ctrl_core_init() {
void mhi_ac_ctrl_core_init(const Config& config) {
esp_err_t err;

miso_semaphore_handle = xSemaphoreCreateMutexStatic( &miso_semaphore_buffer );
snapshot_semaphore_handle = xSemaphoreCreateBinaryStatic( &snapshot_semaphore_buffer );
gpio_cs_out = config.cs_out;

// configuration for the SPI bus
spi_bus_config_t buscfg = {
.mosi_io_num = GPIO_MOSI,
.miso_io_num = GPIO_MISO,
.sclk_io_num = GPIO_SCLK,
.mosi_io_num = config.mosi,
.miso_io_num = config.miso,
.sclk_io_num = config.sclk,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.flags = SPICOMMON_BUSFLAG_GPIO_PINS | SPICOMMON_BUSFLAG_SLAVE,
};

// configuration for the SPI slave interface
spi_slave_interface_config_t slvcfg = {
.spics_io_num = GPIO_CS_IN,
.spics_io_num = config.cs_in,
.flags = SPI_SLAVE_BIT_LSBFIRST,
.queue_size = 1,
.mode = 3, //CPOL=1, CPHA=1
Expand All @@ -601,14 +602,14 @@ void mhi_ac_ctrl_core_init() {
ESP_ERROR_CHECK(err);

// Select and initialize basic parameters of the timer
timer_config_t config = {
timer_config_t timer_config = {
.alarm_en = TIMER_ALARM_DIS,
.counter_en = TIMER_PAUSE,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = TIMER_AUTORELOAD_DIS,
.divider = TIMER_DIVIDER,
}; // default clock source is APB
timer_init(TIMER_GROUP_0, TIMER_0, &config);
timer_init(TIMER_GROUP_0, TIMER_0, &timer_config);

// Configure the alarm value (in milliseconds) and the interrupt on alarm. there is a delay between each frame of 40ms.
// so we set the alarm to 20ms. once the alarm triggers, spi_slave_queue_trans is called which will get the data from the next spi packet. This is also the point to toggle the CS line to mark the end/start of a SPI transaction.
Expand All @@ -617,7 +618,7 @@ void mhi_ac_ctrl_core_init() {

gpio_config_t io_conf = {};
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL<<GPIO_SCLK);
io_conf.pin_bit_mask = (1ULL<<config.sclk);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // required to prevent abort() caused by floating pin when daughtboard not connected
io_conf.intr_type = GPIO_INTR_LOW_LEVEL; // when this is set to NEGEDGE, DMA sometimes doesn't read the last 4 bytes
Expand All @@ -626,16 +627,16 @@ io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf);

gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(GPIO_SCLK, gpio_isr_handler, NULL);
gpio_intr_enable(GPIO_SCLK);
gpio_isr_handler_add(config.sclk, gpio_isr_handler, NULL);
gpio_intr_enable(config.sclk);

io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL<<GPIO_CS_OUT);
io_conf.pin_bit_mask = (1ULL<<gpio_cs_out);
io_conf.intr_type =GPIO_INTR_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);

gpio_set_level(GPIO_CS_OUT, 1);
gpio_set_level(gpio_cs_out, 1);

xTaskCreatePinnedToCore(
mhi_poll_task, // Function to implement the task
Expand Down
18 changes: 10 additions & 8 deletions esphome/components/MhiAcCtrl/MHI-AC-Ctrl-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
#include <stdint.h>
#include <atomic>
#include "esp_timer.h"
#include "driver/gpio.h"

// # Config
// ## pin defintions
#define GPIO_MOSI 7
#define GPIO_MISO 2
#define GPIO_SCLK GPIO_NUM_6
#define GPIO_CS_OUT GPIO_NUM_9
#define GPIO_CS_IN 10

#define RCV_HOST SPI2_HOST

#define MHI_FRAME_LEN 20
Expand Down Expand Up @@ -41,6 +35,14 @@

namespace mhi_ac {

struct Config {
gpio_num_t mosi;
gpio_num_t miso;
gpio_num_t sclk;
gpio_num_t cs_in;
gpio_num_t cs_out;
};

enum class ACPower {
power_off = 0,
power_on = 1
Expand Down Expand Up @@ -72,7 +74,7 @@ enum class ACVanes { // Vanes enum
//public: virtual void cbiStatusFunction(ACStatus status, int value) = 0;
//};

void mhi_ac_ctrl_core_init();
void mhi_ac_ctrl_core_init(const Config& config);
bool mhi_ac_ctrl_core_snapshot(uint32_t wait_time_ms);

void mhi_ac_ctrl_core_active_mode_set(bool state);
Expand Down
43 changes: 40 additions & 3 deletions esphome/components/MhiAcCtrl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,58 @@
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.components import climate
from esphome import pins

AUTO_LOAD = ["sensor", "climate"]
CODEOWNERS = ["hberntsen"]

CONF_MHI_AC_CTRL_ID = "mhi_ac_ctrl_id"

mhi_core_ns = cg.global_ns.namespace("mhi_ac")
ConfigStruct = mhi_core_ns.struct("Config")
MhiAcCtrl = cg.global_ns.class_("MhiAcCtrl", cg.Component, climate.Climate)

CONF_MOSI_PIN = "mosi_pin"
CONF_MISO_PIN = "miso_pin"
CONF_SCLK_PIN = "sclk_pin"
CONF_CS_IN_PIN = "cs_in_pin"
CONF_CS_OUT_PIN = "cs_out_pin"

TYPES = [
CONF_MOSI_PIN,
CONF_MISO_PIN,
CONF_SCLK_PIN,
CONF_CS_IN_PIN,
CONF_CS_OUT_PIN,
]

CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(MhiAcCtrl),
cv.Optional(CONF_MOSI_PIN, default="GPIO7"): pins.gpio_input_pin_schema,
cv.Optional(CONF_MISO_PIN, default="GPIO2"): pins.gpio_output_pin_schema,
cv.Optional(CONF_SCLK_PIN, default="GPIO6"): pins.gpio_input_pin_schema,
cv.Optional(CONF_CS_IN_PIN, default="GPIO10"): pins.gpio_input_pin_schema,
cv.Optional(CONF_CS_OUT_PIN, default="GPIO9"): pins.gpio_output_pin_schema,
}
).extend(cv.COMPONENT_SCHEMA)


def to_code(config):
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)

mosi_pin = await cg.gpio_pin_expression(config[CONF_MOSI_PIN])
cg.add(var.set_mosi_pin(mosi_pin))

miso_pin = await cg.gpio_pin_expression(config[CONF_MISO_PIN])
cg.add(var.set_miso_pin(miso_pin))

sclk_pin = await cg.gpio_pin_expression(config[CONF_SCLK_PIN])
cg.add(var.set_sclk_pin(sclk_pin))

cs_in_pin = await cg.gpio_pin_expression(config[CONF_CS_IN_PIN])
cg.add(var.set_cs_in_pin(cs_in_pin))

cs_out_pin = await cg.gpio_pin_expression(config[CONF_CS_OUT_PIN])
cg.add(var.set_cs_out_pin(cs_out_pin))

return await cg.register_component(var, config)
20 changes: 19 additions & 1 deletion esphome/components/MhiAcCtrl/mhi_ac_ctrl.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "MHI-AC-Ctrl-core.h"
#include "esphome/core/gpio.h"

#include "esphome/components/climate/climate.h"
#include "esphome/components/sensor/sensor.h"
Expand Down Expand Up @@ -159,6 +160,22 @@ class MhiVanesUD : public select::Select {
class MhiAcCtrl : public climate::Climate,
public Component {
public:
void set_mosi_pin(InternalGPIOPin *pin) {
this->ac_config_.mosi = static_cast<gpio_num_t>(pin->get_pin());
}
void set_miso_pin(InternalGPIOPin *pin) {
this->ac_config_.miso = static_cast<gpio_num_t>(pin->get_pin());
}
void set_sclk_pin(InternalGPIOPin *pin) {
this->ac_config_.sclk = static_cast<gpio_num_t>(pin->get_pin());
}
void set_cs_in_pin(InternalGPIOPin *pin) {
this->ac_config_.cs_in = static_cast<gpio_num_t>(pin->get_pin());
}
void set_cs_out_pin(InternalGPIOPin *pin) {
this->ac_config_.cs_out = static_cast<gpio_num_t>(pin->get_pin());
}

void setup() override
{
auto restore = this->restore_state_();
Expand All @@ -178,7 +195,7 @@ class MhiAcCtrl : public climate::Climate,
//current_power.set_unit_of_measurement("A");
//current_power.set_accuracy_decimals(2);

mhi_ac_ctrl_core_init();
mhi_ac_ctrl_core_init(this->ac_config_);
}

void loop() override
Expand Down Expand Up @@ -415,6 +432,7 @@ class MhiAcCtrl : public climate::Climate,
MhiFrameErrors *frame_errors_sensor_;
MhiTotalEnergy *total_energy_sensor_;
MhiPower *power_sensor_;
Config ac_config_;

public:
#ifdef USE_SELECT
Expand Down
7 changes: 7 additions & 0 deletions esphome/example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ logger:

MhiAcCtrl:
id: ${mhi_device_id}
# Optional PIN config
# The default values are:
mosi_pin: GPIO7
miso_pin: GPIO2
sclk_pin: GPIO6
cs_in_pin: GPIO10
cs_out_pin: GPIO9

climate:
- platform: MhiAcCtrl
Expand Down

0 comments on commit c0253ac

Please sign in to comment.