after many years i finally got around to rebuild one of these boxes.

so this old soviet made device is now a wireless controller that send out osc. there are in total 34 buttons, 16 knobs and an additional rgb status led. it automatically connects via wifi to max or supercollider and run on 5v (usb powerbank).

kicad schematics, arduino firmware, supercollider classes and maxmsp abstractions attached below.


the inside is quite a mess. i use an atmega168 together with six 4051 multiplexers to read all the inputs. the wifi module is an esp8266-01.


Package icon udssrKontroll_1.0.zip116.03 KB


this box can drive eight speakers (3watt each) and play soundfiles independently from eight micro sd cards (mp3 or wav). an arduino nano is programmed to do sequencing, randomise playback, control volume and set equaliser settings. it all runs on a single 9-12v power supply.


very cheap to build. the biggest cost are all the sd cards.

to send commands out to all eight mp3 players, i'm bit banging 9600 baud serial data out on eight digital pins.


here is another project built around the esp8266. it's a wireless osc controlled 100w led. as the led should act as a stroboscope and not be kept on for long durations of time, i could save space and cost using a smaller sized heatsink. via wifi (opensound control) the led can be turned on/off, the level, attack and release times adjusted etc. there is also a push button trigger input as well as a microphone input (both not connected in the picture). so the strobe can be triggered manually by the musician, by the sound of the nearby instrument or remotely by a computer.

the strobe also send out osc data from the button and mic so it can in turn be used to trigger additional sounds in the computer.

supercollider example code...


n= NetAddr("", 15555);
n.sendMsg(\led, 0.5, 0.1)   //val, fade
n.sendMsg(\led, 0.0, 0.01)  //val, fade
n.sendMsg(\micMode, 1);  //mic on/off
n.sendMsg(\micFade, 1.0, 0.1);  //mic atk rel
n.sendMsg(\butFade, 1.0, 0.1);  //but atk rel

OSCdef(\oscin, {|msg| msg.postln}, \f0led, NetAddr("", 15555));

f0led 1

the battery is a 12v sealed lead-acid and i measured up toward 8 amps current draw. it weights about 0.5kg.

f0led schematics

bill of material...

1       ESP8266-01
1       4x2 socket
1       heatsink
2       100uF cap
1       100 resistor
1       10k resistor
1       10k log pot     (reichelt ACP 6-L 10K)
1       regulator       (reichelt LF 33 CV)
1       mosfet          (reichelt IRLZ 34N)
1       mic             (reichelt MCE 101)
4       screwterminals  (reichelt AKL 101-02)
1       12v lead-acid   (pollin 94‑271194)
1       heatsink        (ebay 2.4x2.4inch Aluminum Alloy Heat Sink for 1W/3W/5W/10W LED Silver White)
1       dcdc            (ebay DC DC boost converter Constant Current Mobile Power supply 250W)
1       100w led        (ebay 100W Cool White High Power LED LIGHT SMD chip Panel 9000-10000LM)

thick wires
heat paste
screws and nuts

arduino code...

// * install OSC from https://github.com/CNMAT/OSC
// * edit where it says EDIT below
// * choose board: "Generic ESP8266 Module" 160 MHz

//TODO: gamma correction

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <OSCMessage.h>
#include <OSCData.h>

//pin3 (urxd) can do pwm out
//pin2 and pin0 can not do pwm
//pin2 and pin0 have to be 3v3 at powerup

#define PINMIC 0
#define PINBUT 2
#define PINPWM 3
#define PORT 15555
#define UPDATERATE 16
#define PINGRATE 600

const char *ssid = "mywlan"; //EDIT your accessPoint network name
const char *password = "mypass";  //EDIT your password
const char *espname = "f0led";
const unsigned int outPort = 57120;
IPAddress outIp;
float micFadeAtk = 1.0, micFadeRel = 0.1; //default fade times
float butFadeAtk = 1.0, butFadeRel = 0.1; //default fade times
float val = 0.0, valTarget = 0.0, fade = 1.0;
unsigned long nextTime;
byte micMode = 0;  //allow mic trigger led on/off
byte micState = 1;
byte butState = 1;
int cnt;
WiFiUDP Udp;
OSCMessage msgPing("/f0led");
OSCMessage msgMic("/f0led");

