diff --git a/app/config.json b/app/config.json
new file mode 100644
index 0000000..b0822d1
--- /dev/null
+++ b/app/config.json
@@ -0,0 +1,27 @@
+{
+ "web_port": ":8080",
+ "i2c_bus": "1",
+ "ads1115_address_1": 72,
+ "ads1115_address_2": 73,
+ "name_adc1_ch0": "23cm",
+ "factor_adc1_ch0": 2.0,
+ "name_adc1_ch1": "13 cm",
+ "factor_adc1_ch1": 2.0,
+ "name_adc1_ch2": "9 cm",
+ "factor_adc1_ch2": 2.0,
+ "name_adc1_ch3": "6 cm",
+ "factor_adc1_ch3": 2.0,
+ "name_adc2_ch0": "3 cm",
+ "factor_adc2_ch0": 2.0,
+ "relais_pins": [
+ {"id": 1, "name": "Relais 1 (230V Hauptschalter)", "gpio": "GPIO12"},
+ {"id": 2, "name": "USB-Relais 1 (Reserve)", "gpio": "USB_CH1"},
+ {"id": 3, "name": "USB-Relais 2 23 cm", "gpio": "USB_CH2"},
+ {"id": 4, "name": "USB-Relais 3 13 cm", "gpio": "USB_CH3"},
+ {"id": 5, "name": "USB-Relais 4 9 cm", "gpio": "USB_CH4"},
+ {"id": 6, "name": "USB-Relais 5 6 cm", "gpio": "USB_CH5"},
+ {"id": 7, "name": "USB-Relais 6 3 cm", "gpio": "USB_CH6"},
+ {"id": 8, "name": "USB-Relais 7 (Reserve)", "gpio": "USB_CH7"},
+ {"id": 9, "name": "USB-Relais 8 (Reserve)", "gpio": "USB_CH8"}
+ ]
+}
diff --git a/app/spannung-arm32_7 b/app/spannung-arm32_7
new file mode 100755
index 0000000..c481a7f
Binary files /dev/null and b/app/spannung-arm32_7 differ
diff --git a/app/spannung-arm64 b/app/spannung-arm64
new file mode 100755
index 0000000..f8d6620
Binary files /dev/null and b/app/spannung-arm64 differ
diff --git a/app/spannung.service b/app/spannung.service
new file mode 100644
index 0000000..bfeb329
--- /dev/null
+++ b/app/spannung.service
@@ -0,0 +1,33 @@
+# ==============================================================================
+# KURZANLEITUNG ZUR INSTALLATION UND VERWALTUNG
+# ==============================================================================
+# 1. Datei kopieren nach: /etc/systemd/system/spannung.service
+# 2. Systemd neu laden: sudo systemctl daemon-reload
+# 3. Autostart aktivieren: sudo systemctl enable spannung.service
+# 4. Dienst starten: sudo systemctl start spannung.service
+# ==============================================================================
+
+[Unit]
+Description=Spannung Control Service
+After=network-online.target i2c.service
+Wants=network-online.target
+StartLimitIntervalSec=0
+
+[Service]
+Type=simple
+ExecStartPre=/bin/bash -c 'for i in {1..10}; do i2cdetect -y 1 | grep -E "^40:" | grep -q -E "\b48\b" && i2cdetect -y 1 | grep -E "^40:" | grep -q -E "\b49\b" && exit 0; echo "warte auf ADS1115 Module..."; sleep 2; done; exit 1'
+
+# Erkennt automatisch ob 32- oder 64-Bit OS vorliegt und startet die passende Version
+ExecStart=/bin/bash -c 'if [ "$(uname -m)" = "aarch64" ]; then exec %h/spannung/app/spannung-arm64; else exec %h/spannung/app/spannung-arm32_7; fi'
+WorkingDirectory=%h/spannung/app
+User=%u
+Restart=on-failure
+RestartSec=5s
+StartLimitIntervalSec=60s
+StartLimitBurst=3
+
+StandardOutput=append:%h/spannung/app/spannung.log
+StandardError=append:%h/spannung/app/spannung.log
+
+[Install]
+WantedBy=multi-user.target
diff --git a/app/version.json b/app/version.json
new file mode 100644
index 0000000..4ed9dca
--- /dev/null
+++ b/app/version.json
@@ -0,0 +1,10 @@
+{
+ "version": "0.2",
+ "date": "02.08.2026 um 09:34",
+ "description": "Messung von zwei Spannung und Steuerung von Relais",
+ "features": [
+ "ADS1115-Abfrage über go-i2c",
+ "Weboberfläche mit Steuerbuttons für Reilais",
+ "Definiton der GPIO in der config.json"
+ ]
+}
diff --git a/app/web/server.go b/app/web/server.go
new file mode 100644
index 0000000..f83a6e3
--- /dev/null
+++ b/app/web/server.go
@@ -0,0 +1,97 @@
+package web
+
+import (
+ "encoding/json"
+ "html/template"
+ "net/http"
+ "strconv"
+)
+
+type RelaisData struct {
+ ID int `json:"id"`
+ Name string `json:"name"`
+ State bool `json:"state"`
+}
+
+type StatusResponse struct {
+ V1 float64 `json:"v1"`
+ V2 float64 `json:"v2"`
+ V3 float64 `json:"v3"`
+ V4 float64 `json:"v4"`
+ V5 float64 `json:"v5"`
+ Names []string `json:"names"` // Neu: Sendet die Namen als Array mit
+ Relais []RelaisData `json:"relais"`
+ Version string `json:"version"`
+ BuildDate string `json:"build_date"`
+}
+
+type Server struct {
+ port string
+ readVoltFunc func(channel int) float64
+ toggleFunc func(id int)
+ getRelais func() []RelaisData
+ versionFunc func() (string, string)
+ getNamesFunc func() []string // Neu hinzugefügt
+}
+
+func NewServer(port string, readVolt func(int) float64, toggle func(int), getRelais func() []RelaisData, getVersion func() (string, string), getNames func() []string) *Server {
+ return &Server{
+ port: port,
+ readVoltFunc: readVolt,
+ toggleFunc: toggle,
+ getRelais: getRelais,
+ versionFunc: getVersion,
+ getNamesFunc: getNames, // Neu hinzugefügt
+ }
+}
+
+func (s *Server) Start() error {
+ http.HandleFunc("/", s.handleIndex)
+ http.HandleFunc("/api/status", s.handleAPIStatus)
+ http.HandleFunc("/api/toggle", s.handleAPIToggle)
+ return http.ListenAndServe(s.port, nil)
+}
+
+func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
+ ver, date := s.versionFunc()
+ tmpl, err := template.ParseFiles("web/static/index.html")
+ if err != nil {
+ http.Error(w, "Template nicht gefunden: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ data := StatusResponse{
+ Names: s.getNamesFunc(), // Namen für das erste Rendern bereitstellen
+ Relais: s.getRelais(),
+ Version: ver,
+ BuildDate: date,
+ }
+ tmpl.Execute(w, data)
+}
+
+func (s *Server) handleAPIStatus(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+
+ ver, date := s.versionFunc()
+ resp := StatusResponse{
+ V1: s.readVoltFunc(0),
+ V2: s.readVoltFunc(1),
+ V3: s.readVoltFunc(2),
+ V4: s.readVoltFunc(3),
+ V5: s.readVoltFunc(4),
+ Names: s.getNamesFunc(),
+ Relais: s.getRelais(),
+ Version: ver,
+ BuildDate: date,
+ }
+ json.NewEncoder(w).Encode(resp)
+}
+
+func (s *Server) handleAPIToggle(w http.ResponseWriter, r *http.Request) {
+ idStr := r.URL.Query().Get("id")
+ id, err := strconv.Atoi(idStr)
+ if err == nil {
+ s.toggleFunc(id)
+ }
+ s.handleAPIStatus(w, r)
+}
diff --git a/app/web/static/index.html b/app/web/static/index.html
new file mode 100644
index 0000000..e71ab3d
--- /dev/null
+++ b/app/web/static/index.html
@@ -0,0 +1,148 @@
+
+
+
+ Funkstation Steuerung
+
+
+
+
+
+
Funkstations-Monitor
+
+
+
+
+
+
Relaissteuerung
+
+ {{range .Relais}}
+
+ {{end}}
+
+
+
+
+
+
+
+
{{index .Names 0}}
+
--.-- V
+
+
+
+
{{index .Names 1}}
+
--.-- V
+
+
+
+
{{index .Names 2}}
+
--.-- V
+
+
+
+
{{index .Names 3}}
+
--.-- V
+
+
+
+
{{index .Names 4}}
+
--.-- V
+
+
+
+
+
+
+
+
+
+
diff --git a/build.sh b/build.sh
index 3081563..62d0559 100755
--- a/build.sh
+++ b/build.sh
@@ -14,38 +14,37 @@ else
DATE=$(date +'%Y-%m-%d')
fi
-echo "🔧 Baue Spannung-Monitor v$VERSION ($DATE)"
+echo "🔧 Baue Spannung-Monitor v$VERSION ($DATE) für Raspberry Pi 3 & 4"
# KORREKTUR: Einfache Anführungszeichen fangen die Leerzeichen für den Go-Linker ab
LDFLAGS="-X 'main.Version=$VERSION' -X 'main.BuildDate=$DATE'"
-# Multi-Plattform Builds ausführen
-CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$LDFLAGS" -o spannung-amd64 .
-echo "✅ Fertig gebaut: ./spannung-amd64"
-
-CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -o spannung-arm64 .
-echo "✅ Fertig gebaut: ./spannung-arm64"
-
-CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags "$LDFLAGS" -o spannung-arm32_7 .
-echo "✅ Fertig gebaut: ./spannung-arm32_7"
-
-CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags "$LDFLAGS" -o spannung-arm32_6 .
-echo "✅ Fertig gebaut: ./spannung-arm32_6"
-
-# Dateiberechtigungen setzen
-chmod +x ./spannung-*
-
-# Zielverzeichnis 'app' neu erstellen
-rm -rf app
+# Zielverzeichnis 'app' bereinigen und neu erstellen
+rm -rf app spannung-arm*
mkdir app
-# Binaries, Konfiguration und Web-Ordner kopieren
-cp spannung-* ./app/
+# --- BUILD-PROZESS MIT CGO ---
+
+# 1. Für 64-Bit Raspberry Pi (Standard bei Pi 4 und neueren Pi 3 OS)
+echo "📦 Kompiliere für ARM64 (64-Bit)..."
+CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc go build -ldflags "$LDFLAGS" -o ./app/spannung-arm64 .
+echo "✅ Fertig gebaut: ./app/spannung-arm64"
+
+# 2. Für 32-Bit Raspberry Pi (Klassisch für Pi 3 / ältere OS)
+echo "📦 Kompiliere für ARM32 v7 (32-Bit)..."
+CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 CC=arm-linux-gnueabihf-gcc go build -ldflags "$LDFLAGS" -o ./app/spannung-arm32_7 .
+echo "✅ Fertig gebaut: ./app/spannung-arm32_7"
+
+# Dateiberechtigungen im Zielordner setzen
+chmod +x ./app/spannung-*
+
+# Konfiguration und Web-Ordner kopieren
cp config.json ./app/
cp version.json ./app/
cp web ./app/ -r
-# 3. Die Service-Datei universell generiert (nutzt systemd %h/%u Specifiers)
+# 3. Die Service-Datei generieren
+# Ein kleiner Bash-Wrapper in ExecStart sorgt dafür, dass automatisch das richtige Binary gestartet wird!
cat << 'EOF' > ./app/spannung.service
# ==============================================================================
# KURZANLEITUNG ZUR INSTALLATION UND VERWALTUNG
@@ -66,7 +65,8 @@ StartLimitIntervalSec=0
Type=simple
ExecStartPre=/bin/bash -c 'for i in {1..10}; do i2cdetect -y 1 | grep -E "^40:" | grep -q -E "\b48\b" && i2cdetect -y 1 | grep -E "^40:" | grep -q -E "\b49\b" && exit 0; echo "warte auf ADS1115 Module..."; sleep 2; done; exit 1'
-ExecStart=%h/spannung/app/spannung-arm32_7
+# Erkennt automatisch ob 32- oder 64-Bit OS vorliegt und startet die passende Version
+ExecStart=/bin/bash -c 'if [ "$(uname -m)" = "aarch64" ]; then exec %h/spannung/app/spannung-arm64; else exec %h/spannung/app/spannung-arm32_7; fi'
WorkingDirectory=%h/spannung/app
User=%u
Restart=on-failure
@@ -81,6 +81,6 @@ StandardError=append:%h/spannung/app/spannung.log
WantedBy=multi-user.target
EOF
-echo "📝 Service-Datei universell generiert (nutzt systemd %h/%u Specifiers)"
+echo "📝 Service-Datei mit automatischer Architektur-Erkennung generiert"
echo "📂 Inhalt des fertigen app-Verzeichnisses:"
ls -la app
diff --git a/deploy.sh b/deploy.sh
new file mode 100644
index 0000000..194051b
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+set -e
+
+# Prüfen, ob eine IP oder ein Hostname übergeben wurde
+if [ -z "$1" ]; then
+ echo "❌ Fehler: Keine Ziel-Adresse angegeben!"
+ echo "💡 Nutzung: ./deploy.sh [Benutzername]"
+ echo "📌 Beispiel: ./deploy.sh 192.168.178.50"
+ echo "📌 Beispiel: ./deploy.sh raspi-4.local pi"
+ exit 1
+fi
+
+# Ziel-Konfiguration festlegen
+TARGET_HOST="$1"
+TARGET_USER="${2:-$USER}" # Nutzt den aktuellen Ubuntu-User, falls kein User übergeben wird
+TARGET_DIR="~/spannung"
+
+echo "🚀 Starte Deployment auf $TARGET_USER@$TARGET_HOST..."
+
+# 1. Prüfen, ob der lokale 'app'-Ordner existiert
+if [ ! -d "app" ]; then
+ echo "❌ Fehler: Der Ordner 'app' existiert nicht."
+ echo "🛠️ Bitte führen Sie zuerst ./build.sh aus!"
+ exit 1
+fi
+
+# 2. Zielverzeichnis auf dem Raspberry Pi vorbereiten (alte App-Dateien löschen)
+echo "🧹 Bereinige altes App-Verzeichnis auf dem Raspberry Pi..."
+ssh "$TARGET_USER@$TARGET_HOST" "mkdir -p $TARGET_DIR && rm -rf $TARGET_DIR/app"
+
+# 3. Den neuen 'app'-Ordner via SCP auf den Pi kopieren
+echo "📦 Kopiere 'app'-Ordner auf den Raspberry Pi..."
+scp -r ./app "$TARGET_USER@$TARGET_HOST:$TARGET_DIR/"
+
+echo "✅ Deployment erfolgreich abgeschlossen!"
+echo "📍 Die Dateien liegen auf dem Pi unter: $TARGET_DIR/app"
diff --git a/go.mod b/go.mod
index 24fa030..1511b1b 100644
--- a/go.mod
+++ b/go.mod
@@ -3,12 +3,8 @@ module spannung
go 1.25.0
require (
+ github.com/google/gousb v1.1.3
periph.io/x/conn/v3 v3.7.3
periph.io/x/devices/v3 v3.7.4
periph.io/x/host/v3 v3.8.5
)
-
-require (
- github.com/adriankumpf/abacom-relay-board v0.7.1 // indirect
- github.com/google/gousb v1.1.3 // indirect
-)
diff --git a/go.sum b/go.sum
index a087781..3678ca2 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,3 @@
-github.com/adriankumpf/abacom-relay-board v0.7.1 h1:BEq26Z+mstFFO12Anla7QXmi/C5tkZT/8dCqB/vyfwY=
-github.com/adriankumpf/abacom-relay-board v0.7.1/go.mod h1:j1JZRj26BIUMuxdtiBP1G5dF9piCbX/xQATZB5+CPs4=
github.com/google/gousb v1.1.3 h1:xt6M5TDsGSZ+rlomz5Si5Hmd/Fvbmo2YCJHN+yGaK4o=
github.com/google/gousb v1.1.3/go.mod h1:GGWUkK0gAXDzxhwrzetW592aOmkkqSGcj5KLEgmCVUg=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
diff --git a/relais.py b/relais.py
deleted file mode 100644
index 23affb3..0000000
--- a/relais.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env python3
-import sys
-import usb.core
-import usb.util
-
-# Abacom CH341A IDs
-VENDOR_ID = 0x1a86
-PRODUCT_ID = 0x5512
-
-def main():
- if len(sys.argv) < 2:
- print("Nutzung: python3 relais.py ")
- sys.exit(1)
-
- try:
- data_value = int(sys.argv[1])
- if not (0 <= data_value <= 255):
- raise ValueError
- except ValueError:
- print("Fehler: Bitte eine Zahl zwischen 0 und 255 eingeben.")
- sys.exit(1)
-
- # Gerät suchen
- dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
- if dev is None:
- print("Relaiskarte nicht gefunden! USB-Verbindung prüfen.")
- sys.exit(1)
-
- # Kernel-Treiber lösen, falls aktiv
- if dev.is_kernel_driver_active(0):
- dev.detach_kernel_driver(0)
-
- # Konfiguration setzen
- dev.set_configuration()
-
- # CH341 Befehl zum Schreiben von IO-Pins (Abacom-Protokoll)
- # Befehl 0xA6 setzt die Ausgänge beim CH341
- # Das Format ist oft: [0xA6, Schalter-Byte, 0x00] oder direkte Control-Transfers
- try:
- # Steuerbefehl direkt an den Chip senden (CH341 spezifisch)
- # 0x40 = Vendor-Write, 0x9a = CH341_REQ_WRITE_REG
- dev.ctrl_transfer(0x40, 0x9a, 0x2525, data_value)
- print(f"Erfolgreich geschaltet. Wert: {data_value} (Binär: {data_value:08b})")
- except Exception as e:
- print(f"Fehler beim Senden: {e}")
-
-if __name__ == "__main__":
- main()
diff --git a/relais2.py b/relais2.py
deleted file mode 100644
index 9d3130c..0000000
--- a/relais2.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python3
-import sys
-import usb.core
-import usb.util
-
-VENDOR_ID = 0x1a86
-PRODUCT_ID = 0x5512
-
-def main():
- if len(sys.argv) < 2:
- print("Nutzung: sudo python3 relais2.py ")
- sys.exit(1)
-
- try:
- data_value = int(sys.argv[1])
- if not (0 <= data_value <= 255):
- raise ValueError
- except ValueError:
- print("Fehler: Bitte eine Zahl zwischen 0 und 255 eingeben.")
- sys.exit(1)
-
- dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
- if dev is None:
- print("Relaiskarte nicht gefunden! USB-Verbindung prüfen.")
- sys.exit(1)
-
- if dev.is_kernel_driver_active(0):
- try:
- dev.detach_kernel_driver(0)
- except Exception:
- pass
-
- dev.set_configuration()
-
- try:
- # CH341A GPIO-Befehl (0x5a)
- # Aufbau des Pakets:
- # Byte 0: 0x5a (Befehl für GPIO-Steuerung)
- # Byte 1: 0x51 (Sub-Befehl für I/O-Wechsel)
- # Byte 2: Richtungsmaske/Enable für die Pins (0xff = Alle Pins D0-D7 auf Ausgang)
- # Byte 3: Datenwert für Pins D0-D7 (deine Relais 0-255)
- # Byte 4: Richtung/Enable für obere Pins (0x00 = nicht genutzt)
- # Byte 5: Datenwert für obere Pins (0x00)
-
- cmd_packet = bytes([0x5a, 0x51, 0xff, data_value, 0x00, 0x00])
-
- # Senden an den Standard-Bulk-Ausgang (Endpoint 0x02)
- dev.write(0x02, cmd_packet, 1000)
-
- print(f"Befehl gesendet. Wert: {data_value} (Binär: {data_value:08b})")
-
- except Exception as e:
- print(f"Fehler beim Senden: {e}")
-
-if __name__ == "__main__":
- main()
diff --git a/spannung.service b/spannung.service
deleted file mode 100644
index 3636934..0000000
--- a/spannung.service
+++ /dev/null
@@ -1,44 +0,0 @@
-# ==============================================================================
-# KURZANLEITUNG ZUR INSTALLATION UND VERWALTUNG
-# ==============================================================================
-# 1. Datei kopieren nach: /etc/systemd/system/spannung.service
-# 2. Systemd neu laden: sudo systemctl daemon-reload
-# 3. Autostart aktivieren: sudo systemctl enable spannung.service
-# 4. Dienst starten: sudo systemctl start spannung.service
-#
-# Befehle zur Steuerung:
-# - Status prüfen: sudo systemctl status spannung.service
-# - Live-Logs einsehen: sudo journalctl -u spannung.service -f
-# - Dienst stoppen: sudo systemctl stop spannung.service
-# - Dienst neu starten: sudo systemctl restart spannung.service
-# ==============================================================================
-#
-# gewohnter rsync-Befehl (Beispiel)
-# rsync -avz ./app/ pi@raspirotor:~/spannung/
-# 2. Den Dienst auf dem Pi remote aktualisieren und neu starten
-# ssh -t afu@raspirotor "sudo cp ~/spannung/app/spannung.service /etc/systemd/system/ && sudo systemctl daemon-reload && sudo systemctl restart spannung.service"
-
-[Unit]
-Description=Spannung Control Service
-After=network-online.target i2c.service
-Wants=network-online.target
-StartLimitIntervalSec=0
-
-[Service]
-Type=simple
-# Die ExecStartPre-Zeile muss bei 32-Bit auskommentiert werden, da kein i2cdetect verfügbar ist
-ExecStartPre=/bin/bash -c 'for i in {1..10}; do i2cdetect -y 1 | grep -q "48" && exit 0; echo "warte auf ADS1115..."; sleep 2; done; exit 1'
-ExecStart=/home/afu/spannung/spannung
-WorkingDirectory=/home/afu/spannung
-User=afu
-#Restart=always
-Restart=on-failure # Startet den Dienst nur bei Fehlern neu
-RestartSec=5s # Wartet 5 Sekunden vor dem nächsten Versuch
-StartLimitIntervalSec=60s # Innerhalb von 60 Sekunden
-StartLimitBurst=3 # Maximal 3 Versuche in diesem Intervall
-
-StandardOutput=append:/home/afu/spannung/spannung.log
-StandardError=append:/home/afu/spannung/spannung.log
-
-[Install]
-WantedBy=multi-user.target