2026-08-01 Anzeige der 5 Spannungen und Schalten des einen Relais funktioniert

This commit is contained in:
Dieter Lang 2026-08-01 16:40:02 +02:00
parent f29a8bbd8d
commit 10ce58541e
5 changed files with 116 additions and 47 deletions

View file

@ -64,7 +64,8 @@ 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" && exit 0; echo "warte auf ADS1115..."; sleep 2; done; exit 1'
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
WorkingDirectory=%h/spannung/app
User=%u

View file

@ -1,9 +1,13 @@
{
"web_port": ":8080",
"i2c_bus": "/dev/i2c-1",
"ads1115_address": 72,
"voltage_factor_1": 5.0,
"voltage_factor_2": 5.0,
"i2c_bus": "1",
"ads1115_address_1": 72,
"ads1115_address_2": 73,
"factor_adc1_ch0": 5.0,
"factor_adc1_ch1": 5.0,
"factor_adc1_ch2": 1.0,
"factor_adc1_ch3": 1.0,
"factor_adc2_ch0": 1.0,
"relais_pins": [
{"id": 1, "name": "Relais 1 (Hauptschalter)", "gpio": "GPIO12"},
{"id": 2, "name": "Relais 2 (unbenutzt)", "gpio": "GPIO16"},

100
main.go
View file

@ -7,7 +7,7 @@ import (
"os"
"sync"
"spannung/web" // Importiert das neue eigene web-Paket
"spannung/web" // Importiert das eigene web-Paket
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
@ -18,12 +18,16 @@ import (
)
type Config struct {
WebPort string `json:"web_port"`
I2CBus string `json:"i2c_bus"`
ADS1115Address uint16 `json:"ads1115_address"`
Factor1 float64 `json:"voltage_factor_1"`
Factor2 float64 `json:"voltage_factor_2"`
Relais []RelaisConf `json:"relais_pins"`
WebPort string `json:"web_port"`
I2CBus string `json:"i2c_bus"`
ADS1115Address1 uint16 `json:"ads1115_address_1"`
ADS1115Address2 uint16 `json:"ads1115_address_2"`
FactorAdc1Ch0 float64 `json:"factor_adc1_ch0"`
FactorAdc1Ch1 float64 `json:"factor_adc1_ch1"`
FactorAdc1Ch2 float64 `json:"factor_adc1_ch2"`
FactorAdc1Ch3 float64 `json:"factor_adc1_ch3"`
FactorAdc2Ch0 float64 `json:"factor_adc2_ch0"`
Relais []RelaisConf `json:"relais_pins"`
}
type RelaisConf struct {
@ -34,13 +38,13 @@ type RelaisConf struct {
}
var (
config Config
adsDevice *ads1x15.Dev
relaisMap = make(map[int]gpio.PinIO)
mutex sync.Mutex
// Diese Variablen werden durch -ldflags beim Kompilieren befüllt:
Version = "Entwicklung"
BuildDate = "Unbekannt"
config Config
adsDevice1 *ads1x15.Dev
adsDevice2 *ads1x15.Dev
relaisMap = make(map[int]gpio.PinIO)
mutex sync.Mutex
Version = "Entwicklung"
BuildDate = "Unbekannt"
)
func main() {
@ -61,12 +65,20 @@ func main() {
log.Fatalf("I2C Bus Fehler: %v", err)
}
opts := ads1x15.DefaultOpts
opts.I2cAddress = config.ADS1115Address
adsDevice, err = ads1x15.NewADS1115(bus, &opts)
// 1. Ersten ADS1115 initialisieren (z.B. Adresse 72 / 0x48)
opts1 := ads1x15.DefaultOpts
opts1.I2cAddress = config.ADS1115Address1
adsDevice1, err = ads1x15.NewADS1115(bus, &opts1)
if err != nil {
log.Fatalf("ADS1115 Init fehlgeschlagen: %v", err)
log.Fatalf("ADS1115 #1 Init fehlgeschlagen: %v", err)
}
// 2. Zweiten ADS1115 initialisieren (z.B. Adresse 73 / 0x49)
opts2 := ads1x15.DefaultOpts
opts2.I2cAddress = config.ADS1115Address2
adsDevice2, err = ads1x15.NewADS1115(bus, &opts2)
if err != nil {
log.Fatalf("ADS1115 #2 Init fehlgeschlagen: %v", err)
}
// GPIO-Pins registrieren
@ -85,28 +97,49 @@ func main() {
// Instanziierung des Webservers mit Übergabe der Callback-Funktionen
server := web.NewServer(config.WebPort, readVoltageCallback, toggleRelaisCallback, getRelaisStateCallback, callbackGetVersion)
fmt.Printf("Webserver gestartet auf Port %s\n", config.WebPort)
log.Fatal(server.Start())
}
// Callback: Wird vom Webserver aufgerufen, um Kanäle zu messen
// Kanäle 0-3 = ADS1115 Nr. 1 (A0-A3)
// Kanal 4 = ADS1115 Nr. 2 (A0)
func readVoltageCallback(channel int) float64 {
mutex.Lock()
defer mutex.Unlock()
var targetDevice *ads1x15.Dev
var adsChannel ads1x15.Channel
var factor float64
if channel == 0 {
switch channel {
case 0:
targetDevice = adsDevice1
adsChannel = ads1x15.Channel0
factor = config.Factor1
} else {
factor = config.FactorAdc1Ch0
case 1:
targetDevice = adsDevice1
adsChannel = ads1x15.Channel1
factor = config.Factor2
factor = config.FactorAdc1Ch1
case 2:
targetDevice = adsDevice1
adsChannel = ads1x15.Channel2
factor = config.FactorAdc1Ch2
case 3:
targetDevice = adsDevice1
adsChannel = ads1x15.Channel3
factor = config.FactorAdc1Ch3
case 4:
targetDevice = adsDevice2
adsChannel = ads1x15.Channel0 // Erster Eingang vom zweiten Chip
factor = config.FactorAdc2Ch0
default:
fmt.Printf("Ungültiger Kanal abgefragt: %v\n", channel)
return 0.0
}
pin, err := adsDevice.PinForChannel(adsChannel, physic.Volt*4096/1000, 128*physic.Hertz, 0)
pin, err := targetDevice.PinForChannel(adsChannel, physic.Volt*4096/1000, 128*physic.Hertz, 0)
if err != nil {
fmt.Printf("Fehler beim Erzeugen des Pins für Kanal %v: %v\n", channel, err)
return 0.0
@ -132,21 +165,24 @@ func toggleRelaisCallback(id int) {
return
}
for i, rel := range config.Relais {
if rel.ID == id {
newState := !rel.State
config.Relais[i].State = newState
// Direkt über den Index das globale Slice manipulieren
for i := range config.Relais {
if config.Relais[i].ID == id {
// Status sauber invertieren
config.Relais[i].State = !config.Relais[i].State
if newState {
pin.Out(gpio.High)
// Hardware basierend auf dem neuen, echten Zustand schalten
if config.Relais[i].State {
pin.Out(gpio.High) // Wenn True -> Pin High
} else {
pin.Out(gpio.Low)
pin.Out(gpio.Low) // Wenn False -> Pin Low
}
break
}
}
}
// Callback: Wird vom Webserver aufgerufen, um den aktuellen UI-Status abzufragen
func getRelaisStateCallback() []web.RelaisData {
mutex.Lock()

View file

@ -12,10 +12,13 @@ type RelaisData struct {
State bool
}
// TemplateData erweitern um Version und Datum
// TemplateData erweitert um alle 5 Spannungskanäle
type TemplateData struct {
V1 float64
V2 float64
V1 float64 // ADC 1 - Kanal 0
V2 float64 // ADC 1 - Kanal 1
V3 float64 // ADC 1 - Kanal 2
V4 float64 // ADC 1 - Kanal 3
V5 float64 // ADC 2 - Kanal 0
Relais []RelaisData
Version string
BuildDate string
@ -26,10 +29,9 @@ type Server struct {
readVoltFunc func(channel int) float64
toggleFunc func(id int)
getRelais func() []RelaisData
versionFunc func() (string, string) // Neu hinzugefügt
versionFunc func() (string, string)
}
// NewServer um das version-Callback erweitern
func NewServer(port string, readVolt func(int) float64, toggle func(int), getRelais func() []RelaisData, getVersion func() (string, string)) *Server {
return &Server{
port: port,
@ -47,9 +49,13 @@ func (s *Server) Start() error {
}
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
// Alle 5 Kanäle nacheinander über das Callback einlesen
v1 := s.readVoltFunc(0)
v2 := s.readVoltFunc(1)
ver, date := s.versionFunc() // Version abfragen
v3 := s.readVoltFunc(2)
v4 := s.readVoltFunc(3)
v5 := s.readVoltFunc(4)
ver, date := s.versionFunc()
tmpl, err := template.ParseFiles("web/static/index.html")
if err != nil {
@ -60,9 +66,12 @@ func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
data := TemplateData{
V1: v1,
V2: v2,
V3: v3,
V4: v4,
V5: v5,
Relais: s.getRelais(),
Version: ver, // Daten an Template übergeben
BuildDate: date, // Daten an Template übergeben
Version: ver,
BuildDate: date,
}
tmpl.Execute(w, data)
}

View file

@ -22,15 +22,33 @@
<div class="container">
<h1>U12V Funkstations-Monitor</h1>
<!-- Alle 5 Spannungen strikt untereinander -->
<div class="card">
<h3>Spannungsebene 1</h3>
<h3>ADC 1 - Kanal 0</h3>
<div class="val">{{printf "%.2f" .V1}} V</div>
</div>
<div class="card">
<h3>Spannungsebene 2</h3>
<h3>ADC 1 - Kanal 1</h3>
<div class="val">{{printf "%.2f" .V2}} V</div>
</div>
<div class="card">
<h3>ADC 1 - Kanal 2</h3>
<div class="val">{{printf "%.2f" .V3}} V</div>
</div>
<div class="card">
<h3>ADC 1 - Kanal 3</h3>
<div class="val">{{printf "%.2f" .V4}} V</div>
</div>
<div class="card">
<h3>ADC 2 - Kanal 0</h3>
<div class="val">{{printf "%.2f" .V5}} V</div>
</div>
<!-- Relaissteuerung -->
<div class="card">
<h3>Relaissteuerung</h3>
<div class="grid">
@ -43,6 +61,7 @@
{{end}}
</div>
</div>
<div class="footer-nav">
Aktualisiert alle 3 Sekunden automatisch<br>
<span style="font-size: 0.8em; color: #475569; margin-top: 5px; display: block;">