83 lines
1.6 KiB
Go
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,
|
|
}
|