170 lines
4.4 KiB
C++
170 lines
4.4 KiB
C++
#include <Arduino.h>
|
|
#include <ESP8266HTTPClient.h>
|
|
#include <ESP8266WiFi.h>
|
|
#include <U8g2lib.h>
|
|
|
|
#include "PulseTimer.h"
|
|
#include "config.h"
|
|
|
|
namespace {
|
|
|
|
constexpr uint8_t kOledAddress = 0x3C;
|
|
constexpr uint8_t kPinEncoderA = 14;
|
|
constexpr uint8_t kPinEncoderB = 13;
|
|
constexpr uint8_t kPinButton = 12;
|
|
constexpr uint8_t kPinSda = 4;
|
|
constexpr uint8_t kPinScl = 5;
|
|
|
|
constexpr unsigned long kDisplayRefreshMs = 150;
|
|
constexpr unsigned long kWifiRetryMs = 10000;
|
|
constexpr unsigned long kButtonDebounceMs = 35;
|
|
constexpr unsigned long kStatusHoldMs = 2500;
|
|
|
|
uint16_t timerMs = trbc::clampTimerMs(INITIAL_TIMER_MS);
|
|
bool needsDisplayUpdate = true;
|
|
unsigned long lastDisplayRefresh = 0;
|
|
unsigned long lastWifiAttempt = 0;
|
|
unsigned long statusUntil = 0;
|
|
String statusLine = "Bereit";
|
|
|
|
uint8_t lastEncoderState = 0;
|
|
int8_t encoderAccumulator = 0;
|
|
bool lastButtonReading = HIGH;
|
|
bool stableButtonState = HIGH;
|
|
unsigned long lastButtonChange = 0;
|
|
|
|
U8G2_SSD1309_128X64_NONAME0_F_HW_I2C display(U8G2_R0, U8X8_PIN_NONE, kPinScl, kPinSda);
|
|
|
|
void setStatus(const String &text) {
|
|
statusLine = text;
|
|
statusUntil = millis() + kStatusHoldMs;
|
|
needsDisplayUpdate = true;
|
|
}
|
|
|
|
String formatTimer() {
|
|
const uint16_t seconds = timerMs / 1000;
|
|
const uint16_t tenths = (timerMs % 1000) / 100;
|
|
return String(seconds) + "." + String(tenths) + " S";
|
|
}
|
|
|
|
String wifiStatus() {
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
return String("IP ") + WiFi.localIP().toString();
|
|
}
|
|
return "WLAN verbindet";
|
|
}
|
|
|
|
void refreshDisplay() {
|
|
display.clearBuffer();
|
|
display.setFont(u8g2_font_6x10_tf);
|
|
display.drawStr(0, 10, "TASMOTA PULSE");
|
|
display.drawStr(0, 26, ("ZEIT " + formatTimer()).c_str());
|
|
display.drawStr(0, 42, wifiStatus().c_str());
|
|
display.drawStr(0, 58, (millis() < statusUntil ? statusLine : "DRUECKEN START").c_str());
|
|
display.sendBuffer();
|
|
}
|
|
|
|
void connectWifi() {
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
return;
|
|
}
|
|
if (millis() - lastWifiAttempt < kWifiRetryMs && lastWifiAttempt != 0) {
|
|
return;
|
|
}
|
|
lastWifiAttempt = millis();
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
|
needsDisplayUpdate = true;
|
|
}
|
|
|
|
bool sendTasmotaCommand(const String &command) {
|
|
if (WiFi.status() != WL_CONNECTED) {
|
|
return false;
|
|
}
|
|
|
|
WiFiClient client;
|
|
HTTPClient http;
|
|
const String url = String("http://") + TASMOTA_HOST + "/cm?cmnd=" + command;
|
|
if (!http.begin(client, url)) {
|
|
return false;
|
|
}
|
|
const int code = http.GET();
|
|
http.end();
|
|
return code >= 200 && code < 300;
|
|
}
|
|
|
|
void triggerPulse() {
|
|
setStatus("SENDE...");
|
|
const uint16_t pulseValue = trbc::tasmotaPulseTimeValue(timerMs);
|
|
const bool pulseOk = sendTasmotaCommand("PulseTime1%20" + String(pulseValue));
|
|
const bool powerOk = pulseOk && sendTasmotaCommand("Power1%20ON");
|
|
setStatus(powerOk ? "GETRIGGERT" : "FEHLER");
|
|
}
|
|
|
|
void readEncoder() {
|
|
const uint8_t state = (digitalRead(kPinEncoderA) << 1) | digitalRead(kPinEncoderB);
|
|
if (state == lastEncoderState) {
|
|
return;
|
|
}
|
|
|
|
const uint8_t transition = (lastEncoderState << 2) | state;
|
|
if (transition == 0b0001 || transition == 0b0111 || transition == 0b1110 || transition == 0b1000) {
|
|
encoderAccumulator++;
|
|
} else if (transition == 0b0010 || transition == 0b1011 || transition == 0b1101 || transition == 0b0100) {
|
|
encoderAccumulator--;
|
|
}
|
|
lastEncoderState = state;
|
|
|
|
if (encoderAccumulator >= 4 || encoderAccumulator <= -4) {
|
|
const int ticks = encoderAccumulator / 4;
|
|
encoderAccumulator = 0;
|
|
timerMs = trbc::adjustTimerMs(timerMs, ticks);
|
|
needsDisplayUpdate = true;
|
|
}
|
|
}
|
|
|
|
void readButton() {
|
|
const bool reading = digitalRead(kPinButton);
|
|
if (reading != lastButtonReading) {
|
|
lastButtonChange = millis();
|
|
lastButtonReading = reading;
|
|
}
|
|
if (millis() - lastButtonChange <= kButtonDebounceMs || reading == stableButtonState) {
|
|
return;
|
|
}
|
|
|
|
stableButtonState = reading;
|
|
if (stableButtonState == LOW) {
|
|
triggerPulse();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
pinMode(kPinEncoderA, INPUT_PULLUP);
|
|
pinMode(kPinEncoderB, INPUT_PULLUP);
|
|
pinMode(kPinButton, INPUT_PULLUP);
|
|
|
|
lastEncoderState = (digitalRead(kPinEncoderA) << 1) | digitalRead(kPinEncoderB);
|
|
|
|
display.setI2CAddress(kOledAddress << 1);
|
|
display.begin();
|
|
connectWifi();
|
|
refreshDisplay();
|
|
}
|
|
|
|
void loop() {
|
|
connectWifi();
|
|
readEncoder();
|
|
readButton();
|
|
|
|
if (needsDisplayUpdate || millis() - lastDisplayRefresh >= kDisplayRefreshMs) {
|
|
refreshDisplay();
|
|
lastDisplayRefresh = millis();
|
|
needsDisplayUpdate = false;
|
|
}
|
|
delay(2);
|
|
}
|