Skip to content

Commit

Permalink
Added the ability to send ArtSync message in the Art-Net protocol imp…
Browse files Browse the repository at this point in the history
…lementation. (#27)

* Added possibility to send ArtSync.

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
kacper516 and coderabbitai[bot] authored Mar 16, 2024
1 parent 4c4beba commit 831f2b3
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 11 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/run tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ jobs:

steps:
- uses: actions/checkout@master
- name: Set up Python 3.10.12
- name: Set up Python 3.11.8
uses: actions/setup-python@v1
with:
python-version: 3.10.12
python-version: 3.11.8

- name: Run test
run: python -m unittest discover --v
72 changes: 63 additions & 9 deletions stupidArtnet/StupidArtnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class StupidArtnet():
UDP_PORT = 6454

def __init__(self, target_ip='127.0.0.1', universe=0, packet_size=512, fps=30,
even_packet_size=True, broadcast=False):
even_packet_size=True, broadcast=False, artsync=False):
"""Initializes Art-Net Client.
Args:
Expand All @@ -30,6 +30,7 @@ def __init__(self, target_ip='127.0.0.1', universe=0, packet_size=512, fps=30,
fps - transmition rate
even_packet_size - Some receivers enforce even packets
broadcast - whether to broadcast in local sub
artsync - if we want to synchronize buffer
Returns:
None
Expand All @@ -41,6 +42,7 @@ def __init__(self, target_ip='127.0.0.1', universe=0, packet_size=512, fps=30,
self.physical = 0
self.universe = universe
self.subnet = 0
self.if_sync = artsync
self.net = 0
self.packet_size = put_in_range(packet_size, 2, 512, even_packet_size)
self.packet_header = bytearray()
Expand All @@ -61,13 +63,19 @@ def __init__(self, target_ip='127.0.0.1', universe=0, packet_size=512, fps=30,
self.fps = fps
self.__clock = None

self.make_header()
self.make_artdmx_header()

if self.if_sync:
self.artsync_header = bytearray()
self.make_artsync_header()


def __del__(self):
"""Graceful shutdown."""
self.stop()
self.close()


def __str__(self):
"""Printable object state."""
state = "===================================\n"
Expand All @@ -82,7 +90,8 @@ def __str__(self):

return state

def make_header(self):

def make_artdmx_header(self):
"""Make packet header."""
# 0 - id (7 x bytes + Null)
self.packet_header = bytearray()
Expand Down Expand Up @@ -125,18 +134,49 @@ def make_header(self):
self.packet_header.append(msb)
self.packet_header.append(lsb)


def make_artsync_header(self):
"""Make ArtSync header"""
self.artsync_header = bytearray() # Initialize as empty bytearray
# ID: Array of 8 characters, the final character is a null termination.
self.artsync_header.extend(bytearray('Art-Net', 'utf8'))
self.artsync_header.append(0x0)
# OpCode: Defines the class of data within this UDP packet. Transmitted low byte first.
self.artsync_header.append(0x00)
self.artsync_header.append(0x52)
# ProtVerHi and ProtVerLo: Art-Net protocol revision number. Current value =14.
# Controllers should ignore communication with nodes using a protocol version lower than =14.
self.artsync_header.append(0x0)
self.artsync_header.append(14)
# Aux1 and Aux2: Should be transmitted as zero.
self.artsync_header.append(0x0)
self.artsync_header.append(0x0)


def send_artsync(self):
"""Send Artsync"""
self.make_artsync_header()
try:
self.socket_client.sendto(self.artsync_header, (self.target_ip, self.UDP_PORT))
except socket.error as error:
print(f"ERROR: Socket error with exception: {error}")


def show(self):
"""Finally send data."""
packet = bytearray()
packet.extend(self.packet_header)
packet.extend(self.buffer)
try:
self.socket_client.sendto(packet, (self.target_ip, self.UDP_PORT))
if self.if_sync: # if we want to send artsync
self.send_artsync()
except socket.error as error:
print(f"ERROR: Socket error with exception: {error}")
finally:
self.sequence = (self.sequence + 1) % 256


def close(self):
"""Close UDP socket."""
self.socket_client.close()
Expand All @@ -150,6 +190,7 @@ def start(self):
self.__clock.daemon = True
self.__clock.start()


def stop(self):
"""Stops thread clock."""
if self.__clock is not None:
Expand All @@ -169,42 +210,47 @@ def set_universe(self, universe):
self.universe = put_in_range(universe, 0, 255, False)
else:
self.universe = put_in_range(universe, 0, 15, False)
self.make_header()
self.make_artdmx_header()


def set_subnet(self, sub):
"""Setter for subnet address (0 - 15).
Set simplify to false to use
"""
self.subnet = put_in_range(sub, 0, 15, False)
self.make_header()
self.make_artdmx_header()


def set_net(self, net):
"""Setter for net address (0 - 127).
Set simplify to false to use
"""
self.net = put_in_range(net, 0, 127, False)
self.make_header()
self.make_artdmx_header()


def set_packet_size(self, packet_size):
"""Setter for packet size (2 - 512, even only)."""
self.packet_size = put_in_range(packet_size, 2, 512, self.make_even)
self.make_header()
self.make_artdmx_header()

# SETTERS - DATA #

def clear(self):
"""Clear DMX buffer."""
self.buffer = bytearray(self.packet_size)


def set(self, value):
"""Set buffer."""
if len(value) != self.packet_size:
print("ERROR: packet does not match declared packet size")
return
self.buffer = value


def set_16bit(self, address, value, high_first=False):
"""Set single 16bit value in DMX buffer."""
if address > self.packet_size:
Expand All @@ -223,6 +269,7 @@ def set_16bit(self, address, value, high_first=False):
self.buffer[address - 1] = (value) & 0xFF # low
self.buffer[address] = (value >> 8) & 0xFF # high


def set_single_value(self, address, value):
"""Set single value in DMX buffer."""
if address > self.packet_size:
Expand All @@ -233,6 +280,7 @@ def set_single_value(self, address, value):
return
self.buffer[address - 1] = put_in_range(value, 0, 255, False)


def set_single_rem(self, address, value):
"""Set single value while blacking out others."""
if address > self.packet_size:
Expand All @@ -244,6 +292,7 @@ def set_single_rem(self, address, value):
self.clear()
self.buffer[address - 1] = put_in_range(value, 0, 255, False)


def set_rgb(self, address, red, green, blue):
"""Set RGB from start address."""
if address > self.packet_size:
Expand All @@ -268,6 +317,7 @@ def send(self, packet):
self.set(packet)
self.show()


def set_simplified(self, simplify):
"""Builds Header accordingly.
Expand All @@ -279,21 +329,25 @@ def set_simplified(self, simplify):
if simplify == self.is_simplified:
return
self.is_simplified = simplify
self.make_header()
self.make_artdmx_header()


def see_header(self):
"""Show header values."""
print(self.packet_header)


def see_buffer(self):
"""Show buffer values."""
print(self.buffer)


def blackout(self):
"""Sends 0's all across."""
self.clear()
self.show()


def flash_all(self, delay=None):
"""Sends 255's all across."""
self.set([255] * self.packet_size)
Expand All @@ -311,7 +365,7 @@ def flash_all(self, delay=None):
UNIVERSE_TO_SEND = 15 # see docs
PACKET_SIZE = 20 # it is not necessary to send whole universe

a = StupidArtnet(TARGET_IP, UNIVERSE_TO_SEND, PACKET_SIZE)
a = StupidArtnet(TARGET_IP, UNIVERSE_TO_SEND, PACKET_SIZE, artsync=True)
a.set_simplified(False)
a.set_net(129)
a.set_subnet(16)
Expand Down

0 comments on commit 831f2b3

Please sign in to comment.