From c32883a1d7718481bea05b0e7e2584465fe5325f Mon Sep 17 00:00:00 2001 From: Ben Savage Date: Mon, 26 Jul 2021 13:49:46 +1000 Subject: [PATCH 1/5] Fixing typo in ports --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81583d8..0c7ecd8 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ git clone https://git.sav.net.au/Public/osc_led.git ``` # Setup Reaper, load the template -Set up reaper to listen on port 9000 and send to port 8000. +Set up reaper to listen on port 8000 and send to port 9000. Control surface mode: OSC Mode: Configure device IP + local port Device port: 9000 From e014e78ee0e42c6319b21e230d8d06f035dd1474 Mon Sep 17 00:00:00 2001 From: Ben Savage Date: Tue, 27 Jul 2021 17:03:35 +1000 Subject: [PATCH 2/5] adding gpio in to control LEDs for realsies --- osc_server.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osc_server.py b/osc_server.py index c11b1d8..e6dcdc0 100755 --- a/osc_server.py +++ b/osc_server.py @@ -7,6 +7,7 @@ import logging from pprint import pprint import argparse import colorsys +import gpiozero logging.basicConfig(format='%(asctime)s - %(threadName)s ø %(name)s - ' '%(levelname)s - %(message)s') @@ -32,14 +33,15 @@ class LED: state=True pin=None mode=["off","on"] - def __init__(self,pin): self.pin=pin + self.led=gpiozero.PWMLED(pin) def set_brightness(self,value): if abs(self.brightness-value) > .005: self.brightness=value + self.led.value=self.state*self.brightness print("Setting brightness for LED on pin {} to {}".format(self.pin, self.brightness)) def set_colour(self,value): @@ -50,11 +52,13 @@ class LED: def set_state(self,value): self.state=value + self.led.value=self.state*self.brightness log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) def toggle_state(self,value): if (value): self.state=not self.state + self.led.value=self.state*self.brightness log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) @@ -62,7 +66,8 @@ def led_colour(value): leds[0].set_colour(value) def led_brightness(value): - leds[0].set_brightness(value) + for led in leds: + led.set_brightness(value) def led_state(addr,value): led=addr.split("/")[6] @@ -97,7 +102,7 @@ osc_method("/track/2/fx/1/fxparam/[!12]/value",led_state,argscheme=OSCARG_ADDRES log.info("Creating LEDs...") # Create LEDs -leds=[LED(i) for i in range(0,4)] +leds=[LED(i) for i in [16,20,21]] finished=False log.info("Starting loop") From 2d98f5f707d31294d7a3803f647f1e410c537d64 Mon Sep 17 00:00:00 2001 From: Ben Savage Date: Tue, 27 Jul 2021 17:17:19 +1000 Subject: [PATCH 3/5] Adding comments --- osc_server.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/osc_server.py b/osc_server.py index e6dcdc0..716a783 100755 --- a/osc_server.py +++ b/osc_server.py @@ -9,15 +9,18 @@ import argparse import colorsys import gpiozero +# Set up basic vars and logging logging.basicConfig(format='%(asctime)s - %(threadName)s ø %(name)s - ' '%(levelname)s - %(message)s') log = logging.getLogger("osc") log.setLevel(logging.INFO) clientname="ledserver" +# Get the normalised RGB values for the HSV (i.e, the colour float designating hue from OSC) def hsv2rgb(h,s,v): return tuple(round(i*255) for i in colorsys.hsv_to_rgb(h,s,v)) +# Some little helper methods to set colours in the terminal output def termrgb(rgb): return "\033[38;2;{};{};{}m".format(rgb[0],rgb[1],rgb[2]) def termendc(): @@ -26,35 +29,50 @@ def output_led_colour(rgb): return termrgb(rgb)+"█"+termendc() +# Our main class, the LED + class LED: + # These should be largely self-explanatory + # Float, from OSC FX param 2 brightness=0 + # RGB array, currently as int 0-255, but will probably end up float 0-1.0, converted from hue colour=[0,0,0] + #Float, from OSC FX param 1 hue=0 - state=True + # Bool, Intial state, off. Should always align to whether the LED is on or off, brightness being >0 + state=False + # The pin number of the LED pin=None + + # List for logging state mode=["off","on"] + # Initialize the object, set up GPIO def __init__(self,pin): self.pin=pin self.led=gpiozero.PWMLED(pin) + # Set the brightness, this only triggers on a delta threshold so that we don't go crazy def set_brightness(self,value): if abs(self.brightness-value) > .005: self.brightness=value self.led.value=self.state*self.brightness print("Setting brightness for LED on pin {} to {}".format(self.pin, self.brightness)) + # Set the colour, only on a delta threshold def set_colour(self,value): if abs(self.hue-value) > .01: self.colour=hsv2rgb(value,1,1) self.hue=value log.info("Setting colour for LED on pin {} to {} ({})".format(self.pin,self.colour,output_led_colour(self.colour))) + # Set an absolute state, on or off. Brightness is set regardless, so we can turn it on with 40% brightness if desired def set_state(self,value): self.state=value self.led.value=self.state*self.brightness log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) + # Toggle on or off, edge detecting. I.e, send True to turn on, send True again to turn off. False is ignored def toggle_state(self,value): if (value): self.state=not self.state @@ -62,48 +80,62 @@ class LED: log.info("Turning LED {} {}".format(self.pin, self.mode[self.state])) +# Set the colour of the LEDs. This only does the first LED, but when the REAPER controls are finalised, it'll be more controllable def led_colour(value): leds[0].set_colour(value) +# Set the brightness of all LEDs. As above, when REAPER is finalised it'll be controllable (or everything, as required) def led_brightness(value): for led in leds: led.set_brightness(value) +# Turn an individual LED on or off based on FX param 3+ def led_state(addr,value): led=addr.split("/")[6] leds[int(led)-3].toggle_state(bool(value)) + +# Set up command line arguments parser = argparse.ArgumentParser(description='OSC listener for LEDs') -parser.add_argument('--listen',action='store',default='127.0.0.1:9000') -parser.add_argument('--debug-osc',action='store_true',default=False) +parser.add_argument('--listen',action='store',default='127.0.0.1:9000',help="Listen on a specific IP/port. Defaults to 127.0.0,.1:9000") +parser.add_argument('--debug-osc',action='store_true',default=False,help="Debug OSC messages. Very verbose") args=parser.parse_args() # Start the system. log.info("Starting OSC...") -pprint(args) +# Log OSC stuff if requested if args.debug_osc: osc_startup(logger=log) else: osc_startup() +# Split up port/address from command line listen_address=args.listen.split(':')[0] listen_port=args.listen.split(':')[1] + +# Start up the OSC server osc_udp_server(listen_address,listen_port, clientname) + # Set up osc paths +# Each FX param has a callback function that is triggered whenever a message for that path is received log.info("Binding targets...") osc_method("/track/2/fx/1/fxparam/1/value",led_colour) osc_method("/track/2/fx/1/fxparam/2/value",led_brightness) osc_method("/track/2/fx/1/fxparam/[!12]/value",led_state,argscheme=OSCARG_ADDRESS+OSCARG_DATAUNPACK) + log.info("Creating LEDs...") + # Create LEDs leds=[LED(i) for i in [16,20,21]] + +# Do a massive loop listening for messages. This should really be cleaner... finished=False log.info("Starting loop") while not finished: From 79beeaf72e99853c309f2a36226cffa402e9ca3c Mon Sep 17 00:00:00 2001 From: Ben Savage Date: Tue, 27 Jul 2021 19:36:29 +1000 Subject: [PATCH 4/5] reset button on pin 2 and new LEDs --- osc_client.py | 7 ++++--- osc_server.py | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osc_client.py b/osc_client.py index d3722c9..38ee74b 100755 --- a/osc_client.py +++ b/osc_client.py @@ -24,13 +24,14 @@ def cmd_stop(): osc_msg_send("/stop","i",1) def cmd_restart(): - stop() + cmd_stop() osc_msg_send("/gotomarker","i",1) - play() + cmd_play() parser = argparse.ArgumentParser(description='OSC controller for REAPER') -parser.add_argument('command', action='store',choices=[cmd.replace("cmd_",'') for cmd in globals() if 'cmd_' in cmd],help='Command to send via OSC') +if __name__ == '__main__': + parser.add_argument('command', action='store',choices=[cmd.replace("cmd_",'') for cmd in globals() if 'cmd_' in cmd],help='Command to send via OSC') parser.add_argument('--debug-osc',action='store_true',default=False,help='Debug OSC messages') parser.add_argument('--server',action='store',default='127.0.0.1:8000',help="Server to connect to :. Defaults to localhost:8000",metavar='server:port') args=parser.parse_args() diff --git a/osc_server.py b/osc_server.py index 716a783..5526fa0 100755 --- a/osc_server.py +++ b/osc_server.py @@ -8,6 +8,7 @@ from pprint import pprint import argparse import colorsys import gpiozero +import osc_client # Set up basic vars and logging logging.basicConfig(format='%(asctime)s - %(threadName)s ø %(name)s - ' @@ -132,7 +133,9 @@ osc_method("/track/2/fx/1/fxparam/[!12]/value",led_state,argscheme=OSCARG_ADDRES log.info("Creating LEDs...") # Create LEDs -leds=[LED(i) for i in [16,20,21]] +leds=[LED(i) for i in [16,20,21,12,5]] +restart_button=gpiozero.Button(2) +restart_button.when_pressed=osc_client.cmd_restart # Do a massive loop listening for messages. This should really be cleaner... From 89bab3f584618e5672c896b56178a5d976b1ae67 Mon Sep 17 00:00:00 2001 From: Ben Savage Date: Tue, 27 Jul 2021 23:24:51 +1000 Subject: [PATCH 5/5] added reset button, config --- config.json | 31 +++++++++++++++++++ osc_server.py | 85 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 config.json diff --git a/config.json b/config.json new file mode 100644 index 0000000..deb02ff --- /dev/null +++ b/config.json @@ -0,0 +1,31 @@ +{ + "inputs":{ + "buttons":{ + "restart":{ + "pin": 2, + "when_pressed": "restart" + } + } + }, + + "outputs":{ + "leds":[ + {"name":"1", + "pin": 21, + "target": 3 + }, + { "name":"2", "pin":20, "target":"4", "defaults": { "state":0,"brightness":1.0}}, + { "name":"3", "pin":16, "target":"5"}, + { "name":"4", "pin":12, "target":"6"}, + { "name":"5", "pin":5, "target":"7"}], + + "defaults":{ + "led":{ + "brightness": 1.0, + "colour": 1.0 , + "state": 0 + } + } + } +} + diff --git a/osc_server.py b/osc_server.py index 5526fa0..026058c 100755 --- a/osc_server.py +++ b/osc_server.py @@ -9,12 +9,15 @@ import argparse import colorsys import gpiozero import osc_client +import json # Set up basic vars and logging logging.basicConfig(format='%(asctime)s - %(threadName)s ø %(name)s - ' '%(levelname)s - %(message)s') -log = logging.getLogger("osc") +log = logging.getLogger("ledserver") log.setLevel(logging.INFO) +osc_log=logging.getLogger("osc") +osc_log.setLevel(logging.WARNING) clientname="ledserver" # Get the normalised RGB values for the HSV (i.e, the colour float designating hue from OSC) @@ -44,21 +47,24 @@ class LED: state=False # The pin number of the LED pin=None + # The OSC target FX param + target=0 + name="" # List for logging state mode=["off","on"] # Initialize the object, set up GPIO - def __init__(self,pin): + def __init__(self,pin,target): self.pin=pin + self.target=target self.led=gpiozero.PWMLED(pin) - # Set the brightness, this only triggers on a delta threshold so that we don't go crazy def set_brightness(self,value): if abs(self.brightness-value) > .005: self.brightness=value self.led.value=self.state*self.brightness - print("Setting brightness for LED on pin {} to {}".format(self.pin, self.brightness)) + log.info("Setting brightness for LED on pin {} to {}".format(self.pin, self.brightness)) # Set the colour, only on a delta threshold def set_colour(self,value): @@ -83,19 +89,68 @@ class LED: # Set the colour of the LEDs. This only does the first LED, but when the REAPER controls are finalised, it'll be more controllable def led_colour(value): - leds[0].set_colour(value) + for name,led in leds.items(): + led.set_colour(value) # Set the brightness of all LEDs. As above, when REAPER is finalised it'll be controllable (or everything, as required) def led_brightness(value): - for led in leds: + for name,led in leds.items(): led.set_brightness(value) # Turn an individual LED on or off based on FX param 3+ def led_state(addr,value): - led=addr.split("/")[6] - leds[int(led)-3].toggle_state(bool(value)) - - + target=addr.split("/")[6] + for led in [led for name,led in leds.items() if led.target==target]: + led.toggle_state(bool(value)) + +def btn_restart(): + initialise() + osc_client.cmd_restart() + +def btn_play(): + osc_client.cmd_play() + +def btn_stop(): + osc_client.cmd_stop() + +def initialise(filename='config.json'): + global config + global leds + global buttons + config=json.load(open(filename)) + + log.info("Creating LEDs...") + + # Create LEDs + if 'leds' in globals(): + del leds + leds={led['name']:LED(led['pin'],led['target']) for led in config['outputs']['leds']} + for led in config['outputs']['leds']: + if "defaults" not in led: + led['defaults']={} + for i in config['outputs']['defaults']['led'].keys(): + led['defaults'][i]=config['outputs']['defaults']['led'][i] + + state=led['defaults']["state"] if "state" in led["defaults"] else config['outputs']["defaults"]['led']["state"] + colour=led['defaults']['colour'] if 'colour' in led['defaults'] else config['outputs']['defaults']['led']['colour'] + brightness=led['defaults']['brightness'] if 'brightness' in led['defaults'] else config['outputs']['defaults']['led']['brightness'] + + + leds[led['name']].set_state(bool(state)) + leds[led['name']].set_colour(colour) + leds[led['name']].set_brightness(brightness) + # Don't re-initialise buttons, specifically because we expect this to be called by a button + # And unassigning the calling button will probably break things + if 'buttons' not in globals(): + buttons={} + for name,button in config['inputs']['buttons'].items(): + log.info('Binding button {} to pin {}'.format(name,button['pin'])) + buttons[name]=gpiozero.Button(button['pin']) + log.info('Binding button {} when_pressed to {}'.format(name,button['when_pressed'])) + buttons[name].when_pressed=globals()['btn_'+button['when_pressed']] + pprint(buttons[name].when_pressed) + + # Set up command line arguments parser = argparse.ArgumentParser(description='OSC listener for LEDs') parser.add_argument('--listen',action='store',default='127.0.0.1:9000',help="Listen on a specific IP/port. Defaults to 127.0.0,.1:9000") @@ -109,7 +164,7 @@ log.info("Starting OSC...") # Log OSC stuff if requested if args.debug_osc: - osc_startup(logger=log) + osc_startup(logger=osc_log) else: osc_startup() @@ -130,12 +185,10 @@ osc_method("/track/2/fx/1/fxparam/2/value",led_brightness) osc_method("/track/2/fx/1/fxparam/[!12]/value",led_state,argscheme=OSCARG_ADDRESS+OSCARG_DATAUNPACK) -log.info("Creating LEDs...") -# Create LEDs -leds=[LED(i) for i in [16,20,21,12,5]] -restart_button=gpiozero.Button(2) -restart_button.when_pressed=osc_client.cmd_restart +initialise() +#restart_button=gpiozero.Button(2) +#restart_button.when_pressed=osc_client.cmd_restart # Do a massive loop listening for messages. This should really be cleaner...