use define for to_code + more conf validation

This commit is contained in:
oarcher 2024-07-23 12:27:28 +02:00
parent b8c965b3fc
commit 98ba007de3
3 changed files with 80 additions and 80 deletions

View file

@ -31,6 +31,14 @@ CONF_POWER_PIN = "power_pin"
CONF_INIT_AT = "init_at" CONF_INIT_AT = "init_at"
CONF_ON_NOT_RESPONDING = "on_not_responding" CONF_ON_NOT_RESPONDING = "on_not_responding"
MODEM_MODELS = ["BG96", "SIM800", "SIM7000", "SIM7600", "GENERIC"]
MODEM_MODELS_POWER = {
"BG96": {"ton": 600, "tonuart": 4900, "toff": 650, "toffuart": 2000},
"SIM800": {"ton": 1300, "tonuart": 3000, "toff": 200, "toffuart": 3000},
"SIM7000": {"ton": 1100, "tonuart": 4500, "toff": 1300, "toffuart": 1800},
"SIM7600": {"ton": 500, "tonuart": 12000, "toff": 2800, "toffuart": 25000},
}
modem_ns = cg.esphome_ns.namespace("modem") modem_ns = cg.esphome_ns.namespace("modem")
ModemComponent = modem_ns.class_("ModemComponent", cg.Component) ModemComponent = modem_ns.class_("ModemComponent", cg.Component)
ModemComponentState = modem_ns.enum("ModemComponentState") ModemComponentState = modem_ns.enum("ModemComponentState")
@ -51,7 +59,7 @@ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(ModemComponent), cv.GenerateID(): cv.declare_id(ModemComponent),
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_schema, cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_RX_PIN): pins.internal_gpio_output_pin_schema, cv.Required(CONF_RX_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_MODEL): cv.string, cv.Required(CONF_MODEL): cv.one_of(*MODEM_MODELS, upper=True),
cv.Required(CONF_APN): cv.string, cv.Required(CONF_APN): cv.string,
cv.Optional(CONF_STATUS_PIN): pins.internal_gpio_input_pin_schema, cv.Optional(CONF_STATUS_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_schema,
@ -87,6 +95,21 @@ CONFIG_SCHEMA = cv.All(
) )
def _final_validate(config):
if config.get(CONF_POWER_PIN, None) and not config.get(CONF_STATUS_PIN, None):
raise cv.Invalid(
f"'{CONF_STATUS_PIN}' must be declared if using '{CONF_POWER_PIN}'"
)
if config.get(CONF_POWER_PIN, None):
if config[CONF_MODEL] not in MODEM_MODELS_POWER:
raise cv.Invalid(
f"Modem model '{config[CONF_MODEL]}' has no power power specs."
)
FINAL_VALIDATE_SCHEMA = _final_validate
@coroutine_with_priority(60.0) @coroutine_with_priority(60.0)
async def to_code(config): async def to_code(config):
add_idf_component( add_idf_component(
@ -135,7 +158,15 @@ async def to_code(config):
for cmd in init_at: for cmd in init_at:
cg.add(var.add_init_at_command(cmd)) cg.add(var.add_init_at_command(cmd))
cg.add(var.set_model(config[CONF_MODEL])) modem_model = config[CONF_MODEL]
cg.add_define("USE_MODEM_MODEL", modem_model)
cg.add_define(f"USE_MODEM_MODEL_{modem_model}")
if power_spec := MODEM_MODELS_POWER.get(modem_model, None):
cg.add_define("USE_MODEM_POWER")
for spec, value in power_spec.items():
cg.add_define(f"USE_MODEM_POWER_{spec.upper()}", value)
cg.add(var.set_apn(config[CONF_APN])) cg.add(var.set_apn(config[CONF_APN]))
tx_pin = await cg.gpio_pin_expression(config[CONF_TX_PIN]) tx_pin = await cg.gpio_pin_expression(config[CONF_TX_PIN])
@ -147,6 +178,7 @@ async def to_code(config):
if status_pin := config.get(CONF_STATUS_PIN, None): if status_pin := config.get(CONF_STATUS_PIN, None):
pin = await cg.gpio_pin_expression(status_pin) pin = await cg.gpio_pin_expression(status_pin)
cg.add(var.set_status_pin(pin)) cg.add(var.set_status_pin(pin))
cg.add_define("USE_MODEM_STATUS")
if power_pin := config.get(CONF_POWER_PIN, None): if power_pin := config.get(CONF_POWER_PIN, None):
pin = await cg.gpio_pin_expression(power_pin) pin = await cg.gpio_pin_expression(power_pin)

View file

@ -50,7 +50,22 @@ ModemComponent::ModemComponent() {
global_modem_component = this; global_modem_component = this;
} }
void ModemComponent::dump_config() { ESP_LOGCONFIG(TAG, "Config Modem:"); } void ModemComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Config Modem:");
ESP_LOGCONFIG(TAG, " Model : %s", USE_MODEM_MODEL);
ESP_LOGCONFIG(TAG, " APN : %s", this->apn_.c_str());
ESP_LOGCONFIG(TAG, " PIN code : %s", (this->pin_code_.empty()) ? "No" : "Yes (not shown)");
ESP_LOGCONFIG(TAG, " Tx Pin : GPIO%u", this->tx_pin_->get_pin());
ESP_LOGCONFIG(TAG, " Rx Pin : GPIO%u", this->rx_pin_->get_pin());
ESP_LOGCONFIG(TAG, " Power pin : %s",
(this->power_pin_) ? ("GPIO" + std::to_string(this->power_pin_->get_pin())).c_str() : "Not defined");
if (this->status_pin_) {
std::string current_status = this->get_power_status() ? "ON" : "OFF";
ESP_LOGCONFIG(TAG, " Status pin: GPIO%u (current state %s)", this->status_pin_->get_pin(), current_status.c_str());
} else {
ESP_LOGCONFIG(TAG, " Status pin: Not defined");
}
}
float ModemComponent::get_setup_priority() const { return setup_priority::WIFI; } float ModemComponent::get_setup_priority() const { return setup_priority::WIFI; }
@ -153,33 +168,21 @@ void ModemComponent::reset_() {
this->dte_ = create_uart_dte(&this->dte_config_); this->dte_ = create_uart_dte(&this->dte_config_);
assert(this->dte_);
ESP_LOGV(TAG, "DCE setup"); ESP_LOGV(TAG, "DCE setup");
// NOLINTBEGIN(bugprone-branch-clone) #if defined(USE_MODEM_MODEL_GENERIC)
// ( because create_modem_dce(dce_factory::ModemType, config, std::move(dte), netif) is private ) this->dce = create_generic_dce(&this->dce_config_, this->dte_, this->ppp_netif_);
switch (this->model_) { #elif defined(USE_MODEM_MODEL_BG96)
case ModemModel::BG96: this->dce = create_BG96_dce(&this->dce_config_, this->dte_, this->ppp_netif_);
this->dce = create_BG96_dce(&this->dce_config_, this->dte_, this->ppp_netif_); #elif defined(USE_MODEM_MODEL_SIM800)
break; this->dce = create_SIM800_dce(&this->dce_config_, this->dte_, this->ppp_netif_);
case ModemModel::SIM800: #elif defined(USE_MODEM_MODEL_SIM7000)
this->dce = create_SIM800_dce(&this->dce_config_, this->dte_, this->ppp_netif_); this->dce = create_SIM7000_dce(&this->dce_config_, this->dte_, this->ppp_netif_);
break; #elif defined(USE_MODEM_MODEL_SIM7600)
case ModemModel::SIM7000: this->dce = create_SIM7600_dce(&this->dce_config_, this->dte_, this->ppp_netif_);
this->dce = create_SIM7000_dce(&this->dce_config_, this->dte_, this->ppp_netif_); #else
break; #error Modem model not known
case ModemModel::SIM7600: #endif
this->dce = create_SIM7600_dce(&this->dce_config_, this->dte_, this->ppp_netif_);
break;
default:
ESP_LOGE(TAG, "Unknown modem model");
return;
break;
}
// NOLINTEND(bugprone-branch-clone)
assert(this->dce);
// flow control not fully implemented, but kept here for future work // flow control not fully implemented, but kept here for future work
if (this->dte_config_.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) { if (this->dte_config_.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
@ -346,6 +349,7 @@ void ModemComponent::loop() {
this->state_ = ModemComponentState::DISCONNECTED; this->state_ = ModemComponentState::DISCONNECTED;
} }
break; break;
case ModemComponentState::CONNECTED: case ModemComponentState::CONNECTED:
if (!this->start_) { if (!this->start_) {
this->state_ = ModemComponentState::DISCONNECTED; this->state_ = ModemComponentState::DISCONNECTED;
@ -363,7 +367,7 @@ void ModemComponent::loop() {
if (!this->modem_ready()) { if (!this->modem_ready()) {
ESP_LOGE(TAG, "modem not ready after hang up"); ESP_LOGE(TAG, "modem not ready after hang up");
} }
this->set_timeout("wait_lost_ip", 60000, [this]() { this->set_timeout("wait_lost_ip", 15000, [this]() {
// often reached on 7600, but not reached on 7670 // often reached on 7600, but not reached on 7670
ESP_LOGW(TAG, "No lost ip event received. Forcing disconnect state"); ESP_LOGW(TAG, "No lost ip event received. Forcing disconnect state");
@ -442,10 +446,7 @@ void ModemComponent::exit_cmux_() {
} }
bool ModemComponent::get_power_status() { bool ModemComponent::get_power_status() {
if (!this->status_pin_) { #ifdef USE_MODEM_STATUS
ESP_LOGV(TAG, "No status pin, assuming the modem is ON");
return true;
}
bool init_status = this->status_pin_->digital_read(); bool init_status = this->status_pin_->digital_read();
// The status pin might be floating when supposed to be low, at least on lilygo tsim7600 // The status pin might be floating when supposed to be low, at least on lilygo tsim7600
// as GPIO34 doesn't support pullup, we have to debounce it manually // as GPIO34 doesn't support pullup, we have to debounce it manually
@ -458,21 +459,25 @@ bool ModemComponent::get_power_status() {
// ESP_LOGV(TAG, "Floating status pin detected for state %d", final_status); // ESP_LOGV(TAG, "Floating status pin detected for state %d", final_status);
} }
return final_status; return final_status;
#else
// No status pin, assuming the modem is ON
return true;
#endif
} }
void ModemComponent::poweron_() { void ModemComponent::poweron_() {
#ifdef USE_MODEM_POWER
if (this->power_pin_) { if (this->power_pin_) {
Watchdog wdt(60); Watchdog wdt(60);
ESP_LOGV(TAG, "Powering up modem with power_pin..."); ESP_LOGV(TAG, "Powering up modem with power_pin...");
this->power_transition_ = true; this->power_transition_ = true;
this->power_pin_->digital_write(false); this->power_pin_->digital_write(false);
// min 100 for SIM7600, but min 1200 for SIM800. min BG96: 650 // min 100 for SIM7600, but min 1200 for SIM800. min BG96: 650
delay(this->modem_model_ton_[this->model_]); // NOLINT delay(USE_MODEM_POWER_TON);
this->power_pin_->digital_write(true); this->power_pin_->digital_write(true);
// use a timout for long wait delay // use a timout for long wait delay
uint32_t tonuart = this->modem_model_tonuart_[this->model_]; ESP_LOGD(TAG, "Will check that the modem is on in %.1fs...", float(USE_MODEM_POWER_TONUART) / 1000);
ESP_LOGD(TAG, "Will check that the modem is on in %.1fs...", float(tonuart) / 1000); this->set_timeout("wait_poweron", USE_MODEM_POWER_TONUART, [this]() {
this->set_timeout("wait_poweron", tonuart, [this]() {
Watchdog wdt(60); Watchdog wdt(60);
while (!this->get_power_status()) { while (!this->get_power_status()) {
delay(this->command_delay_); delay(this->command_delay_);
@ -487,9 +492,11 @@ void ModemComponent::poweron_() {
this->power_transition_ = false; this->power_transition_ = false;
}); });
} }
#endif // USE_MODEM_POWER
} }
void ModemComponent::poweroff_() { void ModemComponent::poweroff_() {
#ifdef USE_MODEM_POWER
if (this->get_power_status()) { if (this->get_power_status()) {
if (this->power_pin_) { if (this->power_pin_) {
ESP_LOGV(TAG, "Powering off modem with power pin..."); ESP_LOGV(TAG, "Powering off modem with power pin...");
@ -498,12 +505,11 @@ void ModemComponent::poweroff_() {
this->power_pin_->digital_write(true); this->power_pin_->digital_write(true);
delay(10); delay(10);
this->power_pin_->digital_write(false); this->power_pin_->digital_write(false);
delay(this->modem_model_toff_[this->model_]); delay(USE_MODEM_POWER_TOFF);
this->power_pin_->digital_write(true); this->power_pin_->digital_write(true);
uint32_t toffuart = this->modem_model_toffuart_[this->model_]; ESP_LOGD(TAG, "Will check that the modem is off in %.1fs...", float(USE_MODEM_POWER_TOFFUART) / 1000);
ESP_LOGD(TAG, "Will check that the modem is off in %.1fs...", float(toffuart) / 1000); this->set_timeout("wait_poweron", USE_MODEM_POWER_TOFFUART, [this]() {
this->set_timeout("wait_poweron", toffuart, [this]() {
Watchdog wdt(60); Watchdog wdt(60);
while (this->get_power_status()) { while (this->get_power_status()) {
@ -513,10 +519,8 @@ void ModemComponent::poweroff_() {
this->power_transition_ = false; this->power_transition_ = false;
}); });
} }
} else {
ESP_LOGD(TAG, "Modem poweroff with AT command");
this->dce->power_down();
} }
#endif // USE_MODEM_POWER
} }
void ModemComponent::dump_connect_params_() { void ModemComponent::dump_connect_params_() {

View file

@ -40,8 +40,6 @@ enum class ModemComponentState {
DISABLED, DISABLED,
}; };
enum class ModemModel { BG96, SIM800, SIM7000, SIM7070, SIM7600, UNKNOWN };
class ModemComponent : public Component { class ModemComponent : public Component {
public: public:
ModemComponent(); ModemComponent();
@ -62,9 +60,6 @@ class ModemComponent : public Component {
void set_password(const std::string &password) { this->password_ = password; } void set_password(const std::string &password) { this->password_ = password; }
void set_pin_code(const std::string &pin_code) { this->pin_code_ = pin_code; } void set_pin_code(const std::string &pin_code) { this->pin_code_ = pin_code; }
void set_apn(const std::string &apn) { this->apn_ = apn; } void set_apn(const std::string &apn) { this->apn_ = apn; }
void set_model(const std::string &model) {
this->model_ = this->modem_model_map_.count(model) ? modem_model_map_[model] : ModemModel::UNKNOWN;
}
void set_not_responding_cb(Trigger<> *not_responding_cb) { this->not_responding_cb_ = not_responding_cb; } void set_not_responding_cb(Trigger<> *not_responding_cb) { this->not_responding_cb_ = not_responding_cb; }
void add_init_at_command(const std::string &cmd) { this->init_at_commands_.push_back(cmd); } void add_init_at_command(const std::string &cmd) { this->init_at_commands_.push_back(cmd); }
std::string send_at(const std::string &cmd); std::string send_at(const std::string &cmd);
@ -95,12 +90,6 @@ class ModemComponent : public Component {
std::string password_; std::string password_;
std::string apn_; std::string apn_;
std::vector<std::string> init_at_commands_; std::vector<std::string> init_at_commands_;
ModemModel model_;
std::unordered_map<std::string, ModemModel> modem_model_map_ = {{"BG96", ModemModel::BG96},
{"SIM800", ModemModel::SIM800},
{"SIM7000", ModemModel::SIM7000},
{"SIM7070", ModemModel::SIM7070},
{"SIM7600", ModemModel::SIM7600}};
std::shared_ptr<DTE> dte_{nullptr}; std::shared_ptr<DTE> dte_{nullptr};
esp_netif_t *ppp_netif_{nullptr}; esp_netif_t *ppp_netif_{nullptr};
esp_modem_dte_config_t dte_config_; esp_modem_dte_config_t dte_config_;
@ -117,31 +106,6 @@ class ModemComponent : public Component {
uint32_t command_delay_ = 500; uint32_t command_delay_ = 500;
// Will be true when power transitionning // Will be true when power transitionning
bool power_transition_ = false; bool power_transition_ = false;
// time needed for power_pin to be low for poweron
std::unordered_map<ModemModel, uint32_t> modem_model_ton_ = {{ModemModel::BG96, 600},
{ModemModel::SIM800, 1300},
{ModemModel::SIM7000, 1100},
{ModemModel::SIM7070, 1100},
{ModemModel::SIM7600, 500}};
// time to wait after poweron for uart to be ready
std::unordered_map<ModemModel, uint32_t> modem_model_tonuart_ = {{ModemModel::BG96, 4900},
{ModemModel::SIM800, 3000},
{ModemModel::SIM7000, 4500},
{ModemModel::SIM7070, 2500},
{ModemModel::SIM7600, 12000}};
// time needed for power_pin to be high for poweroff
std::unordered_map<ModemModel, uint32_t> modem_model_toff_ = {{ModemModel::BG96, 650},
{ModemModel::SIM800, 200},
{ModemModel::SIM7000, 1300},
{ModemModel::SIM7070, 1300},
{ModemModel::SIM7600, 2800}};
// time to wait after for poweroff for uart to be really closed
std::unordered_map<ModemModel, uint32_t> modem_model_toffuart_ = {{ModemModel::BG96, 2000},
{ModemModel::SIM800, 3000},
{ModemModel::SIM7000, 1800},
{ModemModel::SIM7070, 1800},
{ModemModel::SIM7600, 25000}};
// separate handler for `on_not_responding` (we want to know when it's ended) // separate handler for `on_not_responding` (we want to know when it's ended)
Trigger<> *not_responding_cb_{nullptr}; Trigger<> *not_responding_cb_{nullptr};
CallbackManager<void(ModemComponentState)> on_state_callback_; CallbackManager<void(ModemComponentState)> on_state_callback_;