|
|
Line 81: |
Line 81: |
| quit | | quit |
| </source> | | </source> |
− | === Python Helper Scripts === | + | == Example script == |
− | ==== obdii_read.py ====
| + | <source lang='bash'> |
− | <source lang='python'> | + | #!/bin/bash |
− | # WF 2017-02-10 | + | # WF 2019-01-24 |
− | # Python access to OBD trial
| + | cd $HOME/source/java/can4eve/can4eve_bluetooth |
− | import serial
| + | ./obdii_blue 00:04:3E:9F:A3:C0 |
− | import time
| |
− | import logging
| |
− | import string
| |
− | import io
| |
− | import os
| |
− | import sys
| |
− | | |
− | #
| |
− | #
| |
− | # Elm327 helper class
| |
− | #
| |
− | class Elm327:
| |
− | # initialize the serial communication
| |
− | def __init__(self,port="/dev/rfcomm0",baud=115200,timeout=1):
| |
− | self.log = logging.getLogger(__name__)
| |
− | self.port=port
| |
− | self.baud=baud
| |
− | self.timeout=timeout
| |
− | self.ser = serial.Serial(port,baud)
| |
− | self.ser.timeout=timeout
| |
− | | |
− | # send the given string
| |
− | def send(self,s):
| |
− | if (self.ser):
| |
− | self.ser.flushInput()
| |
− | self.ser.write(bytes(s + '\r\n', encoding = 'utf-8'))
| |
− | self.ser.flush()
| |
− | return self.read()
| |
− | else:
| |
− | raise RuntimeError('Serial port not initialized') from error
| |
− | | |
− | # read data from the serial device
| |
− | # http://stackoverflow.com/a/13018267/1497139
| |
− | def read(self):
| |
− | if (not self.ser):
| |
− | raise RuntimeError('Serial port not initialized') from error
| |
− | buffer=bytearray()
| |
− | time.sleep(1)
| |
− | # get all available data
| |
− | waiting=self.ser.inWaiting()
| |
− | if (waiting>0):
| |
− | readbytes= self.ser.read(self.ser.inWaiting())
| |
− | else:
| |
− | readbytes=[]
| |
− | # check
| |
− | if not readbytes:
| |
− | self.log.warning("read from "+self.port+" failed")
| |
− | | |
− | buffer.extend(readbytes)
| |
− | | |
− | return buffer.decode()
| |
− | | |
− | def close(self):
| |
− | if (not self.ser):
| |
− | raise RuntimeError('Serial port not initialized') from error
| |
− | self.ser.close()
| |
− | | |
− | def dcmd(self,cmd):
| |
− | response=self.send(cmd)
| |
− | print(response)
| |
− | | |
− | #elm=Elm327("/dev/rfcomm0",115200)
| |
− | elm=Elm327()
| |
− | elm.dcmd("ATD")
| |
− | elm.dcmd("ATZ")
| |
− | elm.dcmd("ATE0") # switch off echo
| |
− | elm.dcmd("ATL1") # switch on newlines
| |
− | elm.dcmd("ATI")
| |
− | elm.dcmd("ATSP6")
| |
− | elm.dcmd("ATDP")
| |
− | elm.dcmd("ATH1")
| |
− | elm.dcmd("ATD1")
| |
− | elm.dcmd("ATCAF0")
| |
− | if (len(sys.argv) >1):
| |
− | pid=sys.argv[1]
| |
− | elm.dcmd("ATCRA "+pid)
| |
− | elm.dcmd("AT MA")
| |
− | while True:
| |
− | response=elm.read()
| |
− | print(response)
| |
− | elm.close()
| |
− | | |
− | #s = input('Enter AT command --> ')
| |
− | #print ('AT command = ' + s)
| |
− | #ser.timeout = 1
| |
− | #response = ser.read(999).decode('utf-8')
| |
− | #print(response)
| |
− | #ser.close()
| |
− | </source>
| |
− | | |
− | ==== 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> |