void setup() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
  outIp = WiFi.localIP();
  outIp[3] = 99;  //send to ip x.x.x.99 on same network (e.g.
  Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
  pinMode(PINMIC, INPUT);
  pinMode(PINPWM, OUTPUT);

void oscLed(OSCMessage &msg) {
  valTarget = msg.getFloat(0);
  fade = msg.getFloat(1);
void oscMicMode(OSCMessage &msg) {
  micMode = msg.getInt(0);
void oscMicFade(OSCMessage &msg) {
  micFadeAtk = msg.getFloat(0);
  micFadeRel = msg.getFloat(1);
void oscButFade(OSCMessage &msg) {
  butFadeAtk = msg.getFloat(0);
  butFadeRel = msg.getFloat(1);
void sendOscBut(byte val) {
  OSCMessage msg("/f0led");
  Udp.beginPacket(outIp, outPort);
void sendOscMic() {
  Udp.beginPacket(outIp, outPort);
void sendOscPing() {
  Udp.beginPacket(outIp, outPort);

void loop() {

  //--osc input
  OSCMessage oscMsg;
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    while (packetSize--) {
    if (!oscMsg.hasError()) {
      oscMsg.dispatch("/led", oscLed);
      oscMsg.dispatch("/micMode", oscMicMode);
      oscMsg.dispatch("/micFade", oscMicFade);
      oscMsg.dispatch("/butFade", oscButFade);

  //--mic input
  if (digitalRead(PINMIC) == 0) {
    if (micState == 0) {
      micState = 1;

  if (millis() >= nextTime) {
    nextTime = millis() + UPDATERATE;
    if (cnt % PINGRATE == 0) {

    //--mic input2
    if (micState == 1) {
      micState = 2;
      if (micMode == 1) {
        valTarget = 1.0;
        fade = micFadeAtk;
    } else if (micState == 2) {
      if (digitalRead(PINMIC) == 1) {
        valTarget = 0.0;
        fade = micFadeRel;
        micState = 0;

    //--button input
    if (digitalRead(PINBUT) == 0) {
      if (butState == 0) {
        butState = 1;
        valTarget = 1.0;
        fade = butFadeAtk;
    } else {
      if (butState == 1) {
        butState = 0;
        valTarget = 0.0;
        fade = butFadeRel;

    //--fade in/out
    if (val < valTarget) {
      val = val + fade;
      if (val > valTarget) {
        val = valTarget;
    } else if (val > valTarget) {
      val = val - fade;
      if (val < valTarget) {
        val = valTarget;

    analogWrite(PINPWM, int(val * 1023));


here is how i build super cheap wireless osc controlled rgb ledstrips. the main components for these are an esp8266, a 5v powerbank, a voltage regulator and some leds. the leds i've used so far are the SK6812 RGBW, but it is easy to adapt the arduino code to work with other models like the WS2812B.

f0neo 1

f0neo 2

f0neo schematics

a basic version of the arduino code shown here below. when it starts it creates a soft access point. connect to it with a computer or phone, figure out the ip address of the esp8266 and start sending osc commands to it.

// * install OSC from https://github.com/CNMAT/OSC
// * install Adafruit_NeoPixel from library manager
// * edit where it says EDIT below
// * choose board: "Generic ESP8266 Module"
// * upload and connect to softap with laptop
// * try to send osc messages to ip port 19999
//protocol: [\rgbw, index, red, green, blue, white] example red: [\rgbw, 0, 255, 0, 0, 0]

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <OSCMessage.h>
#include <OSCData.h>
#include <Adafruit_NeoPixel.h>

#define PORT 19999
#define NUMNEO 12  //EDIT number of neo pixels in use
#define PINNEO 2

const char *ssid = "f0neo"; //EDIT softAccessPoint network name
const char *password = "mypass";  //EDIT password

WiFiUDP Udp;

//EDIT to match type of leds (see example/Adafruit_NeoPixel/strandtest)
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMNEO, PINNEO, NEO_RGBW + NEO_KHZ800);

void setup() {
  WiFi.softAP(ssid, password);

void rgbw(OSCMessage &msg) {
  pixels.setPixelColor(msg.getInt(0), msg.getInt(2), msg.getInt(1), msg.getInt(3), msg.getInt(4));

void loop() {
  OSCMessage oscMsg;
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    while (packetSize--) {
    if (!oscMsg.hasError()) {
      oscMsg.dispatch("/rgbw", rgbw);

attached (zip file) are more elaborate versions of this code - also including maxmsp and supercollider examples and kicad schematics.

Package icon f0neo.zip26.95 KB


here is how i built a wireless isolated dmx controller that takes osc input. the box uses an esp8266 to create a wifi access point that one can connect to with a laptop (or phone or whatever). opensound control messages sent to the box are converted into standard dmx commands. multiple clients can be connected and send dmx commands at the same time.

f0dmx 1

f0dmx 2

below is arduino code for the esp8266, the kicad schematics and some supercollider test code.

//Generic ESP8266 Module, 80 MHz

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <OSCMessage.h>
#include <OSCData.h>
#include <LXESP8266UARTDMX.h>

#define PORT 19999  //EDIT osc port
const char *ssid = "f0dmx"; //EDIT softAccessPoint network name
const char *password = "mypass";  //EDIT password
#define CHANNEL 3 //EDIT wifi channel

WiFiUDP Udp;

void setup() {
  WiFi.softAP(ssid, password, CHANNEL);

void dmx(OSCMessage &msg) {
  int channel = msg.getInt(0);
  int value = msg.getInt(1);
  ESP8266DMX.setSlot(channel, value);

void start(OSCMessage &msg) {

void stop(OSCMessage &msg) {

void loop() {
  OSCMessage oscMsg;
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    while (packetSize--) {
    if (!oscMsg.hasError()) {
      oscMsg.dispatch("/dmx", dmx);
      oscMsg.dispatch("/start", start);
      oscMsg.dispatch("/stop", stop);

bill of material...

1       dcdc            ROE-0505S       reichelt
1       xlr female      XLR 3KU         reichelt
1       optocoupler     6N 137          reichelt
1       ic              SN 75176BP      reichelt
1       box             BOPLA KS 420    reichelt
1       resistor        10K
1       resistor        470
3       resistor        10
1       resistor        120
2       cap             10uF
1       cap             100uF
1       regulator       LF 33 CV
1       micro           ESP8266-01
1       socket          4x2
1       usb cable

f0dmx kicad schematics

example of how to send osc from supercollider to the f0dmx box. make sure you send integers and not floats.

//make sure you are connected to the f0dmx wifi network
n= NetAddr("", 19999);  //the ip and port of the f0dmx box
n.sendMsg(\dmx, 9, 255)  //dmx channel 9, value 255
n.sendMsg(\dmx, 9, 0)
n.sendMsg(\dmx, 7, 100)  //dmx channel 7, value 100
n.sendMsg(\dmx, 7, 0)

n.sendMsg(\stop)  //usually not needed

Package icon kicad schematics27.48 KB

wireless mqtt circuits

i've stared using mqtt for talking to microcontrollers over wifi and here's some code and instructions on how to set up such a system.

there are two programs that have to run in the background. they handle all the communication between the wireless hardware and the client software (maxmsp, supercollider etc). one is mosquitto. mosquitto is a mqtt broker and the central pub/sub hub of the system. the second program is a python mqtt-osc bridge script using the paho client. this python script lets programs like maxmsp or supercollider talk to mosquitto via osc. see the readme.txt included below on how to install and configure these programs.

on the hardware side i build send/receive circuit nodes consisting of a esp8266 module and an arduino pro-mini. these circuits run on 3v, are small and configurable and the parts cost almost nothing. the esp8266 module provides wifi communication and runs a mqtt client (i'm using the adafruit mqtt library), while the pro-mini does the physical inputs and outputs (sensors, leds etc). the two modules talk to each other via serial.
some circuits i've build do 12 digital + 8 analog inputs, while others have 12 leds in combination with 8 analog inputs. but any combination is possible and the number of ins/outs depends on how the pro-mini is programmed. (see portable_promini_ana and portable_promini_led in the zip archive below.)



so far i'm really pleased with this new technique. it seems to scale well and work more reliable than what i used before (sending raw osc via cc3000 or esp8266).

Package icon portable.zip16.11 KB


i got to design and build version 2 of syntjuntan's sewable synthesizer circuit. for this version they wanted to add an on-board amplifier that could drive a passive speaker element.

the circuit now has three schmitt triggers and can run on 3-12V. the amplifier is the classic lm386 and the connector pads around the board are made to fit needle and conductive thread as well as being crocodile friendly.

there are some options as standard through-hole soldering pads (a fourth schmitt trigger and x10 extra gain). the circuit can also be used as a standalone audio amplifier - just ignore the schmitt triggers and connect your own signal to the in pad.

anyway, lots of fun mass producing this and in the process i learned how to do hot-air smd soldering with stencil and solder paste plus got to know kicad a bit better.

i also built a test rig with an arduino and some pogo pins. it both scans for short-cuts and tests the sound.

syntjuntakrets 2 2

syntjuntakrets 2 4

syntjuntakrets 2 5


this board is using an old raspberry pi 1 to control the speed of computer fans. the electronics are pretty simple (see attached schematics below): it takes 7-36V input power, has twelve mosfets for pwm control and finally a dc/dc converter to power the rpi.
it was built for controlling pc cooling fans but can also drive other types of dc motors, lightbulbs or solenoids.
the off button is there to safely power down the raspberry pi.

the trick with this though is that the system can be livecoded over wifi using supercollider, maxmsp or any other osc capable program. so when you start the board the rpi sets up a wireless access point and starts a python script that accepts incoming opensoundcontrol messages. at startup the rpi1 will also start supercollider and load a file (dragspelFans.scd) that is meant to contain whatever code you'd like to run as default. this file you later overwrite with your own sc code that you've developed/livecoded using your laptop.


below are step-by-step instructions on how i set this up plus the relevant python and supercollider code.

* download and install raspbian-jessie-lite onto a 2gb sd card
* connect your rpi to your home router (use a rpi2 with ethernet) and type the following in terminal on your laptop:
* ssh pi@raspberrypi.local #default passwork is raspberry
* sudo raspi-config #change password to _____, set gpu memory to 16, change hostname to fans and reboot
* sudo apt-get update
* sudo apt-get upgrade
* sudo apt-get dist-upgrade
* sudo apt-get clean

//--wifi ap
* sudo apt-get install dnsmasq hostapd firmware-atheros firmware-ralink firmware-realtek
* sudo nano /etc/hostapd/hostapd.conf #and add the following:


* sudo nano /etc/default/hostapd #and change one line to the following:


* sudo nano /etc/dnsmasq.conf #and add the following two lines to the bottom:


* sudo nano /etc/network/interfaces #and edit eth0 to look like:

allow-hotplug eth0
#auto eth0
iface eth0 inet dhcp

* and also change/add wlan0 to look like:

allow-hotplug wlan0
auto wlan0
iface wlan0 inet static
    wireless-power off

* sudo apt-get install liblo-dev python-dev python-pip
* sudo pip install cython #this takes a long time
* sudo pip install pyliblo
* sudo crontab -e #and add the following line at the end

@reboot /usr/bin/python /home/pi/dragspelFans.py

(this step is basically the same as installing sc for rpi1 from here)
* sudo apt-get install libqt5webkit5 libqt5sensors5 libqt5positioning5 libcwiid-dev libfftw3-dev
* sudo apt-get install git dbus-x11 xvfb jackd2 #enable realtime when asked
* git clone git://github.com/redFrik/supercolliderStandaloneRPI1 --depth 1
* mkdir -p ~/.config/SuperCollider
* cp supercolliderStandaloneRPI1/sc_ide_conf_temp.yaml ~/.config/SuperCollider/sc_ide_conf.yaml
* sudo reboot #and log in again with ssh
* cd supercolliderStandaloneRPI1
* nano autostart.sh #and change the two lines to look like this

/usr/bin/jackd -P95 -dalsa -dhw:0 -p1024 -n3 -s &
./sclang -a -l sclang.yaml ../dragspelFans.scd

* crontab -e #and add the following to the end

@reboot cd /home/pi/supercolliderStandaloneRPI1 && xvfb-run ./autostart.sh

* mkdir share/user
* nano share/user/startup.scd #and add the following two lines

OSCFunc({"/home/pi/dragspelFans.scd".load}, \start).permanent= true;
OSCFunc({CmdPeriod.run}, \stop).permanent= true;

* mkdir share/user/Extensions

* from your laptop copy over some files using these commands...

scp ~/arbeten/dragspel/dragspelFans.py pi@fans.local:
scp ~/Library/Application\ Support/SuperCollider/Extensions/DragspelFans.sc pi@fans.local:supercolliderStandaloneRPI1/share/user/Extensions
scp ~/arbeten/dragspel/dragspelFans.scd pi@fans.local:

now log on to dragspel wifi network and try to send some osc commands using sc on your laptop.

(for logging on to the rpi and start supercollider from terminal)
* pkill jackd
* pkill scsynth
* pkill sclang
* export DISPLAY=:0.0
* export `dbus-launch | grep ADDRESS`
* export `dbus-launch | grep PID`
* jackd -P95 -dalsa -dhw:0 -p1024 -n3 -s -r44100 &
* cd supercolliderStandaloneRPI1
* xvfb-run --auto-servernum ./sclang -a -l sclang.yaml

(for starting sc and run the default program)
* cd supercolliderStandaloneRPI1
* xvfb-run ./autostart.sh

save this as dragspelFans.py...

#pwm control for 12 fans

import sys
from os import system
from time import sleep
import RPi.GPIO as GPIO
from liblo import *

inport= 9999
pinoff= 3
pins= [5, 7, 8, 10, 11, 12, 13, 15, 16, 18, 19, 21]
target= ('', 57120)  #to sclang
hz= 50  #pwm frequency

GPIO.setup(pinoff, GPIO.IN)  #no internal pullup needed
GPIO.setup(pins, GPIO.OUT)  #set all outputs
pwms= []
for pin in pins:
        pwms.append(GPIO.PWM(pin, hz))
for pwm in pwms:

class MyServer(ServerThread):
        def __init__(self):
                ServerThread.__init__(self, inport)
        @make_method('/pwms', 'iiiiiiiiiiii')
        def pwms_callback(self, path, args):
                #print args  #debug
                i= 0
                for pwm in pwms:
                        i= i+1
        @make_method('/shutdown', '')
        def shutdown_callback(self, path, args):
                stop('sudo halt -p')  #turn off rpi
        @make_method('/reboot', '')
        def reboot_callback(self, path, args):
                stop('sudo reboot')  #reboot rpi
        @make_method('/start', '')
        def start_callback(self, path, args):
                send(target, '/start', 1)  #start default program in supercollider
        @make_method('/stop', '')
        def stop_callback(self, path, args):
                send(target, '/stop', 0)  #stop default program in supercollider
                for pwm in pwms:
        @make_method(None, None)
        def fallback(self, path, args):
                print 'received unknown message "%s"' % path

def stop(cmd):
        for pwm in pwms:

        server= MyServer()
except ServerError, err:
        print str(err)

def main():
        while True:
                if GPIO.input(pinoff)==0:
                        print 'shutting down...'
                        stop('sudo halt -p')

if __name__ == '__main__':
        except KeyboardInterrupt:

and here's the default dragspelFans.scd demo file that will be run by the rpi at startup. it just uses a pbind to set random pwm duty cycles (0-100) on all twelve pins. overwrite this file with your own code. save this as dragspelFans.scd

        d= DragspelFans.new;
        Event.addEventType(\fans, {d.val(~index, ~val)});
        Pbind(\type, \fans, \dur, 0.5, \index, Pseq([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], inf), \val, Pwhite(0, 100, inf)).play;

here's a screenshot of a simple max patch to manually control the fans...


PDF icon dragspel_schematics.pdf50.67 KB
Binary Data DragspelFans.sc2.01 KB


Subscribe to RSS - electronics