Serial plugin extended with a general purpose read() method - to communicate for e.g. Espruino and compatible devices
This commit is contained in:
parent
28223cab30
commit
7586412ded
1 changed files with 126 additions and 9 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import base64
|
||||||
import json
|
import json
|
||||||
import serial
|
import serial
|
||||||
import threading
|
import threading
|
||||||
|
@ -17,7 +18,7 @@ class SerialPlugin(GpioSensorPlugin):
|
||||||
https://github.com/bblanchon/ArduinoJson.
|
https://github.com/bblanchon/ArduinoJson.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, device, baud_rate=9600, *args, **kwargs):
|
def __init__(self, device=None, baud_rate=9600, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param device: Device path (e.g. ``/dev/ttyUSB0`` or ``/dev/ttyACM0``)
|
:param device: Device path (e.g. ``/dev/ttyUSB0`` or ``/dev/ttyACM0``)
|
||||||
:type device: str
|
:type device: str
|
||||||
|
@ -64,14 +65,24 @@ class SerialPlugin(GpioSensorPlugin):
|
||||||
|
|
||||||
return output.decode().strip()
|
return output.decode().strip()
|
||||||
|
|
||||||
def _get_serial(self, reset=False):
|
def _get_serial(self, device=None, baud_rate=None, reset=False):
|
||||||
|
if not device:
|
||||||
|
if not self.device:
|
||||||
|
raise RuntimeError('No device specified nor default device configured')
|
||||||
|
device = self.device
|
||||||
|
|
||||||
|
if baud_rate is None:
|
||||||
|
if self.baud_rate is None:
|
||||||
|
raise RuntimeError('No baud_rate specified nor default configured')
|
||||||
|
baud_rate = self.baud_rate
|
||||||
|
|
||||||
if self.serial:
|
if self.serial:
|
||||||
if not reset:
|
if not reset:
|
||||||
return self.serial
|
return self.serial
|
||||||
|
|
||||||
self._close_serial()
|
self._close_serial()
|
||||||
|
|
||||||
self.serial = serial.Serial(self.device, self.baud_rate)
|
self.serial = serial.Serial(device, baud_rate)
|
||||||
return self.serial
|
return self.serial
|
||||||
|
|
||||||
def _close_serial(self):
|
def _close_serial(self):
|
||||||
|
@ -84,21 +95,37 @@ class SerialPlugin(GpioSensorPlugin):
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_measurement(self):
|
def get_measurement(self, device=None, baud_rate=None):
|
||||||
"""
|
"""
|
||||||
Reads JSON data from the serial device and returns it as a message
|
Reads JSON data from the serial device and returns it as a message
|
||||||
|
|
||||||
|
:param device: Device path (default: default configured device)
|
||||||
|
:type device: str
|
||||||
|
|
||||||
|
:param baud_rate: Baud rate (default: default configured baud_rate)
|
||||||
|
:type baud_rate: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not device:
|
||||||
|
if not self.device:
|
||||||
|
raise RuntimeError('No device specified nor default device configured')
|
||||||
|
device = self.device
|
||||||
|
|
||||||
|
if baud_rate is None:
|
||||||
|
if self.baud_rate is None:
|
||||||
|
raise RuntimeError('No baud_rate specified nor default configured')
|
||||||
|
baud_rate = self.baud_rate
|
||||||
|
|
||||||
data = None
|
data = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
serial_available = self.serial_lock.acquire(timeout=2)
|
serial_available = self.serial_lock.acquire(timeout=2)
|
||||||
if serial_available:
|
if serial_available:
|
||||||
try:
|
try:
|
||||||
ser = self._get_serial()
|
ser = self._get_serial(device=device)
|
||||||
except:
|
except:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
ser = self._get_serial(reset=True)
|
ser = self._get_serial(device=device, reset=True)
|
||||||
|
|
||||||
data = self._read_json(ser)
|
data = self._read_json(ser)
|
||||||
|
|
||||||
|
@ -120,15 +147,105 @@ class SerialPlugin(GpioSensorPlugin):
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def write(self, data):
|
def read(self, device=None, baud_rate=None, size=None, end=None):
|
||||||
|
"""
|
||||||
|
Reads raw data from the serial device
|
||||||
|
|
||||||
|
:param device: Device to read (default: default configured device)
|
||||||
|
:type device: str
|
||||||
|
|
||||||
|
:param baud_rate: Baud rate (default: default configured baud_rate)
|
||||||
|
:type baud_rate: int
|
||||||
|
|
||||||
|
:param size: Number of bytes to read
|
||||||
|
:type size: int
|
||||||
|
|
||||||
|
:param end: End of message byte or character
|
||||||
|
:type end: int, bytes or str
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not device:
|
||||||
|
if not self.device:
|
||||||
|
raise RuntimeError('No device specified nor default device configured')
|
||||||
|
device = self.device
|
||||||
|
|
||||||
|
if baud_rate is None:
|
||||||
|
if self.baud_rate is None:
|
||||||
|
raise RuntimeError('No baud_rate specified nor default configured')
|
||||||
|
baud_rate = self.baud_rate
|
||||||
|
|
||||||
|
if (size is None and end is None) or (size is not None and end is not None):
|
||||||
|
raise RuntimeError('Either size or end must be specified')
|
||||||
|
|
||||||
|
if end and len(end) > 1:
|
||||||
|
raise RuntimeError('The serial end must be a single character, not a string')
|
||||||
|
|
||||||
|
data = bytes()
|
||||||
|
|
||||||
|
try:
|
||||||
|
serial_available = self.serial_lock.acquire(timeout=2)
|
||||||
|
if serial_available:
|
||||||
|
try:
|
||||||
|
ser = self._get_serial(device=device)
|
||||||
|
except:
|
||||||
|
time.sleep(1)
|
||||||
|
ser = self._get_serial(device=device, reset=True)
|
||||||
|
|
||||||
|
if size is not None:
|
||||||
|
for _ in range(0, size):
|
||||||
|
data += ser.read()
|
||||||
|
elif end is not None:
|
||||||
|
if isinstance(end, str):
|
||||||
|
end = end.encode()
|
||||||
|
|
||||||
|
ch = None
|
||||||
|
while ch != end:
|
||||||
|
ch = ser.read()
|
||||||
|
|
||||||
|
if ch != end:
|
||||||
|
data += ch
|
||||||
|
else:
|
||||||
|
self.logger.warning('Serial read timeout')
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
self.serial_lock.release()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = data.decode()
|
||||||
|
except:
|
||||||
|
data = base64.encodebytes(data)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@action
|
||||||
|
def write(self, data, device=None, baud_rate=None):
|
||||||
"""
|
"""
|
||||||
Writes data to the serial device.
|
Writes data to the serial device.
|
||||||
|
|
||||||
|
:param device: Device to write (default: default configured device)
|
||||||
|
:type device: str
|
||||||
|
|
||||||
|
:param baud_rate: Baud rate (default: default configured baud_rate)
|
||||||
|
:type baud_rate: int
|
||||||
|
|
||||||
:param data: Data to send to the serial device
|
:param data: Data to send to the serial device
|
||||||
:type data: str, bytes or dict. If dict, it will be serialized as JSON.
|
:type data: str, bytes or dict. If dict, it will be serialized as JSON.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not device:
|
||||||
|
if not self.device:
|
||||||
|
raise RuntimeError('No device specified nor default device configured')
|
||||||
|
device = self.device
|
||||||
|
|
||||||
|
if baud_rate is None:
|
||||||
|
if self.baud_rate is None:
|
||||||
|
raise RuntimeError('No baud_rate specified nor default configured')
|
||||||
|
baud_rate = self.baud_rate
|
||||||
|
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
data = json.dumps(data)
|
data = json.dumps(data)
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
|
@ -138,10 +255,10 @@ class SerialPlugin(GpioSensorPlugin):
|
||||||
serial_available = self.serial_lock.acquire(timeout=2)
|
serial_available = self.serial_lock.acquire(timeout=2)
|
||||||
if serial_available:
|
if serial_available:
|
||||||
try:
|
try:
|
||||||
ser = self._get_serial()
|
ser = self._get_serial(device=device)
|
||||||
except:
|
except:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
ser = self._get_serial(reset=True)
|
ser = self._get_serial(device=device, reset=True)
|
||||||
|
|
||||||
self.logger.info('Writing {} to {}'.format(data, self.device))
|
self.logger.info('Writing {} to {}'.format(data, self.device))
|
||||||
ser.write(data)
|
ser.write(data)
|
||||||
|
|
Loading…
Reference in a new issue