package at import ( "fmt" "io" "log" "sync" "time" "go.bug.st/serial" ) // Some constants const ( ReadTimeout = time.Second InputBufSize = 128 ) type atPort struct { logger *log.Logger mutex sync.Mutex // Mutex for all operation with serial port baudrate int portName string port serial.Port inputBuf []byte } type Port interface { GetName() string GetBaudrate() int SerialPort() serial.Port // For extra need Mutex() sync.Locker // retruns pointer to mutex for advanced use like readign NMEA reports Connect() error Disconnect() error IsConnected() bool RawSend(msg string) (string, error) Send(cmd string) (Resp, error) io.Closer } func New(logger *log.Logger, portName string, baudrate int) Port { return &atPort{ logger: logger, portName: portName, baudrate: baudrate, inputBuf: make([]byte, InputBufSize), } } func (p *atPort) GetName() string { return p.portName } func (p *atPort) GetBaudrate() int { return p.baudrate } func (p *atPort) SerialPort() serial.Port { return p.port } func (p *atPort) Mutex() sync.Locker { return &p.mutex } func (p *atPort) Connect() error { p.mutex.Lock() defer p.mutex.Unlock() p.logger.Println("Connecting to", p.portName, "...") s, err := serial.Open(p.portName, &serial.Mode{BaudRate: p.baudrate}) if err != nil { return fmt.Errorf("open port: %w", err) } // s.Close() There is no open f // s.Open() p.port = s p.port.SetReadTimeout(ReadTimeout) p.port.ResetInputBuffer() p.port.ResetOutputBuffer() return nil } func (p *atPort) Disconnect() error { p.mutex.Lock() defer func() { p.port = nil p.mutex.Unlock() }() if err := p.port.Close(); err != nil { return fmt.Errorf("close port: %w", err) } return nil } func (p *atPort) IsConnected() bool { p.mutex.Lock() defer p.mutex.Unlock() return p.port != nil } // Low level write/read function func (p *atPort) RawSend(msg string) (string, error) { p.mutex.Lock() defer p.mutex.Unlock() // Write if _, err := p.port.Write([]byte(msg)); err != nil { return "", fmt.Errorf("serial port write: %w", err) } // time.Sleep(time.Millisecond) // Read readLen, err := p.port.Read(p.inputBuf) // p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(p.inputBuf[:readLen]), "\x1b[38;2;255;255;255m") if err != nil { return "", fmt.Errorf("port read: %w", err) } return string(p.inputBuf[:readLen]), nil } func (p *atPort) Send(cmd string) (Resp, error) { cmd += "\r\n" rawResp, err := p.RawSend(cmd) if err != nil { return RespNil, fmt.Errorf("make request: %w", err) } if len(rawResp) <= 4 { return RespNil, fmt.Errorf("read too small msg: %d byte - %s", len(rawResp), string(rawResp)) } resp := rawResp[2 : len(rawResp)-2] // Cut \r\n return Resp(resp), nil } func (p *atPort) Close() error { p.mutex.Lock() defer p.mutex.Unlock() return p.port.Close() }