Arduino/HW
Hardware
This page lists or contains several projects I realized using small devices such as RPi, Arduino and other micro-controllers or just plain (simple) HW.
Several (most) projects are dedicated to controlling some aspects of the X32 digital mixing console I own, and are listed below, with a link to their description, source code and implementation example.
Other projects are just for fun or because people asked me to help with implementing some of their ideas or needs.
In all cases, source code is provided, and in many cases, I provide project construction example, but you are of course welcome to use you imagination to make your own version.
X32 Deck Lamp
I wanted to install a light for the X32 Deck, but did not find the one I was looking for at local places; and the ones I found on the internet seemed a little too expensive for what they seemed to be... so I looked for an alternative and found this LED (3W) desk light at Ikea. Cheap enough to play with and with enough goose neck length (2 feet / 60cm) to go with the X32. I also liked the smaller diameter (about 8mm) of the spring, much lighter than the XLR goose neck flexes I ordered on the internet.
Home with my LED lamp, I realized it works on 3.3 Volts when the X32 provides 12 Volts! :-<
I still looked for and ordered and 4 pins XLR to see it it would fit at the other end of the light. Good (almost) fit. Once I removed (using a Dremel tool) the plastic ending, I was left with a 9.5mm diameter fileted tube. The Neutrik 4-pin XLR top can take this, with the little help of a drill bit. The two (XLR top and lamp footing) are now secured with 2component epoxy glue.
I ordered a small 3W LED lamp that worked on 12V. When it arrived, I found it was connected to a buck power converter (12Vac->whatever volt) that fit on a tiny PCB, but still too big to go either in the top part of the lamp or in the XLR itself. Fortunately that PCB had extra components I did not need, I removed the 4 diodes that are used to convert the AC to 12V DC before the power converter. I also cut the PCB to its minimal size (a mere 12x8 mm), I also removed the XLR pins parts that are used for soldering the wires so they became flush with the XLR plug itself, freeing room inside the XLR cavity.
With flex wires in place, the PCB fit (barely) in the top cavity of the XLR, and I could screw back the XLR jack/pins in place after testing and making sure there were no shorts.
I'm quite happy with result!, a lightweight desk lamp that accepts 4 different useful levels of intensity (not too bad for a buck power converter) giving some dimming function.
And because it's really lightweight, it won't put too much stress on the X32 lamp XLR connector.
Overall cost: 12€ for the Ikea lamp, 4.5€ for the 4pin Neutrik XLR, and 3.2€ for the 12V LED bulb and its converter. And 3h or work.
Note: I noted the X32 when being switched OFF, will send a 12V pulse to the lamp, even if the lamp setting is on OFF. This means it's better to remove the lamp BEFORE switching OFF the console, as it's highly recommended to disconnect output XLRs or switch OFF speakers before switching OFF the console. Nothing bad, but a little annoying.MIDI stomp box
MIDI stomp box for controlling X32 parameters
There's been a few demands in the X32 and XAir forums for a way to control some parameters using a stomp box (a simple foot pedal) and possible MIDI as a way to send information. I have been looking at different options and decided to use one of these tiny Arduino devices; In my case I opted for a cheap and simple, yet USB programmable Arduino Leonardo device. This is a Chinese made board, using an Atmega32U4 chip at 16MHz, equivalent to the SparkFun pro-micro board - (board Schematics).
After installing the necessary software to play/program the board, I looked at the few examples available and used the MIDI example program as a basis for the job. Before programing, I wanted to ensure the board would work as a pedal, on battery; Typical batteries used in foot pedals are 9V, and powering a 5V board from 9V is possible on the pro-micro but that would mead using the on-board LDO, which would waist in heat about 1/2 of the battery power in going from 9V to 5V. A better option was to use a buck DC/DC converter with an efficiency above 85%. Again a small Chinese made board was used for that. The battery itself is a rechargeable one, with a capacity of 800mA; quite enough for the job.
By the way the pedal works fine when USB is plugged in (no need for a battery in that case).
As this is a pedal board, I have decided to 3D print the pedal enclosure, went through one or two prototypes, and came out with a simple model, using the online TinkerCad software (main box, bottom, battery door).
Back to software:
The source-code is quite simple and presented below; In my case, I want to send SYSEX messages each time I release the foot switch, and I want a LED to blink when the SYSEX message is sent to MIDI out.
MIDI stomp box Source Code
The program waits for a HW pin to becon=me active (when the user presses a switch). This event is then used to generate the sending of the SYSEX MIDI array.
This project started from an Arduino project/sketch file: MIDI note player / created 13 Jun 2006
The circuit for MIDI out: Leonardo digital pin 1 connected to MIDI jack pin 5 MIDI jack pin 2 connected to ground MIDI jack pin 4 connected to +5V through 220-ohm resistor Attach a MIDI cable to the jack, then to a MIDI synth, and play music.
The circuit for the switch: Pushbutton attached to Leonardo digital pin pin 2 and to ground*/
unsigned char S_array[] = { // MIDI SYSEX or command array data 0xF0, 0x7F, 0x02, 0x06, 0x02, 0xF7};//const int S_size = sizeof(S_array) / sizeof(char); // MIDI SYSEX or command array sizeconst int B_pin = 2; // Switch pin (in)const int L_pin = 7; // LED pin (out)int D_delay = 100; // Debounce time; increase if neededint S_state; // state of the switchint L_state; // state of the LED
void setup() { // called once at program start // Set MIDI baud rate: Serial1.begin(31250); // Set pins directions pinMode(B_pin, INPUT_PULLUP); pinMode(L_pin, OUTPUT); S_state = digitalRead(B_pin); digitalWrite(L_pin, HIGH);}
void loop() { // start main loop // read the state of the switch, check for state change: S_state = digitalRead(B_pin); // wait a few milliseconds delay(D_delay); // stop LED if needed if (L_state) { digitalWrite(L_pin, HIGH); L_state = 0; }
// only send MIDI data if the new button state is LOW if ((digitalRead(B_pin) == 1) && (S_state == 0)) { // LED on digitalWrite(L_pin, LOW); L_state = 1; // play all bytes from SYSEX data array: for (int i = 0; i < S_size; i ++) { Serial1.write(S_array[i]); } } else { // wait a little to save CPU power delay(10); }}
To program a different SYSEX sequence, just connect the pedal to USB on a PC and flash the program with a different SYSEX byte array.
It is also quite simple to add a choice of SYSEX strings if one decides to have several strings selected based on a switch or a selector that would be read prior the foot switch being activated.
Below some pictures of the project, and a small video of the system is also available, visit this Youtube link.
X32 MIDI Tap Tempo
X32 MIDI Tap Tempo Stompbox
Using the stompbox described above as Tap Tempo for X32 or Xair devices:
This is quite easy: Just replace the software :-).
There will be two foot switch press actions required: The first one to initialize a timer, and the second one to get the time difference between the two consecutive switch actions.
This time delta is used as a value sent part of a SYSEX MIDI data that contains the OSC command for setting the delay value of an existing effect in X32 or XAir.
A small video illustrates the tap tempo function, how it changes X32 data for delay effects, and the program source is shown below:
X32 MIDI Tap Tempo Source Code
The program waits for a HW pin to becon=me active (when the user presses a switch). These events are used to generate a delay sent as SYSEX MIDI array to the connected device.
This project started from an Arduino project/sketch file: MIDI note player / created 13 Jun 2006
The circuit for MIDI out: Leonardo digital pin 1 connected to MIDI jack pin 5 MIDI jack pin 2 connected to ground MIDI jack pin 4 connected to +5V through 220-ohm resistor Attach a MIDI cable to the jack, then to a MIDI synth, and play music.
The circuit for the switch: Pushbutton attached to Leonardo digital pin pin 2 and to ground*/
unsigned char S_array[] = { // MIDI SYSEX or command array data 0xF0, 0x00, 0x20, 0x32, 0x32, 0x2F, 0x66, 0x78, 0x2F, 0x30, 0x2F, 0x70, 0x61, 0x72, 0x2F, 0x30, 0x32, 0x20, 0x33, 0x30, 0x30, 0x30, 0xF7};//const int S_size = sizeof(S_array) / sizeof(char); // MIDI SYSEX or command array sizeconst int B_pin = 2; // Switch pin (in)const int L_pin = 7; // LED pin (out)int D_delay = 20; // Debounce time; increase if neededint S_state; // state of the switchint L_state; // state of the LEDunsigned long Time1; // Start timeunsigned long Dtime; // Delta timeint Tcount; // Indicate first or secont press for timer calculation
void setup() { // called once at program start // Set MIDI baud rate: Serial1.begin(31250); // Set pins directions pinMode(B_pin, INPUT_PULLUP); pinMode(L_pin, OUTPUT); S_state = digitalRead(B_pin); digitalWrite(L_pin, HIGH); Time1 = Dtime = Tcount = 0;}
void loop() { // start main loop // read the state of the switch, check for state change: S_state = digitalRead(B_pin); // wait a few milliseconds delay(D_delay); // stop LED if needed if (L_state) { digitalWrite(L_pin, HIGH); L_state = 0; } // // only send MIDI data if the new button state is LOW if ((digitalRead(B_pin) == 1) && (S_state == 0)) { if (Tcount == 0) { // first time press: Get time Time1 = millis(); Tcount = 1; } else { // second time press: calculate time difference and send data // calculate Dtime in milliseconds Dtime = millis() - Time1; // Dtime as valid X32 data 0 to 3000 ms if (Dtime < 1) Dtime = 0; if (Dtime > 3000) Dtime = 3000; // set value of FX number in SYSEX buffer S_array[9] = 0x32; // set value of Ftime into SYSEX buffer S_array[18] = 0x30 + Dtime / 1000; S_array[19] = 0x30 + (Dtime - (Dtime/1000) * 1000) / 100; S_array[20] = 0x30 + (Dtime - (Dtime/100) * 100) / 10; S_array[21] = 0x30 + (Dtime - (Dtime/10) * 10); // reset time press counter Tcount = 0; // play all bytes from SYSEX data array: for (int i = 0; i < S_size; i ++) { Serial1.write(S_array[i]); } // LED on digitalWrite(L_pin, LOW); L_state = 1; } }}
Changing the FX number for X32 or Xair is easy: just replace the byte data value assigned to S_array[9]. The default/arbitrary value is FX #2, or 0x32. Use 0x31 for FX #1, 0x33 for FX #3, etc.
Usage: It's a matter of taste, but it is also possible to generate a new Dtime value at each press of the footswitch.
To achieve this, just delete the Tcount variable, its initialization in the setup()section and the if statement that uses it.
Within the loop() section, replace the line Tcount = 0; by Time1 = millis();
This way the first press will typically be bogus or greater than 3 seconds, but each subsequent press will provide a valid tap tempo value corresponding to your foot rythm.
X32 Encoder
X32 audio scrub/jog and transport for X-Live
Replacing the X32 "phone holder" by a Transport and Audio scrub/jog control device for X-Live
I had in mind for a long time the idea of removing the useless phone holder and use that space for additional X32 Controls. But what to choose or implement?
I was glad Behringer finally came out with the XLive board, adding a really great feature and a new set of capabilities to the desk.
This was the trigger for starting [at last] this project. My goal is to provide a set of programmable keys, pretty much like the User Assign Section, except I want to be able set my own colors! :) I also want it to provide a transport section for the XLive board, something I tested with a small Windows application (see X32Jog4Xlive above) and showing it can be done.
I kept a jog/shuttle encode I savaged from a 15 years old Philips VCR remote control. Turns out this is an ALPS encoder such as this one (below) still available from Aliexpress, but other similar devices can be adapted too, such as this one I will try too.
This device combines two functions:
Shuttle (to perform audio scrubbing)
-1- it provides an encoder which gives CW and CCW rotation information from the centre axis
Jog (to set specific action in addition to shuttle)
-2- it provides an indication of the position of the outer ring axis, coded on 5 bits (+/- 15 positions plus center), which is mounted on a spring so it automatically returns to its center position
I started to work on the actual device after ordering a WIFI enable/capable Arduino (a device known as NodeMCU V3, that combines the ease of use of Arduino and an ESP8266 WIFI device), as shown below. Such device is typically used in IoT developments.
The characteristics are impressive for such a small device:
16MB flash, 80MHz (or 160MHz) clock, 32bits processor, 17 GPIOs, enough RAM and... integrated WIFI.
It also comes with the ease of use of the Arduino ecosystem, which makes it (very) easy to use for someone who codes in C :).
My idea of the device is therefore a combination of the Jog/Shuttle encoder, the nodeMCU processor, and a series of 8 push buttons I want to be able to program actions for and assign colors to. My idea for this was initially to find buttons with incorporated LEDs. I did find some, but they came with several issues: each color was fixed (and therefore not programmable), and they would require more electronics and IO pins I would have at hand after using some GPIOs for the jog/shuttle and the keypad itself. The 8 separate keys also were a problem by themselves, as I would have to use a de-multiplexer to limit the number of IOs.
I went for a one-wire, analog keypad approach which is documented in several web pages, and there's a dedicated library to manage the resulting device. It is connected to the analog input which I didn't use, so this is a good thing as it doesn't cost me any GPIOs. I've had trouble with the onewirekeypad library, and couldn't get a reliable keypad to work (i.e. just repetitively give me the key I pressed) and ended up writing my own small key selection routine based on the analog voltage I measured. The rest of the library is actually quite good at de-bouncing the signal and managing key events, so I'm keeping this part!
As for the LEDs, I am using WS2811 based chips/modules that are commonly found in LED strips where each LED can be programmed individually. Again, the Arduino ecosystem is great as there are libraries to manage so-called neopixels. In my case I just need 8 of them!. I got single chip devices from Aliexpress I then cut to their strict minimum size and wired together as a string of 8 LEDs. As a result, a single GPIO is used to control the 8 LEDs, providing the capability to animate, change color and intensity of any of the LEDs, using the FastLed library.
Next was to figure out how to combine keys and LEDs so the color of LEDs would be visible with the keys. IF properly isolated, each LED could light its respective key, as long as there is transparent material to let the light shine through. I found these simple and cheap keys that would fit perfect for what I wanted. The plastic enclosure would have to be "special" in order to isolate each key and its associated LED from the other.
The enclosure I designed is made of two parts (bottom and top). The bottom (V2) or (V3) is quite simple and secures the nodeMCU processor board so its power socket can be plugged in with no problems. The top (V2) or (V3) part is more complex as it must: -1- support the keypad and LEDs, properly isolated, -2- support the ALPS jog/shuttle, -3- be low enough to still fit under the Desksaver protective hardcover (a must for X32 protection). I tested several prototypes before arriving at at design that agrees with the constraints above.
The difference between V2 and V3 is the thickness of the enclosure;2mm and 1.5mm respectively. Pictures here are from V2 enclosure.
Prototype
Below are a few pictures of the current prototype; I am still moderately happy with the analog keypad which is a little capricious sometimes: I probably need to adjust the 5 resistor values to better distribute the analog output of each key over the 3.3V supply.
The hardware is quite simple and a schematic file is available from my website.
Time to write and test some software :) I selected the following functionality for the 8 keys of the device's keypad:
#1 Set the device in audio scrub mode or in wind (FF or RW) the position of the jog wheel ring sets the direction and speed.
#2 STOP transport function
#3 PAUSE (default) or PLAY transport function
#4 RECORD
#5 Set Marker at the current position
#6 Delete Marker
#7 unassigned
#8 1 click to X-Live screen
Depending on the state of key #1, the device is in audio scrub mode or wind mode
Audio scrub mode: The jog wheel (outer ring) is used as a 1/8 to 128 acceleration factor for the increments set by the center shuttle encoder. Depending on the X-Live being in PLAY or PAUSE, the unit increment is 1000ms or 25ms, respectively.
Wind mode: The jog wheel is used as a way to FF or RW transport device with speeds of ±1ms to ±300s every 10ms (an arbitrary delay added to the main loop).
As the device is WIFI connected to the X32, it is possible to get X32 OSC notifications, while this complicates SW development, it enables for example to manage the device's LEDs brightness in sync with the X32 one as the user rotates the brightness knob. This is shown in a short video: animated-3.
The software uses number of libraries: WiFiUdp.h, OnewireKeypad.h, Encoder.h, and FastLED.h.
The source code for the prototype is given below. Make sure you set your WIFI SSID, password and the X32 IP address in their respective variables.
X32Encoder Source Code
Note: Interestingly, I found this device after making mine; nice piece of gear and the company has other programmable sets of keys that can be of interest too. My small device as described above is a lot cheaper and was fun to build. I learned a lot in the process, and I can program it in any ways I need.
X32 FX MIDI control box
(a project based on an idea from Chris K.)
Here's an example of a possible modification/extension of the design used for the MIDI stompbox above. How to control the 4 FX returns mutes from an X32, using MIDI. The idea is to provide 6 foot switches (simple push-ON ones) to control the following:
unmute FX1 to FX4,
mute all FX, and
provide FX4 unmute/mute respective of the press/release of a switch.
The schematic is quite similar to what's used for the MIDI stompbox, keeping the component number to the strict minimum. I also use 4 LED chips that are linked in series to provide the state of FX1 to FX4 above the foot switches. These LED chips are similar to what I used in the X32Encoder project above.
A small video shows the device in action.
The software is shown below:
X32 FX MIDI control box Source Code
The program waits for a HW pin to becon=me active (when the user presses a switch). These events are used to generate a delay sent as SYSEX MIDI array to the connected device.
The circuit for MIDI out: Leonardo digital pin 1 connected to MIDI jack pin 5 MIDI jack pin 2 connected to ground MIDI jack pin 4 connected to +5V through 220-ohm resistor Attach a MIDI cable to the jack, then to a MIDI synth, and play music.
The circuit for the switch: Pushbutton attached to Leonardo digital pin pin 2 and to ground*///#define SMAX 6 // number of supported footswitches#define LMAX 4 // number of footswitch LEDs
#define FASTLED_INTERNAL#include <FastLED.h>// LEDs// LED control on D8// color codes for HSV can be found at https://github.com/FastLED/FastLED/wiki/Pixel-reference#chsv// S: typicall 255; Avoid setting a V value above 128 (1/2 full) as it consumes a lot of power#define LED_CTRL_PIN 8 // LED control will be driven from Data pin 8CRGB C_Leds[LMAX]; // LED color data arrayint A_Leds[LMAX]; // used to store the color (Hue value [0..255]) assigned to each LEDint V_value; // used to set/update the max luminosity//unsigned char S0_array[] = { // MIDI SYSEX for /fxrtn/01/mix/on ON 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x31, // /01.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x4E, // <sp>ON 0xF7 // SYSEX tail};const int S0_size = sizeof(S0_array) / sizeof(char); // MIDI SYSEX or command S0 array size//unsigned char S1_array[] = { // MIDI SYSEX for /fxrtn/02/mix/on ON 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x32, // /02.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x4E, // <sp>ON 0xF7 // SYSEX tail};const int S1_size = sizeof(S1_array) / sizeof(char); // MIDI SYSEX or command S1 array size//unsigned char S2_array[] = { // MIDI SYSEX for /fxrtn/03/mix/on ON 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x33, // /03.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x4E, // <sp>ON 0xF7 // SYSEX tail};const int S2_size = sizeof(S2_array) / sizeof(char); // MIDI SYSEX or command S2 array size//unsigned char S3_array[] = { // MIDI SYSEX for /fxrtn/04/mix/on ON 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x34, // /04.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x4E, // <sp>ON 0xF7 // SYSEX tail};const int S3_size = sizeof(S3_array) / sizeof(char); // MIDI SYSEX or command S3 array size//unsigned char S4_array[] = { // MIDI SYSEX for /fxrtn/[01…04]/mix/on OFF 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x31, // /01.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x46, 0x46, // <sp>OFF 0xF7, // SYSEX tail 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x32, // /02.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x46, 0x46, // <sp>OFF 0xF7, // SYSEX tail 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x33, // /03.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x46, 0x46, // <sp>OFF 0xF7, // SYSEX tail 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x34, // /04.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x46, 0x46, // <sp>OFF 0xF7 // SYSEX tail};const int S4_size = sizeof(S4_array) / sizeof(char); // MIDI SYSEX or command S0 array size//unsigned char S5_array[] = { // MIDI SYSEX for /fxrtn/04/mix/on OFF 0xF0, 0x00, 0x20, 0x32, 0x32, // SYSEX header 0x2F, 0x66, 0x78, 0x72, 0x74, 0x6E, // /fxrtn.. 0x2F, 0x30, 0x34, // /04.. 0x2F, 0x6D, 0x69, 0x78, // /mix.. 0x2F, 0x6F, 0x6E, // /on.. 0x20, 0x4F, 0x46, 0x46, // <sp>OFF 0xF7 // SYSEX tail};const int S5_size = sizeof(S5_array) / sizeof(char); // MIDI SYSEX or command S5 array size
int S_pin[SMAX]; // array of I/O pin number associated to each switch lineint S_read[SMAX]; // array of input readings from digital input linesint S_state[SMAX]; // array of current switches' statesint S_check[SMAX]; // array of switch state changesint S_change[SMAX]; // array of current switches' change status [0/1]int D_delay; // general debounce delay (typically between 20 and 50(ms))
void setup() { // set LEDs FastLED.addLeds < WS2811, LED_CTRL_PIN, RGB > (C_Leds, LMAX); // assign IO pin numbers to switches according to schematics S_pin[0] = 2; S_pin[1] = 3; S_pin[2] = 4; S_pin[3] = 5; S_pin[4] = 6; S_pin[5] = 7; // assign debounce delay D_delay = 30; // typical values are less than 50ms // set LED brightness value V_value = 127; // mid-intensity // pinMode(LED_CTRL_PIN, OUTPUT); for (int i = 0; i < SMAX; i++) { pinMode(S_pin[i], INPUT_PULLUP); S_state[i] = 1; // 1 by default as IN is PULLUP S_read[i] = 1; // 1 by default as IN is PULLUP S_change[i] = 0; // all change states to OFF S_check[i] = 0; // all check states to OFF } for (int i = 0; i < LMAX; i++) { A_Leds[i] = i * 96; // whatever these are (green, red,...) C_Leds[i] = CHSV(A_Leds[i], 255, 0); // all LEDs black at startup } FastLED.show(); // resets all LEds at startup // set MIDI baud rate: Serial1.begin(31250);}//void loop() { // scan for changes and get time of state change(s) for (int i = 0; i < SMAX; i++) { if ((S_change[i] == 0) && (S_check[i] == 0)) { // only consider switches that are 'ready' S_read[i] = digitalRead(S_pin[i]); if (S_state[i] ^ S_read[i]) { // consider all transitions S_check[i] = millis(); } } } // done for all switches, still stable after debounce time? for (int i = 0; i < SMAX; i++) { if ((S_change[i] == 0) && (S_check[i] != 0)) { // only for switches to consider if ((millis() - S_check[i]) > D_delay) { S_read[i] = digitalRead(S_pin[i]); // still stable ? if (S_state[i] ^ S_read[i]) { S_change[i] = 1; // consider switch has changed state S_check[i] = 0; // prevent further checks } S_state[i] = S_read[i]; // remember state (pressing ON is 0) } } } // at this point we know which switches have been pressed // S4 mutes all effecs (set state) if ((S_change[4] == 1) && (S_state[4] == 0)) { // only for OFF->ON trasition // send corresponding SYSEX for (int i = 0; i < S4_size; i ++) { Serial1.write(S4_array[i]); } // done; reset switch state S_change[4] = 0; //update LEDs (reset all) C_Leds[0] = CHSV(A_Leds[0], 255, 0); C_Leds[1] = CHSV(A_Leds[1], 255, 0); C_Leds[2] = CHSV(A_Leds[2], 255, 0); C_Leds[3] = CHSV(A_Leds[3], 255, 0); FastLED.show(); } else { S_change[4] = 0; } // S0 unmutes Fxrtn/1 (set state) if ((S_change[0] == 1) && (S_state[0] == 0)) { // only for OFF->ON trasition // send corresponding SYSEX for (int i = 0; i < S0_size; i ++) { Serial1.write(S0_array[i]); } // done; reset switch state S_change[0] = 0; //update LEDs (set LED 0) C_Leds[0] = CHSV(A_Leds[0], 255, V_value); FastLED.show(); } else { S_change[0] = 0; } // S1 unmutes Fxrtn/2 (set state) if ((S_change[1] == 1) && (S_state[1] == 0)) { // only for OFF->ON trasition // send corresponding SYSEX for (int i = 0; i < S1_size; i ++) { Serial1.write(S1_array[i]); } // done; reset switch state S_change[1] = 0; //update LEDs (set LED 1) C_Leds[1] = CHSV(A_Leds[1], 255, V_value); FastLED.show(); } else { S_change[1] = 0; } // S2 unmutes Fxrtn/3 (set state) if ((S_change[2] == 1) && (S_state[2] == 0)) { // only for OFF->ON trasition // send corresponding SYSEX for (int i = 0; i < S2_size; i ++) { Serial1.write(S2_array[i]); } // done; reset switch state S_change[2] = 0; //update LEDs {set LED 2) C_Leds[2] = CHSV(A_Leds[2], 255, V_value); FastLED.show(); } else { S_change[2] = 0; } // S3 unmutes Fxrtn/4 (set state) if ((S_change[3] == 1) && (S_state[3] == 0)) { // only for OFF->ON trasition // send corresponding SYSEX for (int i = 0; i < S3_size; i ++) { Serial1.write(S3_array[i]); } // done; reset switch state S_change[3] = 0; //update LEDs (set LED 3) C_Leds[3] = CHSV(A_Leds[3], 255, V_value); FastLED.show(); } else { S_change[3] = 0; } // S5 unmutes Fxrtn/4 (as long as switch pressed) if ((S_change[5] == 1) && (S_state[5] == 0)) { // for OFF->ON trasition // send corresponding SYSEX for (int i = 0; i < S3_size; i ++) { Serial1.write(S3_array[i]); } // done; reset switch state S_change[5] = 0; //update LEDs (set LED 3 to Color5) C_Leds[3] = CHSV(128, 255, V_value); // a special color for the momentary FastLED.show(); // switch } if ((S_change[5] == 1) && (S_state[5] == 1)) { // for ON->OFF trasition // send corresponding SYSEX for (int i = 0; i < S5_size; i ++) { Serial1.write(S5_array[i]); } // done; reset switch state S_change[5] = 0; //update LEDs (reset LED 3) C_Leds[3] = CHSV(A_Leds[3], 255, 0); FastLED.show(); }}
Maybe you'd prefer to control the four FX as stereo effects, in which case you'll keep the links as set by the init state of the X32, and the 4 effects will be as follows: FX1: fxrtn 1&2, FX2: fxrtn 3&4, FX3: fxrtn 5&6, FX4: fxrtn 7&8. A few changes to the code above and we're done!
Home Automation - Lights control
I was asked to build a system that would enable controlling the lights of an apartment from a mobile phone. There are numerous solutions, web based for some of them (Alexa, Google Home, etc.) or other commercial systems which may or may not connect to the web. It was OK for me to use WIFI, but why should someone accept to send personal data to the web, a web operator for just putting on or off lights at home? This doesn't feel right to me. So I looked at solutions that would enable a relay to function, based on a WIFI connected device (and the WIFI being limited to your home only, no operator involved), and controlled by a mobile phone, through WIFI, and therefore not getting your mobile operator involved.
I initially developed the SW using a nodeMCU (ESP8266) device. But that is too big, (and expensive) for the little thing I'm asking from the controlling device. A generic ESP-01s is quite enough! The device runs on 5V, and I need to control the mains line. A tiny, yet 700mA capable 5V power supply provides the DC power for the ESP01. A relay is used to open or close a contact in series with one of the mains live line. Interestingly, you can find the ESP and relay in one bundle.
WARNING:
If you undergo this project, remember to apply strict security. 230V can be very dangerous, and really hurts.
The principle is as follows: A self-contained device is connected to the mains, and contains an ESP is connected to your home WIFI, when getting the order to set lights on, it closes the relay, thus passing 230VAC to the lights that are connected to the output of the device. When receiving the order to switch off the lights, the ESP opens the relay, and the mains is disconnected from the output. This is shown in a schematics below (left column), photos of the components on the right:
So far, so good... A few wires to add, a little bit of Arduino SW and we'll be done? No... This needs to be controlled from a mobile phone. And this implies Android code which is a more difficult to develop than Arduino code, but thankfully, there's MIT App Inventor. This is a site managed by MIT that enables just about anyone with a little bit of coding practice to write Android (or other mobile device) applications, very easily. One still need to have some programing knowledge, but it makes things a lot easier.
The Mobile Application runs as follows:
The app gathers the IP address of the ESP device(s) and keeps it in a file. An Icon is used for sending an "ON" order, and another one for sending an "OFF" order. All this runs using a simple UDP link between the phone and the ESP. But how to get the IP address of the ESP, without having to reprogram it (using USB between a computer and the ESP) each time? Well, upon reset we set the ESP to be a WIFI access point. The mobile phone can be used to connect to that access point in order to provide the SSID and password of the home network. These two information will stay in the ESP at the time we cancel the access point and ask it to connect to the home network. The user can then set the mobile phone to connect to the home network and use the mobile application to send a broadcast message on the network to get back the IP address of the device. The IP is then kept as the address for the mobile phone app to send orders to the ESP.
The Mobile Phone application supports two separate devices. More can be added of course. To install the Android app, just copy it to a folder in the mobile phone and click on the file. This will prompt for installing the application as any other Android app, not requiring any authorization to access anything from your phone.
The ESP application SW is provided below:
Home Automation - Lights control Source Code
////// Project Setup section:// A first part sets the Access Point modevoid setup() { Serial.begin(115200); Serial.println(); Serial.println("Configuring access point..."); WiFi.softAP("LiGhTs", "12345678"); // Set AP mode AP_on = 1; IPAddress myIP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(myIP); server.on("/", HTTP_GET, webpage); server.on("/", HTTP_POST, response); server.begin(); Serial.println("HTTP server started"); // // // Manage ESP full reset by reading home network credentials as we are in AP mode while (AP_on) { server.handleClient(); } // Done! Time to restart the ESP. // This time (and from now on) we want to be in standard wifi mode WiFi.disconnect(true); delay(200); WiFi.softAPdisconnect(); delay(200); // Set ESP to Station mode (standard WIFI appliance) WiFi.mode(WIFI_STA); delay(200); // Not a good idea to display credentials... Serial.println("credentials to Home Network received"); // set Is_new flag to 'new' device Is_new = 1; // Connect to WiFi network Serial.printf("\nConnecting to %s Home Network as standard appliance", ssid_str); WiFi.begin(ssid_str, pass_str); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // Acknowledge connection ip = WiFi.localIP(); sprintf(ipbuf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); Serial.printf(" done!\n"); Serial.printf("UDP ready, IP: %s, port %d\n", ipbuf, localUdpPort); Udp.begin(localUdpPort); delay(500); pinMode(RELAY, OUTPUT);}////void loop() { // In the loop() section, we only manage UDP requests // and answer them based on current state of the device if (Udp.parsePacket() > 0) { r_len = Udp.read(r_buf, 255); r_buf[r_len] = 0; Serial.printf("mes# %d r'ved, len = %d, message: %s\n", m++, r_len, r_buf); if (strcmp(r_buf, "/L/getip") == 0) { if (Is_new) { send_udp("P-lights", 8); } else { Serial.printf("-> Ignore broadcast request\n"); } } else if (strcmp(r_buf, "/L/off") == 0) { Is_new = 0; // set GPIO 0 & 2 to high (OFF) digitalWrite(RELAY, HIGH); Serial.printf("lights OFF\n"); } else if (strcmp(r_buf, "/L/on") == 0) { Is_new = 0; // set GPIO 0 & 2 to low (ON) digitalWrite(RELAY, LOW); Serial.printf("lights ON\n"); } r_len = 0; } else { // No need to use too much CPU resources, // a request every 10ms is quite enough! delay(10); }}
A small document explains how to use the final device, i.e. the different steps and actions to perform to setup one or more ESP devices controlled form a single mobile application. You can then just click on the green or red icon respective of the relay you want to action.