Nun will ich mal mein Programm beschreiben, die Fragen, die aufgekommen sind und wie ich sie gelöst habe:
Ziel: Der Wert von mehreren Temperatur-Sensoren soll auf einer kleinen Webseite ausgegeben und in einem festen Zyklus aktualisiert werden. Die Temperatursensoren messen die Zuluft und Abluft unserer Lünftungsanlage jeweils vor dem Wärmetauscher. Der Wärmetauscher besitzt eine Bypass-Klappe, mit dem der Wärmetauscher umgangen werden kann.
Über ein kleines Regelwerk soll der ESP32 einen Schrittmotor ansteuern, der dann je nach Temperatur und Regel die Bypassklappe öffnet oder schließt. Da zu Programmstart das Programm nicht weiß, ob die Klappe geschlossen oder geöffnet ist oder der Schrittmotor gar in irgendeiner Position zwischen geschlossen und geöffnet steht, muss der Schrittmotor zu Programmstart einen Endtaster anfahren, der sich in der Position "geschlossen" befindet. Diese Position wird dann als Nullposition gesetzt.
Controller: ESP32 Wroom
Sensoren: 3 x DS18B20
Motor: N42HS3418-24B22
Motortreiber: A4988
Hilfebeiträge dazu von mir erstellt waren:
Basis-Programme:
- Zum Auslesen der Sensor-IDs habe ich den Beispiel-Sketch von ESP32 with Multiple DS18B20 Temperature Sensors | Random Nerd Tutorials verwendet. Damit werden die Sensor-IDs im Serial Monitor ausgegeben. Am besten die Sensoren durchnummerieren, mit Silber-Edding auf den Sensoren die Nummer markieren. Vorteil der digitalen Sensoren ist, dass alle parallel an einem GPIO angeschlossen und über ihre Adresse getrennt ausgelesen werden können.
- Als Web-Server habe ich den Advanced Webserver von Majenko Technologies verwendet, der in der Arduino IDE zur Verfügung steht.
- Für die Sensoren sind die Bibliotheken OneWire und DallasTemperature einzubinden.
Das Programm
Hier nun vorab der vollständige Programmcode. Danach werde ich auf die Realisierung der Startposition zu Programmstart eingehen:
/*
Copyright (c) 2015, Majenko Technologies
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* * Neither the name of Majenko Technologies nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <AccelStepper.h>
const int DIR = 27;
const int STEP = 14;
const char *ssid = "xxxxxxxxxx";
const char *password = "yyyyyyyyyyyyy";
#define motorInterfaceType 1
AccelStepper motor(motorInterfaceType, STEP, DIR);
WebServer server(80);
const int led = 13;
// Data wire is connected to GPIO15
#define ONE_WIRE_BUS 15
// Setup a oneWire instance to communicate with a OneWire device
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
DeviceAddress sensor1 = { 0x28, 0x41, 0x6B, 0x49, 0xF6, 0x4D, 0x3C, 0x1D };
DeviceAddress sensor2 = { 0x28, 0x84, 0x63, 0x49, 0xF6, 0x54, 0x3C, 0x9 };
DeviceAddress sensor3 = { 0x28, 0x86, 0x57, 0x49, 0xF6, 0xC3, 0x3C, 0x51 };
float temperatur1 = sensors.getTempC(sensor1);
float temperatur2 = sensors.getTempC(sensor2);
float temperatur3 = sensors.getTempC(sensor3);
char bypassTxt[20];
unsigned long previousMillis = 0;
const long interval = 5000;
const byte homeButton = 23;
byte hBval; // new variable
byte ButtonInit = 0;
void handleRoot() {
digitalWrite(led, 1);
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
snprintf(temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\
<title>Lueftungsklappensteuerung</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Bypassklappenstatus</h1>\
<p>Temperatur 1: %2.1f</p>\
<p>Temperatur 2: %2.1f</p>\
<p>Temperatur 3: %2.1f</p>\
<p>Bypassklappe: %12s</p>\
</body>\
</html>",
temperatur1, temperatur2, temperatur3, bypassTxt
);
server.send(200, "text/html", temp);
digitalWrite(led, 0);
}
void handleNotFound() {
digitalWrite(led, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
digitalWrite(led, 0);
}
void setup(void) {
pinMode(led, OUTPUT);
digitalWrite(led, 0);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
Serial.begin(115200);
motor.setMaxSpeed(500);
motor.setAcceleration(60);
motor.setSpeed(50);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
hBval = digitalRead(homeButton);
Serial.print("Homeschleife Button = ");
Serial.println(hBval);
while ((hBval == 0) && (ButtonInit == 0)) {
motor.move(-5);
motor.run();
hBval = digitalRead(homeButton);
Serial.print("Homeschleife Button = ");
Serial.println(hBval);
motor.setCurrentPosition(0);
}
ButtonInit = 1;
}
void loop(void) {
server.handleClient();
hBval = digitalRead(homeButton);
Serial.print("Homeschleife Button = ");
Serial.println(hBval);
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.print("Sensor 1(*C): ");
temperatur1 = sensors.getTempC(sensor1);
Serial.print(temperatur1);
Serial.print("Sensor 2(*C): ");
temperatur2 = sensors.getTempC(sensor2);
Serial.print(temperatur2);
Serial.print("Sensor 3(*C): ");
temperatur3 = sensors.getTempC(sensor3);
Serial.print(temperatur3);
Serial.println("DONE");
}
if ((temperatur2 >= 20.0) && (temperatur1 < temperatur2)) {
motor.moveTo(100);
strcpy(bypassTxt, "offen");
Serial.println("Move Motor in Position 100");
}
if ((temperatur2 >= 20.0) && (temperatur1 > temperatur2)) {
motor.moveTo(0);
strcpy(bypassTxt, "geschlossen");
Serial.println("Move Motor in Position 0");
}
if ((temperatur2 < 20.0) && (temperatur1 < temperatur2)) {
motor.moveTo(0);
strcpy(bypassTxt, "geschlossen");
Serial.println("Move Motor in Position 0");
}
Serial.println(bypassTxt);
motor.run();
}
Die Bestimmung der Nullposition
Zur Bestimmung der Nullposition verwende ich einen Taster aus einem 3D-Drucker. Die Initialisierung nehme ich in der Setup-Routine durch:
while ((hBval == 0) && (ButtonInit == 0)) {
strcpy(bypassTxt, "Initialisierung");
motor.move(-5);
motor.run();
hBval = digitalRead(homeButton);
Serial.print("Homeschleife Button = ");
Serial.println(hBval);
motor.setCurrentPosition(0);
}
ButtonInit = 1;
Wenn der Taster geschlossen ist, gibt der Befehl
hBval = digitalRead(homeButton);
den Wert 1 aus. Ist der Schalter offen, ist der Wert 0. Außerdem setze ich die Variable ButtonInit zu Beginn auf den Wert 0. Somit wird die While-Schleife im Setup durchlaufen, wenn der Motor sich nicht in der Ruheposition 0 befindet (Schalter geschlossen) und die Variable ButtonInit noch nicht auf 1 gesetzt wurde.
In der While-Schleife wird der Motor in kleinen Schritten motor.move(-5); langsam um jeweils 5 Steps im Uhrzeigersinn gedreht, bis der Taster geschlossen ist. Anders als motor.moveTo(100); dreht sich der Motor mit jedem Schleifendurchgang um weitere 5 Steps. Anschließend wird die Variable ButtonInit = 1 gesetzt. Die Initialisierung ist damit abgeschlossen.
Auslesen der Temperaturen in Intervallen
Von Tommy hatte ich den Hinweis auf millis() bekommen. Hier (Arduino millis anstatt von delay - Electric Junkie) wird die Verwendung auch gut beschrieben.