# LED lighing project

**URL:** <https://community.volumio.com/t/led-lighing-project/9252>\
**Category:** Diy Volumio Projects\
**Created:** [October 20, 2018, 7:34pm UTC](https://community.volumio.com/t/led-lighing-project/9252 "2018-10-20T19:34:28Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![colin\_dickie](https://community.volumio.com/letter_avatar_proxy/v4/letter/c/898d66/32.png) [@colin\_dickie](https://community.volumio.com/u/colin_dickie)\
**Post date:** [October 20, 2018, 7:34pm UTC](https://community.volumio.com/t/led-lighing-project/9252/1 "2018-10-20T19:34:28Z")

</div>

Hi everyone,

I’m building a volumio system from a 1950s HMV radiogram, and the next stage is building a dynamic lighting effect similar to this: [http://julip.co/2012/05/arduino-python-soundlight-spectrum/](http://julip.co/2012/05/arduino-python-soundlight-spectrum/)

I don’t have a sound card as yet, and am therefore using the audio jack output to drive my speakers. In order to process the audio data to control the lighting, I have created a audio LOOPBACK with a custom `
/etc/asound.conf
` file.

After much tweaking this works fine, playing wave files from the command line `
aplay test.wav
`. However, Volumio doesn’t appear to read the same /etc/asound.conf file, so I don’t get this looped back to my lighting script.

Any suggestions on how to do this? Can I write a custom preset for the Volumio UI?

Also, whilst working on this the serial connection to my Arduino appears to have gone down. The rpi and Arduino scripts appear to be running fine, but without any communication.

I will share all code and photos soon.

---

<div class="post-metadata">

**Author:** ![balbuze](https://community.volumio.com/user_avatar/community.volumio.com/balbuze/32/30223_2.png) [@balbuze](https://community.volumio.com/u/balbuze)\
**Post date:** [October 20, 2018, 7:43pm UTC](https://community.volumio.com/t/led-lighing-project/9252/2 "2018-10-20T19:43:33Z")

</div>

You have to configure Volumio to use the Loopback device as output. I suggest you to have a look in the plugin “volumio simple equalizer” to see how it can be done. 😉

---

<div class="post-metadata">

**Author:** ![colin\_dickie](https://community.volumio.com/letter_avatar_proxy/v4/letter/c/898d66/32.png) [@colin\_dickie](https://community.volumio.com/u/colin_dickie)\
**Post date:** [October 27, 2018, 4:24pm UTC](https://community.volumio.com/t/led-lighing-project/9252/3 "2018-10-27T16:24:09Z")

</div>

Thank you very much balbuze, that was a great help.

I made some minor changes to the “volumio simple equaliser” plugin and it all works great together.

So the setup so far is:

- a python script running on the raspberry pi, picks up the audio stream via a loopback
- the audio is processed ‘near real-time’ using numpy’s fft package. This needs much more work to make the effect more dramatic.
- the pi sends a byte of data for each LED over a serial link to an arduino mega
- the arduino reads the data from the pi and sets PWM outputs to the appropriate values
- the LEDs are hidden between and behind the old vacuum tubes from the radio, and are powered by the arduino outputs.

This all needs much improvement, but the idea works quite well. I’ll post all the code when I’ve tidied it up a little, and a video when I’m sure I’m not going to fall foul of music copyright infringements.

Thanks again!

![20181027_163520.jpg](https://community.volumio.com/uploads/default/original/2X/b/bf95eec02d763b557154fb59df32d180667d7294.jpeg)

---

<div class="post-metadata">

**Author:** ![David\_Gottardo](https://community.volumio.com/user_avatar/community.volumio.com/david_gottardo/32/7929_2.png) [@David\_Gottardo](https://community.volumio.com/u/David_Gottardo)\
**Post date:** [November 26, 2020, 8:59am UTC](https://community.volumio.com/t/led-lighing-project/9252/4 "2020-11-26T08:59:33Z")

</div>

Thanks for this presentation.

Would you be possible to share your plugin based on the “volumio simple equaliser”?

---

<div class="post-metadata">

**Author:** ![colin\_dickie](https://community.volumio.com/letter_avatar_proxy/v4/letter/c/898d66/32.png) [@colin\_dickie](https://community.volumio.com/u/colin_dickie)\
**Post date:** [November 26, 2020, 9:17am UTC](https://community.volumio.com/t/led-lighing-project/9252/5 "2020-11-26T09:17:51Z")

</div>

Hi David,

Thank you for your interest in this. Unfortunately I’ve disassembled the set-up intending to improve on it, but haven’t managed to put it all together again.

Is there specific items of code you would like?

---

<div class="post-metadata">

**Author:** ![David\_Gottardo](https://community.volumio.com/user_avatar/community.volumio.com/david_gottardo/32/7929_2.png) [@David\_Gottardo](https://community.volumio.com/u/David_Gottardo)\
**Post date:** [December 8, 2020, 1:34pm UTC](https://community.volumio.com/t/led-lighing-project/9252/6 "2020-12-08T13:34:23Z")

</div>

Hi Colin,

Thanks for your quick enswer!  
I want to use a WS2812B LED pixel stip to react from the music like this :[https://github.com/scottlawsonbc/audio-reactive-led-strip](https://github.com/scottlawsonbc/audio-reactive-led-strip)

I would like to start from your implementation to see how Can I integrate and control a LED strip.

thanks.

---

<div class="post-metadata">

**Author:** ![colin\_dickie](https://community.volumio.com/letter_avatar_proxy/v4/letter/c/898d66/32.png) [@colin\_dickie](https://community.volumio.com/u/colin_dickie)\
**Post date:** [December 8, 2020, 2:03pm UTC](https://community.volumio.com/t/led-lighing-project/9252/7 "2020-12-08T14:03:56Z")

</div>

Unfortunately I cannot remember exactly how I configured everything, but I have managed to locate the configuration files.

The first thing was to amend the **asound.conf** file to:

# .asoundrc

> ctl.equal {  
> type equal
> 
> }  
> pcm.plugequal1 {  
> type equal  
> slave.pcm “plughw:0,0”  
> #slave.pcm “turntable2” #no sound  
> }
> 
> pcm.equal {  
> type plug;  
> slave.pcm plugequal1;  
> #slave.pcm “turntable” #no difference  
> }
> 
> pcm.splitter1 {  
> type softvol  
> slave.pcm mono  
> control { name “PCM”; card 0;}  
> max\_dB 50.0  
> }
> 
> pcm.mono {  
> type route  
> slave.pcm splitter2  
> ttable.0.0 1  
> ttable.1.1 1  
> }
> 
> pcm.splitter2 {  
> type plug;  
> slave.pcm {  
> type multi;  
> slaves.a.pcm “plugequal1”;  
> #slaves.a.pcm “turntable”; #no sound  
> slaves.b.pcm “loopout”;  
> #slaves.b.pcm “turntable2”; #no difference  
> slaves.a.channels 2;  
> slaves.b.channels 2;  
> bindings.0.slave a;  
> bindings.0.channel 0;  
> bindings.1.slave a;  
> bindings.1.channel 1;  
> bindings.2.slave b;  
> bindings.2.channel 0;  
> bindings.3.slave b;  
> bindings.3.channel 1;  
> }
> 
> ```
> ttable.0.0 1;
> ttable.1.1 1;
> ttable.0.2 1;
> ttable.1.3 1;
> 
> ```
> 
> }
> 
> pcm.output {  
> type hw  
> card ALSA  
> }
> 
> pcm.loopin {  
> type plug  
> slave.pcm “hw:Loopback,0”  
> #slave.pcm “turntable”  
> }
> 
> pcm.loopdevice {  
> type hw  
> card Loopback  
> device 1  
> }
> 
> pcm.turntable {  
> type hw  
> card Controller  
> device 0  
> }
> 
> pcm.turntable2 {  
> type plug  
> #slave.pcm turntable  
> #slave.channels 1  
> #ttable.0.0 0.5  
> #ttable.0.0 0.5  
> #ttable.1.0 0.5  
> #ttable.1.0 0.5  
> #type dsnoop  
> slave.pcm turntable  
> #slave.channels 2  
> #slave.period\_size 1024  
> #slave.buffer\_size 4096  
> #slave.rate 44100  
> #slave.periods 0  
> #slave.period\_time 0  
> }
> 
> pcm.loopout {  
> type plug  
> slave.pcm “loopdevice”  
> #slave.pcm “turntable”  
> }

Then I wrote a python script to read the audio and produce an output.

```auto
#!/usr/bin/env python3
# BEGIN INIT INFO
# Provides: dickie_audio_service.py
# Required-Start: $all
# Required-Stop: $remote_fs $syslog
# Default-Start: 5
# Default-Stop: 0 1 6
# Short-Description: LED lighting script
# Description: DICKIE innovation addon to volumio Simple Equaliser Plugin
### END INIT INFO
# -*- coding: utf-8 -*-
"""
ÐICKIE innovation

Created on Tue Oct 23 20:55:56 2018

Copyright Dickie Innovation Ltd
"""

import numpy
import math
import pyaudio
import wave
import serial
import sys
import time
import struct
import datetime

freq_low = 20 #Hz
freq_high =20000 #Hz

scale = 50 # Change if too dim/bright
exponent = 5 # Change if too little/too much difference between loud and quiet sounds

main_exit = False

def serial_init():
    print('Openning port at {0} baud'.format(19200))
    try:
        ser = serial.Serial('/dev/ttyACM0',19200, timeout=1 )
        print('\tOpen on port /dev/ttyACM0')

    except serial.SerialException:
        ser = serial.Serial('/dev/ttyACM1', 19200, timeout=1 )
        print('\tOpen on port /dev/ttyACM1')
        
    time.sleep(1)
    if not ser.isOpen():
        ser.open()

    ser.flushInput()
    ser.flushOutput()
    ser.write(b'\n')
    #inline = ser.readline()
    return ser

class Lighting():
    def __init__ (self, serial_link):
        self.serial_link = serial_link
        self.chunk_n = 11
        self.chunk = 2 **self.chunk_n # Change if too fast/slow, never less than 2** 11    
        self.device = 1
        self.samplerate = 22050 #44100
        self.band_no = 8
        self.frame_count = 0

        self.freqs = numpy.fft.fftfreq(self.chunk,1/self.samplerate)
        self.idxs = numpy.argsort(self.freqs)
        self.drange = 1600
        #self.drange[1] *= 0
        #self.drange[2] *= 100
        try:
            print('\nOpenning Device:', self.device)
            self.p = pyaudio.PyAudio()
            self.dev = self.p.get_device_info_by_index(self.device)

            print('No of Input channels', self.dev['maxInputChannels'])
            print('No of Output channels', self.dev['maxOutputChannels'])
            self.stream = self.p.open(format= pyaudio.paInt16,
		    #channels=dev['maxInputChannels'],
		    channels=1,
		    rate=44100,
		    input=True,
		    frames_per_buffer = self.chunk,
		    input_device_index = self.device,
		    stream_callback=self.callback)
        except:
            self.device = 2
            print('\nOpenning Device:', self.device)
            self.p = pyaudio.PyAudio()
            self.dev = self.p.get_device_info_by_index(self.device)

            print('No of Input channels', self.dev['maxInputChannels'])
            print('No of Output channels', self.dev['maxOutputChannels'])
            self.stream = self.p.open(format= pyaudio.paInt16,
		    #channels=dev['maxInputChannels'],
		    channels=1,
		    rate=44100,
		    input=True,
		    frames_per_buffer = self.chunk,
		    input_device_index = self.device,
		    stream_callback=self.callback)

        start = datetime.datetime.now()
        self.stream.start_stream()

        while self.stream.is_active() :
            time.sleep(1)
        

        end = datetime.datetime.now()
        rate = (end-start).seconds/self.frame_count
        print('elapsed {0:5.4f} seconds per run, or {1:5.2f}Hz'.format(rate, 1/rate))
        print('total count', self.frame_count)
        #print('min/max', self.drange[1:])
   
    def callback(self,in_data, frame_size, time_info, status):
        self.frame_size = frame_size
        #print('current status', status, len(in_data))
        #data = wf.readframes(frame_count)
        self.data = in_data
        #print('frame count', frame_count)
        self.calculate_levels()
        levels_list = self.levels.tolist()
        if status == pyaudio.paComplete:
            print('pyaudio terminated')
        elif status == pyaudio.paAbort:
            print('pyaudio aborted- ignoring')
            status = pyaudio.paContinue
        if sum(levels_list) == 0:
            print('levels zero')
            main_exit = True
            status = pyaudio.paComplete
        levels_list.append(10)
        #print(levels_list)
        self.serial_link.write(bytearray(levels_list))
        #inline = ser.readline()[:-2]
        return (self.data, status)
    
    def calculate_levels(self):
        # Use FFT to calculate volume for each frequency
        datalen = len(self.data)
        # Convert raw sound data to Numpy array
        fmt = "{0}H".format(datalen//2)
        data2 = struct.unpack(fmt, self.data)
        data2 = numpy.array(data2, dtype=numpy.int32)

        # Apply FFT
        fourier = numpy.fft.fft(data2)
        #print(len(data),fourier.shape, chunk)

        gaps = [0] + [2**x for x in range(self.chunk_n-self.band_no, self.chunk_n)]
        fft = numpy.abs(fourier[:self.chunk]) #/ (128*chunk)
        fft = fft[self.idxs]
    
        levels = numpy.zeros((self.band_no+4), dtype=numpy.int32)
        for band in range(self.band_no):
            levels[band] = numpy.mean(fft[gaps[band]:gaps[band+1]])
            levels[band] = numpy.max([1,levels[band]])
        levels[8] = levels[0]
        levels[9] = levels[1]
        levels[10] = levels[0]
        levels[11] = levels[2]
        levels_a = (numpy.log(levels)-12)*150
        #print([int(a) for a in levels_a])
        #self.drange[1] = numpy.min(self.drange, axis=0)
        #self.drange[2] = numpy.max(self.drange, axis=0)

        #range_running = self.drange[2]-self.drange[1]
        #levels = (levels_a)*255/self.drange
        levels = numpy.array(levels_a, dtype=numpy.int16)
        levels[levels<10] = 0
        levels[levels>255] =255
        
        #levels = levels.astype(numpy.uint8)
        levels[levels==10] = 9
        self.levels = levels
        self.frame_count +=1
    def __enter__ (self):
        return self

    def __exit__ (self, exc_type, exc_value, traceback):
        print('stopping stream')
        self.stream.stop_stream()
        self.stream.close()
        self.p.terminate()
        del self.stream
        del self.p
        #del self.callback

if __name__ == " __main__":
    ser = serial_init()
    while True:
        with Lighting(ser) as buray:
            while buray.stream.is_active():
                print('Buray running')
                time.sleep(1)

```

Which is run as a service as soon as the raspberry Pi/ Volumio is started:

> [Unit]  
> Description=DICKIEs lighting  
> After=volsimpleequal.service
> 
> [Service]  
> Type=simple  
> ExecStart=/usr/bin/python3 /home/volumio/dickie\_audio\_service.py \> /home/volumio/log.text 2\>&1  
> Restart=always  
> StandardOutput=syslog  
> StandardError=syslog  
> SyslogIdentifier=volumio  
> User=volumio  
> Group=volumio
> 
> [Install]  
> WantedBy=multi-user.target

I hope this helps.

---

<div class="post-metadata">

**Author:** ![KripyPicli](https://community.volumio.com/user_avatar/community.volumio.com/kripypicli/32/12103_2.png) [@KripyPicli](https://community.volumio.com/u/KripyPicli)\
**Post date:** [October 21, 2021, 7:51am UTC](https://community.volumio.com/t/led-lighing-project/9252/13 "2021-10-21T07:51:36Z")

</div>

This is based on an idea that’s been floating around in my head for a while

---

<div class="post-metadata">

**Author:** ![Rosemary\_Demetris](https://community.volumio.com/user_avatar/community.volumio.com/rosemary_demetris/32/9000_2.png) [@Rosemary\_Demetris](https://community.volumio.com/u/Rosemary_Demetris)\
**Post date:** [May 10, 2022, 3:38pm UTC](https://community.volumio.com/t/led-lighing-project/9252/14 "2022-05-10T15:38:45Z")

</div>

Did you manage to end this project? I know someone who has had a similar project, but he wasn’t able to do it.

---

<div class="post-metadata">

**Author:** ![Rosemary\_Demetris](https://community.volumio.com/user_avatar/community.volumio.com/rosemary_demetris/32/9000_2.png) [@Rosemary\_Demetris](https://community.volumio.com/u/Rosemary_Demetris)\
**Post date:** [May 16, 2022, 11:13am UTC](https://community.volumio.com/t/led-lighing-project/9252/15 "2022-05-16T11:13:35Z")

</div>

He said the old radiogram just doesn’t fit for that. However, the radiogram that he tried to use was much older than the one you tried to use. He actually tried to do something similar to these lights [https://www.vont.com/product/smart-strip-lights-led-strip-lights/](https://www.vont.com/product/smart-strip-lights-led-strip-lights/), using a newer radiogram from the 1980s, and it worked much better. He even bought a newer smart strip just to take it as a sample.
