package at import ( "fmt" "io" "log" "sync" "time" "go.bug.st/serial" ) // Some constants const ( ReadTimeout = time.Second InputBufSize = 512 ) 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, timeout time.Duration) (string, error) Send(cmd string) (Resp, error) SendWithTimeout(cmd string, timeout time.Duration) (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, timeout time.Duration) (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(timeout) // Read outBuf := make([]byte, 0) readLoop: for { readLen, err := p.port.Read(p.inputBuf) if err != nil { return "", fmt.Errorf("port read: %w", err) } if readLen == 0 { break readLoop } outBuf = append(outBuf, p.inputBuf[:readLen]...) if readLen < len(p.inputBuf) { break readLoop } } // p.logger.Println(msg, "\x1b[38;2;150;150;150mRAWREAD:", string(outBuf), "\x1b[38;2;255;255;255m") return string(outBuf), nil } func (p *atPort) Send(cmd string) (Resp, error) { rawResp, err := p.RawSend(cmd+"\r\n", time.Microsecond) if err != nil { return RespNil, fmt.Errorf("%s request: %w", cmd, err) } if len(rawResp) <= 4 { return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, len(rawResp), string(rawResp)) } resp := rawResp[2 : len(rawResp)-2] // Cut \r\n return Resp(resp), nil } func (p *atPort) SendWithTimeout(cmd string, timeout time.Duration) (Resp, error) { rawResp, err := p.RawSend(cmd+"\r\n", timeout) if err != nil { return RespNil, fmt.Errorf("%s request: %w", cmd, err) } if len(rawResp) <= 4 { return RespNil, fmt.Errorf("%s request: read too small msg: %d byte - %s", cmd, 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() }