[GUIDE] Setup I2C control for ES9018K2M DAC with Volumio 2

Guides to let you easily discover and enjoy Volumio

Ads helps Volumio remain Free and Open Source. Please consider donating to help us continue to serve you.

[GUIDE] Setup I2C control for ES9018K2M DAC with Volumio 2

Postby DedeHai » Tue Feb 04 2020 19:59


this guide shows how to setup Volumio 2 with an ES9018K2M DAC with I2C control. It is based on this guide but instead of using UART commands I rewrote the pyton script to communicate to the DAC through it's native I2C interface. It works on the cheap boards like shown in foto of my setup (RPi 3 with Waveshare 7" touch screen, see here).

volumio_electronics.jpg (220.79 KiB) Viewed 379 times

The problem with this DAC is that information about it is scarce and with it's default settings it can only play 32bit and 24bit I2S streams so all audio has to be resampled. To make it play 16bit audio natively the configuration register has to be adjusted (for example to play Spotify on Volumio).
Also in order to take advantage of the hardware volume control the registers for left and right channel attenuation have to be set.
The python script takes care of that by constantly polling the Volumio status and updating the DACs registers if needed.

Step by step setup:

Install Volumio, set I2S DAC to 'on' and choose 'Volumio ESS 9028QM' as the DAC. Reboot to make it work (selecting another I2C DACs may also work, I tested it only like this)

Setup the network connection through the Volumio webinterface, an internet connection is required. Connect to your RPi through SSH (Activate SSH on Volumio first)

Check that a DAC is recognized (listing as 'hifiberry')
Code: Select all
aplay -l

must show card1 as something like:
card 1: sndrpihifiberry [snd_rpi_hifiberry_dac], device 0: HifiBerry DAC HiFi pcm5102a-hifi-0 [HifiBerry DAC HiFi pcm5102a-hifi-0] Subdevices: 1/1

Leave the other output settings unchanged. You should now be able to play 24bit files and radio streams but 16bit files will sound horrible. Check that you have the I2C lines properly connected: SCL is on pin 3 and SDA is on pin 4 of the DAC. Also make sure your board has pullup resistors to the 3.3V supply, add 4.7k resistors if it does not.

Now that the DAC is up and running, let's add hardware volume control through I2C. First add a dummy volume control:
Code: Select all
sudo nano /var/lib/alsa/asound.state

