Files
workspace-led-controller/controller/relay/relay.go
2026-04-07 23:46:35 +03:00

83 lines
1.6 KiB
Go

package relay
import (
"fmt"
"os"
"golang.org/x/sys/unix"
)
// CH341 serial relay protocol (LCUS-type modules on /dev/ttyUSBx).
//
// Command format:
// ON: 0xA0 <channel> 0x01 <checksum>
// OFF: 0xA0 <channel> 0x00 <checksum>
// checksum = (0xA0 + channel + state) & 0xFF
type Relay struct {
devicePath string
channel byte
baud int
}
func New(devicePath string, channel, baud int) *Relay {
return &Relay{
devicePath: devicePath,
channel: byte(channel),
baud: baud,
}
}
func (r *Relay) Set(on bool) error {
f, err := os.OpenFile(r.devicePath, os.O_RDWR|unix.O_NOCTTY, 0)
if err != nil {
return fmt.Errorf("relay open %s: %w", r.devicePath, err)
}
defer f.Close()
fd := int(f.Fd())
if err := configureSerial(fd, r.baud); err != nil {
return fmt.Errorf("relay serial config: %w", err)
}
var state byte
if on {
state = 0x01
}
checksum := (0xA0 + r.channel + state) & 0xFF
cmd := []byte{0xA0, r.channel, state, checksum}
if _, err := f.Write(cmd); err != nil {
return fmt.Errorf("relay write: %w", err)
}
return nil
}
func (r *Relay) Close() error {
return nil
}
func configureSerial(fd, baud int) error {
baudRate, ok := baudRates[baud]
if !ok {
return fmt.Errorf("unsupported baud rate: %d", baud)
}
var t unix.Termios
t.Cflag = unix.CS8 | unix.CLOCAL | unix.CREAD | baudRate
t.Cc[unix.VMIN] = 1
t.Cc[unix.VTIME] = 0
return unix.IoctlSetTermios(fd, unix.TCSETS, &t)
}
var baudRates = map[int]uint32{
9600: unix.B9600,
19200: unix.B19200,
38400: unix.B38400,
57600: unix.B57600,
115200: unix.B115200,
}