95 Commits

Author SHA1 Message Date
eeeck 03adefeee7 Minor fixes in EDA 2026-03-13 15:15:02 +01:00
eeeck 60d7752721 Change log types 2025-12-13 13:08:38 +01:00
eeeck 514f4d5d69 Add delay and log 2025-12-13 12:53:08 +01:00
eeeck d8a29c30d4 Correct build info 2025-12-13 12:43:14 +01:00
eeeck b108d57c12 Add new definition for I2C commands 2025-12-13 12:42:53 +01:00
eeeck af8e330460 Add definition for default slave address 2025-12-13 12:42:36 +01:00
eeeck 46fcb137b4 Add includes 2025-12-13 12:42:23 +01:00
eeeck 2a3eaf2acc Fix task handle name and stack overflow issue 2025-12-13 12:42:05 +01:00
eeeck 3d841230bf Fix include 2025-12-13 12:41:46 +01:00
eeeck 26bbe5b518 Send packet when notified from MQTT 2025-12-13 12:41:38 +01:00
eeeck 68e5a67fb7 Implement new function to send packet via I2C/TWI 2025-12-13 12:41:12 +01:00
eeeck 50fbe9e9eb Add a second slave for testing 2025-12-13 12:40:39 +01:00
eeeck 5dff5da328 Fix initialization of master 2025-12-13 12:40:22 +01:00
eeeck 72974c41a7 Configure multiple slave management 2025-12-13 12:40:00 +01:00
eeeck 9fab98af61 Add includes 2025-12-13 12:39:33 +01:00
eeeck aba6253e70 Add delay and log 2025-12-13 12:39:16 +01:00
eeeck 6bfdc19fa8 Implement parsing method for MQTT messages 2025-12-13 12:39:04 +01:00
eeeck 184a0eed27 Remove port as per new Espressif MQTT lib 2025-12-13 12:38:41 +01:00
eeeck 1bbd73ff49 Buffering implementation with local buffer 2025-12-13 12:38:21 +01:00
eeeck cd36415807 Subscribe to topics 2025-12-13 12:37:51 +01:00
eeeck dc5c2c751b Change message receive implementation to allow for only one action at a
time
2025-12-13 12:36:15 +01:00
eeeck 137814206c It's a plant thing! 2025-12-13 12:32:03 +01:00
eeeck 2b8bbd703b Fix indentation 2025-12-13 12:31:24 +01:00
eeeck 1c21ddee89 Add #pragma once to ESP32 headers 2025-12-13 12:30:49 +01:00
eeeck 14fc015d1b Wait for Wi‑Fi and MQTT before I2C read 2025-12-12 14:08:09 +01:00
eeeck b9c8a5d2a7 Change logs from informative to verbose 2025-12-10 23:09:11 +01:00
eeeck f961cc62a0 Move definition to config 2025-12-10 23:09:06 +01:00
eeeck 0d0750b746 Merge branch 'dev-esp32-mqtt' into dev-twi 2025-12-10 09:41:41 +01:00
eeeck 626bbe9dbb Remove task handle: managed in dev-twi main.cpp 2025-12-10 00:27:30 +01:00
eeeck 63c358fd87 Add task handle for task notifications 2025-12-10 00:24:44 +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 9143bd0f76 Add MQTT checks 2025-12-01 00:18:39 +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 455ab94449 Add TODO 2025-11-29 14:13:28 +01:00
eeeck 49cf49db87 Create boilerplate for task
- To change even group - To create i2c_read and i2c_write functions
2025-11-29 14:13:18 +01:00
eeeck 50dd39fd60 Add task log 2025-11-29 14:04:38 +01:00
eeeck a02d3e3294 Add build instructions for I2C 2025-11-27 15:55:42 +01:00
eeeck 3a91791180 Fix TWI address reading error 2025-11-27 15:55:29 +01:00
eeeck 71106c2542 Add I2C task 2025-11-27 15:54:57 +01:00
eeeck 4f0dd9faaa Add TWI (I2C) init for ESP32 2025-11-27 15:54:40 +01:00
eeeck cd4d2658b9 Initialize MQTT task 2025-11-06 19:19:52 +01:00
eeeck a1781af096 Merge branch 'main' into dev-twi 2025-11-06 17:00:06 +01:00
eeeck 92e0bcc2f5 Merge branch 'dev-esp32-wifi' 2025-11-06 16:19:08 +01:00
eeeck 2be65a0b76 Implement plant watering based on current moisture levels 2025-10-29 14:45:40 +01:00
eeeck 620eb57920 Reorder respond cases for better clarity 2025-10-29 14:44:50 +01:00
eeeck b28b20f46a Refactor TWI response system
- Avoid setting respondFlag in main, instead let it be managed directly
inside twiListen() and read inside twiRespond()
2025-10-29 14:30:23 +01:00
eeeck 1721be5e66 Remove triggerMoistureRead() call in ISR and it's trigger variable
- This was done at the very beginning when i thought to develop this
part of the program in a different way. It is unnecessary with how I
actually ended up to structure the program. - WDT should manage TWI
only, as per current developement
2025-10-29 13:51:04 +01:00
eeeck 2a36d1356c Move TWI response handling from watchdog to main loop 2025-10-12 21:45:01 +02:00
eeeck 0f0408df9b Simplify moisture value access and update header structure 2025-10-12 14:27:15 +02:00
eeeck fcff8f9900 Add License 2025-10-12 00:33:33 +02:00
eeeck 2d9a4f3bb5 Fix TWI slave address shift and buffer index 2025-10-09 11:04:16 +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 2d6643601d Implement TWI response method 2025-10-05 23:52:09 +02:00
eeeck 74aab766fc Add read-only pointer accessor for previously read moisture value 2025-10-05 22:21:21 +02:00
eeeck 6c48ebb65f Add data receiving capabilities using a buffer with overflow protection 2025-10-05 17:38:49 +02:00
eeeck 443c62593d Add EEPROM flush function to clear address contents 2025-10-04 14:37:38 +02:00
eeeck 40a5ad5a35 Add TWI module to build sources 2025-10-01 16:18:37 +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
29 changed files with 1353 additions and 512 deletions
+5 -11
View File
@@ -22,9 +22,13 @@ build/
*.arduino/ *.arduino/
*.ino.cpp *.ino.cpp
# Arduino libraries # Arduino libraries you might not want tracked
libraries/ libraries/
# PlatformIO (if you ever use it)
.pio/
.vscode/
# === OS and Editor specific === # === OS and Editor specific ===
# macOS # macOS
.DS_Store .DS_Store
@@ -52,9 +56,6 @@ $RECYCLE.BIN/
# VS Code # VS Code
.vscode/ .vscode/
# Zed
.zed
# Backup files # Backup files
*~ *~
*.orig *.orig
@@ -62,10 +63,3 @@ $RECYCLE.BIN/
# Logs # Logs
*.log *.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.
+1
View File
@@ -440,6 +440,7 @@
"single_global_label": "ignore", "single_global_label": "ignore",
"unannotated": "error", "unannotated": "error",
"unconnected_wire_endpoint": "warning", "unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error", "unit_value_mismatch": "error",
"unresolved_variable": "error", "unresolved_variable": "error",
"wire_dangling": "error" "wire_dangling": "error"
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,4 +1,4 @@
PROJECT = vase-control PROJECT = plant-manager
MCU = atmega328p MCU = atmega328p
F_CPU = 16000000UL F_CPU = 16000000UL
@@ -13,7 +13,7 @@ O_FLAG = -Os
BUILD_DIR = build BUILD_DIR = build
OBJ_DIR = $(BUILD_DIR)/obj 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 source/twi.c
OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(SRCS_C:.c=.o))) OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(SRCS_C:.c=.o)))
CFLAGS = -Wall $(O_FLAG) -DF_CPU=$(F_CPU) -mmcu=$(MCU) -Iinclude CFLAGS = -Wall $(O_FLAG) -DF_CPU=$(F_CPU) -mmcu=$(MCU) -Iinclude
+47 -12
View File
@@ -1,23 +1,58 @@
#ifndef CONFIG_H #ifndef CONFIG_H
#define 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_A0 PC0
#define MOISTURE_SENSOR_A1 PC1 //#define MOISTURE_SENSOR_A1 PC1
#define MOISTURE_SENSOR_A2 PC2 //#define MOISTURE_SENSOR_A2 PC2
#define MOISTURE_SENSOR_A3 PC3 //#define MOISTURE_SENSOR_A3 PC3
#define MOISTURE_SENSOR_A4 PC4 //#define MOISTURE_SENSOR_A4 PC4
#define MOISTURE_SENSOR_A5 PC5 //#define MOISTURE_SENSOR_A5 PC5
//#define MOISTURE_SENSOR_A6 PC6 //#define MOISTURE_SENSOR_A6 PC6
//#define MOISTURE_SENSOR_A7 PC7 //#define MOISTURE_SENSOR_A7 PC7
#define PUMP_D3 PD3 //#define PUMP_D3 PD3
#define PUMP_D4 PD4 //#define PUMP_D4 PD4
#define PUMP_D5 PD5 //#define PUMP_D5 PD5
//#define PUMP_D6 PD6 //#define PUMP_D6 PD6
//#define PUMP_D7 PD7 #define PUMP_D7 PD7
// TODO: write these to eeprom #define TWI_SDA (1 << PD2)
#define SENSOR_READINGS 5 #define TWI_SCL (1 << PD3)
//#define VASE_NUM 0
inline void eeprom_init() {
#define DICT_SIZE 4
// EEPROM addresses
#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
};
// actual values
uint16_t values[DICT_SIZE] = {
0x30,
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]);
}
extern volatile uint16_t *pLastMoistureVal;
#endif #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
-2
View File
@@ -44,8 +44,6 @@ static const int8_t sensorPins[] = {
extern volatile bool readSensors; extern volatile bool readSensors;
void sensorsInit(void); void sensorsInit(void);
void triggerMoistureRead(void);
uint16_t moistureAverage(uint8_t sensorPin);
uint16_t moistureRead(void); uint16_t moistureRead(void);
#endif #endif
-1
View File
@@ -33,7 +33,6 @@ static const int8_t actuatorPins[] = {
#define ACTUATORS_NUM (sizeof(actuatorPins) / sizeof(actuatorPins[0])) #define ACTUATORS_NUM (sizeof(actuatorPins) / sizeof(actuatorPins[0]))
void pumpsInit(void); void pumpsInit(void);
void triggerPump(uint8_t digital_pins);
void waterPlant(void); void waterPlant(void);
#endif #endif
+10
View File
@@ -0,0 +1,10 @@
#ifndef TWI_H
#define TWI_H
extern volatile uint16_t *pLastMoistureVal;
void twiInit(void);
void twiListen(void);
void twiRespond(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);
}
}
+16 -13
View File
@@ -3,15 +3,20 @@
#include <avr/sleep.h> #include <avr/sleep.h>
#include <avr/wdt.h> #include <avr/wdt.h>
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include "config.h"
#include "adc.h" #include "adc.h"
#include "moisture.h" #include "moisture.h"
#include "pump.h" #include "pump.h"
#include "twi.h"
ISR(WDT_vect) { volatile uint16_t lastMoistureVal = 0;
triggerMoistureRead(); volatile uint16_t *pLastMoistureVal = &lastMoistureVal;
ISR(TWI_vect) {
twiListen();
} }
void watchdogs(void) { static void watchdogs(void) {
cli(); cli();
MCUSR &= ~(1 << WDRF); MCUSR &= ~(1 << WDRF);
WDTCSR |= (1 << WDCE) | (1 << WDE); WDTCSR |= (1 << WDCE) | (1 << WDE);
@@ -19,15 +24,16 @@ void watchdogs(void) {
sei(); sei();
} }
void enterSleep(void) { static inline void enterSleep(void) {
set_sleep_mode(SLEEP_MODE_PWR_DOWN); set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable(); sleep_enable();
sleep_cpu(); sleep_cpu();
sleep_disable(); sleep_disable();
} }
void setup(void) { static void setup(void) {
adcInit(); adcInit();
twiInit();
sensorsInit(); sensorsInit();
pumpsInit(); pumpsInit();
watchdogs(); watchdogs();
@@ -36,16 +42,13 @@ void setup(void) {
int main(void) { int main(void) {
setup(); setup();
while(true) { pLastMoistureVal = &lastMoistureVal;
if (readSensors) {
uint16_t result = moistureRead();
//espSend(VASE_NUM, result)
}
// if(espGet("ask if i need to water plants") == true) {waterPlant()}
// or maybe it's better to do this with interrupts?
readSensors = false; while(true) {
if ((*pLastMoistureVal = moistureRead()) < MOISTURE_MIN) waterPlant();
twiRespond();
enterSleep(); enterSleep();
} }
return -1; return -1;
} }
+2 -6
View File
@@ -17,11 +17,7 @@ void sensorsInit(void) {
PORTC &= ~sensorMask; PORTC &= ~sensorMask;
} }
void triggerMoistureRead(void) { static uint16_t moistureAverage(uint8_t sensorPin) {
readSensors = true;
}
uint16_t moistureAverage(uint8_t sensorPin) {
uint16_t sum = 0; uint16_t sum = 0;
for (uint8_t i = 0; i < SENSOR_READINGS; i++) { for (uint8_t i = 0; i < SENSOR_READINGS; i++) {
sum += adcRead(sensorPin); sum += adcRead(sensorPin);
@@ -36,7 +32,7 @@ uint16_t moistureRead(void) {
for (uint8_t i = 0; i < SENSORS_NUM; i++) { for (uint8_t i = 0; i < SENSORS_NUM; i++) {
uint16_t sensorAverage = moistureAverage(sensorPins[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; overallSum += sensorAverage;
} }
+1 -1
View File
@@ -14,7 +14,7 @@ void pumpsInit(void) {
//PORTD &= ~(1 << actuatorMask); //PORTD &= ~(1 << actuatorMask);
} }
void triggerPump(uint8_t pump_pin) { static void triggerPump(uint8_t pump_pin) {
// Turn on pump // Turn on pump
PORTD |= (1 << pump_pin); PORTD |= (1 << pump_pin);
+101
View File
@@ -0,0 +1,101 @@
#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include "config.h"
#include "pump.h"
#include "twi.h"
#define BUFFER_SIZE 4
static uint8_t twiBuffer[BUFFER_SIZE] = {0};
static uint8_t bufferIndex = 0;
bool respondFlag = false;
static inline void twiReset(void) {
// Enable TWI; enable ACK; clear interrupt flag; enable twi external interrupt
TWCR = (1 << TWEN) | (1 << TWEA) | (1 << TWINT) | (1 << TWIE);
}
void twiInit(void) {
TWAR = ((eepromRead(TWI_ADDR)) << 1); //address definition found in config.h
twiReset();
}
static inline void twiSendAck(void) {
TWCR = (1 << TWEA) | (1 << TWINT);
}
static inline void twiSendNack(void) {
TWCR = (TWCR & ~(1 << TWEA)) | (1 << TWINT);
}
static void twiSendData(uint8_t data) {
TWDR = data;
twiSendAck();
}
void twiRespond(void) {
if (!respondFlag) return;
uint8_t cmd = twiBuffer[0];
uint8_t param = twiBuffer[1];
switch(cmd) {
// READ
case 0x01:
twiSendData(*pLastMoistureVal);
break;
case 0x02:
twiSendData(eepromRead(SENSOR_READINGS));
break;
case 0x03:
twiSendData(eepromRead(MOISTURE_MIN));
break;
case 0x04:
twiSendData(eepromRead(MOISTURE_MAX));
break;
// WRITE
case 0x05:
eepromWrite(SENSOR_READINGS, param);
twiSendAck();
break;
case 0x06:
eepromWrite(MOISTURE_MIN, param);
twiSendAck();
break;
case 0x07:
eepromWrite(MOISTURE_MAX, param);
twiSendAck();
break;
case 0x08:
waterPlant();
twiSendAck();
break;
default:
twiSendNack();
break;
}
}
void twiListen(void) {
switch (TWSR & 0xF8) {
case 0x60: // SLA+W received
bufferIndex = 0;
twiSendAck();
break;
case 0x80: // data received
if(bufferIndex < BUFFER_SIZE) {
twiBuffer[bufferIndex++] = TWDR;
twiSendAck();
} else twiSendNack();
break;
case 0xA0: // stop condition
respondFlag = true;
twiReset();
break;
default:
twiSendNack();
break;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(plant-manager) project(plant-thing)
+1 -1
View File
@@ -45,4 +45,4 @@ RUN rm -rf build && /bin/bash -c "source $IDF_PATH/export.sh && idf.py set-targe
RUN mkdir -p /usr/src/build && \ RUN mkdir -p /usr/src/build && \
cp build/bootloader/bootloader.bin /usr/src/build/ && \ cp build/bootloader/bootloader.bin /usr/src/build/ && \
cp build/partition_table/partition-table.bin /usr/src/build/ && \ cp build/partition_table/partition-table.bin /usr/src/build/ && \
cp build/plant-manager.bin /usr/src/build/ cp build/plant-thing.bin /usr/src/build/
+1 -1
View File
@@ -22,6 +22,6 @@ esptool --chip esp32c3 --port /dev/tty.usbmodem101 --baud 115200 write-flash \
--flash-size 4MB \ --flash-size 4MB \
0x0000 "$DEST_DIR/bootloader.bin" \ 0x0000 "$DEST_DIR/bootloader.bin" \
0x8000 "$DEST_DIR/partition-table.bin" \ 0x8000 "$DEST_DIR/partition-table.bin" \
0x10000 "$DEST_DIR/plant-manager.bin" 0x10000 "$DEST_DIR/plant-thing.bin"
screen -fn /dev/tty.usbmodem101 115200 screen -fn /dev/tty.usbmodem101 115200
+21
View File
@@ -1,10 +1,31 @@
#ifndef CONFIG_H #ifndef CONFIG_H
#define CONFIG_H #define CONFIG_H
#pragma once
// WI-FI
#define SSID "your_wifi_ssid" #define SSID "your_wifi_ssid"
#define PASSWORD "your_wifi_password" #define PASSWORD "your_wifi_password"
#define AUTH_MODE WIFI_AUTH_WPA2_PSK #define AUTH_MODE WIFI_AUTH_WPA2_PSK
// MQTT
#define MQTT_ADDR "mqtt://yourmqttserver" #define MQTT_ADDR "mqtt://yourmqttserver"
#define MQTT_PORT 1883 #define MQTT_PORT 1883
#define MQTT_USER "your_username"
#define MQTT_PASS "your_password"
#define CMD_GET_MOISTURE 0x01
#define CMD_GET_SENSOR_READS 0x02
#define CMD_GET_MOISTURE_MIN 0x03
#define CMD_GET_MOISTURE_MAX 0x04
#define CMD_SET_SENSOR_READS 0x05 // [cmd, value]
#define CMD_SET_PARAMS 0x06 // [cmd, min, max, reads]
#define CMD_FORCE_PUMP 0x07 // [cmd]
// Change based on how many vases max to manage [1 to ~100]
// larger numberss will require larger arrays stored in memory
// see AVR limitation on TWI addresses
#define SLAVES_NUMBER 8
#endif #endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef I2C_HPP
#define I2C_HPP
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
extern TaskHandle_t i2c_task;
void i2cTask(void *pvParameters);
#endif
+13 -7
View File
@@ -1,9 +1,15 @@
#ifdef __cplusplus #ifndef MAIN_HPP
extern "C" { #define MAIN_HPP
#endif
extern volatile bool criticalErrorFlag; #pragma once
#ifdef __cplusplus #include "freertos/FreeRTOS.h"
} #include "freertos/event_groups.h"
#endif
extern volatile bool criticalErrorFlag;
extern EventGroupHandle_t connectivity_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define MQTT_CONNECTED_BIT BIT1
#endif
+8
View File
@@ -0,0 +1,8 @@
#ifndef MQTT_HPP
#define MQTT_HPP
#pragma once
void mqttTask(void *pvParameters);
#endif
+2
View File
@@ -1,6 +1,8 @@
#ifndef WIFI_HPP #ifndef WIFI_HPP
#define WIFI_HPP #define WIFI_HPP
#pragma once
void wifiTask(void *pvParameters); void wifiTask(void *pvParameters);
#endif #endif
+16 -10
View File
@@ -1,10 +1,16 @@
idf_component_register(SRCS "./main.cpp" idf_component_register(
"./wifi.cpp" SRCS "./main.cpp"
INCLUDE_DIRS "../include" "./wifi.cpp"
REQUIRES esp_wifi "./i2c.cpp"
nvs_flash "./mqtt.cpp"
esp_pm INCLUDE_DIRS "../include"
esp_hw_support REQUIRES esp_wifi
esp_system nvs_flash
freertos esp_pm
log) esp_hw_support
esp_system
freertos
log
mqtt
esp_driver_i2c
)
+100
View File
@@ -0,0 +1,100 @@
#include <cstdint>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_task_wdt.h"
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "config-erick.h"
#define I2C_TASK_TIMEOUT_MS 4500
// for ESP32-C3
#define I2C_MASTER_SCL_IO ((gpio_num_t)9)
#define I2C_MASTER_SDA_IO ((gpio_num_t)8)
#define DATA_LENGTH 100
static const char* TAG = "I2C";
TaskHandle_t i2c_task = NULL;
i2c_master_bus_handle_t bus_handle;
static i2c_master_dev_handle_t slave_handles[SLAVES_NUMBER];
uint8_t slave_count = 0;
uint8_t slave_addrs[SLAVES_NUMBER];
uint8_t data_rd[DATA_LENGTH];
static void register_slave(uint8_t addr) {
i2c_device_config_t slave_dev_cfg = {};
slave_dev_cfg.dev_addr_length = I2C_ADDR_BIT_LEN_7;
slave_dev_cfg.device_address = addr;
slave_dev_cfg.scl_speed_hz = 100000;
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &slave_dev_cfg, &slave_handles[slave_count]));
slave_addrs[slave_count++] = addr;
}
static void i2c_init(void) {
i2c_master_bus_config_t i2c_mst_config = {};
i2c_mst_config.i2c_port = I2C_NUM_0;
i2c_mst_config.scl_io_num = I2C_MASTER_SCL_IO;
i2c_mst_config.sda_io_num = I2C_MASTER_SDA_IO;
i2c_mst_config.clk_source = I2C_CLK_SRC_DEFAULT;
i2c_mst_config.glitch_ignore_cnt = 7;
i2c_mst_config.intr_priority = 1;
i2c_mst_config.trans_queue_depth = 4;
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
register_slave(0x30);
register_slave(0x31);
}
static void i2c_send(uint32_t packet) {
uint8_t cmd = (packet >> 24) & 0xFF;
uint8_t slave_addr = (packet >> 16) & 0xFF;
uint8_t param = packet & 0xFF;
i2c_master_dev_handle_t dev = NULL;
for(uint8_t i = 0; i < slave_count; i++) {
if(slave_addrs[i] == slave_addr) {
dev = slave_handles[i];
break;
}
}
if(!dev) { ESP_LOGE(TAG,"Slave 0x%02X not found", slave_addr); return; }
uint8_t tx[2] = {cmd, param};
esp_err_t err;
switch(cmd) {
case CMD_GET_MOISTURE:
case CMD_GET_SENSOR_READS:
case CMD_GET_MOISTURE_MIN:
case CMD_GET_MOISTURE_MAX: {
uint8_t rx;
err = i2c_master_transmit_receive(dev, tx, 2, &rx, 1, 250);
if(err == ESP_OK) ESP_LOGI(TAG,"I2C READ cmd=0x%02X val=%u", cmd, rx);
break;
}
default:
err = i2c_master_transmit(dev, tx, 2, 250);
break;
}
if(err != ESP_OK) ESP_LOGE(TAG,"I2C transaction failed: %s", esp_err_to_name(err));
}
void i2cTask(void *pvParameters) {
i2c_init();
ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
uint32_t packet;
for(;;) {
if(xTaskNotifyWait(0, 0, &packet, pdMS_TO_TICKS(I2C_TASK_TIMEOUT_MS)) == pdPASS) {
i2c_send(packet);
}
ESP_ERROR_CHECK(esp_task_wdt_reset());
vTaskDelay(pdMS_TO_TICKS(150));
}
}
+2
View File
@@ -0,0 +1,2 @@
dependencies:
espressif/mqtt: "*"
+13 -5
View File
@@ -6,6 +6,12 @@
#include "esp_pm.h" #include "esp_pm.h"
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "wifi.hpp" #include "wifi.hpp"
#include "i2c.hpp"
#include "mqtt.hpp"
#define WATCHDOG_KEEPALIVE_MS 8000
EventGroupHandle_t connectivity_event_group;
static const char* TAG = "MAIN"; static const char* TAG = "MAIN";
@@ -22,18 +28,16 @@ static void watchdogTask(void *pvParameters) {
ESP_ERROR_CHECK(esp_task_wdt_add(NULL)); ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
const TickType_t keepAlivePeriod = pdMS_TO_TICKS(8000);
for(;;) { for(;;) {
if (criticalErrorFlag) { if(criticalErrorFlag) {
ESP_LOGE(TAG, "Critical error flag raised. Rebooting."); ESP_LOGE(TAG, "Critical error flag raised. Rebooting.");
esp_restart(); esp_restart();
} }
ESP_ERROR_CHECK(esp_task_wdt_reset()); 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 +50,10 @@ extern "C" void app_main(void)
}; };
esp_pm_configure(&pm_cfg); esp_pm_configure(&pm_cfg);
connectivity_event_group = xEventGroupCreate();
xTaskCreate(watchdogTask, "watchdogs", 2048, NULL, configMAX_PRIORITIES-1, NULL); xTaskCreate(watchdogTask, "watchdogs", 2048, NULL, configMAX_PRIORITIES-1, NULL);
xTaskCreate(wifiTask, "wifi", 4096, NULL, configMAX_PRIORITIES-4, NULL); xTaskCreate(wifiTask, "wifi", 4096, NULL, configMAX_PRIORITIES-4, NULL);
xTaskCreate(i2cTask, "i2c", 4096, NULL, configMAX_PRIORITIES-6, &i2c_task);
xTaskCreate(mqttTask, "mqtt", 2048, NULL, configMAX_PRIORITIES-8, NULL);
} }
+156
View File
@@ -0,0 +1,156 @@
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_task_wdt.h"
#include "esp_event.h"
#include "esp_log.h"
#include "stdlib.h"
#include "string.h"
#include "mqtt_client.h"
#include "main.hpp"
#include "i2c.hpp"
#include "config.h"
#include <cstdint>
#define MQTT_TASK_TIMEOUT_MS 3000
#define DEFAULT_SLAVE_ADDR 0x30
static const char* TAG = "MQTT";
static esp_mqtt_client_handle_t mqtt_client = NULL;
static char rx_buffer[50];
static void mqtt_subscribe(void) {
esp_mqtt_client_subscribe(mqtt_client, "/plant/moisture/qos0", 0);
esp_mqtt_client_subscribe(mqtt_client, "/plant/humidity/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);
ESP_LOGV(TAG, "Connected to broker");
mqtt_subscribe();
ESP_LOGV(TAG, "Subscribed to topic");
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(rx_buffer) - 1) len = sizeof(rx_buffer) - 1;
memcpy(rx_buffer, event->data, len);
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.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");
uint8_t params[3] = {0};
uint8_t param_len = 0;
uint8_t i2c_cmd = 0;
uint8_t slave_addr = DEFAULT_SLAVE_ADDR;
char *action = strtok(rx_buffer, ":");
char *target = strtok(NULL, ":");
char *value = strtok(NULL, ":");
char *slave = strtok(NULL, ":");
if(slave) slave_addr = (uint8_t)strtol(slave, NULL, 0);
if(action && target) {
if (strcmp(action, "READ") == 0) {
if (strcmp(target, "moisture") == 0) i2c_cmd = CMD_GET_MOISTURE;
else if (strcmp(target, "eeprom") == 0 && value) {
if (strcmp(value, "sensor_reads") == 0) i2c_cmd = CMD_GET_SENSOR_READS;
else if (strcmp(value, "moisture_min") == 0) i2c_cmd = CMD_GET_MOISTURE_MIN;
else if (strcmp(value, "moisture_max") == 0) i2c_cmd = CMD_GET_MOISTURE_MAX;
}
} else if (strcmp(action, "WRITE") == 0) {
if (strcmp(target, "force") == 0) i2c_cmd = CMD_FORCE_PUMP;
else if (strcmp(target, "eeprom") == 0 && value) {
if (strncmp(value, "params:", 7) == 0) {
sscanf(value + 7, "%hhu,%hhu,%hhu", &params[0], &params[1], &params[2]);
i2c_cmd = CMD_SET_PARAMS;
param_len = 3;
}
else {
uint8_t v = (uint8_t)atoi(value);
if (strcmp(value, "sensor_reads") == 0) {
i2c_cmd = CMD_SET_SENSOR_READS;
params[0] = v;
param_len = 1;
}
}
}
}
}
uint32_t packet = (i2c_cmd<<24) | (slave_addr<<16) | params[0];
xTaskNotify(i2c_task, packet, eSetValueWithOverwrite);
} else ESP_LOGW(TAG, "Wi-Fi or MQTT down");
ESP_ERROR_CHECK(esp_task_wdt_reset());
ESP_LOGV(TAG, "Task reset");
vTaskDelay(pdMS_TO_TICKS(3000));
}
ESP_LOGW(TAG, "Task exited");
}
+19 -30
View File
@@ -8,53 +8,49 @@
#include "main.hpp" #include "main.hpp"
#include "config.h" #include "config.h"
#define WIFI_TIMEOUT_TICKS pdMS_TO_TICKS(8000) #define WIFI_TIMEOUT_MS 4500
static const char* TAG = "WIFI"; 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) { 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) { if(event_base == WIFI_EVENT) {
switch (event_id) { switch(event_id) {
case WIFI_EVENT_STA_START: case WIFI_EVENT_STA_START:
ESP_LOGI(TAG, "Wi-Fi process started, connecting..."); ESP_LOGV(TAG, "Process started, connecting...");
esp_wifi_connect(); esp_wifi_connect();
break; break;
case WIFI_EVENT_STA_DISCONNECTED: { 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); 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(); esp_wifi_connect();
vTaskDelay(pdMS_TO_TICKS(2000));
break; break;
} }
default: default:
break; break;
} }
} }
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 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; ip_event_got_ip_t* event =(ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); ESP_LOGD(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
xEventGroupSetBits(connectivity_event_group, WIFI_CONNECTED_BIT);
} }
} }
static void wifi_init_sta(void) { static void wifi_init_sta(void) {
esp_err_t nvs_err = nvs_flash_init(); 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_erase());
ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(nvs_flash_init());
} }
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta(); esp_netif_create_default_wifi_sta();
@@ -77,22 +73,15 @@ static void wifi_init_sta(void) {
} }
void wifiTask(void *pvParameters) { void wifiTask(void *pvParameters) {
ESP_LOGI(TAG, "WiFi task started"); ESP_LOGV(TAG, "Task started");
wifi_init_sta(); wifi_init_sta();
ESP_ERROR_CHECK(esp_task_wdt_add(NULL)); ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
for(;;) { 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_ERROR_CHECK(esp_task_wdt_reset());
ESP_LOGI(TAG, "Wi-Fi task reset"); ESP_LOGV(TAG, "Task reset");
vTaskDelay(pdMS_TO_TICKS(WIFI_TIMEOUT_TICKS)); vTaskDelay(pdMS_TO_TICKS(WIFI_TIMEOUT_MS));
} }
ESP_LOGW(TAG, "Task exited");
} }