2026-08-02 05:30 USB-Relais schalten noch nicht
This commit is contained in:
parent
2c63235b54
commit
e39598103d
19 changed files with 241 additions and 239 deletions
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"web_port": ":8080",
|
||||
"i2c_bus": "/dev/i2c-1",
|
||||
"ads1115_address": 72,
|
||||
"voltage_factor_1": 5.0,
|
||||
"voltage_factor_2": 5.0,
|
||||
"relais_pins": [
|
||||
{"id": 1, "name": "Relais 1 (Hauptschalter)", "gpio": "GPIO12"},
|
||||
{"id": 2, "name": "Relais 2 (unbenutzt)", "gpio": "GPIO16"},
|
||||
{"id": 3, "name": "Relais 3 (unbenutzt)", "gpio": "GPIO20"},
|
||||
{"id": 4, "name": "Relais 4 (unbenutzt)", "gpio": "GPIO21"}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,31 +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
|
||||
# ==============================================================================
|
||||
|
||||
[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" && exit 0; echo "warte auf ADS1115..."; sleep 2; done; exit 1'
|
||||
ExecStart=%h/spannung/app/spannung-arm32_7
|
||||
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
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"version": "0.1",
|
||||
"date": "06.06.2026 um 20:57",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type RelaisData struct {
|
||||
ID int
|
||||
Name string
|
||||
State bool
|
||||
}
|
||||
|
||||
// TemplateData erweitern um Version und Datum
|
||||
type TemplateData struct {
|
||||
V1 float64
|
||||
V2 float64
|
||||
Relais []RelaisData
|
||||
Version string
|
||||
BuildDate string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
port string
|
||||
readVoltFunc func(channel int) float64
|
||||
toggleFunc func(id int)
|
||||
getRelais func() []RelaisData
|
||||
versionFunc func() (string, string) // Neu hinzugefügt
|
||||
}
|
||||
|
||||
// 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,
|
||||
readVoltFunc: readVolt,
|
||||
toggleFunc: toggle,
|
||||
getRelais: getRelais,
|
||||
versionFunc: getVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
http.HandleFunc("/", s.handleIndex)
|
||||
http.HandleFunc("/toggle", s.handleToggle)
|
||||
return http.ListenAndServe(s.port, nil)
|
||||
}
|
||||
|
||||
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
v1 := s.readVoltFunc(0)
|
||||
v2 := s.readVoltFunc(1)
|
||||
ver, date := s.versionFunc() // Version abfragen
|
||||
|
||||
tmpl, err := template.ParseFiles("web/static/index.html")
|
||||
if err != nil {
|
||||
http.Error(w, "Template nicht gefunden: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := TemplateData{
|
||||
V1: v1,
|
||||
V2: v2,
|
||||
Relais: s.getRelais(),
|
||||
Version: ver, // Daten an Template übergeben
|
||||
BuildDate: date, // Daten an Template übergeben
|
||||
}
|
||||
tmpl.Execute(w, data)
|
||||
}
|
||||
|
||||
func (s *Server) handleToggle(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := r.URL.Query().Get("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err == nil {
|
||||
s.toggleFunc(id)
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Funkstation Steuerung</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', Arial, sans-serif; text-align: center; background: #0f172a; color: #f8fafc; padding: 20px; }
|
||||
.container { max-width: 500px; margin: 0 auto; }
|
||||
.card { background: #1e293b; padding: 20px; margin: 15px 0; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.3); border: 1px solid #334155; }
|
||||
.val { font-size: 2.5em; color: #38bdf8; font-weight: bold; font-family: monospace; }
|
||||
.grid { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 15px; }
|
||||
.btn { width: 100%; padding: 14px; font-size: 1.1em; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; transition: background 0.2s; color: white; }
|
||||
.btn-on { background: #ef4444; }
|
||||
.btn-on:hover { background: #dc2626; }
|
||||
.btn-off { background: #10b981; }
|
||||
.btn-off:hover { background: #059669; }
|
||||
.footer-nav { margin-top: 25px; font-size: 0.9em; color: #64748b; }
|
||||
</style>
|
||||
<meta http-equiv="refresh" content="3">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>U12V Funkstations-Monitor</h1>
|
||||
|
||||
<div class="card">
|
||||
<h3>Spannungsebene 1</h3>
|
||||
<div class="val">{{printf "%.2f" .V1}} V</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Spannungsebene 2</h3>
|
||||
<div class="val">{{printf "%.2f" .V2}} V</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Relaissteuerung</h3>
|
||||
<div class="grid">
|
||||
{{range .Relais}}
|
||||
<a href="/toggle?id={{.ID}}" style="text-decoration: none;">
|
||||
<button class="btn {{if .State}}btn-on{{else}}btn-off{{end}}">
|
||||
{{.Name}}: {{if .State}}AKTIV (Ausschalten){{else}}INAKTIV (Einschalten){{end}}
|
||||
</button>
|
||||
</a>
|
||||
{{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;">
|
||||
Version v{{.Version}} ({{.BuildDate}})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
13
config.json
13
config.json
|
|
@ -14,9 +14,14 @@
|
|||
"name_adc2_ch0": "Zusatzakku Funk",
|
||||
"factor_adc2_ch0": 1.0,
|
||||
"relais_pins": [
|
||||
{"id": 1, "name": "Relais 1 (Hauptschalter)", "gpio": "GPIO12"},
|
||||
{"id": 2, "name": "Relais 2 (unbenutzt)", "gpio": "GPIO16"},
|
||||
{"id": 3, "name": "Relais 3 (unbenutzt)", "gpio": "GPIO20"},
|
||||
{"id": 4, "name": "Relais 4 (unbenutzt)", "gpio": "GPIO21"}
|
||||
{"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": 9, "name": "USB-Relais 8 (Reserve)", "gpio": "USB_CH8"}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
5
go.mod
5
go.mod
|
|
@ -7,3 +7,8 @@ require (
|
|||
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
|
||||
)
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -1,3 +1,7 @@
|
|||
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=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
periph.io/x/conn/v3 v3.7.3 h1:+8UblkC4omTB1M+jZTvTj3qoxQOTJy0ZRQm8DLUuVzc=
|
||||
|
|
|
|||
168
main.go
168
main.go
|
|
@ -7,8 +7,9 @@ import (
|
|||
"os"
|
||||
"sync"
|
||||
|
||||
"spannung/web" // Importiert das eigene web-Paket
|
||||
"spannung/web" // Importiert Ihr eigenes web-Paket
|
||||
|
||||
"github.com/google/gousb"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/gpio/gpioreg"
|
||||
"periph.io/x/conn/v3/i2c/i2creg"
|
||||
|
|
@ -70,7 +71,7 @@ func main() {
|
|||
log.Fatalf("I2C Bus Fehler: %v", err)
|
||||
}
|
||||
|
||||
// 1. Ersten ADS1115 initialisieren (z.B. Adresse 72 / 0x48)
|
||||
// 1. Ersten ADS1115 initialisieren
|
||||
opts1 := ads1x15.DefaultOpts
|
||||
opts1.I2cAddress = config.ADS1115Address1
|
||||
adsDevice1, err = ads1x15.NewADS1115(bus, &opts1)
|
||||
|
|
@ -78,7 +79,7 @@ func main() {
|
|||
log.Fatalf("ADS1115 #1 Init fehlgeschlagen: %v", err)
|
||||
}
|
||||
|
||||
// 2. Zweiten ADS1115 initialisieren (z.B. Adresse 73 / 0x49)
|
||||
// 2. Zweiten ADS1115 initialisieren
|
||||
opts2 := ads1x15.DefaultOpts
|
||||
opts2.I2cAddress = config.ADS1115Address2
|
||||
adsDevice2, err = ads1x15.NewADS1115(bus, &opts2)
|
||||
|
|
@ -86,48 +87,32 @@ func main() {
|
|||
log.Fatalf("ADS1115 #2 Init fehlgeschlagen: %v", err)
|
||||
}
|
||||
|
||||
// GPIO-Pins registrieren
|
||||
// GPIO-Pins registrieren (ID 1 ist der 230V Hauptschalter am Raspberry Pi)
|
||||
for i, r := range config.Relais {
|
||||
pin := gpioreg.ByName(r.GPIO)
|
||||
if pin == nil {
|
||||
log.Fatalf("GPIO-Pin %s existiert nicht", r.GPIO)
|
||||
if r.ID == 1 {
|
||||
pin := gpioreg.ByName(r.GPIO)
|
||||
if pin == nil {
|
||||
log.Fatalf("GPIO-Pin %s existiert nicht", r.GPIO)
|
||||
}
|
||||
if err := pin.Out(gpio.Low); err != nil {
|
||||
log.Fatalf("Pin-Konfiguration für %s fehlgeschlagen: %v", r.GPIO, err)
|
||||
}
|
||||
relaisMap[r.ID] = pin
|
||||
}
|
||||
if err := pin.Out(gpio.Low); err != nil {
|
||||
log.Fatalf("Pin-Konfiguration für %s fehlgeschlagen: %v", r.GPIO, err)
|
||||
}
|
||||
relaisMap[r.ID] = pin
|
||||
config.Relais[i].State = false
|
||||
}
|
||||
|
||||
// Instanziierung des Webservers mit Übergabe der Callback-Funktionen
|
||||
// Beim Start alle USB-Relais initial in den definierten AUS-Zustand versetzen
|
||||
updateUsbRelays()
|
||||
|
||||
server := web.NewServer(
|
||||
config.WebPort,
|
||||
readVoltageCallback,
|
||||
toggleRelaisCallback,
|
||||
getRelaisStateCallback,
|
||||
callbackGetVersion,
|
||||
getVoltNamesCallback,
|
||||
)
|
||||
// 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())
|
||||
}
|
||||
|
||||
// NEUES CALLBACK: Liefert die Namen der Kanäle an das Web-Paket
|
||||
func getVoltNamesCallback() []string {
|
||||
return []string{
|
||||
config.NameAdc1Ch0,
|
||||
config.NameAdc1Ch1,
|
||||
config.NameAdc1Ch2,
|
||||
config.NameAdc1Ch3,
|
||||
config.NameAdc2Ch0,
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
|
@ -155,9 +140,8 @@ func readVoltageCallback(channel int) float64 {
|
|||
factor = config.FactorAdc1Ch3
|
||||
case 4:
|
||||
targetDevice = adsDevice2
|
||||
adsChannel = ads1x15.Channel0 // Erster Eingang vom zweiten Chip
|
||||
adsChannel = ads1x15.Channel0
|
||||
factor = config.FactorAdc2Ch0
|
||||
|
||||
default:
|
||||
fmt.Printf("Ungültiger Kanal abgefragt: %v\n", channel)
|
||||
return 0.0
|
||||
|
|
@ -184,34 +168,109 @@ func toggleRelaisCallback(id int) {
|
|||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
pin, exists := relaisMap[id]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Hardware basierend auf dem neuen, echten Zustand schalten
|
||||
if config.Relais[i].State {
|
||||
pin.Out(gpio.High) // Wenn True -> Pin High
|
||||
if id == 1 {
|
||||
// Hardware-Schaltung Raspberry Pi GPIO
|
||||
pin, exists := relaisMap[id]
|
||||
if exists {
|
||||
if config.Relais[i].State {
|
||||
pin.Out(gpio.High)
|
||||
} else {
|
||||
pin.Out(gpio.Low)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pin.Out(gpio.Low) // Wenn False -> Pin Low
|
||||
// Hardware-Schaltung über Abacom USB-Karte via Bulk-Streaming
|
||||
updateUsbRelays()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Schaltet die Abacom-Karte nach dem Python-Skript-Muster via Bulk-Stream
|
||||
func updateUsbRelays() {
|
||||
fmt.Println("\n--- [USB DEBUG START] ---")
|
||||
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)
|
||||
return
|
||||
}
|
||||
if dev == nil {
|
||||
fmt.Println("[❌] Hardware-Fehler: Abacom-Karte wurde am USB-Port nicht gefunden!")
|
||||
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)
|
||||
return
|
||||
}
|
||||
defer cfg.Close()
|
||||
|
||||
intf, err := cfg.Interface(0, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("[❌] Interface-Fehler: %v\n", err)
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
fmt.Println("--- [USB DEBUG ENDE] ---\n")
|
||||
}
|
||||
|
||||
// Callback: Wird vom Webserver aufgerufen, um den aktuellen UI-Status abzufragen
|
||||
func getRelaisStateCallback() []web.RelaisData {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
data := make([]web.RelaisData, len(config.Relais))
|
||||
for i, r := range config.Relais {
|
||||
data[i] = web.RelaisData{
|
||||
|
|
@ -223,6 +282,17 @@ func getRelaisStateCallback() []web.RelaisData {
|
|||
return data
|
||||
}
|
||||
|
||||
// Callback: Liefert die Namen der Kanäle an das Web-Paket
|
||||
func getVoltNamesCallback() []string {
|
||||
return []string{
|
||||
config.NameAdc1Ch0,
|
||||
config.NameAdc1Ch1,
|
||||
config.NameAdc1Ch2,
|
||||
config.NameAdc1Ch3,
|
||||
config.NameAdc2Ch0,
|
||||
}
|
||||
}
|
||||
|
||||
// Versionangaben aus version.json in Webserver übernehmen
|
||||
func callbackGetVersion() (string, string) {
|
||||
return Version, BuildDate
|
||||
|
|
|
|||
48
relais.py
Normal file
48
relais.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#!/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 <Wert_0_bis_255>")
|
||||
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()
|
||||
56
relais2.py
Normal file
56
relais2.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#!/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 <Wert_0_bis_255>")
|
||||
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()
|
||||
BIN
spannung-amd64
BIN
spannung-amd64
Binary file not shown.
BIN
spannung-arm32_6
BIN
spannung-arm32_6
Binary file not shown.
BIN
spannung-arm32_7
BIN
spannung-arm32_7
Binary file not shown.
BIN
spannung-arm64
BIN
spannung-arm64
Binary file not shown.
Loading…
Reference in a new issue