Хочу представить вашему вниманию контроллер управления насосами в зависимости от датчиков влажности.
Программа написана на C++ с использованием фреймворка Arduino.
Но никаких дополнительных библиотек типа Thread для реализации кода без блокировок(delay).
Динамическое добавление насосов в класс контроллера. Для добавления насоса в контроллер надо добавить строку:
pumpController.addPump(SoilSensor(A0, 800, 100000), 2, 2000); // Насос 1
где указывается пин датчика влажности, порог сухости, интервал проверки датчика, пин насоса, длительность работы насоса. но это можно и в конструкторе классов подсмотреть.
Важно отметить:
Программа использует объектно-ориентированное программирование с классами SoilSensor, PumpController, ProcessStats и Pump, используются классы, конструкторы и другие объектно-ориентированные возможности.
Программа предназначена для выполнения на микроконтроллерах Arduino или совместимых платформах.
-
Arduino-специфичные элементы:
-
Функции
pinMode(),digitalWrite(),analogRead() -
Константы
HIGH,LOW -
Функции времени
millis(),micros() -
Объект
Serialдля работы с последовательным портом -
Стандартные функции Arduino
setup()иloop()
-
// Инвертированные значения HIGH и LOW const int MYHIGH = LOW; const int MYLOW = HIGH; // Буфер для форматированной строки char buffer[100]; // Структура для статистики выполнения struct ProcessStats { unsigned long callCount = 0; unsigned long totalTime = 0; unsigned long minTime = 0xFFFFFFFF; // Максимальное значение unsigned long unsigned long maxTime = 0; void update(unsigned long executionTime) { callCount++; totalTime += executionTime; if (executionTime < minTime) minTime = executionTime; if (executionTime > maxTime) maxTime = executionTime; } unsigned long getAverageTime() const { return callCount > 0 ? totalTime / callCount : 0; } String getStatsString() const { return "(" + String(callCount) + ") " + String(minTime) + "/" + String(maxTime) + "/" + String(getAverageTime()); } }; // Глобальная статистика для всех насосов ProcessStats globalProcessStats; // Класс для датчика влажности class SoilSensor { private: int soilPin; // Пин датчика влажности int dryThreshold; // Порог сухости unsigned long checkInterval; // Интервал проверки unsigned long lastCheckTime; // Время последней проверки public: // Конструктор SoilSensor(int soilPin, int dryThreshold, unsigned long checkInterval) : soilPin(soilPin), dryThreshold(dryThreshold), checkInterval(checkInterval), lastCheckTime(0) {} // Метод для проверки, пришло ли время для следующей проверки bool isCheckTime() { return millis() - lastCheckTime >= checkInterval; } // Метод для чтения влажности int readMoisture() { // Обновляем время последней проверки lastCheckTime = millis(); return analogRead(soilPin); } // Метод для проверки необходимости полива bool needsWatering(int moisture) { return moisture >= dryThreshold; } // Метод для получения информации о датчике String getInfo(int moisture, int getPumpPin) { return String(millis() /1000) + " сек. - Датчик " + String(soilPin) + "(p="+ String(getPumpPin)+"): влажность=" + String(moisture) + "(" + String(dryThreshold) + ")" + String(moisture - dryThreshold) + " | " + globalProcessStats.getStatsString(); } // Геттеры int getSoilPin() const { return soilPin; } int getDryThreshold() const { return dryThreshold; } unsigned long getCheckInterval() const { return checkInterval; } unsigned long getLastCheckTime() const { return lastCheckTime; } }; // Класс для управления насосом class Pump { private: SoilSensor sensor; // Объект датчика (копия) int pumpPin; // Пин реле насоса unsigned long pumpDuration; // Длительность работы насоса bool isPumping; // Флаг активности насоса unsigned long pumpStartTime; // Время начала работы насоса public: // Конструктор принимает временный объект SoilSensor Pump(SoilSensor sensor, int pumpPin, unsigned long pumpDuration) : sensor(sensor), pumpPin(pumpPin), pumpDuration(pumpDuration), isPumping(false), pumpStartTime(0) {} // Метод для настройки пина насоса void setupPin() { pinMode(pumpPin, OUTPUT); } // Метод для проверки, истекло ли время работы насоса bool isPumpTimeExpired() { return millis() - pumpStartTime >= pumpDuration; } // Метод для включения насоса void startPumping() { digitalWrite(pumpPin, MYHIGH); isPumping = true; pumpStartTime = millis(); Serial.println("Насос на пине " + String(pumpPin) + " включен s=" + String(getSensor().getSoilPin()) + ""); } // Метод для выключения насоса void stopPumping() { digitalWrite(pumpPin, MYLOW); isPumping = false; Serial.println("Насос на пине " + String(pumpPin) + " выключен"); } // Основной метод обработки насоса void process() { unsigned long startTime = micros(); // Засекаем время начала // Проверяем, не активен ли насос в данный момент if (isPumping) { // Если насос работает, проверяем, не истекло ли время работы if (isPumpTimeExpired()) { stopPumping(); } unsigned long endTime = micros(); // Засекаем время окончания globalProcessStats.update(endTime - startTime); // Обновляем статистику return; // Прерываем выполнение, если насос активен } // Проверяем, пришло ли время для следующей проверки датчика if (sensor.isCheckTime()) { int moisture = sensor.readMoisture(); // Выводим информацию в монитор порта Serial.println(sensor.getInfo(moisture, getPumpPin())); // Проверяем, слишком ли сухая почва if (sensor.needsWatering(moisture)) { startPumping(); } } unsigned long endTime = micros(); // Засекаем время окончания globalProcessStats.update(endTime - startTime); // Обновляем статистику } // Геттеры bool getIsPumping() const { return isPumping; } unsigned long getPumpStartTime() const { return pumpStartTime; } unsigned long getPumpDuration() const { return pumpDuration; } SoilSensor getSensor() const { return sensor; } int getPumpPin() const { return pumpPin; } }; // Класс контроллера насосов class PumpController { private: Pump** pumps; // Массив указателей на насосы int pumpCount; // Количество насосов int maxPumps; // Максимальное количество насосов public: // Конструктор PumpController(int maxPumps = 10) : pumpCount(0), maxPumps(maxPumps) { pumps = new Pump*[maxPumps]; for (int i = 0; i < maxPumps; i++) { pumps[i] = nullptr; } } // Деструктор для очистки памяти ~PumpController() { for (int i = 0; i < pumpCount; i++) { delete pumps[i]; } delete[] pumps; } // Метод для добавления насоса bool addPump(SoilSensor sensor, int pumpPin, unsigned long pumpDuration) { if (pumpCount >= maxPumps) { Serial.println("Ошибка: достигнуто максимальное количество насосов"); return false; } pumps[pumpCount] = new Pump(sensor, pumpPin, pumpDuration); pumpCount++; return true; } // Метод инициализации для раздела setup void setup() { for (int i = 0; i < pumpCount; i++) { pumps[i]->setupPin(); pumps[i]->stopPumping(); } Serial.println("Система автоматического полива инициализирована"); Serial.println("Количество насосов: " + String(pumpCount)); } // Метод process для loop void process() { for (int i = 0; i < pumpCount; i++) { pumps[i]->process(); } } // Геттеры int getPumpCount() const { return pumpCount; } int getMaxPumps() const { return maxPumps; } }; // Создание контроллера насосов PumpController pumpController(10); // Максимум 10 насосов void setup() { // Инициализация последовательного порта Serial.begin(9600); // Добавление насосов в контроллер pumpController.addPump(SoilSensor(A0, 800, 100000), 2, 2000); // Насос 1 pumpController.addPump(SoilSensor(A1, 275, 16000), 3, 2000); // Насос 2 pumpController.addPump(SoilSensor(A2, 600, 700000), 4, 2000); // Насос 3 pumpController.addPump(SoilSensor(A3, 700, 7000000), 5, 2000); // Насос 4 // Инициализация контроллера pumpController.setup(); } void loop() { // Обработка всех насосов через контроллер pumpController.process(); }
Спасибо за внимание! Ожидаю комментарии и рекомендации.
ссылка на оригинал статьи https://habr.com/ru/articles/943302/
Добавить комментарий