f0mid

wireless midi <-> osc bridge using an esp8266-01. this circuit is extremely cheap to build. schematics, arduino code and examples for supercollider below.

i'm using the great Arduino MIDI Library that allows for both sending and receiving a multitude of midi messages including sysex, system realtime and time code messages. my arduino code just converts all these to/from osc and send or broadcast them over wifi network.

note: sending midi over wifi udp is generally a bad idea. there will be delays, glitches and even lost messages (hanging notes). this is specially problematic for midi time code (sync) messages. that said, in many situations this is ok and in my tests with simple note on/off messages + bend and control, things seem to work just fine.

f0mid

the circuit takes in 5v and then the regulator steps this down to 3.3v. notice the huge 220uF capacitor that's needed to provide power for the esp8266 during its infamous current draw spikes. it wouldn't be hard to add a midi thru section if needed.

f0mid_schematics

supercollider example code...

//http://arduinomidilib.fortyseveneffects.com/a00041.html
//http://arduinomidilib.fortyseveneffects.com/a00043.html

OSCFunc.trace(true, true);  //your laptop should have ip x.x.x.99 to receive - check in the arduino code
OSCFunc.trace(false);

n= NetAddr("192.168.1.100", 18120);  //ip of esp8266

n.sendMsg(\noteOn, 66, 127, 1);  //(note, velo, chan)

n.sendMsg(\noteOff, 66, 0, 1);  //(note, velo, chan)

n.sendMsg(\afterTouchPoly, 50, 60, 3);  //poly pressure (note, press, chan)

n.sendMsg(\controlChange, 1, 64, 3);  //(num, val, chan)

n.sendMsg(\programChange, 10, 4);  //(num, chan)  note the -1 offset

n.sendMsg(\afterTouchChannel, 40, 2);  //(press, chan)

n.sendMsg(\pitchBend, -8000, 1);  //(bend, chan)  -8192 - 8191

n.sendMsg(\sysEx, 240, 14, 5, 0, 5, 247);  //(sysex) - 240 a b c d e ... 247

//realtime
(
var clock= 0xf8;  //248
var start= 0xfa;  //250
var continue= 0xfb;  //251
var stop= 0xfc;  //252
Routine.run({
        n.sendMsg(\realTime, start);
        100.do{
                n.sendMsg(\realTime, clock);
                0.02.wait;
        };
        n.sendMsg(\realTime, stop);
        1.wait;
        n.sendMsg(\realTime, continue);
        100.do{
                n.sendMsg(\realTime, clock);
                0.02.wait;
        };
        n.sendMsg(\realTime, stop);
});
)
n.sendMsg(\realTime, 0xfe);  //active sensing
n.sendMsg(\realTime, 0xff);  //system reset

n.sendMsg(\songPosition, 100);
n.sendMsg(\songSelect, 3);
n.sendMsg(\tuneRequest);

n.sendMsg(\beginNrpn, 10, 3);  //(number, channel)
n.sendMsg(\nrpnDecrement, 40, 3);  //(amount, channel)
n.sendMsg(\nrpnIncrement, 30, 3);  //(amount, channel)
n.sendMsg(\endNrpn, 3);  //(channel)

n.sendMsg(\beginRpn, 10, 4);  //(number, channel)
n.sendMsg(\rpnDecrement, 40, 4);  //(amount, channel)
n.sendMsg(\rpnIncrement, 30, 4);  //(amount, channel)
n.sendMsg(\endRpn, 4);  //(channel)

