wjmnelis
Posts: 25
Joined: Thu Oct 26, 2017 5:03 pm

[Solved] serial port API documentation

Thu Aug 16, 2018 5:29 pm

My next project is to read asynchronous (9600, 8N1) data from a bus. Each message is terminated by a break signal. I hope it will be possible to read messages from the serial async port using Python 3. I have been searching for documentation of the API for the serial port on a Raspberry Pi 0W (or 3B) running a recent version of Raspbian, but I did not find it. Can someone give a pointer to such documentation?
Last edited by wjmnelis on Mon Feb 04, 2019 9:09 am, edited 1 time in total.

User avatar
joan
Posts: 13661
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: serial port API documentation

Thu Aug 16, 2018 5:35 pm

What documentation do you expect for a serial port?

Any relevant documentation will be in the Python serial module you choose to use.

wjmnelis
Posts: 25
Joined: Thu Oct 26, 2017 5:03 pm

Re: serial port API documentation

Fri Aug 17, 2018 10:43 am

The most important I am looking for are ways to handle the reception of a break signal. The only serial Python module I know of is PySerial. With this module, one can send a break, but I've found nothing about receiving a break signal. Perhaps I need to go one level deeper, and use Linux I/O and ioctl to do what I want to be done. Even with the use of Google, I could not find any serial port specific ioctl documentation.

User avatar
joan
Posts: 13661
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: serial port API documentation

Fri Aug 17, 2018 11:36 am

I'd avoid break if possible. Is it even part of any standard? Can you find an alternative way to signal the condition?

wjmnelis
Posts: 25
Joined: Thu Oct 26, 2017 5:03 pm

Re: serial port API documentation

Sun Aug 19, 2018 2:50 pm

Yes, it's part of a proprietary standard called EMS, which is the protocol used between in the central heating systems of a few manufacturers, amongst which Nefit.

Paul Hutch
Posts: 334
Joined: Fri Aug 25, 2017 2:58 pm
Location: Blackstone River Valley, MA, USA

Re: serial port API documentation

Mon Aug 20, 2018 2:31 pm

I think you will need to use lower level code to detect a break and depending on the hardware UART's architecture it may not be detectable. Break is simply a signal outside the limits set in the TIA-232 standard (above -3V and below +3V, like a break in the wiring will give). Some modern hardware UARTS consider 0 volts a valid signal for a simpler hardware design (not TIA-232 compliant) so with those UARTS a break is not detectable.

wjmnelis
Posts: 25
Joined: Thu Oct 26, 2017 5:03 pm

Re: serial port API documentation

Wed Aug 29, 2018 8:30 am

A break is a logical zero which lasts longer than one word, thus from start bit up to and including the stop bits.
The original question still stands: where can I find some documentation about his "lower level coding", which is hopefully an API.
This APi in turn can hopefully be used to extend module PySerial to recognise a received break signal when using the PL011 of an RPi.

User avatar
joan
Posts: 13661
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: serial port API documentation

Wed Aug 29, 2018 9:38 am

I doubt such general documentation exists.

Here is a method of detecting the condition.

Code: Select all

#!/usr/bin/env python

import pigpio
import time

RX=15

BREAK_MICROS = int(11.0/9600.0*1e6)

def break_detected():
   print("break detected")

def callback_func(gpio, level, tick):
   break_detected()           # long low detected

pi = pigpio.pi()              # connect to daemon
if not pi.connected:
   exit()

pi.set_glitch_filter(RX, BREAK_MICROS) # ignore short level changes

print(BREAK_MICROS)

cb = pi.callback(RX, pigpio.FALLING_EDGE, callback_func)

time.sleep(300)

cb.cancel()                    # cancel callback
pi.set_glitch_filter(RX, 0)    # cancel glitch filter
pi.stop()                      # disconnect from daemon


6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 6195
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: serial port API documentation

Wed Aug 29, 2018 11:11 am

https://github.com/raspberrypi/linux/bl ... al/tty.txt
set_termios()
Notify the tty driver that the device's termios
settings have changed. New settings are in
tty->termios. Previous settings should be passed in
the "old" argument.

