Difference between revisions of "Raspberry PI"
Line 73: | Line 73: | ||
[NEW] Device 00:04:3E:9F:A3:C0 OBDLink LX | [NEW] Device 00:04:3E:9F:A3:C0 OBDLink LX | ||
quit | quit | ||
+ | </source> | ||
+ | === Python Helper Scripts === | ||
+ | ==== tcp_serial_redirect.py ==== | ||
+ | <source lang='python'> | ||
+ | #!/usr/bin/env python | ||
+ | # | ||
+ | # Redirect data from a TCP/IP connection to a serial port and vice versa. | ||
+ | # | ||
+ | # (C) 2002-2016 Chris Liechti <cliechti@gmx.net> | ||
+ | # | ||
+ | # SPDX-License-Identifier: BSD-3-Clause | ||
+ | |||
+ | import sys | ||
+ | import socket | ||
+ | import serial | ||
+ | import serial.threaded | ||
+ | import time | ||
+ | import datetime | ||
+ | |||
+ | |||
+ | class SerialToNet(serial.threaded.Protocol): | ||
+ | """serial->socket""" | ||
+ | |||
+ | def __init__(self): | ||
+ | self.socket = None | ||
+ | |||
+ | def __call__(self): | ||
+ | return self | ||
+ | |||
+ | # callback function | ||
+ | # this is called by the ReaderThread on receiving data from | ||
+ | # the serial device | ||
+ | def data_received(self, data): | ||
+ | if self.socket is not None: | ||
+ | # first send data over network | ||
+ | self.socket.sendall(data) | ||
+ | # optionally show it for debug | ||
+ | if (self.debug): | ||
+ | print("r"+datetime.datetime.now().isoformat()+":"+data) | ||
+ | |||
+ | if __name__ == '__main__': # noqa | ||
+ | import argparse | ||
+ | |||
+ | parser = argparse.ArgumentParser( | ||
+ | description='Simple Serial to Network (TCP/IP) redirector.', | ||
+ | epilog="""\ | ||
+ | NOTE: no security measures are implemented. Anyone can remotely connect | ||
+ | to this service over the network. | ||
+ | |||
+ | Only one connection at once is supported. When the connection is terminated | ||
+ | it waits for the next connect. | ||
+ | """) | ||
+ | |||
+ | parser.add_argument( | ||
+ | 'SERIALPORT', | ||
+ | help="serial port name") | ||
+ | |||
+ | parser.add_argument( | ||
+ | 'BAUDRATE', | ||
+ | type=int, | ||
+ | nargs='?', | ||
+ | help='set baud rate, default: %(default)s', | ||
+ | default=9600) | ||
+ | |||
+ | parser.add_argument( | ||
+ | '-d', '--debug', | ||
+ | action='store_true', | ||
+ | help='debug', | ||
+ | default=False) | ||
+ | |||
+ | parser.add_argument( | ||
+ | '-q', '--quiet', | ||
+ | action='store_true', | ||
+ | help='suppress non error messages', | ||
+ | default=False) | ||
+ | |||
+ | parser.add_argument( | ||
+ | '--develop', | ||
+ | action='store_true', | ||
+ | help='Development mode, prints Python internals on errors', | ||
+ | default=False) | ||
+ | |||
+ | group = parser.add_argument_group('serial port') | ||
+ | |||
+ | group.add_argument( | ||
+ | "--parity", | ||
+ | choices=['N', 'E', 'O', 'S', 'M'], | ||
+ | type=lambda c: c.upper(), | ||
+ | help="set parity, one of {N E O S M}, default: N", | ||
+ | default='N') | ||
+ | |||
+ | group.add_argument( | ||
+ | '--rtscts', | ||
+ | action='store_true', | ||
+ | help='enable RTS/CTS flow control (default off)', | ||
+ | default=False) | ||
+ | |||
+ | group.add_argument( | ||
+ | '--xonxoff', | ||
+ | action='store_true', | ||
+ | help='enable software flow control (default off)', | ||
+ | default=False) | ||
+ | |||
+ | group.add_argument( | ||
+ | '--rts', | ||
+ | type=int, | ||
+ | help='set initial RTS line state (possible values: 0, 1)', | ||
+ | default=None) | ||
+ | |||
+ | group.add_argument( | ||
+ | '--dtr', | ||
+ | type=int, | ||
+ | help='set initial DTR line state (possible values: 0, 1)', | ||
+ | default=None) | ||
+ | |||
+ | group = parser.add_argument_group('network settings') | ||
+ | |||
+ | exclusive_group = group.add_mutually_exclusive_group() | ||
+ | |||
+ | exclusive_group.add_argument( | ||
+ | '-P', '--localport', | ||
+ | type=int, | ||
+ | help='local TCP port', | ||
+ | default=7777) | ||
+ | |||
+ | exclusive_group.add_argument( | ||
+ | '-c', '--client', | ||
+ | metavar='HOST:PORT', | ||
+ | help='make the connection as a client, instead of running a server', | ||
+ | default=False) | ||
+ | |||
+ | args = parser.parse_args() | ||
+ | |||
+ | # connect to serial port | ||
+ | ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True) | ||
+ | ser.baudrate = args.BAUDRATE | ||
+ | ser.parity = args.parity | ||
+ | ser.rtscts = args.rtscts | ||
+ | ser.xonxoff = args.xonxoff | ||
+ | |||
+ | if args.rts is not None: | ||
+ | ser.rts = args.rts | ||
+ | |||
+ | if args.dtr is not None: | ||
+ | ser.dtr = args.dtr | ||
+ | |||
+ | if not args.quiet: | ||
+ | sys.stderr.write( | ||
+ | '--- TCP/IP to Serial redirect on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n' | ||
+ | '--- type Ctrl-C / BREAK to quit\n'.format(p=ser)) | ||
+ | |||
+ | try: | ||
+ | ser.open() | ||
+ | except serial.SerialException as e: | ||
+ | sys.stderr.write('Could not open serial port {}: {}\n'.format(ser.name, e)) | ||
+ | sys.exit(1) | ||
+ | |||
+ | ser_to_net = SerialToNet() | ||
+ | ser_to_net.debug=args.debug | ||
+ | serial_worker = serial.threaded.ReaderThread(ser, ser_to_net) | ||
+ | serial_worker.start() | ||
+ | |||
+ | if not args.client: | ||
+ | # open the socket as a streaming socket | ||
+ | srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
+ | srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
+ | srv.bind(('', args.localport)) | ||
+ | srv.listen(1) | ||
+ | try: | ||
+ | intentional_exit = False | ||
+ | while True: | ||
+ | if args.client: | ||
+ | host, port = args.client.split(':') | ||
+ | sys.stderr.write("Opening connection to {}:{}...\n".format(host, port)) | ||
+ | client_socket = socket.socket() | ||
+ | try: | ||
+ | client_socket.connect((host, int(port))) | ||
+ | except socket.error as msg: | ||
+ | sys.stderr.write('WARNING: {}\n'.format(msg)) | ||
+ | time.sleep(5) # intentional delay on reconnection as client | ||
+ | continue | ||
+ | sys.stderr.write('Connected\n') | ||
+ | client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | ||
+ | #~ client_socket.settimeout(5) | ||
+ | else: | ||
+ | sys.stderr.write('Waiting for connection on {}...\n'.format(args.localport)) | ||
+ | client_socket, addr = srv.accept() | ||
+ | sys.stderr.write('Connected by {}\n'.format(addr)) | ||
+ | # More quickly detect bad clients who quit without closing the | ||
+ | # connection: After 1 second of idle, start sending TCP keep-alive | ||
+ | # packets every 1 second. If 3 consecutive keep-alive packets | ||
+ | # fail, assume the client is gone and close the connection. | ||
+ | try: | ||
+ | client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1) | ||
+ | client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1) | ||
+ | client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3) | ||
+ | client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) | ||
+ | except AttributeError: | ||
+ | pass # XXX not available on windows | ||
+ | client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | ||
+ | try: | ||
+ | ser_to_net.socket = client_socket | ||
+ | # enter network <-> serial loop | ||
+ | while True: | ||
+ | try: | ||
+ | # read data from serial | ||
+ | data = client_socket.recv(2048) | ||
+ | if not data: | ||
+ | break | ||
+ | ser.write(data) # get a bunch of bytes and send them | ||
+ | if (args.debug): | ||
+ | print("s"+datetime.datetime.now().isoformat()+":"+data) | ||
+ | except socket.error as msg: | ||
+ | if args.develop: | ||
+ | raise | ||
+ | sys.stderr.write('ERROR: {}\n'.format(msg)) | ||
+ | # probably got disconnected | ||
+ | break | ||
+ | except KeyboardInterrupt: | ||
+ | intentional_exit = True | ||
+ | raise | ||
+ | except socket.error as msg: | ||
+ | if args.develop: | ||
+ | raise | ||
+ | sys.stderr.write('ERROR: {}\n'.format(msg)) | ||
+ | finally: | ||
+ | ser_to_net.socket = None | ||
+ | sys.stderr.write('Disconnected\n') | ||
+ | client_socket.close() | ||
+ | if args.client and not intentional_exit: | ||
+ | time.sleep(5) # intentional delay on reconnection as client | ||
+ | except KeyboardInterrupt: | ||
+ | pass | ||
+ | |||
+ | sys.stderr.write('\n--- exit ---\n') | ||
+ | serial_worker.stop() | ||
</source> | </source> |
Revision as of 08:59, 24 January 2019
To run on a Raspberry PI you need a properly configured device. See e.g. http://ion.bitplan.com/index.php/Ion2017-07-18#Raspberry_PI
Hardware
See e.g.
Raspberry Setup
You'll find setup guides all over the internet here are just two links:
- https://www.macworld.co.uk/how-to/mac/how-to-set-up-raspberry-pi-3-with-mac-3637490/
- https://www.raspberrypi.org/downloads/raspbian/
Software configuration
cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
Java 8 installation
apt-get install oracle-java8-jdk
Run script
If you have an official touchscreen you can control the brightness of the screen with the darkmode button of can4eve.
You might want to make sure that you use the run script to start the app. The important lines are:
sudo chmod 0664 /sys/class/backlight/rpi_backlight/brightness
sudo chown root.pi /sys/class/backlight/rpi_backlight/brightness
Autostart
Example Can4Eve.desktop file to be placed in .config/autostart of your user. Please adapt the exec line to where you installed can4eve
[Desktop Entry]
Type=Application
Name=Can4Eve
Comment=Onboard Diagnostic software for Electric Vehicles
NoDisplay=false
Exec=/home/wf/source/java/can4eve/run
Bluetooth
see https://www.raspberrypi.org/forums/viewtopic.php?t=214373
check connection
Bluetooth services
service bluetooth status
● bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset
Active: active (running) since Thu 2019-01-24 08:51:05 CET; 3min 2s ago
Docs: man:bluetoothd(8)
Main PID: 849 (bluetoothd)
Status: "Running"
CGroup: /system.slice/bluetooth.service
└─849 /usr/lib/bluetooth/bluetoothd
Pairing
Pair the device using the Graphical User Interface Bluetooth button in the top right. You will not be able to connect the message is "Connection failed - No usabel services on this device". We'll work around this with some python helper software Check result with commmand line:
bluetoothctl
[NEW] Controller B8:27:EB:2E:AB:2B pito [default]
[NEW] Device 00:04:3E:9F:A3:C0 OBDLink LX
quit
Python Helper Scripts
tcp_serial_redirect.py
#!/usr/bin/env python
#
# Redirect data from a TCP/IP connection to a serial port and vice versa.
#
# (C) 2002-2016 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import sys
import socket
import serial
import serial.threaded
import time
import datetime
class SerialToNet(serial.threaded.Protocol):
"""serial->socket"""
def __init__(self):
self.socket = None
def __call__(self):
return self
# callback function
# this is called by the ReaderThread on receiving data from
# the serial device
def data_received(self, data):
if self.socket is not None:
# first send data over network
self.socket.sendall(data)
# optionally show it for debug
if (self.debug):
print("r"+datetime.datetime.now().isoformat()+":"+data)
if __name__ == '__main__': # noqa
import argparse
parser = argparse.ArgumentParser(
description='Simple Serial to Network (TCP/IP) redirector.',
epilog="""\
NOTE: no security measures are implemented. Anyone can remotely connect
to this service over the network.
Only one connection at once is supported. When the connection is terminated
it waits for the next connect.
""")
parser.add_argument(
'SERIALPORT',
help="serial port name")
parser.add_argument(
'BAUDRATE',
type=int,
nargs='?',
help='set baud rate, default: %(default)s',
default=9600)
parser.add_argument(
'-d', '--debug',
action='store_true',
help='debug',
default=False)
parser.add_argument(
'-q', '--quiet',
action='store_true',
help='suppress non error messages',
default=False)
parser.add_argument(
'--develop',
action='store_true',
help='Development mode, prints Python internals on errors',
default=False)
group = parser.add_argument_group('serial port')
group.add_argument(
"--parity",
choices=['N', 'E', 'O', 'S', 'M'],
type=lambda c: c.upper(),
help="set parity, one of {N E O S M}, default: N",
default='N')
group.add_argument(
'--rtscts',
action='store_true',
help='enable RTS/CTS flow control (default off)',
default=False)
group.add_argument(
'--xonxoff',
action='store_true',
help='enable software flow control (default off)',
default=False)
group.add_argument(
'--rts',
type=int,
help='set initial RTS line state (possible values: 0, 1)',
default=None)
group.add_argument(
'--dtr',
type=int,
help='set initial DTR line state (possible values: 0, 1)',
default=None)
group = parser.add_argument_group('network settings')
exclusive_group = group.add_mutually_exclusive_group()
exclusive_group.add_argument(
'-P', '--localport',
type=int,
help='local TCP port',
default=7777)
exclusive_group.add_argument(
'-c', '--client',
metavar='HOST:PORT',
help='make the connection as a client, instead of running a server',
default=False)
args = parser.parse_args()
# connect to serial port
ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True)
ser.baudrate = args.BAUDRATE
ser.parity = args.parity
ser.rtscts = args.rtscts
ser.xonxoff = args.xonxoff
if args.rts is not None:
ser.rts = args.rts
if args.dtr is not None:
ser.dtr = args.dtr
if not args.quiet:
sys.stderr.write(
'--- TCP/IP to Serial redirect on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'
'--- type Ctrl-C / BREAK to quit\n'.format(p=ser))
try:
ser.open()
except serial.SerialException as e:
sys.stderr.write('Could not open serial port {}: {}\n'.format(ser.name, e))
sys.exit(1)
ser_to_net = SerialToNet()
ser_to_net.debug=args.debug
serial_worker = serial.threaded.ReaderThread(ser, ser_to_net)
serial_worker.start()
if not args.client:
# open the socket as a streaming socket
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('', args.localport))
srv.listen(1)
try:
intentional_exit = False
while True:
if args.client:
host, port = args.client.split(':')
sys.stderr.write("Opening connection to {}:{}...\n".format(host, port))
client_socket = socket.socket()
try:
client_socket.connect((host, int(port)))
except socket.error as msg:
sys.stderr.write('WARNING: {}\n'.format(msg))
time.sleep(5) # intentional delay on reconnection as client
continue
sys.stderr.write('Connected\n')
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
#~ client_socket.settimeout(5)
else:
sys.stderr.write('Waiting for connection on {}...\n'.format(args.localport))
client_socket, addr = srv.accept()
sys.stderr.write('Connected by {}\n'.format(addr))
# More quickly detect bad clients who quit without closing the
# connection: After 1 second of idle, start sending TCP keep-alive
# packets every 1 second. If 3 consecutive keep-alive packets
# fail, assume the client is gone and close the connection.
try:
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
except AttributeError:
pass # XXX not available on windows
client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
ser_to_net.socket = client_socket
# enter network <-> serial loop
while True:
try:
# read data from serial
data = client_socket.recv(2048)
if not data:
break
ser.write(data) # get a bunch of bytes and send them
if (args.debug):
print("s"+datetime.datetime.now().isoformat()+":"+data)
except socket.error as msg:
if args.develop:
raise
sys.stderr.write('ERROR: {}\n'.format(msg))
# probably got disconnected
break
except KeyboardInterrupt:
intentional_exit = True
raise
except socket.error as msg:
if args.develop:
raise
sys.stderr.write('ERROR: {}\n'.format(msg))
finally:
ser_to_net.socket = None
sys.stderr.write('Disconnected\n')
client_socket.close()
if args.client and not intentional_exit:
time.sleep(5) # intentional delay on reconnection as client
except KeyboardInterrupt:
pass
sys.stderr.write('\n--- exit ---\n')
serial_worker.stop()