//--simple midi synth example
(
s.latency= 0.02;
s.waitForBoot{
        var busBend= Bus.control(s);
        var busCF= Bus.control(s);
        var busRQ= Bus.control(s);
        var busVol= Bus.control(s);
        var busPan= Bus.control(s);
        busBend.value= 0;
        busCF.value= 1000;
        busRQ.value= 0.5;
        busVol.value= 0.5;
        busPan.value= 0;
        SynthDef(\note, {|freq= 400, amp= 0.5, gate= 1, busBend, busCF, busRQ, busVol, busPan|
                var env= EnvGen.ar(Env.adsr(0.01, 1, 0.85, 0.1), gate, amp, doneAction:2);
                var bend= In.kr(busBend).lag(0.01);
                var cf= In.kr(busCF).lag(0.01);
                var rq= In.kr(busRQ).lag(0.01);
                var vol= In.kr(busVol).lag(0.01);
                var pan= In.kr(busPan).lag(0.01);
                var src= BLowPass4.ar(VarSaw.ar((freq+bend).midicps), cf, rq);
                OffsetOut.ar(0, Pan2.ar(src*env, pan, vol));
        }).add;
        d= ();
        OSCdef(\f0mid, {|msg|
                switch(msg[1],
                        \activeSensing, {},
                        \noteOn, {
                                d.at(msg[2]).set(\gate, 0);
                                d.put(msg[2], Synth(\note, [
                                        \freq, msg[2],
                                        \amp, msg[3].lincurve(0, 127, 0, 0.75, 4),
                                        \busBend, busBend,
                                        \busCF, busCF,
                                        \busRQ, busRQ,
                                        \busVol, busVol,
                                        \busPan, busPan
                                ]));
                        },
                        \noteOff, {
                                d.at(msg[2]).set(\gate, 0);
                                d.put(msg[2], nil);
                        },
                        \pitchBend, {
                                busBend.value= msg[2]/8192;
                        },
                        \controlChange, {
                                switch(msg[2],
                                        1, {
                                                busCF.value= msg[3].linexp(0, 127, 400, 4000);
                                        },
                                        7, {
                                                busVol.value= msg[3].lincurve(0, 127, 0, 1, 0);
                                        },
                                        10, {
                                                busPan.value= msg[3].linlin(0, 127, -1, 1);
                                        },
                                        74, {
                                                busRQ.value= msg[3].linlin(0, 127, 2, 0.1);
                                        },
                                        {("todo control: "+msg).postln}
                                );
                        },
                        {("todo command: "+msg).postln}
                );
        }, \f0mid);
        CmdPeriod.doOnce({
                busBend.free;
                busCF.free;
                busRQ.free;
                busVol.free;
                busPan.free;
        });
};
)

//mtc - receive
(
var a= MIDISMPTEAssembler({|time, format, dropFrame, srcID|
        [time, format, dropFrame, srcID].postln;
        //time.postln;
});
OSCdef(\f0mid, {|msg, time, addr|
        var chan, valu;
        if(msg[1]==\mtcQF, {
                chan= msg[2].rightShift(4);  //nibble high
                valu= msg[2].bitAnd(15);  //nibble low
                if(chan==7, {
                        valu= switch(valu,
                                6, {valu= 96},  //30fps
                                4, {valu= 64},  //30fps drop
                                2, {valu= 32},  //25fps
                                0, {valu= 0}     //24fps
                        );
                });
                a.value(addr.addr.bitAnd(255), chan, valu);
        });
}, \f0mid);
)

//mtc - send (kind of works - wslib quark required)
(
var startSec= 0;
var t= Main.elapsedTime-startSec;
var a= SMPTE(0, 30);
Routine.run({
        var chan= 0, valu= 0;
        inf.do{
                a.newSeconds(Main.elapsedTime-t);
                switch(chan,
                        0, {valu= a.frames.asInteger.bitAnd(15)},
                        1, {valu= a.frames.asInteger.rightShift(4)},
                        2, {valu= a.seconds.asInteger.bitAnd(15)},
                        3, {valu= a.seconds.asInteger.rightShift(4)},
                        4, {valu= a.minutes.asInteger.bitAnd(15)},
                        5, {valu= a.minutes.asInteger.rightShift(4)},
                        6, {valu= a.hours.asInteger.bitAnd(15)},
                        7, {
                                valu= a.hours.asInteger.bitAnd(1).rightShift(4);
                                switch(a.fps,
                                        30, {valu= valu.bitOr(6)},  //30fps
                                        //30fps drop not supported
                                        25, {valu= valu.bitOr(2)},  //25fps
                                        //24, {valu= valu.bitOr(0)}     //24fps
                                );
                        }
                );
                n.sendMsg(\mtcQF, chan.leftShift(4)+valu.bitAnd(15));
                chan= chan+1;
                if(chan==8, {
                        chan= 0;
                });
                (1/(a.fps*4)).wait;
        };
});
)

AttachmentSize
Package icon f0mid_firmware.zip3.49 KB

f0dim

this simple addition/hack lets me control the velleman k8064 dc dimmer kit via wireless osc or serial. it's based on an esp8266. the kit is isolated, can dim 220v/110v and is rated for up to 750w loads.
normally one control it using 0-12v but by replacing a resistor (R16) it's possible to do it with the 0-3.3v pwm signal that the esp outputs.

f0dim

i probably should cut some air ventilation holes, but so far it hasn't gotten hot inside.

f0dim_schematics

arduino code for the esp8266...

