46 Commits

Author SHA1 Message Date
eeeck 626bbe9dbb Remove task handle: managed in dev-twi main.cpp 2025-12-10 00:27:30 +01:00
eeeck 1a893547b6 Finalize event groups to improve performance 2025-12-09 23:50:26 +01:00
eeeck 06a672d1f7 Remove old code about I2C 2025-12-09 23:40:53 +01:00
eeeck b1a6dfddd3 Wait for Wi-Fi connection before initializing MQTT 2025-12-09 23:23:10 +01:00
eeeck 2e6e829829 Only clear Wi-Fi bit 2025-12-09 23:22:35 +01:00
eeeck 6a2a933002 Better wait implementation with no spinlock from MQTT task 2025-12-09 23:02:21 +01:00
eeeck aa395c381b Remove redundant log 2025-12-09 10:49:32 +01:00
eeeck eaad4469bc Normalize spacing in ESP32 firmware 2025-12-09 10:04:06 +01:00
eeeck 4e78a5f2ed Go in critical error if MQTT is not being initialized 2025-12-09 10:03:06 +01:00
eeeck c53783072e Set MQTT username and password 2025-12-09 10:02:47 +01:00
eeeck 9bad1790a7 Log MQTT errors 2025-12-09 10:02:29 +01:00
eeeck 4cd9a3210c Comment out I2C task handles 2025-12-09 10:01:54 +01:00
eeeck 2c7951c2bb Fix missing code from merge for connectivity_event_group 2025-12-09 01:37:18 +01:00
eeeck 88c7635ed9 Merge branch 'dev-esp32-wifi' into dev-esp32-mqtt 2025-12-09 01:00:26 +01:00
eeeck 985642e0fe Fix compilation issues with espressif mqtt lib 2025-12-04 23:00:46 +01:00
eeeck fb98ce800a Add network speed check for future use 2025-12-04 21:51:16 +01:00
eeeck a332c37e72 Replace polling it with a blocking wait on connectivity flag and
notification
2025-12-04 21:51:02 +01:00
eeeck 567fca2916 Tweak logs 2025-12-01 00:55:59 +01:00
eeeck 04b5fb0a7c Fix task event notification 2025-12-01 00:54:00 +01:00
eeeck 6fd2d2523d Add global event group handle for connectivity 2025-12-01 00:53:12 +01:00
eeeck c6c4011a47 Fix vTaskDelay syntax error 2025-11-30 23:48:08 +01:00
eeeck 213f6b399f Changed some logs type from informative to debug 2025-11-30 23:40:59 +01:00
eeeck 0a418805a4 Changed some logs type from informative to verbose 2025-11-30 23:40:24 +01:00
eeeck 3670b58f8e Implement task handle for managing I2C 2025-11-30 23:36:54 +01:00
eeeck 5dd2c00ac0 Parametrize task delay values 2025-11-30 23:36:27 +01:00
eeeck 2fcc3172aa Fix message logs for consistency with codebase 2025-11-30 22:24:33 +01:00
eeeck 665125e07f Implement MQTT event groups 2025-11-30 22:24:09 +01:00
eeeck 401c610a58 Create shared event group 2025-11-30 22:22:26 +01:00
eeeck 1ef2476f78 MQTT initialization 2025-11-30 17:40:54 +01:00
eeeck 28ac3f44b3 Fix task delay duration 2025-11-29 14:16:11 +01:00
eeeck 29ad9350a3 Fix task name 2025-11-29 14:15:17 +01:00
eeeck cd4d2658b9 Initialize MQTT task 2025-11-06 19:19:52 +01:00
eeeck 92e0bcc2f5 Merge branch 'dev-esp32-wifi' 2025-11-06 16:19:08 +01:00
eeeck fcff8f9900 Add License 2025-10-12 00:33:33 +02:00
eeeck 618965ccc2 Rename Makefile project name to same name as repository 2025-10-05 23:57:03 +02:00
eeeck d7364a6fdc Make triggerMoistureRead function inline 2025-10-05 23:56:41 +02:00
eeeck 443c62593d Add EEPROM flush function to clear address contents 2025-10-04 14:37:38 +02:00
eeeck c24e032981 Add FreeRTOS and GPIO includes 2025-10-01 15:40:05 +02:00
eeeck fb3fdab7df Fix error in comment 2025-10-01 15:39:52 +02:00
eeeck f4e690cf3c Increase EEPROM lifespan by avoiding overwrites on addresses that
contain identical values
2025-10-01 15:34:11 +02:00
eeeck c60493810c Improve EEPROM handling 2025-10-01 15:32:02 +02:00
eeeck fb8423cd9a Clarify inline function name 2025-10-01 01:40:32 +02:00
eeeck 45464300c7 Correct bitmask for TWI SDA and SCL 2025-10-01 01:39:53 +02:00
eeeck 05aae91d12 Mark internal helpers as static and remove from headers 2025-10-01 01:25:59 +02:00
eeeck 6e845c65da Write configuration values to EEPROM at boot 2025-10-01 01:06:20 +02:00
eeeck 2e6fe9ba6b Implement AVR328 EEPROM handling 2025-10-01 00:08:25 +02:00
18 changed files with 285 additions and 74 deletions
+5 -11
View File
@@ -22,9 +22,13 @@ build/
*.arduino/
*.ino.cpp
# Arduino libraries
# Arduino libraries you might not want tracked
libraries/
# PlatformIO (if you ever use it)
.pio/
.vscode/
# === OS and Editor specific ===
# macOS
.DS_Store
@@ -52,9 +56,6 @@ $RECYCLE.BIN/
# VS Code
.vscode/
# Zed
.zed
# Backup files
*~
*.orig
@@ -62,10 +63,3 @@ $RECYCLE.BIN/
# Logs
*.log
# EDA
eda/atmega/atmega-backups
# Personal
*-debug.sh
wifi-credentials-erick.h
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Erick Ahmed
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+2 -2
View File
@@ -1,4 +1,4 @@
PROJECT = vase-control
PROJECT = plant-manager
MCU = atmega328p
F_CPU = 16000000UL
@@ -13,7 +13,7 @@ O_FLAG = -Os
BUILD_DIR = build
OBJ_DIR = $(BUILD_DIR)/obj
SRCS_C = source/adc.c source/moisture.c source/main.c source/pump.c
SRCS_C = source/adc.c source/moisture.c source/main.c source/pump.c source/eeprom.c
OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(SRCS_C:.c=.o)))
CFLAGS = -Wall $(O_FLAG) -DF_CPU=$(F_CPU) -mmcu=$(MCU) -Iinclude
+43 -12
View File
@@ -1,23 +1,54 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <sys/types.h>
#include <stdint.h>
#include <avr/io.h>
#include "eeprom.h"
#define MOISTURE_SENSOR_A0 PC0
#define MOISTURE_SENSOR_A1 PC1
#define MOISTURE_SENSOR_A2 PC2
#define MOISTURE_SENSOR_A3 PC3
#define MOISTURE_SENSOR_A4 PC4
#define MOISTURE_SENSOR_A5 PC5
//#define MOISTURE_SENSOR_A1 PC1
//#define MOISTURE_SENSOR_A2 PC2
//#define MOISTURE_SENSOR_A3 PC3
//#define MOISTURE_SENSOR_A4 PC4
//#define MOISTURE_SENSOR_A5 PC5
//#define MOISTURE_SENSOR_A6 PC6
//#define MOISTURE_SENSOR_A7 PC7
#define PUMP_D3 PD3
#define PUMP_D4 PD4
#define PUMP_D5 PD5
//#define PUMP_D3 PD3
//#define PUMP_D4 PD4
//#define PUMP_D5 PD5
//#define PUMP_D6 PD6
//#define PUMP_D7 PD7
#define PUMP_D7 PD7
// TODO: write these to eeprom
#define SENSOR_READINGS 5
//#define VASE_NUM 0
#define TWI_SDA (1 << PD2)
#define TWI_SCL (1 << PD3)
inline void eeprom_init() {
#define DICT_SIZE 4
#define TWI_ADDR 0x22
#define SENSOR_READINGS 0x23
#define MOISTURE_MIN 0x24
#define MOISTURE_MAX 0x25
uint8_t keys[DICT_SIZE] = {
TWI_ADDR,
SENSOR_READINGS,
MOISTURE_MIN,
MOISTURE_MAX
};
uint16_t values[DICT_SIZE] = {
0x20,
5,
400,
200
};
//NOTE: values are not re-initialized if != 0 because they may
// have been changed by the esp32 during previous runs
for (int i = 0; i < DICT_SIZE; i++) if (eepromRead(keys[i]) == 0) eepromWrite(keys[i], values[i]);
}
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef EEPROM_H
#define EEPROM_H
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
uint8_t eepromRead(uint8_t address);
void eepromWrite(uint8_t address, uint8_t data);
#endif
+1 -2
View File
@@ -44,8 +44,7 @@ static const int8_t sensorPins[] = {
extern volatile bool readSensors;
void sensorsInit(void);
void triggerMoistureRead(void);
uint16_t moistureAverage(uint8_t sensorPin);
inline void triggerMoistureRead(void);
uint16_t moistureRead(void);
#endif
-1
View File
@@ -33,7 +33,6 @@ static const int8_t actuatorPins[] = {
#define ACTUATORS_NUM (sizeof(actuatorPins) / sizeof(actuatorPins[0]))
void pumpsInit(void);
void triggerPump(uint8_t digital_pins);
void waterPlant(void);
#endif
+35
View File
@@ -0,0 +1,35 @@
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
uint8_t eepromRead(uint8_t address) {
// wait for previous write
while (EECR & (1 << EEPE));
//read from address
EEAR = address;
return EECR |= (1 << EERE);
}
void eepromWrite(uint8_t address, uint8_t data) {
// wait for previous write
while (EECR & (1 << EEPE));
if (eepromRead(address) != data) {
EEAR = address;
EEDR = data;
EECR |= (1 << EEMPE);
EECR |= (1 << EEPE);
}
}
void eepromFlush(uint8_t address) {
if (eepromRead(address) != 0) {
EEAR = address;
EEDR = 0;
EECR |= (1 << EEMPE);
EECR |= (1 << EEPE);
}
}
+3 -3
View File
@@ -17,11 +17,11 @@ void sensorsInit(void) {
PORTC &= ~sensorMask;
}
void triggerMoistureRead(void) {
inline void triggerMoistureRead(void) {
readSensors = true;
}
uint16_t moistureAverage(uint8_t sensorPin) {
static uint16_t moistureAverage(uint8_t sensorPin) {
uint16_t sum = 0;
for (uint8_t i = 0; i < SENSOR_READINGS; i++) {
sum += adcRead(sensorPin);
@@ -36,7 +36,7 @@ uint16_t moistureRead(void) {
for (uint8_t i = 0; i < SENSORS_NUM; i++) {
uint16_t sensorAverage = moistureAverage(sensorPins[i]);
//TODO: if a sensor gives 0, discard that sensor entirely (means it is disconnected or not working)
//TODO: if a sensor gives 1023, discard that sensor entirely (means it is disconnected or not working)
overallSum += sensorAverage;
}
+1 -1
View File
@@ -14,7 +14,7 @@ void pumpsInit(void) {
//PORTD &= ~(1 << actuatorMask);
}
void triggerPump(uint8_t pump_pin) {
static void triggerPump(uint8_t pump_pin) {
// Turn on pump
PORTD |= (1 << pump_pin);
+7
View File
@@ -1,10 +1,17 @@
#ifndef CONFIG_H
#define CONFIG_H
// TODO: make this a config.h
// WI-FI
#define SSID "your_wifi_ssid"
#define PASSWORD "your_wifi_password"
#define AUTH_MODE WIFI_AUTH_WPA2_PSK
// MQTT
#define MQTT_ADDR "mqtt://yourmqttserver"
#define MQTT_PORT 1883
#define MQTT_USER "your_username"
#define MQTT_PASS "your_password"
#endif
+9 -5
View File
@@ -1,9 +1,13 @@
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MAIN_HPP
#define MAIN_HPP
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
extern volatile bool criticalErrorFlag;
extern EventGroupHandle_t connectivity_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define MQTT_CONNECTED_BIT BIT1
#ifdef __cplusplus
}
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef MQTT_HPP
#define MQTT_HPP
void mqttTask(void *pvParameters);
#endif
+3 -1
View File
@@ -1,5 +1,6 @@
idf_component_register(SRCS "./main.cpp"
"./wifi.cpp"
"./mqtt.cpp"
INCLUDE_DIRS "../include"
REQUIRES esp_wifi
nvs_flash
@@ -7,4 +8,5 @@ idf_component_register(SRCS "./main.cpp"
esp_hw_support
esp_system
freertos
log)
log
mqtt)
+2
View File
@@ -0,0 +1,2 @@
dependencies:
espressif/mqtt: "*"
+12 -5
View File
@@ -6,6 +6,11 @@
#include "esp_pm.h"
#include "esp_task_wdt.h"
#include "wifi.hpp"
#include "mqtt.hpp"
#define WATCHDOG_KEEPALIVE_MS 8000
EventGroupHandle_t connectivity_event_group;
static const char* TAG = "MAIN";
@@ -22,18 +27,16 @@ static void watchdogTask(void *pvParameters) {
ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
const TickType_t keepAlivePeriod = pdMS_TO_TICKS(8000);
for(;;) {
if (criticalErrorFlag) {
if(criticalErrorFlag) {
ESP_LOGE(TAG, "Critical error flag raised. Rebooting.");
esp_restart();
}
ESP_ERROR_CHECK(esp_task_wdt_reset());
ESP_LOGI(TAG, "WDT reset");
ESP_LOGV(TAG, "WDT reset");
vTaskDelay(keepAlivePeriod);
vTaskDelay(pdMS_TO_TICKS(WATCHDOG_KEEPALIVE_MS));
}
}
@@ -46,6 +49,10 @@ extern "C" void app_main(void)
};
esp_pm_configure(&pm_cfg);
connectivity_event_group = xEventGroupCreate();
xTaskCreate(watchdogTask, "watchdogs", 2048, NULL, configMAX_PRIORITIES-1, NULL);
xTaskCreate(wifiTask, "wifi", 4096, NULL, configMAX_PRIORITIES-4, NULL);
xTaskCreate(mqttTask, "mqtt", 2048, NULL, configMAX_PRIORITIES-8, NULL);
}
+106
View File
@@ -0,0 +1,106 @@
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_task_wdt.h"
#include "esp_event.h"
#include "esp_log.h"
#include "mqtt_client.h"
#include "main.hpp"
//#include "i2c.hpp"
#include "config-erick.h"
#define MQTT_TASK_TIMEOUT_MS 3000
static const char* TAG = "MQTT";
static esp_mqtt_client_handle_t mqtt_client = NULL;
static char global_rx_buffer[50];
static void mqtt_subscribe(void) {
esp_mqtt_client_subscribe(mqtt_client, "/topic/qos0", 0);
}
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
esp_mqtt_event_handle_t event =(esp_mqtt_event_handle_t)event_data;
switch(event->event_id) {
case MQTT_EVENT_CONNECTED:
xEventGroupSetBits(connectivity_event_group, MQTT_CONNECTED_BIT);
mqtt_subscribe();
ESP_LOGV(TAG, "Connected to broker");
break;
case MQTT_EVENT_DISCONNECTED:
xEventGroupClearBits(connectivity_event_group, MQTT_CONNECTED_BIT);
ESP_LOGW(TAG, "Disconnected from broker");
break;
case MQTT_EVENT_DATA:
{
ESP_LOGD(TAG, "Data received");
int len = event->data_len;
if(len > sizeof(global_rx_buffer) - 1) len = sizeof(global_rx_buffer) - 1;
memcpy(global_rx_buffer, event->data, len);
global_rx_buffer[len] = '\0';
break;
}
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "Error event");
if(event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) ESP_LOGE(TAG, "Network Error: %s", strerror(event->error_handle->esp_transport_sock_errno));
else if(event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) ESP_LOGE(TAG, "Connection Refused! Reason code: 0x%x", event->error_handle->connect_return_code);
break;
default:
break;
}
}
static void mqtt_init(void) {
esp_mqtt_client_config_t mqtt_cfg = {};
mqtt_cfg.broker.address.uri = MQTT_ADDR;
mqtt_cfg.broker.address.port = MQTT_PORT;
mqtt_cfg.credentials.username = MQTT_USER;
mqtt_cfg.credentials.authentication.password = MQTT_PASS;
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
if(mqtt_client == NULL) {
ESP_LOGE(TAG, "esp_mqtt_client_init returned NULL");
criticalErrorFlag = true;
}
esp_mqtt_client_register_event(mqtt_client, (esp_mqtt_event_id_t)ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
}
void mqtt_publish(void) {}
void mqttTask(void *pvParameters) {
ESP_LOGV(TAG, "Task started");
ESP_LOGV(TAG, "Waiting for Wi-Fi before initializing server");
xEventGroupWaitBits(connectivity_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
ESP_LOGV(TAG, "Server initialized");
mqtt_init();
ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
for(;;) {
ESP_LOGV(TAG, "Checking Wi-Fi connection...");
EventBits_t bits = xEventGroupWaitBits(connectivity_event_group, WIFI_CONNECTED_BIT | MQTT_CONNECTED_BIT, pdFALSE, pdTRUE, pdMS_TO_TICKS(MQTT_TASK_TIMEOUT_MS));
if ((bits & (WIFI_CONNECTED_BIT | MQTT_CONNECTED_BIT)) == (WIFI_CONNECTED_BIT | MQTT_CONNECTED_BIT)) {
ESP_LOGV(TAG, "Wi-Fi and MQTT up");
} else ESP_LOGV(TAG, "Wi-Fi or MQTT down");
ESP_ERROR_CHECK(esp_task_wdt_reset());
ESP_LOGV(TAG, "Task reset");
}
}
+18 -31
View File
@@ -6,28 +6,26 @@
#include "esp_log.h"
#include "nvs_flash.h"
#include "main.hpp"
#include "config.h"
#include "config-erick.h"
#define WIFI_TIMEOUT_TICKS pdMS_TO_TICKS(8000)
#define WIFI_TIMEOUT_MS 4500
static const char* TAG = "WIFI";
const int WIFI_CONNECTED_BIT = BIT0;
EventGroupHandle_t wifi_event_group;
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT) {
switch (event_id) {
if(event_base == WIFI_EVENT) {
switch(event_id) {
case WIFI_EVENT_STA_START:
ESP_LOGI(TAG, "Wi-Fi process started, connecting...");
ESP_LOGV(TAG, "Process started, connecting...");
esp_wifi_connect();
break;
case WIFI_EVENT_STA_DISCONNECTED: {
wifi_event_sta_disconnected_t* disconn = (wifi_event_sta_disconnected_t*) event_data;
wifi_event_sta_disconnected_t* disconn =(wifi_event_sta_disconnected_t*) event_data;
ESP_LOGW(TAG, "Disconnected, reason: %d", disconn->reason);
xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
xEventGroupClearBits(connectivity_event_group, WIFI_CONNECTED_BIT);
esp_wifi_connect();
break;
}
@@ -35,26 +33,23 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t e
break;
}
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
else if(event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event =(ip_event_got_ip_t*) event_data;
ESP_LOGD(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(connectivity_event_group, WIFI_CONNECTED_BIT);
}
}
static void wifi_init_sta(void) {
esp_err_t nvs_err = nvs_flash_init();
if (nvs_err == ESP_ERR_NVS_NO_FREE_PAGES || nvs_err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
if(nvs_err == ESP_ERR_NVS_NO_FREE_PAGES || nvs_err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
@@ -77,22 +72,14 @@ static void wifi_init_sta(void) {
}
void wifiTask(void *pvParameters) {
ESP_LOGI(TAG, "WiFi task started");
ESP_LOGV(TAG, "Task started");
wifi_init_sta();
ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
for(;;) {
wifi_ap_record_t ap;
if (esp_wifi_sta_get_ap_info(&ap) == ESP_OK) {
ESP_LOGV(TAG, "RSSI: %d", ap.rssi);
}
//TODO: manage data send rate if with slow network
// or just let mqtt know (useful for making a notice on home manager)
ESP_ERROR_CHECK(esp_task_wdt_reset());
ESP_LOGI(TAG, "Wi-Fi task reset");
vTaskDelay(pdMS_TO_TICKS(WIFI_TIMEOUT_TICKS));
ESP_LOGV(TAG, "Task reset");
vTaskDelay(pdMS_TO_TICKS(WIFI_TIMEOUT_MS));
}
}