diff --git a/examples/blinky.py b/examples/blinky.py index 46a5f5c..82b4a04 100644 --- a/examples/blinky.py +++ b/examples/blinky.py @@ -1,21 +1,19 @@ from jacdac import Bus from jacdac.led import LedClient -from jacdac import LoggerPriority from time import sleep if __name__ == '__main__': def main(): bus = Bus() led = LedClient(bus, "led") - speed = 0 - brightness = 128 + led.brightness = 0.5 # fade between colors while True: # blue - led.animate(0, 0, brightness, speed) + led.set_all(0xff0000) sleep(1) # off - led.animate(brightness, 0, 0, speed) + led.set_all(0x000000) sleep(1) main() diff --git a/examples/fade.py b/examples/fade.py index cca7f57..9d40400 100644 --- a/examples/fade.py +++ b/examples/fade.py @@ -6,18 +6,17 @@ def main(): bus = Bus() led = LedClient(bus, "led") - speed = 16 - brightness = 128 - # fade between colors + led.brightness = 0.5 + # change between colors while True: # blue - led.animate(0, 0, brightness, speed) + led.set_all(0x0000ff) sleep(1) # red - led.animate(brightness, 0, 0, speed) + led.set_all(0xff0000) sleep(1) # green - led.animate(0, brightness, 0, speed) + led.set_all(0x00ff00) sleep(1) main() diff --git a/examples/slidy_blinky.py b/examples/slidy_blinky.py index b81c9a6..ce41b68 100644 --- a/examples/slidy_blinky.py +++ b/examples/slidy_blinky.py @@ -8,17 +8,12 @@ def main(): bus = Bus() led = LedClient(bus, "led") slider = PotentiometerClient(bus, "slider") - speed = 16 - brightness = 128 + led.set_all(0x0000ff) # fade between colors while True: position = slider.position or 0. - brightness = int(position / 100. * 255.) - # blue - led.animate(0, 0, brightness, speed) - sleep(1) - # off - led.animate(brightness, 0, 0, speed) + # change brightness + led.brightness = position sleep(1) main() diff --git a/examples/weather_to_csv.py b/examples/weather_to_csv.py index 0a52ccd..3ae87ae 100644 --- a/examples/weather_to_csv.py +++ b/examples/weather_to_csv.py @@ -1,6 +1,6 @@ from jacdac import Bus from jacdac.humidity import HumidityClient -from jacdac.thermometer import ThermometerClient +from jacdac.temperature import TemperatureClient import csv from time import sleep @@ -8,12 +8,12 @@ def main(): bus = Bus() humidity_sensor = HumidityClient(bus, "weather.hum") - thermometer = ThermometerClient(bus, "weather.temp") + temperature = TemperatureClient(bus, "weather.temp") with open('weather.csv', 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=',', ) while True: h = humidity_sensor.humidity - t = thermometer.temperature + t = temperature.temperature writer.writerow([h, t]) sleep(1) main() diff --git a/jacdac/base/__init__.py b/jacdac/base/__init__.py deleted file mode 100644 index c43a639..0000000 --- a/jacdac/base/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Autogenerated file. -from .constants import * diff --git a/jacdac/base/constants.py b/jacdac/base/constants.py deleted file mode 100644 index 3f24d8f..0000000 --- a/jacdac/base/constants.py +++ /dev/null @@ -1,6 +0,0 @@ -# Autogenerated constants for Base service service -from jacdac.constants import * -from jacdac.system.constants import * -JD_PACK_FORMATS = { - -} diff --git a/jacdac/bus.py b/jacdac/bus.py index ad1cd1b..e3c0f49 100644 --- a/jacdac/bus.py +++ b/jacdac/bus.py @@ -22,6 +22,7 @@ from .role_manager.constants import * from .unique_brain.constants import * from .packet import * +from .transport import Transport from .util import now, log, logv from .pack import PackTuple, PackType, jdpack, jdunpack @@ -179,18 +180,6 @@ def _service_matches(dev: 'Device', serv: bytearray): return False return True - -class Transport: - """A base class for packet transports""" - - on_receive: Optional[Callable[[bytes], None]] = None - # Callback to report a received packet to the bus - - def send(self, pkt: bytes) -> None: - # send a packet payload over the transport layer - pass - - def rand_u64(): return bytearray([getrandbits(8) for _ in range(8)]) @@ -1690,7 +1679,7 @@ def packet_count(self): @ property def is_connected(self): - return self.clients != None + return len(self.clients) != 0 @ property def short_id(self): @@ -1735,7 +1724,7 @@ def _destroy(self): self.debug("destroy") for c in self.clients: c._detach() - self.clients = None # type: ignore + self.clients = [] def _log_report_prefix(self) -> str: return "{}>".format(self.short_id) @@ -1784,3 +1773,57 @@ def process_packet(self, pkt: JDPacket): # log(`handle pkt at ${client.role} rep=${pkt.serviceCommand}`) c.device = self c.handle_packet_outer(pkt) + +class BufferClient(Client): + _value: bytearray + _dirty: bool + + """ + A client that handles a double-buffer bytes buffer + """ + def __init__(self, bus: Bus, service_class: int, pack_formats: Dict[int, str], role: str) -> None: + super().__init__(bus, service_class, pack_formats, role) + + self._value = bytearray(0) + self._dirty = False + + @property + def value(self) -> bytearray: + """ + Cached reading value + """ + return self._value + + @value.setter + def value(self, v: bytearray) -> None: + # TODO: check for equality + self._value = v or bytearray(0) + self._dirty = True + # TODO: debounce + self.refresh_value() + + @property + def dirty(self) -> bool: + return self._dirty + + def set_dirty(self) -> None: + self._dirty = True + + def refresh_value(self) -> None: + if self._dirty: + print(self._value) + self.register(JD_REG_VALUE).set_values(self._value) + self._dirty = False + + def update_value_length(self, length: Optional[int]) -> None: + l = len(self._value) + if (not length is None) and l != length: + # harmonize lengths + if length > l: + self._value = self._value + bytearray(length - l) + self._dirty = True + else: + self._value = self._value[0:length -1] + self._dirty = True + + diff --git a/jacdac/led/client.py b/jacdac/led/client.py index 3aa41fe..0c5728f 100644 --- a/jacdac/led/client.py +++ b/jacdac/led/client.py @@ -1,10 +1,10 @@ # Autogenerated file. Do not edit. -from jacdac.bus import Bus, Client +from jacdac.bus import Bus, BufferClient from .constants import * from typing import Optional -class LedClient(Client): +class LedClient(BufferClient): """ A controller for small displays of individually controlled RGB LEDs. * @@ -17,7 +17,6 @@ class LedClient(Client): def __init__(self, bus: Bus, role: str) -> None: super().__init__(bus, JD_SERVICE_CLASS_LED, JD_LED_PACK_FORMATS, role) - @property def pixels(self) -> Optional[bytes]: """ @@ -25,12 +24,14 @@ def pixels(self) -> Optional[bytes]: When writing, if the buffer is too short, the remaining pixels are set to `#000000`; if the buffer is too long, the write may be ignored, or the additional pixels may be ignored., """ - return self.register(JD_LED_REG_PIXELS).value() + return self.value @pixels.setter def pixels(self, value: bytes) -> None: - self.register(JD_LED_REG_PIXELS).set_values(value) - + if value is None: + self.value = bytearray(0) + else: + self.value = bytearray(value) @property def brightness(self) -> Optional[float]: @@ -44,7 +45,6 @@ def brightness(self) -> Optional[float]: def brightness(self, value: float) -> None: self.register(JD_LED_REG_BRIGHTNESS).set_values(value / 100) - @property def actual_brightness(self) -> Optional[float]: """ @@ -79,7 +79,6 @@ def max_power(self) -> Optional[int]: def max_power(self, value: int) -> None: self.register(JD_LED_REG_MAX_POWER).set_values(value) - @property def leds_per_pixel(self) -> Optional[int]: """ @@ -110,4 +109,34 @@ def variant(self) -> Optional[LedVariant]: """ return self.register(JD_LED_REG_VARIANT).value() - + def _sync(self): + self.register(JD_LED_REG_NUM_PIXELS).refresh() + n = self.num_pixels + if not n is None: + self.update_value_length(n * 3) + + def show(self): + """ + Sends the buffer information to the server + """ + self._sync() + self.refresh_value() + + def set_all(self, rgb: int): + """ + Sets all the colors to particular color + """ + self._sync() + r = (rgb >> 16) & 0xff + g = (rgb >> 8) & 0xff + b = (rgb >> 0) & 0xff + buf = self.value + dirty = self.dirty + for i in range(0, len(buf), 3): + dirty = dirty or buf[i] != r or buf[i + 1] != g or buf[i + 2] != b + buf[i] = r + buf[i + 1] = g + buf[i + 2] = b + if dirty: + self.set_dirty() + self.show() diff --git a/jacdac/sensor/__init__.py b/jacdac/sensor/__init__.py deleted file mode 100644 index c43a639..0000000 --- a/jacdac/sensor/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Autogenerated file. -from .constants import * diff --git a/jacdac/sensor/constants.py b/jacdac/sensor/constants.py deleted file mode 100644 index 030459d..0000000 --- a/jacdac/sensor/constants.py +++ /dev/null @@ -1,6 +0,0 @@ -# Autogenerated constants for Sensor service -from jacdac.constants import * -from jacdac.system.constants import * -JD_PACK_FORMATS = { - -} diff --git a/jacdac/transport.py b/jacdac/transport.py new file mode 100644 index 0000000..1e9a73b --- /dev/null +++ b/jacdac/transport.py @@ -0,0 +1,12 @@ +from typing import Callable, Optional + + +class Transport: + """A base class for packet transports""" + + on_receive: Optional[Callable[[bytes], None]] = None + # Callback to report a received packet to the bus + + def send(self, pkt: bytes) -> None: + # send a packet payload over the transport layer + pass \ No newline at end of file diff --git a/jacdac/transports/exec.py b/jacdac/transports/exec.py index b771aa1..93481bd 100644 --- a/jacdac/transports/exec.py +++ b/jacdac/transports/exec.py @@ -1,5 +1,5 @@ import threading -from jacdac.bus import Transport +from jacdac.transport import Transport import subprocess from jacdac.util import buf2hex, hex2buf diff --git a/jacdac/transports/hf2.py b/jacdac/transports/hf2.py index 2b63a62..119ed9d 100644 --- a/jacdac/transports/hf2.py +++ b/jacdac/transports/hf2.py @@ -5,7 +5,7 @@ import struct from typing import List -from jacdac.bus import Transport +from jacdac.transport import Transport HF2_CMD_INFO = 0x0002 HF2_CMD_DMESG = 0x0010 diff --git a/jacdac/transports/spi.py b/jacdac/transports/spi.py index 906ca49..5be3eaf 100644 --- a/jacdac/transports/spi.py +++ b/jacdac/transports/spi.py @@ -2,7 +2,7 @@ from typing import List, Optional from time import sleep from tokenize import Number -from jacdac.bus import Transport +from jacdac.transport import Transport from jacdac.util import buf2hex, hex2buf, now from gpiod import Chip, Line, LineBulk, LINE_REQ_EV_RISING_EDGE, LINE_REQ_FLAG_ACTIVE_LOW, LINE_REQ_DIR_OUT # type: ignore from spidev import SpiDev # type: ignore diff --git a/jacdac/transports/ws.py b/jacdac/transports/ws.py index a3e5078..81d349a 100644 --- a/jacdac/transports/ws.py +++ b/jacdac/transports/ws.py @@ -2,7 +2,7 @@ import threading from typing import Optional import websocket # type: ignore -from jacdac.bus import Transport +from jacdac.transport import Transport class WebSocketTransport(Transport): diff --git a/setup.py b/setup.py index 8544365..c787a7b 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,6 @@ setup( version=JD_VERSION, packages=find_packages( - include=['jacdac', 'jacdac.*'], exclude=['*test.py', 'jacdac.examples.*']) + include=['jacdac', 'jacdac.*'], exclude=['*test.py']) )