2026-08-02 08:50 USB-Relais schalten jetzt
This commit is contained in:
parent
e39598103d
commit
a94c35f665
2 changed files with 93 additions and 51 deletions
14
config.json
14
config.json
|
|
@ -15,13 +15,13 @@
|
|||
"factor_adc2_ch0": 1.0,
|
||||
"relais_pins": [
|
||||
{"id": 1, "name": "Relais 1 (230V Hauptschalter)", "gpio": "GPIO12"},
|
||||
{"id": 2, "name": "USB-Relais 1 (Funkgerät 1)", "gpio": "USB_CH1"},
|
||||
{"id": 3, "name": "USB-Relais 2 (Funkgerät 2)", "gpio": "USB_CH2"},
|
||||
{"id": 4, "name": "USB-Relais 3 (Verstärker)", "gpio": "USB_CH3"},
|
||||
{"id": 5, "name": "USB-Relais 4 (Lüfter)", "gpio": "USB_CH4"},
|
||||
{"id": 6, "name": "USB-Relais 5 (Beleuchtung)", "gpio": "USB_CH5"},
|
||||
{"id": 7, "name": "USB-Relais 6 (Zubehör 1)", "gpio": "USB_CH6"},
|
||||
{"id": 8, "name": "USB-Relais 7 (Zubehör 2)", "gpio": "USB_CH7"},
|
||||
{"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"}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
130
main.go
130
main.go
|
|
@ -54,6 +54,10 @@ var (
|
|||
)
|
||||
|
||||
func main() {
|
||||
// 1. Version aus Datei laden, falls vorhanden
|
||||
loadVersionInfo()
|
||||
|
||||
// 2. Konfigurationsdatei einlesen
|
||||
confFile, err := os.ReadFile("config.json")
|
||||
if err != nil {
|
||||
log.Fatalf("Fehler beim Lesen der config.json: %v", err)
|
||||
|
|
@ -62,6 +66,7 @@ func main() {
|
|||
log.Fatalf("Fehler beim Parsen der config.json: %v", err)
|
||||
}
|
||||
|
||||
// 3. Periph Hardware initialisieren
|
||||
if _, err = host.Init(); err != nil {
|
||||
log.Fatalf("Hardware-Init fehlgeschlagen: %v", err)
|
||||
}
|
||||
|
|
@ -71,7 +76,7 @@ func main() {
|
|||
log.Fatalf("I2C Bus Fehler: %v", err)
|
||||
}
|
||||
|
||||
// 1. Ersten ADS1115 initialisieren
|
||||
// 4. Ersten ADS1115 initialisieren
|
||||
opts1 := ads1x15.DefaultOpts
|
||||
opts1.I2cAddress = config.ADS1115Address1
|
||||
adsDevice1, err = ads1x15.NewADS1115(bus, &opts1)
|
||||
|
|
@ -79,7 +84,7 @@ func main() {
|
|||
log.Fatalf("ADS1115 #1 Init fehlgeschlagen: %v", err)
|
||||
}
|
||||
|
||||
// 2. Zweiten ADS1115 initialisieren
|
||||
// 5. Zweiten ADS1115 initialisieren
|
||||
opts2 := ads1x15.DefaultOpts
|
||||
opts2.I2cAddress = config.ADS1115Address2
|
||||
adsDevice2, err = ads1x15.NewADS1115(bus, &opts2)
|
||||
|
|
@ -87,7 +92,7 @@ func main() {
|
|||
log.Fatalf("ADS1115 #2 Init fehlgeschlagen: %v", err)
|
||||
}
|
||||
|
||||
// GPIO-Pins registrieren (ID 1 ist der 230V Hauptschalter am Raspberry Pi)
|
||||
// 6. GPIO-Pins registrieren (ID 1 ist der 230V Hauptschalter am Raspberry Pi)
|
||||
for i, r := range config.Relais {
|
||||
if r.ID == 1 {
|
||||
pin := gpioreg.ByName(r.GPIO)
|
||||
|
|
@ -102,16 +107,33 @@ func main() {
|
|||
config.Relais[i].State = false
|
||||
}
|
||||
|
||||
// Beim Start alle USB-Relais initial in den definierten AUS-Zustand versetzen
|
||||
// 7. Beim Start alle USB-Relais initial auswerfen
|
||||
updateUsbRelays()
|
||||
|
||||
// Instanziierung des Webservers mit Übergabe der Callback-Funktionen
|
||||
// 8. Instanziierung des Webservers mit Übergabe der Callback-Funktionen
|
||||
server := web.NewServer(config.WebPort, readVoltageCallback, toggleRelaisCallback, getRelaisStateCallback, callbackGetVersion, getVoltNamesCallback)
|
||||
|
||||
fmt.Printf("Webserver gestartet auf Port %s\n", config.WebPort)
|
||||
log.Fatal(server.Start())
|
||||
}
|
||||
|
||||
// Hilfsfunktion zum Laden der version.json
|
||||
func loadVersionInfo() {
|
||||
type VersionInfo struct {
|
||||
Version string `json:"version"`
|
||||
BuildDate string `json:"build_date"`
|
||||
}
|
||||
|
||||
vFile, err := os.ReadFile("version.json")
|
||||
if err == nil {
|
||||
var vInfo VersionInfo
|
||||
if err := json.Unmarshal(vFile, &vInfo); err == nil {
|
||||
Version = vInfo.Version
|
||||
BuildDate = vInfo.BuildDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Callback: Wird vom Webserver aufgerufen, um Kanäle zu messen
|
||||
func readVoltageCallback(channel int) float64 {
|
||||
mutex.Lock()
|
||||
|
|
@ -147,6 +169,7 @@ func readVoltageCallback(channel int) float64 {
|
|||
return 0.0
|
||||
}
|
||||
|
||||
// Pin-Instanz erzeugen und auslesen (periph.io API Standard)
|
||||
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)
|
||||
|
|
@ -183,7 +206,7 @@ func toggleRelaisCallback(id int) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Hardware-Schaltung über Abacom USB-Karte via Bulk-Streaming
|
||||
// Hardware-Schaltung über Abacom USB-Karte via Schieberegister-Emulation
|
||||
updateUsbRelays()
|
||||
}
|
||||
break
|
||||
|
|
@ -191,13 +214,12 @@ func toggleRelaisCallback(id int) {
|
|||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Schaltet die Abacom-Karte nach dem Python-Skript-Muster via Bulk-Stream
|
||||
// Hilfsfunktion: Schaltet die Abacom-Karte über exakte Schieberegister-Logik (Bit-Banging)
|
||||
func updateUsbRelays() {
|
||||
fmt.Println("\n--- [USB DEBUG START] ---")
|
||||
fmt.Println("\n--- [USB ABACOM SCHALTVORGANG] ---")
|
||||
ctx := gousb.NewContext()
|
||||
defer ctx.Close()
|
||||
|
||||
// USB-Gerät anhand der VID:PID öffnen (1a86:5512)
|
||||
dev, err := ctx.OpenDeviceWithVIDPID(0x1a86, 0x5512)
|
||||
if err != nil {
|
||||
fmt.Printf("[❌] USB-Fehler beim Öffnen des Geräts: %v\n", err)
|
||||
|
|
@ -208,29 +230,9 @@ func updateUsbRelays() {
|
|||
return
|
||||
}
|
||||
defer dev.Close()
|
||||
fmt.Println("[✅] USB-Hardware erfolgreich geöffnet.")
|
||||
|
||||
// Kernel-Treiber trennen (erzwingt exklusiven Zugriff unter Linux)
|
||||
_ = dev.SetAutoDetach(true)
|
||||
|
||||
// Bitmaske berechnen (ID 2 -> Bit 0 bis ID 9 -> Bit 7)
|
||||
var bitmask byte = 0x00
|
||||
for _, r := range config.Relais {
|
||||
if r.ID == 1 {
|
||||
continue // ID 1 überspringen (ist Ihr 230V GPIO-Hauptschalter)
|
||||
}
|
||||
if r.State && r.ID >= 2 && r.ID <= 9 {
|
||||
shiftWidth := r.ID - 2
|
||||
bitmask |= (1 << shiftWidth)
|
||||
}
|
||||
}
|
||||
|
||||
// Abacom-LRB nutzt Low-Active-Pegel (0V = Ein, 5V = Aus)
|
||||
dataByte := ^bitmask
|
||||
|
||||
fmt.Printf("[⚙️] Gewünschter UI-Status (Binär): %08b\n", bitmask)
|
||||
fmt.Printf("[⚙️] Zu sendendes Daten-Byte : 0x%02X\n", dataByte)
|
||||
|
||||
cfg, err := dev.Config(1)
|
||||
if err != nil {
|
||||
fmt.Printf("[❌] Konfigurationsfehler: %v\n", err)
|
||||
|
|
@ -245,27 +247,67 @@ func updateUsbRelays() {
|
|||
}
|
||||
defer intf.Close()
|
||||
|
||||
// Endpunkt 2 (Out) für das native CH341A-Parallel-Streaming öffnen
|
||||
outEP, err := intf.OutEndpoint(2)
|
||||
if err != nil {
|
||||
fmt.Printf("[❌] Endpunkt-Fehler: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Erst 4 leere Bytes senden, um das Hardware-Sperrlatch des CH341A aufzuwecken
|
||||
initTrigger := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
_, _ = outEP.Write(initTrigger)
|
||||
|
||||
// Start-Kennung 0x5A, gefolgt von 3x Null und dem Relais-Datenbyte am Ende
|
||||
payload := []byte{0x5A, 0x00, 0x00, 0x00, dataByte}
|
||||
|
||||
// Daten über die Bulk-Pipeline an das Latch-Register übergeben
|
||||
bytesWritten, err := outEP.Write(payload)
|
||||
if err != nil {
|
||||
fmt.Printf("[❌] BULK-SCHALTFEHLER: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("[✅] ERFOLG! Abacom-Stream exakt wie in Python übertragen (%d Bytes geschrieben).\n", bytesWritten)
|
||||
// Bitmaske berechnen (ID 2 -> Bit 0 bis ID 9 -> Bit 7)
|
||||
var bitmask byte = 0x00
|
||||
for _, r := range config.Relais {
|
||||
if r.ID == 1 {
|
||||
continue
|
||||
}
|
||||
if r.State && r.ID >= 2 && r.ID <= 9 {
|
||||
shiftWidth := r.ID - 2
|
||||
bitmask |= (1 << shiftWidth)
|
||||
}
|
||||
}
|
||||
|
||||
// Konstanten für den CH341A-Chip
|
||||
const (
|
||||
ch341aSetOutput = 0xA1
|
||||
LATCH = 0x01
|
||||
CLK = 0x08
|
||||
DATA = 0x20
|
||||
)
|
||||
|
||||
// Anonyme Hilfsfunktion für USB-Anweisungen
|
||||
sendOutput := func(dataByte byte) {
|
||||
msg := []byte{
|
||||
ch341aSetOutput,
|
||||
0x6a, 0x1f, 0x00, 0x10,
|
||||
dataByte,
|
||||
0x3f, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
_, _ = outEP.Write(msg)
|
||||
}
|
||||
|
||||
fmt.Printf("[⚙️] Sende Bitmaske an Schieberegister: %08b\n", bitmask)
|
||||
|
||||
// --- Protokoll-Sequenz (Analog zu Python) ---
|
||||
sendOutput(0x00) // Latch initial auf Low halten
|
||||
|
||||
for i := 0; i < 8; i++ {
|
||||
// MSB first (Bit 7 bis 0 prüfen)
|
||||
if (bitmask & (1 << (7 - i))) != 0 {
|
||||
sendOutput(DATA)
|
||||
sendOutput(CLK | DATA)
|
||||
sendOutput(DATA)
|
||||
} else {
|
||||
sendOutput(0x00)
|
||||
sendOutput(CLK)
|
||||
sendOutput(0x00)
|
||||
}
|
||||
}
|
||||
sendOutput(0x00) // Leitungen leeren
|
||||
|
||||
// Übernahme-Register per Latch freischalten
|
||||
sendOutput(LATCH)
|
||||
sendOutput(0x00)
|
||||
|
||||
fmt.Println("[✅] ERFOLG! Signal-Bytes an Schieberegister übergeben.")
|
||||
fmt.Println("--- [USB DEBUG ENDE] ---\n")
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +335,7 @@ func getVoltNamesCallback() []string {
|
|||
}
|
||||
}
|
||||
|
||||
// Versionangaben aus version.json in Webserver übernehmen
|
||||
// Callback: Wird vom Webserver aufgerufen, um Version und Build-Datum zu erhalten
|
||||
func callbackGetVersion() (string, string) {
|
||||
return Version, BuildDate
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue