#include <UniversalTelegramBot.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_NeoPixel.h>
#include <WiFiClientSecure.h>
#include <TM1637Display.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <ESP32Servo.h>
#include <DHTesp.h>
#include <RTClib.h>
#include <WiFi.h>
#include <Wire.h>
#include <map>
#include <functional>
namespace IOT_06 // PINS
{
constexpr const int BUTTON_PIN = 13;
constexpr const int DHT_PIN = 32;
constexpr const int LED_PIN = 14;
constexpr const int SWITCH_PIN = 12;
constexpr const int LEDSEG_CLK_PIN = 4;
constexpr const int LEDSEG_DIO_PIN = 16;
constexpr const int SONIC_ECHO_PIN = 18;
constexpr const int SONIC_TRIG_PIN = 19;
constexpr const int LCD_SDA_PIN = 23;
constexpr const int LCD_SCL_PIN = 22;
constexpr const int LEDRING_PIN = 21;
constexpr const int SERVO_PIN = 2;
constexpr const int PHOTO_PIN = 15;
constexpr const int POTEN_PIN = 33;
constexpr const int LEDBAR_SIZE = 3;
constexpr const int LEDBAR_PINS[LEDBAR_SIZE] = {25, 26, 27};
}
namespace IOT_06 // VALUES
{
constexpr const int BUTTON_DELAY_MS = 50;
constexpr const int PHOTO_MIN_VALUE = 0;
constexpr const int PHOTO_MAX_VALUE = 4095;
constexpr const int LEDRING_OFF = 0;
constexpr const int LEDRING_ON = 2;
constexpr const int LEDRING_COUNT = 40;
constexpr const int LEDRING_MIN_BRIGHT = 0;
constexpr const int LEDRING_MAX_BRIGHT = 255;
constexpr const int SONIC_INVALID_DISTANCE = -1;
constexpr const int LED_ON = HIGH;
constexpr const int LED_OFF = LOW;
constexpr const int LCD_ADDRESS = 0x27;
constexpr const int LCD_COLUMNS = 16;
constexpr const int LCD_ROWS = 2;
constexpr const int SERVO_MIN_DEGREE = 0;
constexpr const int SERVO_MAX_DEGREE = 180;
constexpr const int POTEN_MIN_VALUE = 0;
constexpr const int POTEN_MAX_VALUE = 4095;
}
namespace IOT_06 // SERVERS
{
constexpr const int MQTT_CONNECT_TIMEOUT_MS = 1000;
constexpr const int MQTT_RECONNECT_WAIT_MS = 5000;
constexpr const int MQTT_DEFAULT_PORT = 1883;
constexpr const int BOT_DELAY_MS = 100;
constexpr const int BOT_TIMEOUT = 1000;
constexpr const char *MQTT_SSID = "Wokwi-GUEST";
constexpr const char *MQTT_PASSWORD = "";
constexpr const char *MQTT_SERVER = "mqtt.eclipseprojects.io";
constexpr const char *BOT_TOKEN = "8141850946:AAFi6xstIWzd-sDCuZP8N7sBgQ4VIxET9wg";
constexpr const char *BOT_CHATID = "5721869894";
namespace MQTT
{
constexpr const char *LIGHT_STATUS = "fish_tank/device/light/status";
constexpr const char *SERVO_STATUS = "fish_tank/device/servo/status";
constexpr const char *LED_STATUS = "fish_tank/device/led/status";
constexpr const char *SWITCH_STATUS = "fish_tank/device/switch/status";
constexpr const char *LCD_STATUS = "fish_tank/device/lcd/status";
constexpr const char *LEDSEGMENT_STATUS = "fish_tank/device/ledsegment/status";
constexpr const char *LEDRING_STATUS = "fish_tank/device/ledring/status";
constexpr const char *LEDBAR_STATUS = "fish_tank/device/ledbar/status";
constexpr const char *BUTTON_STATUS = "fish_tank/device/button/status";
constexpr const char *POTEN_STATUS = "fish_tank/device/poten/status";
}
}
namespace IOT_06 // CORES
{
using CommandType = String;
using DescriptionType = String;
using VoidFunction = std::function<void(const CommandType&, const DescriptionType&)>;
struct Command
{
String command;
String description;
VoidFunction callback;
};
using CommandMapper = std::map<String, Command>;
class FishTank
{
private: // Servers
WiFiClient wifiClient;
PubSubClient mqttClient;
WiFiClientSecure hostClient;
UniversalTelegramBot teleClient;
private: // Devices
Servo servo;
DHTesp dht;
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd;
Adafruit_NeoPixel ledring;
TM1637Display ledsegment;
private: // Internal State Variables
bool ledIsOn;
bool relayIsOn;
bool servoIsOpen;
bool lastButtonState;
unsigned long lastBotRequest;
unsigned long lastReconnected;
unsigned long lastDebounceTime;
unsigned long lastRingBrightness;
private: // Commands
CommandMapper commandMap;
public: // Cores
FishTank();
void reset();
public: // Initializers
void setup();
void setupServers();
void setupInputDevices();
void setupOutputDevices();
public: // Handlers
void loop();
void loopDevices();
void loopMQTT();
void loopTelegramBot();
void handleBotFirstCommand();
public: // Helpers
void toggleServo();
void sendMessage(const String& chatId, const String& msg, const String& publisher = "");
public: // Getters
int getDistance();
String getHelpMessage();
CommandMapper getCommandMap();
};
}
namespace IOT_06 // CORES
{
FishTank::FishTank() :
// Servers
wifiClient(),
mqttClient(wifiClient),
hostClient(),
teleClient({BOT_TOKEN, hostClient}),
// Devices
servo(),
dht(),
rtc(),
lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS),
// Leds
ledring(LEDRING_COUNT, LEDRING_PIN, NEO_GRB + NEO_KHZ800),
ledsegment(LEDSEG_CLK_PIN, LEDSEG_DIO_PIN),
// Commands
commandMap(getCommandMap())
{
reset();
}
void FishTank::reset()
{
Serial.begin(9600);
Serial.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
ledIsOn = false;
relayIsOn = false;
servoIsOpen = false;
lastButtonState = false;
lastBotRequest = 0;
lastReconnected = 0;
lastDebounceTime = 0;
lastRingBrightness = 0;
}
}
namespace IOT_06 // INITIALIZERS
{
void FishTank::setup()
{
setupServers();
setupInputDevices();
setupOutputDevices();
Serial.println("Everything is setup successfully");
}
void FishTank::setupServers()
{
hostClient.setCACert(TELEGRAM_CERTIFICATE_ROOT);
mqttClient.setServer(MQTT_SERVER, MQTT_DEFAULT_PORT);
mqttClient.setCallback([this](char* topic, byte* payload, unsigned int length) {
String message;
for (unsigned int i = 0; i < length; i++) {
message += static_cast<char>(payload[i]);
}
Serial.printf("MQTT Message received on topic %s: %s\n", topic, message.c_str());
});
WiFi.mode(WIFI_STA);
WiFi.begin(MQTT_SSID, MQTT_PASSWORD);
while (WiFi.status() != static_cast<int>(WL_CONNECTED))
{
delay(MQTT_CONNECT_TIMEOUT_MS);
Serial.println("Connecting to WiFi..");
}
Serial.println("\nConnected to WiFi.");
Serial.println(WiFi.localIP());
}
void FishTank::setupInputDevices()
{
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.println("Button setup successfully.");
pinMode(SWITCH_PIN, INPUT);
digitalWrite(SWITCH_PIN, LOW);
Serial.println("Switch setup successfully.");
pinMode(PHOTO_PIN, INPUT);
Serial.println("Photoresistor setup successfully.");
pinMode(SONIC_TRIG_PIN, OUTPUT);
pinMode(SONIC_ECHO_PIN, INPUT);
Serial.println("Ultra Sonic Sensor setup successfully.");
dht.setup(DHT_PIN, DHTesp::DHT22);
Serial.println("DHT Sensor setup successfully.");
}
void FishTank::setupOutputDevices()
{
Wire.begin(LCD_SDA_PIN, LCD_SCL_PIN);
delay(100);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("LCD Initialized");
lcd.setCursor(0, 1);
lcd.print("Use \"/start\"");
Serial.println("LCD setup successfully.");
servo.attach(SERVO_PIN);
servo.write(SERVO_MIN_DEGREE);
Serial.println("Servo initialized to CLOSED position.");
ledsegment.setBrightness(0x0f);
ledsegment.showNumberDec(0, false);
Serial.println("LedSegment setup successfully.");
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (-1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, setting the time!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
Serial.println("RTC setup successfully.");
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Serial.println("Led setup successfully.");
for (int i = 0; i < LEDBAR_SIZE; i++) {
pinMode(LEDBAR_PINS[i], OUTPUT);
digitalWrite(LEDBAR_PINS[i], LOW);
}
Serial.println("LedBar setup successfully.");
pinMode(LEDRING_PIN, OUTPUT);
ledring.begin();
ledring.setBrightness(LEDRING_MIN_BRIGHT);
ledring.show();
Serial.println("LedRing setup successfully.");
}
}
namespace IOT_06 // HANDLERS
{
void FishTank::loop()
{
Serial.println("[" + String(millis()) + "] Starting a new loop");
loopDevices();
loopMQTT();
loopTelegramBot();
}
void FishTank::loopDevices()
{
int currentButtonState = digitalRead(BUTTON_PIN);
if (currentButtonState != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > BUTTON_DELAY_MS) {
if (lastButtonState != currentButtonState) {
lastButtonState = currentButtonState;
toggleServo();
sendMessage(String(BOT_CHATID), "Button pressed, toggling servo.");
}
}
lastButtonState = currentButtonState;
if (digitalRead(SWITCH_PIN) == HIGH) {
setup();
return ;
}
}
void FishTank::loopMQTT()
{
if (mqttClient.connected()) {
mqttClient.loop();
return;
}
unsigned long currentMillis = millis();
if (currentMillis - lastReconnected >= MQTT_RECONNECT_WAIT_MS) {
lastReconnected = currentMillis;
String clientId = "ESP32Client-" + String(random(0xffff), HEX);
Serial.println("Attempting MQTT connection...");
if (mqttClient.connect(clientId.c_str())) {
Serial.println("Successfully connected to MQTT server");
mqttClient.subscribe(MQTT::SWITCH_STATUS);
Serial.println("Send help messages for the user");
teleClient.sendMessage(BOT_CHATID, "For help, please use command /start", "");
} else {
Serial.print("Failed to connect to MQTT, rc=");
Serial.print(mqttClient.state());
Serial.println(" Will retry in 5 seconds");
}
}
mqttClient.loop();
}
void FishTank::loopTelegramBot()
{
unsigned long currentMillis = millis();
if (currentMillis - lastBotRequest >= BOT_DELAY_MS)
{
lastBotRequest = currentMillis;
int numNewMessages = teleClient.getUpdates(teleClient.last_message_received + 1);
unsigned long startTime = millis();
while (numNewMessages > 0 && (millis() - startTime) < BOT_TIMEOUT)
{
handleBotFirstCommand();
numNewMessages = teleClient.getUpdates(teleClient.last_message_received + 1);
}
}
}
void FishTank::handleBotFirstCommand()
{
if (teleClient.messages.size() == 0) return;
const telegramMessage &botMessage = teleClient.messages[0];
const String chatId = String(botMessage.chat_id);
const String message = botMessage.text;
const String username = botMessage.from_name;
if (BOT_CHATID != botMessage.chat_id) {
Serial.println("Unauthorized access attempt by user: " + username);
return ;
}
int firstSpace = message.indexOf(' ');
String command = (firstSpace == -1) ? message : message.substring(0, firstSpace);
String params = (firstSpace == -1) ? "" : message.substring(firstSpace + 1);
auto it = commandMap.find(command);
if (it != commandMap.end()) {
String msg = "Bot read command [" + command + "] with param=[" + params + "]";
sendMessage(chatId, msg, "");
it->second.callback(chatId, params);
}
else {
sendMessage(chatId, "Command not found", "");
}
}
}
namespace IOT_06 // HELPERS
{
void FishTank::toggleServo()
{
if (servoIsOpen) {
servoIsOpen = false;
for (int pos = SERVO_MAX_DEGREE; pos >= SERVO_MIN_DEGREE; --pos) {
servo.write(pos);
delay(10);
}
}
else {
servoIsOpen = true;
for (int pos = SERVO_MIN_DEGREE; pos <= SERVO_MAX_DEGREE; ++pos) {
servo.write(pos);
delay(10);
}
}
}
void FishTank::sendMessage(const String& chatId, const String& msg, const String& publisher)
{
if (!publisher.isEmpty()) {
mqttClient.publish(publisher.c_str(), msg.c_str());
}
teleClient.sendMessage(chatId, msg.c_str(), "");
Serial.println(msg.c_str());
}
}
namespace IOT_06 // GETTERS
{
int FishTank::getDistance()
{
digitalWrite(SONIC_TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(SONIC_TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(SONIC_TRIG_PIN, LOW);
long duration = pulseIn(SONIC_ECHO_PIN, HIGH, 30000);
if (duration <= 0) {
return SONIC_INVALID_DISTANCE;
}
return duration * 0.034 / 2;
}
CommandMapper FishTank::getCommandMap()
{
const Command commands[] = {
{"/relay_on", "Turn Relay ON", [this](const String &chatId, const String ¶ms) {
relayIsOn = true;
digitalWrite(LED_PIN, HIGH);
sendMessage(chatId, "Relay turned ON.", MQTT::LIGHT_STATUS);
}},
{"/relay_off", "Turn Relay OFF", [this](const String &chatId, const String ¶ms) {
relayIsOn = false;
digitalWrite(LED_PIN, LOW);
sendMessage(chatId, "Relay turned OFF.", MQTT::LIGHT_STATUS);
}},
{"/relay_toggle", "Toggle Relay State", [this](const String &chatId, const String ¶ms) {
relayIsOn ^= 1;
String stateStr = relayIsOn ? "ON" : "OFF";
sendMessage(chatId, "Relay toggled to " + stateStr + ".", MQTT::LIGHT_STATUS);
}},
{"/relay_state", "Get Relay Status", [this](const String &chatId, const String ¶ms) {
bool isOn = digitalRead(LED_PIN) == HIGH;
String stateStr = isOn ? "Relay is ON." : "Relay is OFF.";
sendMessage(chatId, stateStr, MQTT::LIGHT_STATUS);
}},
{"/led_on", "Turn LED ON", [this](const String &chatId, const String ¶ms) {
digitalWrite(LED_PIN, HIGH);
ledIsOn = true;
sendMessage(chatId, "LED turned ON.", MQTT::LED_STATUS);
}},
{"/led_off", "Turn LED OFF", [this](const String &chatId, const String ¶ms) {
digitalWrite(LED_PIN, LOW);
ledIsOn = false;
sendMessage(chatId, "LED turned OFF.", MQTT::LED_STATUS);
}},
{"/led_toggle", "Toggle LED State", [this](const String &chatId, const String ¶ms) {
bool currentLEDState = digitalRead(LED_PIN) == HIGH;
digitalWrite(LED_PIN, !currentLEDState ? HIGH : LOW);
ledIsOn = !currentLEDState;
String stateStr = ledIsOn ? "ON" : "OFF";
sendMessage(chatId, "LED toggled to " + stateStr + ".", MQTT::LED_STATUS);
}},
{"/led_status", "Get LED Status", [this](const String &chatId, const String ¶ms) {
bool isOn = digitalRead(LED_PIN) == HIGH;
String stateStr = isOn ? "LED is ON." : "LED is OFF.";
sendMessage(chatId, stateStr, MQTT::LED_STATUS);
}},
{"/ledring_poten", "Adjust LED Ring Brightness based on Potentiometer", [this](const String &chatId, const String ¶ms) {
int potValue = analogRead(POTEN_PIN);
lastRingBrightness = map(potValue, POTEN_MIN_VALUE, POTEN_MAX_VALUE, LEDRING_MIN_BRIGHT, LEDRING_MAX_BRIGHT);
ledring.setBrightness(lastRingBrightness);
ledring.show();
String msg = "LED Ring brightness adjusted based on potentiometer: " + String(lastRingBrightness);
sendMessage(chatId, msg, MQTT::LEDRING_STATUS);
}},
{"/ledring_on", "Turn LED Ring ON", [this](const String &chatId, const String ¶ms) {
lastRingBrightness = LEDRING_MAX_BRIGHT;
ledring.setBrightness(lastRingBrightness);
ledring.show();
sendMessage(chatId, "LED Ring turned ON.", MQTT::LEDRING_STATUS);
}},
{"/ledring_off", "Turn LED Ring OFF", [this](const String &chatId, const String ¶ms) {
lastRingBrightness = LEDRING_MIN_BRIGHT;
ledring.setBrightness(lastRingBrightness);
ledring.show();
sendMessage(chatId, "LED Ring turned OFF.", MQTT::LEDRING_STATUS);
}},
{"/ledring_brightness", "Set LED Ring Brightness (0-255)", [this](const String &chatId, const String ¶ms) {
int brightness = constrain(params.toInt(), LEDRING_MIN_BRIGHT, LEDRING_MAX_BRIGHT);
lastRingBrightness = brightness;
ledring.setBrightness(lastRingBrightness);
ledring.show();
sendMessage(chatId, "LED Ring brightness set to " + String(lastRingBrightness) + ".", MQTT::LEDRING_STATUS);
}},
{"/ledring_status", "Get LED Ring Brightness", [this](const String &chatId, const String ¶ms) {
String statusStr = "LED Ring brightness level: " + String(lastRingBrightness);
sendMessage(chatId, statusStr, MQTT::LEDRING_STATUS);
}},
{"/servo_open", "Open Servo", [this](const String &chatId, const String ¶ms) {
if (servoIsOpen) {
sendMessage(chatId, "Servo is already OPENED.", MQTT::SERVO_STATUS);
}
else {
toggleServo();
sendMessage(chatId, "Servo opened.", MQTT::SERVO_STATUS);
}
}},
{"/servo_close", "Close Servo", [this](const String &chatId, const String ¶ms) {
if (!servoIsOpen) {
sendMessage(chatId, "Servo is already CLOSED.", MQTT::SERVO_STATUS);
}
else {
toggleServo();
sendMessage(chatId, "Servo closed.", MQTT::SERVO_STATUS);
}
}},
{"/servo_toggle", "Toggle Servo", [this](const String &chatId, const String ¶ms) {
String statusStr = servoIsOpen ? "OPEN" : "CLOSED";
toggleServo();
sendMessage(chatId, "Servo toggled to " + statusStr + ".", MQTT::SERVO_STATUS);
}},
{"/servo_status", "Get Servo Status", [this](const String &chatId, const String ¶ms) {
String statusStr = servoIsOpen ? "Servo is OPEN." : "Servo is CLOSED.";
sendMessage(chatId, statusStr, MQTT::SERVO_STATUS);
}},
{"/lcd_show", "<msg> Display Message on LCD", [this](const String &chatId, const String ¶ms) {
lcd.clear();
if (params.length() > LCD_COLUMNS) {
String firstLine = params.substring(0, LCD_COLUMNS);
String secondLine = params.substring(LCD_COLUMNS);
lcd.setCursor(0, 0);
lcd.print(firstLine);
lcd.setCursor(0, 1);
lcd.print(secondLine);
} else {
lcd.setCursor(0, 0);
lcd.print(params);
}
sendMessage(chatId, "Displayed message on LCD", MQTT::LCD_STATUS);
}},
{"/lcd_show_1", "<msg> Display Message on LCD row 1", [this](const String &chatId, const String ¶ms) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(params);
sendMessage(chatId, "Displayed message on LCD row 1", MQTT::LCD_STATUS);
}},
{"/lcd_show_2", "<msg> Display Message on LCD row 2", [this](const String &chatId, const String ¶ms) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(params);
sendMessage(chatId, "Displayed message on LCD row 2", MQTT::LCD_STATUS);
}},
{"/lcd_clear", "Clear LCD Display", [this](const String &chatId, const String ¶ms) {
lcd.clear();
sendMessage(chatId, "LCD cleared.", MQTT::LCD_STATUS);
}},
{"/lcd_rtc", "Display Current Time on LCD", [this](const String &chatId, const String ¶ms) {
DateTime now = rtc.now();
char dateBuffer[20];
char timeBuffer[20];
lcd.clear();
snprintf(dateBuffer, sizeof(dateBuffer), "DATE=%02d/%02d/%04d", now.day(), now.month(), now.year());
lcd.setCursor(0, 0);
lcd.print(dateBuffer);
snprintf(timeBuffer, sizeof(timeBuffer), "TIME=%02d:%02d:%02d.00", now.hour(), now.minute(), now.second());
lcd.setCursor(0, 1);
lcd.print(timeBuffer);
String mqttMessage = String(dateBuffer) + " " + String(timeBuffer);
sendMessage(chatId, "Displayed RTC date and time on LCD:\n" + String(dateBuffer) + "\n" + String(timeBuffer), MQTT::LCD_STATUS);
}},
{"/lcd_humid", "Display Current Humidity on LCD", [this](const String &chatId, const String ¶ms) {
TempAndHumidity data = dht.getTempAndHumidity();
char humidBuffer[20];
lcd.clear();
snprintf(humidBuffer, sizeof(humidBuffer), "Humidity: %.1f%%", data.humidity);
lcd.setCursor(0, 0);
lcd.print(humidBuffer);
snprintf(humidBuffer, sizeof(humidBuffer), "=%.1f%%", data.humidity);
lcd.setCursor(0, 1);
lcd.print(humidBuffer);
String mqttMessage = String("Humidity: ") + String(data.humidity, 1) + "%";
sendMessage(chatId, "Displayed humidity on LCD: " + String(humidBuffer), MQTT::LCD_STATUS);
}},
{"/lcd_temp", "Display Current Temperature on LCD", [this](const String &chatId, const String ¶ms) {
TempAndHumidity data = dht.getTempAndHumidity();
float tempC = data.temperature;
float tempF = tempC * 9.0 / 5.0 + 32.0;
char tempBuffer[20];
lcd.clear();
snprintf(tempBuffer, sizeof(tempBuffer), "Temp: %.2f C", tempC);
lcd.setCursor(0, 0);
lcd.print(tempBuffer);
snprintf(tempBuffer, sizeof(tempBuffer), "Temp: %.2f F", tempF);
lcd.setCursor(0, 1);
lcd.print(tempBuffer);
String mqttMessage = String("Temperature: ") + String(tempC, 2) + " C / " + String(tempF, 2) + " F";
sendMessage(chatId, "Displayed temperature on LCD: " + String(tempBuffer), MQTT::LCD_STATUS);
}},
{"/lcd_display", "Display Digits on LCD", [this](const String &chatId, const String ¶ms) {
String digits = params;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Digits: " + digits);
sendMessage(chatId, "Displayed digits on LCD: " + digits, MQTT::LCD_STATUS);
}},
{"/lcd_photo", "Display Photoresistor Lux Level on LCD", [this](const String &chatId, const String ¶ms) {
int lux = analogRead(PHOTO_PIN);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Photo Lux:");
lcd.setCursor(0, 1);
lcd.print(String(lux) + " lux");
sendMessage(chatId, "Displayed Photoresistor lux level on LCD: " + String(lux) + " lx", MQTT::LCD_STATUS);
}},
{"/lcd_distance", "Display Ultrasonic Distance on LCD", [this](const String &chatId, const String ¶ms) {
int distance = getDistance();
if(distance != SONIC_INVALID_DISTANCE){
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Distance:");
lcd.setCursor(0, 1);
lcd.print(String(distance) + " cm");
sendMessage(chatId, "Displayed Ultrasonic distance on LCD: " + String(distance) + " cm", MQTT::LCD_STATUS);
}
else{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Distance Error");
sendMessage(chatId, "Error reading distance.", MQTT::LCD_STATUS);
}
}},
{"/lcd_poten", "Display Potentiometer Value on LCD", [this](const String &chatId, const String ¶ms) {
int potValue = analogRead(POTEN_PIN);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Potentiometer:");
lcd.setCursor(0, 1);
lcd.print(String(potValue));
sendMessage(chatId, "Displayed Potentiometer value on LCD: " + String(potValue), MQTT::LCD_STATUS);
}},
{"/display_number", "Display Number on 7-Segment", [this](const String &chatId, const String ¶ms) {
int number = params.toInt();
if (number < 0 || number > 9999) {
sendMessage(chatId, "Invalid number for 7-Segment Display: " + String(number), MQTT::LEDSEGMENT_STATUS);
return;
}
ledsegment.showNumberDec(number, false);
sendMessage(chatId, "Displayed number on 7-Segment Display: " + String(number), MQTT::LEDSEGMENT_STATUS);
}},
{"/display_clear", "Clear 7-Segment Display", [this](const String &chatId, const String ¶ms) {
ledsegment.clear();
sendMessage(chatId, "7-Segment Display cleared.", MQTT::LEDSEGMENT_STATUS);
}},
{"/ledbar_set", "Set LED Bar Graph (1-" + String(LEDBAR_SIZE) + ")", [this](const String &chatId, const String ¶ms) {
int value = constrain(params.toInt(), 1, LEDBAR_SIZE);
for (int id = 0; id < LEDBAR_SIZE; id++) {
digitalWrite(LEDBAR_PINS[id], (id < value) ? HIGH : LOW);
}
sendMessage(chatId, "LED Bar Graph set to " + String(value) + " segments.", MQTT::LEDBAR_STATUS);
}},
{"/ledbar_photo", "Set LED Bar Graph based on Photoresistor", [this](const String &chatId, const String ¶ms) {
int lightLevel = analogRead(PHOTO_PIN);
int barCount = map(lightLevel, PHOTO_MIN_VALUE, PHOTO_MAX_VALUE, 0, LEDBAR_SIZE);
for (int id = 0; id < LEDBAR_SIZE; id++) {
digitalWrite(LEDBAR_PINS[id], (id < barCount) ? HIGH : LOW);
}
sendMessage(chatId, "LED Bar Graph updated based on photoresistor: " + String(barCount) + " segments.", MQTT::LEDBAR_STATUS);
}},
{"/ledbar_full", "Set all LED Bar Graph", [this](const String &chatId, const String ¶ms) {
for (int id = 0; id < LEDBAR_SIZE; id++) {
digitalWrite(LEDBAR_PINS[id], HIGH);
}
sendMessage(chatId, "LED Bar Graph set to full.", MQTT::LEDBAR_STATUS);
}},
{"/ledbar_clear", "Clear LED Bar Graph", [this](const String &chatId, const String ¶ms) {
for (int id = 0; id < LEDBAR_SIZE; id++) {
digitalWrite(LEDBAR_PINS[id], LOW);
}
sendMessage(chatId, "LED Bar Graph cleared.", MQTT::LEDBAR_STATUS);
}},
{"/distance_read", "Get Ultrasonic Sensor Distance", [this](const String &chatId, const String ¶ms) {
int distance = getDistance();
if(distance != SONIC_INVALID_DISTANCE){
sendMessage(chatId, "Current Distance: " + String(distance) + " cm.", MQTT::SWITCH_STATUS);
}
else{
sendMessage(chatId, "Error reading distance.", MQTT::SWITCH_STATUS);
}
}},
{"/sensor_read", "Get Temperature and Humidity", [this](const String &chatId, const String ¶ms) {
TempAndHumidity data = dht.getTempAndHumidity();
int potValue = analogRead(POTEN_PIN);
float phValue = float(potValue) / POTEN_MAX_VALUE * 14.0;
String sensorData = "Temperature: " + String(data.temperature, 2) + " C\n";
sensorData += "Humidity: " + String(data.humidity, 1) + " %\n";
sensorData += "pH Level: " + String(phValue, 2);
sendMessage(chatId, "Sensor Read:\n" + sensorData, MQTT::LIGHT_STATUS);
}},
{"/rtc_date", "Get Current RTC Date", [this](const String &chatId, const String ¶ms) {
DateTime now = rtc.now();
String timeStr = "Current RTC Date: ";
timeStr += String(now.year()) + "-" + String(now.month()) + "-" + String(now.day()) + " ";
sendMessage(chatId, timeStr, MQTT::LCD_STATUS);
}},
{"/rtc_time", "Get Current RTC Time", [this](const String &chatId, const String ¶ms) {
DateTime now = rtc.now();
String timeStr = "Current RTC Time: ";
timeStr += String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second());
sendMessage(chatId, timeStr, MQTT::LCD_STATUS);
}},
{"/rtc_datetime", "Get Current RTC DateTime", [this](const String &chatId, const String ¶ms) {
DateTime now = rtc.now();
String timeStr = "Current RTC DateTime: ";
timeStr += String(now.year()) + "-" + String(now.month()) + "-" + String(now.day()) + " ";
timeStr += String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second());
sendMessage(chatId, timeStr, MQTT::LCD_STATUS);
}},
{"/switch_toggle", "Reset the setup process", [this](const String &chatId, const String ¶ms) {
reset();
setup();
sendMessage(chatId, "Reset ESP32", MQTT::SWITCH_STATUS);
}},
{"/button_press", "Toggle Servo", [this](const String &chatId, const String ¶ms) {
toggleServo();
String statusStr = servoIsOpen ? "OPEN" : "CLOSED";
sendMessage(chatId, "Servo toggled to " + statusStr + " via /button_press.", MQTT::SERVO_STATUS);
}},
{"/test_servo", "Test Servo Movement", [this](const String &chatId, const String ¶ms) {
sendMessage(chatId, "Starting servo test.", MQTT::SERVO_STATUS);
for (int pos = 0; pos <= 180; pos += 1) {
servo.write(pos);
delay(10);
}
for (int pos = 180; pos >= 0; pos -= 1) {
servo.write(pos);
delay(10);
}
sendMessage(chatId, "Servo test completed.", MQTT::SERVO_STATUS);
}},
};
CommandMapper cmdMap;
for (const auto& cmd : commands)
{
cmdMap[cmd.command] = cmd;
}
cmdMap["/start"] = Command{
"/start", "Display help", [this](const String &chatId, const String ¶ms) {
String welcome = "Available Commands:\n\n";
for (const auto& pair : commandMap) {
if (pair.first != "/start") {
welcome += pair.first + " -> " + pair.second.description + "\n";
}
}
teleClient.sendMessage(chatId, welcome.c_str(), "");
mqttClient.publish(MQTT::LCD_STATUS, "Displayed help message");
}
};
return cmdMap;
}
}
IOT_06::FishTank iotFishTank;
void setup()
{
iotFishTank.setup();
}
void loop()
{
iotFishTank.loop();
}