add these lines at the end (there may already be a similar block called state.sndrpihifiberry , can just replace that)
Code: Select all
state.sndrpihifiberry {
   control.1 {
      iface MIXER
      name Digital
      value.0 99
      value.1 99
      comment {
         access 'read write user'
         type INTEGER
         count 2
         range '0 - 99'
         tlv '00000001000000080000000000000032'
         dbmin 0
         dbmax 4950
         dbvalue.0 4950
         dbvalue.1 4950

then run
Code: Select all
sudo alsactl restore 1

to load the new digital mixer.
check it shows up by running:

Code: Select all
amixer -c 1

it should sow a mixer called 'Simple mixer control 'Digital' ,0

Go to 'Playback options' on the webinterface, select 'Hardware' under mixer type, and select the
just created 'Digital' mixer. Save the settings. Now the mixer control works but does not do
anything yet as we have not linked it to the I2C bus yet. This is done with a python script.

On my system the I2C bus was already properly initialized.

it will show up as 'i2c-1' under /dev :

Code: Select all
cd /dev

-> check the list for 'i2c-1'

Google on how to install it on your raspberry pi if this is not installed or you can just install this plugin by ChrisPanda which will set it up as well plus this plugin will let you play with all of the DAC's settings like jitter reduction and filters. Also if it does not work
there may be a wiring problem (it crashes if I2C lines are not properly connected, not the plugins fault). Please not that the plugin may not properly work after you setup the python script as it may overwrite some settings constantly.

Code: Select all
cd /home/volumio
wget https://github.com/ChrisPanda/volumio-es9018k2m-plugin/releases/download/0.1.0/volumio-es9018k2m-plugin-0.1.0-sys-2.4x.zip
miniunzip volumio-es9018k2m-plugin-0.1.0-sys-2.4x.zip
cd volumio-es9018k2m-plugin-0.1.0-sys-2.4x
volumio plugin install

install the python I2C library called smbus

Code: Select all
sudo apt-get install python-smbus i2c-tools

check that you have the correct I2C bus (should be loaded in /dev/i2c-1) if you are using port I2C0 then change that in the python file _main_ function.

create a python script:

Code: Select all
nano /volumio/app/plugins/system_controller/i2s_dacs/scripts/ES9018K2M_I2C_Volumio.py

paste this python code:
Code: Select all
# coding: utf-8
# ESS ES9018K2M DAC I2C bus direct hardware management script for Volumio
# by Damian Schneider (January 2020)
# Based on python script by vigo
# https://forum.volumio.org/setup-audiophonics-es9018k2m-dac-with-volumio-t6041.html
# which is based on script for Runeaudio by Audiophonics and Fujitus
# https://github.com/audiophonics/ES9018K2M_serial_sync
# Required: ES9018K2M DAC with I2C pins connected to Raspberry pi

import time , smbus
import subprocess , os
import json
from urllib2 import Request, urlopen, URLError

#global register definitions
DEVICE_ADDRESS = 0x48      #7 bit address (will be left shifted to add the read write bit), alternat address: 0x49 if pin 5 is pulled high

#useful register numbers, refer to datasheet for more info (is hard to find but can be found online, check chinese sites)
#register number refers to its address, reg 0 is addres 0x00
#Reg0 system&oscillator settings, write 0x01 to reset chip (default 0x00)
#Reg1 input configuration, default=I2S 32bit, 0x8C (highest two bits are bit rate, write 0x4C for 24bit and 0x0C for 16bit)
#Reg6 deemphasis filter settings (off by default) and softe-mute speed (0x40 = fastest, 0x47 = slowest (?) not tested but 3 LSB give the speed, default is 0x42)
#Reg7 filter setting and mute, set to 0x80 (default) for unmute and set to 0x83 to mute both channels (2 LSB are mute or unmute) will ramp up the volume, speed can be set in register 6
#Reg15 left channel volume (0 is 0dB = loudest, 255 is -127.5dB = almost mute)
#Reg16 right channel volume (0 is 0dB = loudest, 255 is -127.5dB = almost mute)

def VolumioGetStatus():
    process = subprocess.Popen('/volumio/app/plugins/system_controller/volumio_command_line_client/volumio.sh status', stdout=subprocess.PIPE, shell=True)
    os.waitpid(process.pid, 0)[1]
    Status = process.stdout.read().strip()

    ParamList = json.loads(Status)

    #set the defaults in case the status call does not return a required value
    volumioBitDepth = '32 bit'
    volumioStatus = 'stop' #also sets volume = 0
    volumioMute = False

    # volumioService = ParamList['service'] #service currently not checked

    if 'status' in ParamList:
        volumioStatus = ParamList['status']

    if 'bitdepth' in ParamList:
        volumioBitDepth = ParamList['bitdepth'] #entry is '24 bit' o r '16 bit', we only need to check if it is 16bit (on webradio this string is empty but plays fine with 32bit)

    if 'volume' in ParamList:
        volumioVolume = ParamList['volume'] #volume is an integer

    if 'mute' in ParamList:
        volumioMute = ParamList['mute'] #is true or false

    if(volumioStatus != 'play'):
        volumioVolume = 0 #set volume low if nothing is playing

    return(volumioVolume, volumioBitDepth, volumioMute)

#set the volume by adjusting both left and right channel, input is 0-100
def ES9018K2M_set_volume(vol):
    vol = max(min(100, vol), 0) #limit the input value 0-100
    vol_set = (100-vol) #map 0-100 to 100-0 (register is attenuation not amplification so 0 = max, 256=min, 100 means -50dB which is qute silent already)
    bus.write_byte_data(DEVICE_ADDRESS, 15, vol_set) #set volume of left channel
    bus.write_byte_data(DEVICE_ADDRESS, 16, vol_set) #set volume of right channel

if __name__ == '__main__':
    bus = smbus.SMBus(1)  # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
    #set the defaults (also used to check if value changed)
    volume = 100
    bitrate = 32
    mute = False

        while True: #run the script forever
            volumioStatus = VolumioGetStatus() #read the status of the player: reading volume, bit debth and mute

            #check if a state changed
            if(volumioStatus[0] != volume):
                volume = volumioStatus[0]

            if(volumioStatus[1] == '16 bit'):
                if(bitrate != 16):
                    bitrate = 16
                    bus.write_byte_data(DEVICE_ADDRESS, 1, 0x0C)  # set 16bit mode
                if(bitrate != 32):
                    bitrate = 32
                    bus.write_byte_data(DEVICE_ADDRESS, 1, 0x8C)  # set 32bit mode (works fine for 24bit input but not for 16 bit)
                    #bus.write_byte_data(DEVICE_ADDRESS, 1, 0x4C)  # set 24bit mode

            if(volumioStatus[2] != mute):  #mute changed
                mute = volumioStatus[2]
                    bus.write_byte_data(DEVICE_ADDRESS, 7, 0x83)  # mute both channels
                    bus.write_byte_data(DEVICE_ADDRESS, 7, 0x80)  # unmute both channels

    except KeyboardInterrupt:

create a shell script that starts the python script:
Code: Select all
nano /volumio/app/plugins/system_controller/i2s_dacs/scripts/ES9018K2M-control.sh

with this code

Code: Select all
# ESS ES9018K2M DAC hardware management starting script
# Author: Damian Schneider
python /volumio/app/plugins/system_controller/i2s_dacs/scripts/ES9018K2M_I2C_Volumio.py

make the script executable:
Code: Select all
chmod ugo+x /volumio/app/plugins/system_controller/i2s_dacs/scripts/ES9018K2M-control.sh

Add ES9018K2M DAC definition in Volumio:
Code: Select all
nano /volumio/app/plugins/system_controller/i2s_dacs/dacs.json

add this line (in alphabetically correct order):
Code: Select all
{"id":"es9018k2m","name":"ES9018K2M bare DAC","overlay":"hifiberry-dac","alsanum":"1","mixer":"Digital","modules":"","script":"ES9018K2M-control.sh","needsreboot":"yes"},


Go to 'Playback options' on the web interface, make sure I2S DAC is activated and select the just created 'ES9018K2M bare DAC' as the model and save. Set the Mixer type to 'Hardware' and the control to 'Digital' and save.

The python script is now be running in the background and the Volumio volume control is linked to the DAC hardware volume control. The script checks for the bit-rate and sets it correctly on the DAC. This also works for the Spotify plugin as well as Balbuze's Volspotconnect plugin.

I was not able to fix the startup-sound so for now just disable it in the settings.
Random avatar
Sunday DIYer
Sunday DIYer
Posts: 11
Joined: Fri Jan 24 2020 15:49
Location: Winterthur, Switzerland

Ads helps Volumio remain Free and Open Source. Please consider donating to help us continue to serve you.

[GUIDE] Setup I2C control for ES9018K2M DAC with Volumio 2

Postby mickt » Mon Feb 10 2020 06:03

I have tried to run your script with the latest volumio on a pi3b, it runs until i change the music when it crashes. When I restart in putty it reports an io error writing to a sound channel.
I have tried 4,7k and 10k resistors but the result is the same.
The plugin from Chrispanda works fine. Is there anything else I can try?
Random avatar
Fresh off the boat
Fresh off the boat
Posts: 1
Joined: Mon Feb 10 2020 05:50

[GUIDE] Setup I2C control for ES9018K2M DAC with Volumio 2

Postby DedeHai » Sat Feb 15 2020 13:37

thanks for the feedback.
Hard to say what is going on. You can try to use python commands through ssh directly and see if you can write to the DAC. If the plugin from Chris Panda works fine then your wiring appears to be correct and working.
When the script is running:
-does the volume work on the web UI (i.e. it actually changes the volume)? If this works then the commands arrive at the DAC correctly.
-you can try to add print 'info here' commands in the script to see what exactly is going on, then run the script manually to see these messages
-the error writing to a sound channel is coming from the script or the system?
-what exact DAC chip version are you using?
Random avatar
Sunday DIYer
Sunday DIYer
Posts: 11
Joined: Fri Jan 24 2020 15:49
Location: Winterthur, Switzerland

Return to Guides

Who is online

Users browsing this forum: No registered users and 1 guest