Compare commits
7 Commits
d4745997dc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
e7a0f0890f
|
|||
|
75acb4a6b2
|
|||
|
8e054ca1ec
|
|||
|
558a12340a
|
|||
|
aa977e7c89
|
|||
|
82621398b0
|
|||
|
547f9a42b4
|
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"Jason2866.esp-decoder",
|
||||
"pioarduino.pioarduino-ide",
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
========================================================
|
||||
M1730 ANALOG PANEL METER CONTROLLER — INSTRUCTION CARD
|
||||
========================================================
|
||||
(Meters and current-limiting resistors are already installed.)
|
||||
|
||||
FIRST-TIME WI-FI SETUP
|
||||
- Power on the device (USB or 5V supply).
|
||||
- On your phone/laptop, join the Wi-Fi network named "M1730".
|
||||
- A setup page opens automatically (captive portal).
|
||||
- Pick your Wi-Fi network, enter its password.
|
||||
- Optionally set a Device hostname (default: m1730).
|
||||
- Tap Save. The device joins your network and restarts.
|
||||
|
||||
OPENING THE CONFIG PAGE
|
||||
- Browse to http://m1730.local (or your chosen hostname).
|
||||
- If .local doesn't resolve, use the IP address shown on the
|
||||
serial monitor at boot: http://<IP>
|
||||
- The Info panel at the top shows the current hostname and IP.
|
||||
|
||||
CONFIGURING A METER
|
||||
- Use the "Meters" dropdown to set how many meters (1-10).
|
||||
- For each meter (shown as tabs when more than one):
|
||||
* Pin - GPIO pin the meter is wired to (already set)
|
||||
* Name - label shown in the UI and Home Assistant
|
||||
* Unit - optional (e.g. W, A, °C)
|
||||
* Min/Max - the meter's real-world range (e.g. 0-50 A)
|
||||
* Max Duty - calibration for full-scale deflection (%)
|
||||
* Output - live slider, moves the needle right away
|
||||
- Tap Save to store all settings (survives reboots).
|
||||
|
||||
CALIBRATING FULL SCALE (Max Duty)
|
||||
- Drag the Output slider to 100%.
|
||||
- Slowly raise "Max Duty" until the needle just reaches the
|
||||
end of the scale. Don't overshoot. Tap Save.
|
||||
|
||||
MOVING THE NEEDLE
|
||||
- Web UI: drag a meter's Output slider (0-100%).
|
||||
- Values sent over MQTT/Home Assistant use the real Min-Max
|
||||
range (e.g. send 25 to a 0-50 meter = half scale).
|
||||
|
||||
HTTP CONTROL (DIRECT URL)
|
||||
- Move a needle from a browser or script with a GET request:
|
||||
http://m1730.local/set?i=<meter>&v=<value>
|
||||
* i = meter index, starting at 0 (first meter = 0)
|
||||
* v = value 0-100 (percent of full scale)
|
||||
- Example: http://m1730.local/set?i=0&v=75 -> meter 0 to 75%.
|
||||
- Applies instantly, saves it, and publishes to MQTT.
|
||||
- Returns 200 OK on success, 400 on bad input.
|
||||
|
||||
HOME ASSISTANT (MQTT)
|
||||
- Open the MQTT section on the config page.
|
||||
- Enable, then enter Broker host, Port (1883), User, Pass,
|
||||
and Prefix (default: m1730). Tap Save.
|
||||
- Each meter shows up automatically in Home Assistant as a
|
||||
Number entity, grouped under one device (the hostname).
|
||||
|
||||
SECURITY (OPTIONAL PASSWORD)
|
||||
- In the Security section, turn on "Require password" and set
|
||||
a password. Username is always: admin
|
||||
- Leave the password field blank to keep the existing one.
|
||||
|
||||
RESTART
|
||||
- "Reset Device" button = simple reboot (keeps all settings).
|
||||
|
||||
FACTORY RESET (if locked out or misconfigured)
|
||||
- Power-cycle (or press EN) 5 times quickly, within a few
|
||||
seconds each, before boot completes.
|
||||
* 5x = clears settings BUT keeps meter pins + calibration
|
||||
* 10x = clears everything, including saved Wi-Fi
|
||||
- All meters sweep 0 -> full -> 0 to confirm the reset.
|
||||
- After a 10x reset, redo "First-time Wi-Fi setup" above.
|
||||
|
||||
TIPS
|
||||
- Settings are saved on the device and survive power loss.
|
||||
- If the meter reads zero unexpectedly, check the Pin number
|
||||
and that Max Duty is greater than 0.
|
||||
========================================================
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
========================================================
|
||||
M1730 ANALOG-ZEIGERINSTRUMENT-STEUERUNG — KURZANLEITUNG
|
||||
========================================================
|
||||
(Messwerke und Vorwiderstände sind bereits eingebaut.)
|
||||
|
||||
ERSTE WLAN-EINRICHTUNG
|
||||
- Gerät einschalten (USB oder 5-V-Netzteil).
|
||||
- Am Handy/Laptop das WLAN mit dem Namen "M1730" auswählen.
|
||||
- Die Einrichtungsseite öffnet sich automatisch (Captive Portal).
|
||||
- Eigenes WLAN auswählen und dessen Passwort eingeben.
|
||||
- Optional einen Gerätenamen (Hostname) festlegen (Standard: m1730).
|
||||
- Auf Speichern tippen. Das Gerät verbindet sich und startet neu.
|
||||
|
||||
KONFIGURATIONSSEITE ÖFFNEN
|
||||
- Im Browser aufrufen: http://m1730.local (oder den gewählten
|
||||
Hostnamen).
|
||||
- Falls .local nicht auflöst, die beim Start im seriellen Monitor
|
||||
angezeigte IP-Adresse verwenden: http://<IP>
|
||||
- Das Info-Feld oben zeigt aktuellen Hostnamen und IP-Adresse.
|
||||
|
||||
MESSWERK KONFIGURIEREN
|
||||
- Über das Auswahlfeld "Meters" die Anzahl der Messwerke
|
||||
festlegen (1-10).
|
||||
- Für jedes Messwerk (als Reiter dargestellt, wenn mehr als eins):
|
||||
* Pin - GPIO-Pin, an dem das Messwerk hängt (bereits gesetzt)
|
||||
* Name - Bezeichnung in der Oberfläche und in Home Assistant
|
||||
* Unit - optional (z. B. W, A, °C)
|
||||
* Min/Max - realer Messbereich (z. B. 0-50 A)
|
||||
* Max Duty - Kalibrierung für den Vollausschlag (%)
|
||||
* Output - Live-Schieberegler, bewegt den Zeiger sofort
|
||||
- Auf Speichern tippen, um alle Einstellungen zu sichern
|
||||
(bleiben nach Neustart erhalten).
|
||||
|
||||
VOLLAUSSCHLAG KALIBRIEREN (Max Duty)
|
||||
- Den Output-Schieberegler auf 100 % ziehen.
|
||||
- "Max Duty" langsam erhöhen, bis der Zeiger gerade das Ende
|
||||
der Skala erreicht. Nicht überschreiten. Speichern.
|
||||
|
||||
ZEIGER BEWEGEN
|
||||
- Weboberfläche: den Output-Schieberegler ziehen (0-100 %).
|
||||
- Über MQTT/Home Assistant gesendete Werte verwenden den realen
|
||||
Min-Max-Bereich (z. B. 25 an ein 0-50-Messwerk = halber
|
||||
Ausschlag).
|
||||
|
||||
HTTP-STEUERUNG (DIREKTE URL)
|
||||
- Einen Zeiger per GET-Anfrage aus Browser oder Skript bewegen:
|
||||
http://m1730.local/set?i=<messwerk>&v=<wert>
|
||||
* i = Index des Messwerks, beginnend bei 0 (erstes = 0)
|
||||
* v = Wert 0-100 (Prozent des Vollausschlags)
|
||||
- Beispiel: http://m1730.local/set?i=0&v=75 -> Messwerk 0 auf 75 %.
|
||||
- Wirkt sofort, wird gespeichert und an MQTT veröffentlicht.
|
||||
- Antwort: 200 OK bei Erfolg, 400 bei ungültiger Eingabe.
|
||||
|
||||
HOME ASSISTANT (MQTT)
|
||||
- Auf der Konfigurationsseite den Abschnitt MQTT öffnen.
|
||||
- Aktivieren, dann Broker-Host, Port (1883), Benutzer, Passwort
|
||||
und Präfix (Standard: m1730) eingeben. Speichern.
|
||||
- Jedes Messwerk erscheint automatisch in Home Assistant als
|
||||
Number-Entität, gruppiert unter einem Gerät (dem Hostnamen).
|
||||
|
||||
SICHERHEIT (OPTIONALES PASSWORT)
|
||||
- Im Abschnitt Security "Require password" aktivieren und ein
|
||||
Passwort setzen. Der Benutzername ist immer: admin
|
||||
- Das Passwortfeld leer lassen, um das bestehende beizubehalten.
|
||||
|
||||
NEUSTART
|
||||
- Schaltfläche "Reset Device" = einfacher Neustart (alle
|
||||
Einstellungen bleiben erhalten).
|
||||
|
||||
WERKSRESET (bei Aussperrung oder Fehlkonfiguration)
|
||||
- Gerät 5-mal schnell aus- und einschalten (oder EN drücken),
|
||||
jeweils innerhalb weniger Sekunden, bevor der Start abgeschlossen ist.
|
||||
* 5x = löscht Einstellungen, BEHÄLT aber Pins + Kalibrierung
|
||||
* 10x = löscht alles, einschließlich gespeichertem WLAN
|
||||
- Alle Messwerke fahren 0 -> Voll -> 0 zur Bestätigung des Resets.
|
||||
- Nach einem 10x-Reset die "ERSTE WLAN-EINRICHTUNG" oben
|
||||
erneut durchführen.
|
||||
|
||||
HINWEISE
|
||||
- Einstellungen werden im Gerät gespeichert und überstehen
|
||||
einen Stromausfall.
|
||||
- Zeigt ein Messwerk unerwartet null an, den Pin prüfen und
|
||||
sicherstellen, dass Max Duty größer als 0 ist.
|
||||
========================================================
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
========================================================
|
||||
CONTRÔLEUR DE GALVANOMÈTRE ANALOGIQUE M1730 — GUIDE RAPIDE
|
||||
========================================================
|
||||
(Les galvanomètres et résistances série sont déjà installés.)
|
||||
|
||||
PREMIÈRE CONFIGURATION WI-FI
|
||||
- Mettre l'appareil sous tension (USB ou alimentation 5 V).
|
||||
- Sur votre téléphone/ordinateur, rejoindre le réseau Wi-Fi
|
||||
nommé « M1730 ».
|
||||
- La page de configuration s'ouvre automatiquement (portail captif).
|
||||
- Choisir votre réseau Wi-Fi et saisir son mot de passe.
|
||||
- Éventuellement définir un nom d'hôte (Hostname) (défaut : m1730).
|
||||
- Appuyer sur Save. L'appareil rejoint le réseau et redémarre.
|
||||
|
||||
OUVRIR LA PAGE DE CONFIGURATION
|
||||
- Dans le navigateur, aller à : http://m1730.local
|
||||
(ou le nom d'hôte choisi).
|
||||
- Si .local ne fonctionne pas, utiliser l'adresse IP affichée
|
||||
au démarrage dans le moniteur série : http://<IP>
|
||||
- Le panneau Info en haut indique le nom d'hôte et l'adresse IP.
|
||||
|
||||
CONFIGURER UN GALVANOMÈTRE
|
||||
- Utiliser le menu déroulant « Meters » pour définir le nombre
|
||||
de galvanomètres (1-10).
|
||||
- Pour chaque galvanomètre (affiché en onglets s'il y en a
|
||||
plusieurs) :
|
||||
* Pin - broche GPIO câblée au galvanomètre (déjà définie)
|
||||
* Name - libellé affiché dans l'interface et Home Assistant
|
||||
* Unit - facultatif (p. ex. W, A, °C)
|
||||
* Min/Max - plage réelle de mesure (p. ex. 0-50 A)
|
||||
* Max Duty - calibrage pour la déviation pleine échelle (%)
|
||||
* Output - curseur en direct, déplace l'aiguille aussitôt
|
||||
- Appuyer sur Save pour enregistrer tous les réglages
|
||||
(conservés après redémarrage).
|
||||
|
||||
CALIBRER LA PLEINE ÉCHELLE (Max Duty)
|
||||
- Amener le curseur Output à 100 %.
|
||||
- Augmenter lentement « Max Duty » jusqu'à ce que l'aiguille
|
||||
atteigne juste le bout de l'échelle. Ne pas dépasser. Save.
|
||||
|
||||
DÉPLACER L'AIGUILLE
|
||||
- Interface web : faire glisser le curseur Output (0-100 %).
|
||||
- Les valeurs envoyées via MQTT/Home Assistant utilisent la
|
||||
plage réelle Min-Max (p. ex. envoyer 25 à un galvanomètre
|
||||
0-50 = mi-échelle).
|
||||
|
||||
CONTRÔLE HTTP (URL DIRECTE)
|
||||
- Déplacer une aiguille via une requête GET depuis un
|
||||
navigateur ou un script :
|
||||
http://m1730.local/set?i=<galvanometre>&v=<valeur>
|
||||
* i = index du galvanomètre, à partir de 0 (le premier = 0)
|
||||
* v = valeur 0-100 (pourcentage de la pleine échelle)
|
||||
- Exemple : http://m1730.local/set?i=0&v=75 -> galvanomètre 0 à 75 %.
|
||||
- Effet immédiat, enregistré et publié sur MQTT.
|
||||
- Réponse : 200 OK en cas de succès, 400 si l'entrée est invalide.
|
||||
|
||||
HOME ASSISTANT (MQTT)
|
||||
- Ouvrir la section MQTT sur la page de configuration.
|
||||
- Activer, puis saisir l'hôte du Broker, le Port (1883),
|
||||
l'utilisateur, le mot de passe et le préfixe (défaut : m1730).
|
||||
Save.
|
||||
- Chaque galvanomètre apparaît automatiquement dans Home
|
||||
Assistant comme entité Number, regroupée sous un seul
|
||||
appareil (le nom d'hôte).
|
||||
|
||||
SÉCURITÉ (MOT DE PASSE FACULTATIF)
|
||||
- Dans la section Security, activer « Require password » et
|
||||
définir un mot de passe. Le nom d'utilisateur est toujours : admin
|
||||
- Laisser le champ mot de passe vide pour conserver l'actuel.
|
||||
|
||||
REDÉMARRAGE
|
||||
- Bouton « Reset Device » = simple redémarrage (tous les
|
||||
réglages sont conservés).
|
||||
|
||||
RÉINITIALISATION D'USINE (si verrouillé ou mal configuré)
|
||||
- Éteindre/rallumer (ou appuyer sur EN) 5 fois rapidement,
|
||||
à quelques secondes d'intervalle, avant la fin du démarrage.
|
||||
* 5x = efface les réglages MAIS conserve broches + calibrage
|
||||
* 10x = efface tout, y compris le Wi-Fi enregistré
|
||||
- Tous les galvanomètres balaient 0 -> plein -> 0 pour
|
||||
confirmer la réinitialisation.
|
||||
- Après une réinitialisation 10x, refaire la « PREMIÈRE
|
||||
CONFIGURATION WI-FI » ci-dessus.
|
||||
|
||||
CONSEILS
|
||||
- Les réglages sont enregistrés dans l'appareil et survivent
|
||||
à une coupure de courant.
|
||||
- Si un galvanomètre indique zéro de façon inattendue, vérifier
|
||||
le numéro de Pin et que Max Duty est supérieur à 0.
|
||||
========================================================
|
||||
+27
-24
@@ -8,40 +8,43 @@
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[common]
|
||||
; All ESP32 targets use the pioarduino fork, which ships Arduino-ESP32 3.x
|
||||
; (the registry espressif32 platform is still on 2.0.17 and lacks C6 support).
|
||||
platform_esp32 = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.39/platform-espressif32.zip
|
||||
|
||||
[env]
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
tzapu/WiFiManager@^2.0.17
|
||||
bblanchon/ArduinoJson@^7.4.3
|
||||
knolleary/PubSubClient@^2.8
|
||||
|
||||
[env:esp32-s3-devkitc-1]
|
||||
platform = espressif32@^6.10.0
|
||||
platform = ${common.platform_esp32}
|
||||
board = esp32-s3-devkitc-1
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
tzapu/WiFiManager@^2.0.17
|
||||
bblanchon/ArduinoJson@^7.0.0
|
||||
knolleary/PubSubClient@^2.8
|
||||
|
||||
[env:esp32dev]
|
||||
platform = ${common.platform_esp32}
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
platform = espressif32@^6.10.0
|
||||
monitor_speed = 115200
|
||||
board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
tzapu/WiFiManager@^2.0.17
|
||||
bblanchon/ArduinoJson@^7.4.3
|
||||
knolleary/PubSubClient@^2.8
|
||||
|
||||
[env:esp32-C3-devkitm-1]
|
||||
platform = ${common.platform_esp32}
|
||||
board = esp32-c3-devkitm-1
|
||||
framework = arduino
|
||||
platform = espressif32@^6.10.0
|
||||
monitor_speed = 115200
|
||||
board_build.filesystem = littlefs
|
||||
build_flags =
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
lib_deps =
|
||||
tzapu/WiFiManager@^2.0.17
|
||||
bblanchon/ArduinoJson@^7.4.3
|
||||
knolleary/PubSubClient@^2.8
|
||||
|
||||
[env:esp32-c6-devkitc-1]
|
||||
platform = ${common.platform_esp32}
|
||||
board = esp32-c6-devkitc-1
|
||||
board_build.filesystem = littlefs
|
||||
|
||||
[env:rpipico2w]
|
||||
; RP2350 support requires the maxgerhardt fork of the raspberrypi platform
|
||||
; and the earlephilhower arduino-pico core.
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
board = rpipico2w
|
||||
board_build.core = earlephilhower
|
||||
board_build.filesystem_size = 0.5m
|
||||
|
||||
+45
-12
@@ -152,7 +152,7 @@ static void attachMeters() {
|
||||
|
||||
// Release the channel if it's no longer used or its pin changed.
|
||||
if (attachedPin[i] != -1 && attachedPin[i] != pin) {
|
||||
ledcDetachPin(attachedPin[i]);
|
||||
ledcDetach(attachedPin[i]);
|
||||
pinMode(attachedPin[i], OUTPUT);
|
||||
digitalWrite(attachedPin[i], LOW); // drive freed pin low so the meter reads zero
|
||||
Serial.printf("[PWM] detach ch%d pin%d\n", i, attachedPin[i]);
|
||||
@@ -160,8 +160,7 @@ static void attachMeters() {
|
||||
}
|
||||
|
||||
if (pin > 0 && attachedPin[i] != pin) {
|
||||
ledcSetup(i, PWM_FREQ, PWM_RES);
|
||||
ledcAttachPin(pin, i);
|
||||
ledcAttach(pin, PWM_FREQ, PWM_RES);
|
||||
attachedPin[i] = pin;
|
||||
Serial.printf("[PWM] attach ch%d pin%d\n", i, pin);
|
||||
}
|
||||
@@ -173,7 +172,7 @@ static void applyMeters() {
|
||||
if (meters[i].pin <= 0 || meters[i].maxDuty <= 0) continue;
|
||||
float pct = meters[i].currentValue / 100.0f * meters[i].maxDuty / 100.0f;
|
||||
int duty = constrain((int)(pct * 1023), 0, 1023);
|
||||
ledcWrite(i, duty);
|
||||
ledcWrite(meters[i].pin, duty);
|
||||
Serial.printf("[PWM] ch%d duty=%d (cur=%.1f maxD=%.1f)\n", i, duty, meters[i].currentValue, meters[i].maxDuty);
|
||||
}
|
||||
}
|
||||
@@ -222,7 +221,7 @@ static void mqttCallback(char* topic, byte* payload, unsigned int len) {
|
||||
meters[idx].currentValue = physicalToPct(val, idx);
|
||||
if (meters[idx].pin > 0 && meters[idx].maxDuty > 0) {
|
||||
float pct = meters[idx].currentValue / 100.0f * meters[idx].maxDuty / 100.0f;
|
||||
ledcWrite(idx, constrain((int)(pct * 1023), 0, 1023));
|
||||
ledcWrite(meters[idx].pin, constrain((int)(pct * 1023), 0, 1023));
|
||||
}
|
||||
mqttPublishCurrent(idx);
|
||||
}
|
||||
@@ -441,6 +440,15 @@ static void handleRoot() {
|
||||
if (!requireAuth()) { Serial.println("[HTTP] GET / -> 401"); return; }
|
||||
Serial.println("[HTTP] GET /");
|
||||
String meterRows;
|
||||
bool useTabs = meterCount > 1;
|
||||
if (useTabs) {
|
||||
meterRows += "<div class=tabs><div class=tab-nav>";
|
||||
for (int i = 0; i < meterCount; i++) {
|
||||
String label = strlen(meters[i].name) > 0 ? escHtml(meters[i].name) : "Meter " + String(i);
|
||||
meterRows += "<button type=button class=tab-btn" + String(i == 0 ? " active" : "") + " data-tab=" + String(i) + ">" + label + "</button>";
|
||||
}
|
||||
meterRows += "</div>";
|
||||
}
|
||||
for (int i = 0; i < meterCount; i++) {
|
||||
char maxStr[8], curStr[8];
|
||||
dtostrf(meters[i].maxDuty, 1, 1, maxStr);
|
||||
@@ -452,7 +460,9 @@ static void handleRoot() {
|
||||
dtostrf(meters[i].rangeMin, 1, 1, rMinStr);
|
||||
dtostrf(meters[i].rangeMax, 1, 1, rMaxStr);
|
||||
|
||||
meterRows += "<fieldset><legend>Meter " + String(i) + "</legend>";
|
||||
if (useTabs) meterRows += "<div class=tab-panel" + String(i == 0 ? " active" : "") + " data-tab=" + String(i) + ">";
|
||||
meterRows += "<fieldset>";
|
||||
if (!useTabs) meterRows += "<legend>Meter " + String(i) + "</legend>";
|
||||
meterRows += "<div class=row><label>Pin</label><input name=m" + String(i) + "_pin type=number min=0 max=99 value=" + String(meters[i].pin) + "></div>";
|
||||
meterRows += "<div class=row><label>Name</label><input name=m" + String(i) + "_name value='" + nameVal + "'></div>";
|
||||
meterRows += "<div class=row><label>Unit</label><input name=m" + String(i) + "_unit value='" + unitVal + "'></div>";
|
||||
@@ -464,7 +474,9 @@ static void handleRoot() {
|
||||
meterRows += "<input name=m" + String(i) + "_cur type=range min=0 max=100 step=0.1 value=" + String(curStr) + ">";
|
||||
meterRows += "<span class=val id=m" + String(i) + ">" + String(curStr) + "%</span>";
|
||||
meterRows += "</div></fieldset>";
|
||||
if (useTabs) meterRows += "</div>";
|
||||
}
|
||||
if (useTabs) meterRows += "</div>";
|
||||
|
||||
String countOpts;
|
||||
for (int i = 1; i <= MAX_METERS; i++) {
|
||||
@@ -545,7 +557,11 @@ static void handleRoot() {
|
||||
html += ".tgl-lbl{margin-bottom:0!important;line-height:18px}.switch{position:relative;display:inline-block;width:32px;height:18px;margin-left:auto;flex-shrink:0}fieldset .row label.switch{width:32px}";
|
||||
html += ".switch input{opacity:0;width:0;height:0}.slid{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:#ccc;border-radius:18px;transition:.3s}";
|
||||
html += ".slid::before{content:\"\";position:absolute;height:14px;width:14px;left:2px;top:2px;background:#fff;border-radius:50%;transition:.3s;box-shadow:0 1px 3px rgba(0,0,0,.2)}";
|
||||
html += ".switch input:checked+.slid{background:#4361ee}.switch input:checked+.slid::before{transform:translateX(14px)}";
|
||||
html += ".switch input:checked+.slid{background:#4361ee}.switch input:checked+.slid::before{transform:translateX(14px)}";
|
||||
html += ".tab-nav{display:flex;gap:4px;margin-bottom:16px;flex-wrap:wrap}";
|
||||
html += ".tab-btn{padding:8px 16px;border:1px solid #d1d5db;background:#f8f9fc;border-radius:8px 8px 0 0;cursor:pointer;font-size:13px;font-weight:600;color:#666;transition:all .2s;font-family:inherit}";
|
||||
html += ".tab-btn:hover{background:#e8eaf0}.tab-btn.active{background:#fff;border-color:#4361ee;color:#4361ee;border-bottom-color:#fff}";
|
||||
html += ".tab-panel{display:none}.tab-panel.active{display:block}";
|
||||
html += "</style></head><body><div class=card>";
|
||||
html += "<div class=hdr><img src=/logo.png alt=logo><div><h1>M1730</h1><div class=sub>Configuration</div></div></div><div class=info>";
|
||||
html += "<div><span>Hostname</span><span>" + String(hostname) + "</span></div>";
|
||||
@@ -570,6 +586,9 @@ html += ".switch input:checked+.slid{background:#4361ee}.switch input:checked+.s
|
||||
html += "x.open('GET','/set?i='+this.name.match(/\\d+/)[0]+'&v='+this.value,true);x.send();";
|
||||
html += "});}});";
|
||||
html += "document.querySelector('select[name=meter_count]').addEventListener('change',function(){this.form.submit();});";
|
||||
if (meterCount > 1) {
|
||||
html += "document.querySelectorAll('.tab-btn').forEach(function(b){b.addEventListener('click',function(){var t=this.dataset.tab;document.querySelectorAll('.tab-btn').forEach(function(x){x.classList.remove('active')});document.querySelectorAll('.tab-panel').forEach(function(x){x.classList.remove('active')});this.classList.add('active');document.querySelector('.tab-panel[data-tab=\"'+t+'\"]').classList.add('active')})});";
|
||||
}
|
||||
html += "</script></body></html>";
|
||||
server.send(200, "text/html", html);
|
||||
Serial.printf("[HTTP] GET / -> 200 (%u bytes)\n", html.length());
|
||||
@@ -689,7 +708,7 @@ static void handleSet() {
|
||||
if (meters[idx].pin > 0 && meters[idx].maxDuty > 0) {
|
||||
float pct = val / 100.0f * meters[idx].maxDuty / 100.0f;
|
||||
int duty = constrain((int)(pct * 1023), 0, 1023);
|
||||
ledcWrite(idx, duty);
|
||||
ledcWrite(meters[idx].pin, duty);
|
||||
}
|
||||
mqttPublishCurrent(idx);
|
||||
server.send(200);
|
||||
@@ -767,8 +786,7 @@ static void factoryReset(bool clearWifi) {
|
||||
// Attach meters temporarily for the sweep acknowledgement
|
||||
for (int i = 0; i < count && i < 8; i++) {
|
||||
if (meters[i].pin > 0) {
|
||||
ledcSetup(i, PWM_FREQ, PWM_RES);
|
||||
ledcAttachPin(meters[i].pin, i);
|
||||
ledcAttach(meters[i].pin, PWM_FREQ, PWM_RES);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,7 +798,7 @@ static void factoryReset(bool clearWifi) {
|
||||
for (int i = 0; i < count && i < 8; i++) {
|
||||
if (meters[i].pin > 0 && meters[i].maxDuty > 0) {
|
||||
float duty = pct / 100.0f * meters[i].maxDuty / 100.0f;
|
||||
ledcWrite(i, constrain((int)(duty * 1023), 0, 1023));
|
||||
ledcWrite(meters[i].pin, constrain((int)(duty * 1023), 0, 1023));
|
||||
}
|
||||
}
|
||||
delay(50);
|
||||
@@ -820,6 +838,11 @@ static void applyFactoryResetIfNeeded(int count) {
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("\n\n=== M1730 boot ===");
|
||||
Serial.printf("[BOOT] chip=%s rev=%d cores=%d cpu=%dMHz flash=%uMB\n",
|
||||
ESP.getChipModel(), ESP.getChipRevision(), ESP.getChipCores(),
|
||||
ESP.getCpuFreqMHz(), ESP.getFlashChipSize() / (1024 * 1024));
|
||||
Serial.printf("[BOOT] free heap=%u sketch=%u / %u\n",
|
||||
ESP.getFreeHeap(), ESP.getSketchSize(), ESP.getFreeSketchSpace());
|
||||
if (!LittleFS.begin(false)) {
|
||||
Serial.println("[FS] mount failed, formatting...");
|
||||
LittleFS.begin(true);
|
||||
@@ -833,6 +856,14 @@ void setup() {
|
||||
loadConfig();
|
||||
hostnameParam.setValue(hostname, sizeof(hostname) - 1);
|
||||
|
||||
for (int i = 0; i < meterCount; i++)
|
||||
Serial.printf("[BOOT] meter[%d] pin=%d name=\"%s\" unit=\"%s\" maxD=%.1f range=[%.1f,%.1f] cur=%.1f\n",
|
||||
i, meters[i].pin, meters[i].name, meters[i].unit,
|
||||
meters[i].maxDuty, meters[i].rangeMin, meters[i].rangeMax, meters[i].currentValue);
|
||||
Serial.printf("[BOOT] mqtt=%d broker=%s:%d prefix=%s\n",
|
||||
mqttCfg.enabled, mqttCfg.host, mqttCfg.port, mqttCfg.prefix);
|
||||
Serial.printf("[BOOT] auth=%d\n", authCfg.enabled);
|
||||
|
||||
int resetCount = incrementResetCount();
|
||||
|
||||
attachMeters();
|
||||
@@ -859,7 +890,8 @@ void setup() {
|
||||
Serial.println("[WIFI] failed, restarting");
|
||||
ESP.restart();
|
||||
}
|
||||
Serial.printf("[WIFI] connected, IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
Serial.printf("[WIFI] connected, IP: %s MAC: %s RSSI: %d\n",
|
||||
WiFi.localIP().toString().c_str(), WiFi.macAddress().c_str(), WiFi.RSSI());
|
||||
|
||||
strlcpy(hostname, hostnameParam.getValue(), sizeof(hostname));
|
||||
saveConfig();
|
||||
@@ -869,6 +901,7 @@ void setup() {
|
||||
|
||||
Serial.printf("[BOOT] ready at http://%s.local or http://%s\n",
|
||||
hostname, WiFi.localIP().toString().c_str());
|
||||
Serial.printf("[BOOT] free heap=%u\n", ESP.getFreeHeap());
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
Reference in New Issue
Block a user