/** * ESP32 SvelteKit * * A simple, secure and extensible framework for IoT projects for ESP32 platforms * with responsive Sveltekit front-end built with TailwindCSS and DaisyUI. * https://github.com/theelims/ESP32-sveltekit * * Copyright (C) 2018 - 2023 rjwats * Copyright (C) 2023 theelims * * All Rights Reserved. This software may be modified and distributed under * the terms of the LGPL v3 license. See the LICENSE file for details. **/ #include extern const uint8_t rootca_crt_bundle_start[] asm("_binary_src_certs_x509_crt_bundle_bin_start"); /** * Retains a copy of the cstr provided in the pointer provided using dynamic allocation. * * Frees the pointer before allocation and leaves it as nullptr if cstr == nullptr. */ static char *retainCstr(const char *cstr, char **ptr) { // free up previously retained value if exists free(*ptr); *ptr = nullptr; // dynamically allocate and copy cstr (if non null) if (cstr != nullptr) { *ptr = (char *)malloc(strlen(cstr) + 1); strcpy(*ptr, cstr); } // return reference to pointer for convenience return *ptr; } MqttSettingsService::MqttSettingsService(PsychicHttpServer *server, FS *fs, SecurityManager *securityManager) : _server(server), _securityManager(securityManager), _httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager), _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE), _retainedHost(nullptr), _retainedClientId(nullptr), _retainedUsername(nullptr), _retainedPassword(nullptr), _reconfigureMqtt(false), _mqttClient(), _lastError("None") { addUpdateHandler([&](const String &originId) { onConfigUpdated(); }, false); _mqttClient.setCACertBundle(rootca_crt_bundle_start); } MqttSettingsService::~MqttSettingsService() { } void MqttSettingsService::begin() { WiFi.onEvent( std::bind(&MqttSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); WiFi.onEvent(std::bind(&MqttSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_GOT_IP); _mqttClient.onConnect(std::bind(&MqttSettingsService::onMqttConnect, this, std::placeholders::_1)); _mqttClient.onDisconnect(std::bind(&MqttSettingsService::onMqttDisconnect, this, std::placeholders::_1)); _mqttClient.onError(std::bind(&MqttSettingsService::onMqttError, this, std::placeholders::_1)); _httpEndpoint.begin(); _fsPersistence.readFromFS(); } void MqttSettingsService::loop() { if (_reconfigureMqtt) { // reconfigure MQTT client configureMqtt(); // clear the reconnection flags _reconfigureMqtt = false; } } bool MqttSettingsService::isEnabled() { return _state.enabled; } bool MqttSettingsService::isConnected() { return _mqttClient.connected(); } const char *MqttSettingsService::getClientId() { // return _mqttClient.getClientId(); return _state.clientId.c_str(); } PsychicMqttClient *MqttSettingsService::getMqttClient() { return &_mqttClient; } String MqttSettingsService::getLastError() { return _lastError; } void MqttSettingsService::onMqttConnect(bool sessionPresent) { ESP_LOGI("MQTT", "Connected to MQTT: %s", _mqttClient.getMqttConfig()->uri); #ifdef SERIAL_INFO Serial.printf("Connected to MQTT: %s\n", _mqttClient.getMqttConfig()->uri); #endif _lastError = "None"; } void MqttSettingsService::onMqttDisconnect(bool sessionPresent) { ESP_LOGI("MQTT", "Disconnected from MQTT."); #ifdef SERIAL_INFO Serial.println("Disconnected from MQTT."); #endif } void MqttSettingsService::onMqttError(esp_mqtt_error_codes_t error) { if (error.error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { _lastError = strerror(error.esp_transport_sock_errno); ESP_LOGE("MQTT", "MQTT TCP error: %s", _lastError.c_str()); } } void MqttSettingsService::onConfigUpdated() { _reconfigureMqtt = true; } void MqttSettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { if (_state.enabled) { ESP_LOGI("MQTT", "WiFi connection established, starting MQTT client."); onConfigUpdated(); } } void MqttSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { if (_state.enabled) { ESP_LOGI("MQTT", "WiFi connection dropped, stopping MQTT client."); onConfigUpdated(); } } void MqttSettingsService::configureMqtt() { // disconnect if currently connected _mqttClient.disconnect(); // only connect if WiFi is connected and MQTT is enabled if (_state.enabled && WiFi.isConnected()) { #ifdef SERIAL_INFO Serial.println("Connecting to MQTT..."); #endif _mqttClient.setServer(retainCstr(_state.uri.c_str(), &_retainedHost)); if (_state.username.length() > 0) { _mqttClient.setCredentials( retainCstr(_state.username.c_str(), &_retainedUsername), retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword)); } else { _mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword)); } _mqttClient.setClientId(retainCstr(_state.clientId.c_str(), &_retainedClientId)); _mqttClient.setKeepAlive(_state.keepAlive); _mqttClient.setCleanSession(_state.cleanSession); _mqttClient.connect(); } }