//f.olofsson2017

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

//osc protocol: /dim pwm fade
//  pwm should be 0.0-1.0
//  fade should be 0.0-1.0 (1.0=instant)
//serial protocol: 253 254 pwmhi pwmlo fadehi fadelow 255
//  pwm hi and lo should be 0-1023
//  fade hi and lo should be 0-1023 (1023=instant)

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

#define PORT 15551  //receiving osc port
#define PINPWM 0
#define PAYLOAD 4
#define UPDATERATE 16
#define TRIES 100  //how many times try check for wifi connection at startup

const char *ssid = "wifinetwork"; //EDIT your accessPoint network name
const char *password = "wifipassword";  //EDIT your password
const char *espname = "f0dim";
WiFiUDP Udp;
bool wifi;

byte serial_index = 0;
byte serial_data[PAYLOAD];
unsigned long next_time;
float dim = 0.0, dim_target = 0.0, dim_fade = 1.0;

void setup() {
  analogWriteFreq(5000);   //5khz pwm
  Serial.begin(115200, SERIAL_8N1, SERIAL_RX_ONLY);
  pinMode(BUILTIN_LED, OUTPUT);
  pinMode(PINPWM, OUTPUT);
  analogWrite(PINPWM, 0);
  delay(10);
  WiFi.mode(WIFI_STA);
  WiFi.hostname(espname);
  WiFi.begin(ssid, password);
  byte cnt = 0;
  wifi = false;
  while ((WiFi.status() != WL_CONNECTED) && (cnt < TRIES)) {
    delay(100);
    cnt++;
    digitalWrite(BUILTIN_LED, cnt % 2);
  }
  if (WiFi.status() == WL_CONNECTED) {
    Udp.begin(PORT);
    wifi = true;
  }
  digitalWrite(BUILTIN_LED, !wifi); //blue led on if connected to wifi
}

void oscDim(OSCMessage &msg) {
  dim_target = msg.getFloat(0);
  dim_fade = msg.getFloat(1);
}

void loop() {

  //--serial input
  while (Serial.available() > 0) {
    byte val = Serial.read();
    if ((serial_index == 0) && (val == 253)) {
      serial_index = 1;
    } else if ((serial_index == 1) && (val == 254)) {
      serial_index = 2;
    } else if ((serial_index >= 2) && (serial_index < (PAYLOAD + 2))) {
      serial_data[serial_index - 2] = val;
      serial_index++;
    } else if ((serial_index == (PAYLOAD + 2)) && (val == 255)) {
      dim_target = ((serial_data[0] << 8) + serial_data[1]) / 1023.0;
      dim_fade = ((serial_data[2] << 8) + serial_data[3]) / 1023.0;
      serial_index = 0;
    } else {
      serial_index = 0;
    }
  }

  //--osc input
  if (wifi) {
    int packetSize = Udp.parsePacket();
    if (packetSize) {
      OSCMessage oscMsg;
      while (packetSize--) {
        oscMsg.fill(Udp.read());
      }
      if (!oscMsg.hasError()) {
        oscMsg.dispatch("/dim", oscDim);
      }
    }
  }

  //--fading
  if (millis() >= next_time) {
    next_time = millis() + UPDATERATE;
    if (dim < dim_target) {
      dim = dim + dim_fade;
      if (dim > dim_target) {
        dim = dim_target;
      }
    } else if (dim > dim_target) {
      dim = dim - dim_fade;
      if (dim < dim_target) {
        dim = dim_target;
      }
    }
    analogWrite(PINPWM, int(dim * 1023));
  }
}

supercollider example code...

//f0dim can be controlled either via osc or serial
n= NetAddr("192.168.1.105", 15551);
n.sendMsg(\dim, 1.0, 1.0);  //set 100% instantly
n.sendMsg(\dim, 0.5, 1.0);  //set 50% instantly
n.sendMsg(\dim, 0.0, 1.0);  //set 0% instantly
n.sendMsg(\dim, 1.0, 0.001);  //slow fade in
n.sendMsg(\dim, 0.0, 0.0005);  //slower fade out

SerialPort.listDevices;
(
~port= SerialPort("/dev/tty.usbserial123", 115200, crtscts: true);  //EDIT serialport
CmdPeriod.doOnce({~port.close});
f= {|pwm= 1023, fade= 1023|
        Int8Array[253, 254, pwm>>8, pwm%256, fade>>8, fade%256, 255];
};
)

