First version - rotary not working.

This commit is contained in:
Holger Weber
2026-05-15 00:34:46 +02:00
commit 088eb07ff3
11 changed files with 321 additions and 0 deletions

24
src/PulseTimer.cpp Normal file
View File

@@ -0,0 +1,24 @@
#include "PulseTimer.h"
namespace trbc {
uint16_t clampTimerMs(int valueMs) {
if (valueMs < kMinTimerMs) {
return kMinTimerMs;
}
if (valueMs > kMaxTimerMs) {
return kMaxTimerMs;
}
return static_cast<uint16_t>(valueMs);
}
uint16_t adjustTimerMs(uint16_t currentMs, int ticks) {
const int adjusted = static_cast<int>(currentMs) + ticks * kTimerStepMs;
return clampTimerMs(adjusted);
}
uint16_t tasmotaPulseTimeValue(uint16_t timerMs) {
return clampTimerMs(timerMs) / kTimerStepMs;
}
} // namespace trbc

169
src/main.cpp Normal file
View File

@@ -0,0 +1,169 @@
#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 = 16;
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);
}