github.com/simpleiot/simpleiot@v0.18.3/respreader/doc.go (about) 1 /* 2 Package respreader provides a convenient way to frame response data from devices 3 that use 4 prompt/response protocols such as Modbus, other RS485 protocols, and modem 5 AT commands. The fundamental assumption is a device takes some variable amount of 6 time to respond to a prompt, formats up a response, and then streams it out the 7 serial port. Once the response data starts streaming, any significant gap in the 8 response with 9 no data indicates the response is complete. A Read() blocks until it detects this 10 "gap" or the overall timeout is reached, and then returns with accumulated data. 11 12 This method of framing a response has the following advantages: 13 14 1) minimizes the wasted time waiting for a response to the chunkTimeout defined 15 below. More simplistic implementations often take the worst case response time 16 for all packets and simply wait that amount of time for the response to arrive. 17 This works, but the bus is tied up during this worst case wait that could be used for 18 sending the next packet. 19 20 2) It is simple in that you don't have to parse the response on the fly to determine 21 when it is complete, yet it can detect the end of a response fairly quickly. 22 23 The obvious disadvantage of this method of framing is that the device may insert a 24 significant delay in sending the response that will cause the reader to think the 25 response is complete. As long as this delay is still significantly shorter than 26 the overall response time, it can still work fairly well. Some experimentation may 27 be required to optimize the chunkTimeout setting. 28 29 Example using a serial port: 30 31 import ( 32 "io" 33 34 "github.com/jacobsa/go-serial/serial" 35 "github.com/simpleiot/simpleiot/respreader" 36 ) 37 38 options := serial.OpenOptions{ 39 PortName: "/dev/ttyUSB0", 40 BaudRate: 9600, 41 DataBits: 8, 42 StopBits: 1, 43 MinimumReadSize: 0, 44 // with serial ports, you just set 45 // InterCharacterTimeout to 100 or larger. 46 // Otherwise, the goroutine reading the serial 47 // port will never exit when you close the read 48 // and will still data the next time you open 49 // the port. Be aware it may take 100ms for this 50 // to close. The linux kernel only accepts timeouts 51 // in increments of 0.1s. When using serial ports it 52 // makes sense to set the chunkTimeout to 100ms as well. 53 // With Go files, a read is supposed to return when 54 // the File is closed, but this does not seem to be 55 // working with Linux serial devices. 56 InterCharacterTimeout: 100, 57 RTSCTSFlowControl: true, 58 } 59 60 port, err := serial.Open(options) 61 62 port = respreader.NewResponseReadWriteCloser(port, time.Second, 63 time.Millisecond * 50) 64 65 // send out prompt 66 port.Write("ATAI") 67 68 // read response 69 data := make([]byte, 128) 70 count, err := port.Read(data) 71 data = data[0:count] 72 73 // now process response ... 74 75 // to close the reader process, you must call Close on the reader. 76 // This sets a flag that causes the reader goroutine to exit. 77 port.Close() 78 79 Three types are provided for convenience that wrap io.Reader, io.ReadWriter, and io.ReadWriteCloser. 80 */ 81 package respreader