~port.putAll(f.value(1023, 1023));  //set 100% instantly
~port.putAll(f.value(512, 1023));  //set 50% instantly
~port.putAll(f.value(0, 1023));  //set 0% instantly
~port.putAll(f.value(1023, 3));  //slow fade in
~port.putAll(f.value(0, 2));  //slower fade out

~port.close;

rpi audio codec

here's how to set up the proto WM8731 based audio codec module from MikroElektronika and use it with supercollider on a raspberry pi 3.

power off the rpi and connect the proto board to the rpi with jump wires like this...

proto               raspberry
-----                -----
sck           ->    rpi 12
miso         ->    rpi 38
mosi         ->    rpi 40
adcl+dacl  ->    rpi 35  //both proto pins go to the same rpi pin
sda           ->    rpi 3
scl            ->    rpi 5
3.3v         ->    rpi 1
gnd          ->    rpi 6

see pinout diagram for help with the rpi gpio numbering.

power on the rpi and open a terminal and type...

sudo nano /boot/config.txt

find and uncomment the first line and add the second...

#dtparam=audio=on
dtoverlay=audioinjector-wm8731-audio

press ctrl+o to save and ctrl+x to exit.

sudo reboot

again open a terminal and type...

alsamixer

first press F5 to show all controls, then...
* enable item 'Mic' (space)
* set item 'Mic Boost' to 100 (up arrow key)
* enable item 'Playback Deemphasis' (m key)
* disable item 'ADC High Pass Filter' (m key)
* set item 'Input Mux' to Mic (arrow keys)
* enable item 'Output Mixer HiFi' (m key)

alsamixer-proto

now you should be able to start jackd with for example...

jackd -P75 -dalsa -dhw:0 -r48000 -p256 -n2

and get decent in/out sound at 5.3ms jack latency.

solar powered supercollider

here's how to run supercollider on power coming from the sun...

the main component is a raspberry pi zero with wifi that at startup creates a wireless access point, starts jackd+supercollider and launches a default sound patch.
to play around with the system and change the default sound log on to the access point with a laptop and start livecoding supercollider via the terminal or use the standard scide via vnc. one can for example also set up a couple of osc responders and let friends log on with their phones to control sounds.

front...
solarpoweredsupercollider02

back...
solarpoweredsupercollider01

the connections are pretty straightforward...

solarpanel -> dcdc converter -> battery -> rpi0 -> soundcard -> amplifier -> speaker(s)

the dcdc converter is taking the higher voltage coming out of the solar panel (~6v) and turns it into a stable 5v. this is then either charging the battery, or directly powering the raspberry pi. note that the amplifier also needs 5v and here i have taken that from pins 4 and 6 on the pi.

the powerbank battery is optional and can be omitted but then the solar panel will have to stay in the sun at all times - else the system will turn off or reboot when the power from the panel drops. the battery acts like a reservoir for when clouds are passing by but not only that - it also lets the system be used for a couple of hours in the evening.

material/modules needed:

* rpi zero w
* 8gb micro sd card
* 5v usb powerbank (best if it can charge and output power at the same time)
* 6v 6watt solar panel ( www.adafruit.com/product/1525 )
* dc-dc converter ( www.olimex.com/Products/Power/DCDC6-16-TO5 )
* usb sound adapter
* pam8403 stereo amplifier module
* two full range speakers
* wooden board, double adhesive tape + various cables and screws

download raspbian jessie (here jessie desktop 2017-07-05-raspbian-jessie.zip) and burn it onto the sd card with etcher.

do the usual setup (change default password, activate ssh), optionally activate vnc and then install supercolliderStandaloneRPI1.

to set up a wifi access point do the following (basically the same as this)...

* sudo apt-get install dnsmasq hostapd
* sudo systemctl stop dnsmasq
* sudo systemctl stop hostapd
* sudo nano /etc/dhcpcd.conf  #and add...
        denyinterfaces wlan0
* sudo nano /etc/network/interfaces  #and make sure wlan0 looks like...
        allow-hotplug wlan0
        iface wlan0 inet static
            address 192.168.4.1
            netmask 255.255.255.0
            network 192.168.4.0
* sudo service dhcpcd restart
* sudo ifdown wlan0
* sudo ifup wlan0
* sudo nano /etc/dnsmasq.conf  #and add the following...
        interface=wlan0
        dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h
