-
Notifications
You must be signed in to change notification settings - Fork 58
Description
Some folks have a Lightburn Bridge, it's basically just a raspberry pi that connects to a short USB cord on one side, and wifi on the other. The only use of the network connection to the ruida is from the PI and it's just a simple relay.
class RuidaRelay(Relay):
def __init__(self, *args):
# super().__init__(cfg, status)
self.version = (1, 0)
self.type = RelayType.Ethernet
self.buffer_size = 1024
self.from_laser_port = 40200
self.to_laser_port = 50200
self.stop_lock = threading.Lock()
self.stop = False
self.type = RelayType.Ethernet
self.laser_ip = "127.0.0.1"
self.server_ip = '0.0.0.0'
self.server_port = 5005
def runConnection(self, sock):
outSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
inSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
inSock.bind(('', self.from_laser_port))
inSock.setblocking(0)
serv, addr = sock.accept()
self.status.ok(f"Connection from: {addr[0]}:{addr[1]}")
serv.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
serv.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 4096)
serv.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 32768)
packet = b''
packetLen = 0
lastLen = 0
packetType = ord('L')
ackValue = b''
gotAck = True
lastTime = time.time()
while 1:
readable, writable, exceptional = select.select([inSock, serv], [], [serv], 1.0)
if serv in exceptional:
self.status.error('Server socket error')
break
if gotAck:
if serv in readable:
try:
if packetLen == 0:
data = serv.recv(3 - len(packet))
else:
data = serv.recv(packetLen - len(packet))
except Exception:
pass
if not data:
break
packet += data
if packetLen == 0:
if len(packet) == 3:
packetType = packet[0]
packetLen = (packet[1] << 8) + packet[2]
packet = serv.recv(packetLen)
if packetLen != 0:
if len(packet) == packetLen:
if packetType == ord('L'):
outSock.sendto(packet, (self.laser_ip, self.to_laser_port))
lastLen = packetLen
packetLen = 0
packet = b''
lastTime = time.time()
gotAck = False
else:
if packetType == ord('P'):
data = b'P\x00\x02' + bytes(self.version)
serv.send(data)
else:
self.status.error(f"unhandled packet type {packetType}" + str(bytes(chr(packetType))))
if inSock in readable:
data, addr = inSock.recvfrom(self.buffer_size)
if len(data) > 1 or lastLen <= 500:
hdr = bytes([packetType, len(data) >> 8, len(data) & 255])
serv.send(hdr)
serv.send(data)
if len(data) == 1:
if len(ackValue) == 0:
ackValue = data
gotAck = True
else:
if ackValue[0] != data[0]:
self.status.warn('Non-ack received')
break
else:
gotAck = True
if gotAck == False and time.time() - lastTime > 6:
self.status.error('Laser timeout error')
break
serv.close()
outSock.close()
inSock.close()
self.status.ok('Ruida command complete')
def run(self):
self.stop = False
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("", self.server_port))
print(f"Ruida relay started, laser IP: {self.laser_ip}")
sock.setblocking(0)
while True:
sock.listen(1)
gotConnect = False
while True:
readable, writable, exceptional = select.select([sock], [], [sock], 1.0)
if sock in readable:
gotConnect = True
break
if sock in exceptional:
break
with self.stop_lock:
if self.stop:
break
if gotConnect == False:
break
self.runConnection(sock)
with self.stop_lock:
if self.stop:
break
self.status.info('Ruida relay stopped')It does a few things like non-blocking connections and streaming protocol to improve throughput, but basically accepts two commands L and P. The L <2-byte length> <payload> just sends that data to the laser and the P command replies with the relay version 1.0 which is effectively the whole protocol. They do a similar thing for the camera code where F provides a frame from the camera and T provides a thumbnail. I'm guessing they don't just implement the MJPEG server because they don't have networked camera support. --- In any event:
Connect to port 5005.
Commands are L<length><Payload> and responses are in-kind. These will be sent across the UDP channel so they should have normal UDP checksum, and swizzle. And after sending a batch of data they quickly just disconnect from the TCP server.
This is another connection the would exist for Ruida and is pretty easy to implement. In MeerK40t, I am planning on implementing it pretty soon (since the driver's finally being implemented (I got a beta tester!), and the Ruida emulator thing I wrote into the program already does so.