The API is defined such that the driver should return
the actual modes selected. This means that the
driver function is responsible for modifying any
bits in the request it cannot fulfill to indicate
the actual modes being used. A device with no
hardware capability for change (e.g. a USB dongle or
virtual port) can provide NULL for this method.
https://linux.die.net/man/3/termios
c_iflag flag constants:
...
If IGNBRK is set, a BREAK is ignored. If it is not set but BRKINT is set, then a BREAK causes the input and output queues to be flushed, and if the terminal is the controlling terminal of a foreground process group, it will cause a SIGINT to be sent to this foreground process group. When neither IGNBRK nor BRKINT are set, a BREAK reads as a null byte ('\0'), except when PARMRK is set, in which case it reads as the sequence \377 \0 \0.
So it appears your want to set PARMRK and BRKINT, and then monitor the incoming data stream for 0xFF,0x00,0x00 (don't ask why they quote it in octal!)
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

User avatar
davidcoton
Posts: 3485
Joined: Mon Sep 01, 2014 2:37 pm
Location: Cambridge, UK

Re: serial port API documentation

Wed Aug 29, 2018 1:07 pm

6by9 wrote:
Wed Aug 29, 2018 11:11 am
https://github.com/raspberrypi/linux/bl ... al/tty.txt
c_iflag flag constants:
...
... BRKINT is set, then a BREAK causes the input and output queues to be flushed, and if the terminal is the controlling terminal of a foreground process group, it will cause a SIGINT to be sent to this foreground process group. When neither IGNBRK nor BRKINT are set, a BREAK reads as a null byte ('\0'), except when PARMRK is set, in which case it reads as the sequence \377 \0 \0.
So it appears your want to set PARMRK and BRKINT, and then monitor the incoming data stream for 0xFF,0x00,0x00 (don't ask why they quote it in octal!)
As I read that, to get the byte sequence representing BREAK in the stream, only PARMRK should be set, IGNBRK and BRKINT should be unset. :?
Signature retired

6by9
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 6195
Joined: Wed Dec 04, 2013 11:27 am
Location: ZZ9 Plural Z Alpha, aka just outside Cambridge.

Re: serial port API documentation

Wed Aug 29, 2018 1:32 pm

davidcoton wrote:
Wed Aug 29, 2018 1:07 pm
As I read that, to get the byte sequence representing BREAK in the stream, only PARMRK should be set, IGNBRK and BRKINT should be unset. :?
You're right, my bad. I skim read it and stumbled over the "if this else that" phrasing.
Software Engineer at Raspberry Pi Trading. Views expressed are still personal views.
I'm not interested in doing contracts for bespoke functionality - please don't ask.

wjmnelis
Posts: 25
Joined: Thu Oct 26, 2017 5:03 pm

Re: serial port API documentation

Sun Sep 02, 2018 2:17 pm

Thanks for the pointers. Although anxious to get started, I now have to wait for the hardware (level shifter) to arrive.

wjmnelis
Posts: 25
Joined: Thu Oct 26, 2017 5:03 pm

[Solved]: serial port API documentation

Mon Jan 28, 2019 11:41 am

It has taken quite some time, but after the hardware was delivered and some weeks of programming and testing, there is now a working demonstrator: frames terminated by a BREAK signal on the serial bus can be read, using RPi, raspbian and Python3 code running at user level. The code snippet below shows how BREAKs are passed in the data-stream:

Code: Select all

 #
 # Method open starts communications via the serial port. The serial port is by
 # default set to 9600N1. Using termios, iflag PARMRK is set. This causes breaks
 # to be entered as 3 octets ( 0xff, 0x00, 0x00 ) in the input stream. It also
 # causes octets with a framing error to become strings of 3 octets: 0xff, 0x00
 # and the erred octet. Note that a genuine 0xff octet is translated into two
 # octets: 0xff, 0xff.
 #
  def open( self ):
    try:
      self.serial= serial.Serial( port = SERIAL_Device )
    except serial.SerialException as e:
      self._log_message( "Could not open port {}: {}".format(self.serial.name,e) )
      self.serial= None
      return None

   # Set iflag PARMRK to enable break detection and reporting.
    attr= termios.tcgetattr( self.serial.fd )
    attr[0]|= termios.PARMRK
    termios.tcsetattr( self.serial.fd, termios.TCSANOW, attr )

    return self.serial
    
Of course, when receiving the data-stream, one has to look for the BREAK-indicator and mark the end of a frame if found. Note that if the original data-stream contains the three octets 0xff,0x00,0x00 it is translated to the four octets 0xff,0xff,0x00,0x00, which could be mistaken for a BREAK-indicator. It is found that a real BREAK-indicator is preceded by an even number of 0xff octets, never by an odd number. After detecting the end of a frame, the framing error indicator and the doubling of the 0xff octets should be dealt with.
Last edited by wjmnelis on Wed Feb 06, 2019 3:17 pm, edited 1 time in total.

hippy
Posts: 4348
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: [Solved]: serial port API documentation

Mon Jan 28, 2019 4:10 pm

wjmnelis wrote:
Mon Jan 28, 2019 11:41 am
The code snippet below shows how BREAKs are passed in the data-stream:
Many thanks for that and your work put into it. I have been relying on 'break' being seen as a 0x00 byte in the stream, which works well enough for ASCII data and how I am using serial, but isn't so great for binary streams. Next time I revisit my code I'll see if I can make your code work for me.

Return to “Advanced users”