* sudo nano /etc/hostapd/hostapd.conf  #and add the following...
        interface=wlan0
        driver=nl80211
        ssid=solarsc
        hw_mode=g
        channel=7
        wmm_enabled=0
        macaddr_acl=0
        auth_algs=1
        ignore_broadcast_ssid=0
        wpa=2
        wpa_passphrase=mypass12345
        wpa_key_mgmt=WPA-PSK
        wpa_pairwise=TKIP
        rsn_pairwise=CCMP
* sudo nano /etc/default/hostapd  #and change to the following...
        DAEMON_CONF="/etc/hostapd/hostapd.conf"
* sudo service hostapd start
* sudo service dnsmasq start

last change the file mycode.scd and add this default sound (tweet0340)...

s.waitForBoot{
        play{a=SinOscFB;Mix(AllpassN ar:a.ar(midicps(Duty.ar(c=a.ar(1/[12,8])+3/24,0,Dseq([0,8,5,1,5,4,5]*round(c*18),inf))+60),c*2)/4)}// #SuperCollider
};

if it is distorting try lowering the volume in alsamixer.

udssrKontroll

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.

udssrKontroll01

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.

udssrKontroll02

AttachmentSize
Package icon udssrKontroll_1.0.zip116.03 KB

f08ch

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.

f08ch

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.

f0led

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. so the strobe can be either 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...

OSCFunc.trace(true)
OSCFunc.trace(false)

n= NetAddr("192.168.1.10", 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("192.168.1.10", 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 ID 10       //EDIT device id (also static ip)
#define PINMIC 0
#define PINBUT 2
#define PINPWM 3
#define PORT 15555
#define UPDATERATE 16
#define TRIES 100  //how many times try check for wifi connection at startup
#define PINGRATE 600
#define STATICIP 1  //optional - 0 or 1

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;
#if STATICIP==1
IPAddress ip(192, 168, 1, ID); //EDIT optional static ip
IPAddress gateway(192, 168, 1, 1);  //EDIT optional router
IPAddress subnet(255, 255, 255, 0);
#endif
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;
IPAddress outIp;
WiFiUDP Udp;
OSCMessage msgPing("/f0led");
OSCMessage msgMic("/f0led");

void setup() {
  delay(10);
  WiFi.mode(WIFI_STA);
  WiFi.hostname(espname);
  WiFi.begin(ssid, password);
#if STATICIP==1
  WiFi.config(ip, gateway, subnet);
#endif
  cnt = 0;
  while ((WiFi.status() != WL_CONNECTED) && (cnt < TRIES)) {
    delay(100);
    cnt++;
  }
  outIp = WiFi.localIP();
  outIp[3] = 99;  //send to ip x.x.x.99 on same network (e.g. 192.168.1.99)
  Udp.begin(PORT);
  Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
  pinMode(PINMIC, INPUT);
  pinMode(PINBUT, INPUT_PULLUP);
  pinMode(PINPWM, OUTPUT);
  msgMic.add(ID);
  msgMic.add("mic");
  msgPing.add(ID);
  msgPing.add("ping");
}

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");
  msg.add(ID);
  msg.add("but");
  msg.add(val);
  Udp.beginPacket(outIp, outPort);
  msg.send(Udp);
  Udp.endPacket();
  msg.empty();
}
void sendOscMic() {
  Udp.beginPacket(outIp, outPort);
  msgMic.send(Udp);
  Udp.endPacket();
}
void sendOscPing() {
  Udp.beginPacket(outIp, outPort);
  msgPing.send(Udp);
  Udp.endPacket();
}

void loop() {

  //--osc input
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    OSCMessage oscMsg;
    while (packetSize--) {
      oscMsg.fill(Udp.read());
    }
    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) {
      sendOscPing();
    }
    cnt++;

    //--mic input2
    if (micState == 1) {
      micState = 2;
      sendOscMic();
      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;
        sendOscBut(1);
        valTarget = 1.0;
        fade = butFadeAtk;
      }
    } else {
      if (butState == 1) {
        butState = 0;
        sendOscBut(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));
  }
}

f0led 2

f0led 3

update 171124 added two more photos, a maxpatch and updated the arduino code with static ip option

AttachmentSize
Package icon f0ledtest.maxpat.zip2.07 KB

f0neo

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 192.168.4.1 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() {
  pixels.begin();
  pixels.show();
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, password);
  Udp.begin(PORT);
}

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

void loop() {
  OSCMessage oscMsg;
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    while (packetSize--) {
      oscMsg.fill(Udp.read());
    }
    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.

AttachmentSize
Package icon f0neo.zip26.95 KB

Pages

Subscribe to f0blog RSS