Improve dynamic configuration

* Control framerate in the forwarder
* The processing client can use a smaller framerate than the default/max
* The processing client can send an image smaller than the full screen
    Known Bug: doesn't work if the width is smaller than the one of the screen
* The default config is sent to the processing client by the server
    Sanity checks are made on the client configuration
* Code Cleanup
master
Raphaël Vinot 2015-03-20 00:39:53 +01:00
parent 8d371b95df
commit 9da3123796
6 changed files with 117 additions and 43 deletions

View File

@ -92,12 +92,10 @@ void loop() {
if (startChar == '*') {
unsigned int startAt = micros();
unsigned int usecUntilFrameSync = 0;
count = Serial.readBytes((char *)drawingMemory, sizeof(int) * ledsPerStrip*6);
if (count >= sizeof(int) * ledsPerStrip*6) {
count = Serial.readBytesUntil('#', (char *)drawingMemory, sizeof(int) * ledsPerStrip*6 + 1);
digitalWrite(13, HIGH);
leds.show();
digitalWrite(13, LOW);
}
} else if (startChar >= 0) {
// discard unknown characters
}

View File

@ -1,13 +1,4 @@
Forwarder
=========
Both sides
==========
* Control framerate in send function
* Define a max framerate and announce it in the config (redis)
* Remove commented code
Receiver
========
* Listen on an other port to give the config to the client (H/W, max framerate)
* Allow the user to set a lower framerate than the max one anounced by the forwarder
* Remove debug code
Support screen sizes > 1byte

View File

@ -5,22 +5,31 @@ import time
from serial import Serial, SerialException
import sys
height = 50
width = 20
max_height = 5
max_width = 8
max_framerate = 40
wait_time = None
def update_framerate():
global wait_time
new_framerate = int(r.hget('config', 'cur_framerate'))
if new_framerate >= max_framerate or new_framerate == 0:
wait_time = 1.0 / max_framerate
else:
wait_time = 1.0 / new_framerate
def send(r, s):
print(r.llen('new'))
data = r.rpop('new')
if data is not None and len(data) > 0:
a = bytes([ord('*')]) + bytearray(data)
#la = len(a)
now = time.time()
end = now + wait_time
a = bytes([ord('*')]) + bytearray(data) + bytes([ord('#')])
s.write(a)
#time.sleep(.05)
#data = s.read(la)
#print(data)
# size = s.write(data)
# print('Data sent ({} bytes)'.format(size))
time.sleep(end - now)
def serialConfigure(port_name, baudrate=9600):
@ -38,7 +47,7 @@ def serialConfigure(port_name, baudrate=9600):
sys.stderr.write("Could not open serial port %s: %s\n" % (ser.portstr, e))
return
ledsPerStrip = height * width
ledsPerStrip = max_height * max_width
print(ledsPerStrip)
ser.write(ledsPerStrip.to_bytes(4, byteorder='little'))
@ -64,10 +73,16 @@ def serialDataConfigure(port_name, baudrate=115200):
if __name__ == "__main__":
r = redis.Redis()
r.hset('config', 'imgsize', height * width * 24)
r.hset('config', 'height', max_height)
r.hset('config', 'width', max_width)
r.hset('config', 'imgsize', max_height * max_width * 24)
r.hset('config', 'max_framerate', max_framerate)
r.hset('config', 'cur_framerate', max_framerate)
s = serialConfigure('/dev/ttyACM0')
# s_data = serialDataConfigure('/dev/ttyUSB0')
wait_time = 1.0 / max_framerate
while True:
while r.llen('new') > 0:
send(r, s)
time.sleep(1)
update_framerate()

View File

@ -13,16 +13,51 @@ class MyTCPHandler(socketserver.BaseRequestHandler):
client.
"""
def get_config(self):
def _get_config(self):
self.max_height = int(self.r.hget('config', 'height'))
self.max_width = int(self.r.hget('config', 'width'))
self.max_framerate = int(self.r.hget('config', 'max_framerate'))
self.cur_framerate = int(self.r.hget('config', 'cur_framerate'))
self.imgsize = int(self.r.hget('config', 'imgsize'))
def _set_config(self, framerate, height, width):
self.r.hset('config', 'cur_framerate', framerate)
self.imgsize = height * width * 24
def _send_config_to_client(self):
self.request.sendall(bytearray([self.max_height]))
self.request.sendall(bytearray([self.max_width]))
self.request.sendall(bytearray([self.max_framerate]))
def _receive_client_config(self):
height = int.from_bytes(self.request.recv(1), byteorder='little')
width = int.from_bytes(self.request.recv(1), byteorder='little')
framerate = int.from_bytes(self.request.recv(1), byteorder='little')
good, reason = self._check_config(height, width, framerate)
if good:
self._set_config(framerate, height, width)
return good, reason
def _check_config(self, height, width, framerate):
if height > 0 and height > self.max_height:
return False, "height has to be between 0 and {}. Current: {}".format(self.max_height, height)
if width > 0 and width > self.max_width:
return False, "width has to be between 0 and {}. Current: {}".format(self.max_width, width)
if framerate > 0 and framerate > self.max_framerate:
return False, "framerate has to be between 0 and {}. Current: {}".format(self.max_framerate, framerate)
return True, None
def handle(self):
print('Start receiving from {}...'.format(self.client_address[0]))
self.r = redis.Redis()
self.get_config()
self._get_config()
self._send_config_to_client()
good, reason = self._receive_client_config()
if not good:
print(reason)
return None
print('Start receiving from {}...'.format(self.client_address[0]))
while True:
data = self.request.recv(self.imgsize)
print(len(data))
self.r.lpush('new', data)
if len(data) == 0:
break

View File

@ -1,11 +1,17 @@
add_library('net')
import math
import jarray
import time
import struct
# Config, will be checked upstream
height = 5
width = 8
framerate = 30
#####################################
gamma = 1.7
brightness = 4
errorCount = 0
framerate = 30
dimension = 0
# TODO: test with real serial
@ -58,22 +64,53 @@ def colorWiring(c):
def send_TCP():
image2data(data)
#println(data)
ledTCP.write(data)
def prepare_data():
global data
data = jarray.zeros(dimension * 24, "b")
def receive_config():
max_height = jarray.zeros(1, "b")
max_width = jarray.zeros(1, "b")
max_framerate = jarray.zeros(1, "b")
while True:
available_bytes = ledTCP.available()
if available_bytes > 0:
break
time.sleep(1)
ledTCP.readBytes(max_height)
ledTCP.readBytes(max_width)
ledTCP.readBytes(max_framerate)
return max_height[0], max_width[0], max_framerate[0]
def check_config(max_height, max_width, max_framerate):
if height > max_height:
return False, "height cannot be higher than {}".format(max_height)
if width > max_width:
return False, "width cannot be higher than {}".format(max_width)
if framerate > max_framerate:
return False, "framerate cannot be higher than {}".format(max_framerate)
return True, None
def send_config():
ledTCP.write(height)
ledTCP.write(width)
ledTCP.write(framerate)
def setup():
global gammatable
global dimension
size(50, 30)
TCPConfigure("127.0.0.1", 9999)
max_height, max_width, max_framerate = receive_config()
good, reason = check_config(max_height, max_width, max_framerate)
if not good:
raise Exception(reason)
send_config()
size(width, height)
dimension = width * height
frameRate(framerate)
TCPConfigure("127.0.0.1", 9999)
if (errorCount > 0):
exit()
gammatable = [int((math.pow(i / 255.0, gamma) * 255.0 + 0.5) * brightness) for i in range(0, 256)]
prepare_data()
loadPixels()

View File

@ -1,4 +1,2 @@
* Get config (H/W and max framerate) from the config server
* Set current framerate (lower than max), ad send it to the config server
* Optimize the datastream generator
* Split the processing code (setup & dray) from the datastreem generator => library