HW volume iqaudio Rotary Encoder

Hi

I just got a RPI3 and IQAudio PI-DAC+ and it’s working fine.
I have installed the Rotary encoder SW from IQAudio and it’s working.
Now I want to change something in that SW (IQ_rot.c), but I don’t know how to compile it.
I know I shall use ‘gcc’ but i can’t get it to work.

Can anyone please guide me (be aware that i’m very new to linux :wink: )?

I have placed the tolls from IQAudio in folder /temp/tools

thanks in advance
Bjarne

Hi!
We are sorry but the best is to see with iqaudio. Gordon will surely answer you.

Hi

Got answer from Gordon :slight_smile:

I got the Rotary working. :smiley:
It would be nice if it updated the web gui too. How can I do that.
And please, dont guide me to Gordon again because I think this must be of interest to others in this foum :wink:
And not a bad word of Gordon’s help, it was excellent :sunglasses:

thanks in advance

Could you post here the code?

here it is

[code]// Rotary Encoder Sample app - IQ_rot.c
// Makes use of WIRINGPI Library
// USES Raspberry Pi GPIO 23 and 24.
// Adjusts ALSA volume (based on Left channel value) up or down to correspond with rotary encoder direction
// Assumes IQAUDIO.COM Pi-DAC volume range -103dB to 0dB
//
// G.Garrity Aug 30th 2015 IQaudIO.com
//
// Compile with gcc IQ_rot.c -oIQ_rot -lwiringPi -lasound
//
// Make sure you have the most upto date WiringPi installed on the Pi to be used.

#include <stdio.h>
#include <wiringPi.h>
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <stdlib.h>

#include <errno.h>
#include <string.h>

/*
Rotary encoder connections:
Encoder A - gpio 23 (IQAUDIO.COM PI-DAC 23)
Encoder B - gpio 24 (IQAUDIO.COM PI-DAC 24)
Encoder Common - Pi ground (IQAUDIO.COM PI-DAC GRD)
*/

// Define DEBUG_PRINT TRUE for output
#define DEBUG_PRINT 0 // 1 debug messages, 0 none

// Define the Raspberry Pi IO Pins being used
#define ENCODER_A 4 // GPIO 23
#define ENCODER_B 5 // GPIO 24

#define TRUE 1
#define FALSE 0

static volatile int encoderPos;
static volatile int lastEncoded;
static volatile int encoded;
static volatile int inCriticalSection = FALSE;

/* forward declaration */
void encoderPulse();

int main(int argc, char * argv[])
{
int pos = 125;
long min, max;
long gpiodelay_value = 250; // was 50

snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;

const char *card = “default”;
// Previous linux driver’s mixer name
// const char *selem_name = “Playback Digital”;
// const char *selem_name = “PCM”;
const char *selem_name = “Digital”; // Linux 4.1.6-v7+ #810
int x, mute_state;
long i, currentVolume;

printf(“IQaudIO.com Pi-DAC Volume Control support Rotary Encoder) v1.5 Aug 30th 2015\n\n”);

wiringPiSetup ();

/* pull up is needed as encoder common is grounded */
pinMode (ENCODER_A, INPUT);
pullUpDnControl (ENCODER_A, PUD_UP);
pinMode (ENCODER_B, INPUT);
pullUpDnControl (ENCODER_B, PUD_UP);

encoderPos = pos;

// Setup ALSA access
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);

snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);

snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
if (DEBUG_PRINT) printf(“Returned card VOLUME range - min: %ld, max: %ld\n”, min, max);

// Minimum given is mute, we need the first real value
min++;

// Get current volume
if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, &currentVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf(“Current ALSA volume LEFT: %ld\n”, currentVolume);

if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_RIGHT, &currentVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf(“Current ALSA volume RIGHT: %ld\n”, currentVolume);

/* monitor encoder level changes */
wiringPiISR (ENCODER_A, INT_EDGE_BOTH, &encoderPulse);
wiringPiISR (ENCODER_B, INT_EDGE_BOTH, &encoderPulse);

// Now sit and spin waiting for GPIO pins to go active…
while (1)
{
if (encoderPos != pos)
{
// get current volume
if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, &currentVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf(" Current ALSA volume: %ld\n", currentVolume);

          // Adjust for MUTE
      if (currentVolume < min) 
          {
                    currentVolume = 0;
		if (DEBUG_PRINT) printf(" Current ALSA volume set to min: %ld\n", currentVolume);
          }

          // What way did the encoder go?
      if (encoderPos > pos)
      {
	         pos = encoderPos;
		 currentVolume = currentVolume + 10;
		 // Adjust for MAX volume
		 if (currentVolume > max) currentVolume = max;
	         if (DEBUG_PRINT) printf("Volume UP %d - %ld", pos, currentVolume);
      }
      else if (encoderPos < pos)
      {
	         pos = encoderPos;
                     currentVolume = currentVolume - 10;

		 // Adjust for MUTE
		 if (currentVolume < min) currentVolume = 0;
	         if (DEBUG_PRINT) printf("Volume DOWN %d - %ld", pos, currentVolume);
      }

          if (x = snd_mixer_selem_set_playback_volume_all(elem, currentVolume))
          {
                 printf(" ERROR %d %s\n", x, snd_strerror(x));
          } else if (DEBUG_PRINT) printf(" Volume successfully set to %ld using ALSA!\n", currentVolume);
}

// Check x times per second, MAY NEED TO ADJUST THS FREQUENCY FOR SOME ENCODERS */
delay(gpiodelay_value); /* check pos x times per second */

}

// We never get here but should close the sockets etc. on exit.
snd_mixer_close(handle);
}

// Called whenever there is GPIO activity on the defined pins.
void encoderPulse()
{
/*

         +---------+         +---------+      0
         |         |         |         |

A | | | |
| | | |
±--------+ ±--------+ ±---- 1

   +---------+         +---------+            0
   |         |         |         |

B | | | |
| | | |
----+ ±--------+ ±--------+ 1

*/

if (inCriticalSection==TRUE) return;

inCriticalSection = TRUE;

int MSB = digitalRead(ENCODER_A);
    int LSB = digitalRead(ENCODER_B);

    int encoded = (MSB << 1) | LSB;
    int sum = (lastEncoded << 2) | encoded;

    if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderPos++;
else if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderPos--;

    lastEncoded = encoded;

inCriticalSection = FALSE;

}

[/code]

any help, please?

Thanks in advance
Bjarne

@bjarne
I see you modded your amp… Is it a blue amp ? I own a blue one. Could you give some details about your mod and what is your advice about the result?
Maybe open a new thread in https://volumio.org/forum/diy-and-tweak.html
:wink:

I’m not really good in C, but if you could launch the Volumio command line client from there, you can update the volume.

volumio volume 20

where 20 is the volume…

@michelangelo
with ver 0.979 the command volumio volume 22
returns a error

volumio@volumio2-bureau:/volumio$ volumio volume 23 Setting Volume 23 /volumio/app/plugins/system_controller/volumio_command_line_client/volumio.sh: line 50: /volumio/app/plugins/user_interface/volumio_command_line_client/commands/setvolume.js: No such file or directory

Yes, please pull to latest code

cd /volumio
git pull

It’s the blue amp. I just followed the mods in this diyaudio.com/forums/diyaudio … oards.html

I have not played with it before the mods so I can’t say anything about SQ before and after.
It’s about 1½ year since I made the mods but it’s jst 2 weeks ago I connected it to my stereo :blush:
I think it is sounding well. Next step is to put it into a cabinet with the RPI, But I would like to wait until Volumio is stable enough.

I have tried Volumio, Rune, Moode but none off them I find stable at Wifi connection.
When rebooting Wifi is gone on all of them
Volumio forget my DAC and go back to Jack
Rune forget NAS connection

But the worst is that none of them can get the Wifi stable :frowning:

Regards Bjarne

Hi,

I’ve modified the c code in IQ_rot.c so that it updates the volume level in Volumio. I’ve included it below in case anyone is interested. I should however mention that it is not perfect and there is some lag in updating the UI.

Running the command “volumio volume” in cli takes some time to return a value. I wonder what causes the delay. I’ve added code to spawn a new process for updating the UI value so that it doesn’t block the main program.

I’ve also made sure that the rotary encoder is initially set to the actual volume level of Volumio instead of an arbitrary value.

Please let me know if anyone has any suggestion to improve upon this code.

Thanks.

[code]// Rotary Encoder Sample app - IQ_rot.c
// Makes use of WIRINGPI Library
// USES Raspberry Pi GPIO 23 and 24.
// Adjusts ALSA volume (based on Left channel value) up or down to correspond with rotary encoder direction
// Assumes IQAUDIO.COM Pi-DAC volume range -103dB to 0dB
//
// G.Garrity Aug 30th 2015 IQaudIO.com
//
// Compile with gcc IQ_rot.c -oIQ_rot -lwiringPi -lasound
//
// Make sure you have the most upto date WiringPi installed on the Pi to be used.

#include <stdio.h>
#include <wiringPi.h>
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <stdlib.h>

#include <errno.h>
#include <string.h>

/*
Rotary encoder connections:
Encoder A - gpio 23 (IQAUDIO.COM PI-DAC 23)
Encoder B - gpio 24 (IQAUDIO.COM PI-DAC 24)
Encoder Common - Pi ground (IQAUDIO.COM PI-DAC GRD)
*/

// Define DEBUG_PRINT TRUE for output
#define DEBUG_PRINT 0 // 1 debug messages, 0 none

// Define the Raspberry Pi IO Pins being used
#define ENCODER_A 4 // GPIO 23
#define ENCODER_B 5 // GPIO 24

#define TRUE 1
#define FALSE 0

static volatile int encoderPos;
static volatile int lastEncoded;
static volatile int encoded;
static volatile int inCriticalSection = FALSE;

/* forward declaration */
void encoderPulse();

int main(int argc, char * argv[])
{
int pos = 125;
long min, max;
long gpiodelay_value = 250; // was 50

snd_mixer_t *handle;
snd_mixer_selem_id_t *sid;

const char *card = “default”;
// Previous linux driver’s mixer name
// const char *selem_name = “Playback Digital”;
// const char *selem_name = “PCM”;
const char *selem_name = “Digital”; // Linux 4.1.6-v7+ #810
int x, mute_state;
long i, currentVolume;

printf(“IQaudIO.com Pi-DAC Volume Control support Rotary Encoder) v1.5 Aug 30th 2015\n\n”);

wiringPiSetup ();

/* pull up is needed as encoder common is grounded */
pinMode (ENCODER_A, INPUT);
pullUpDnControl (ENCODER_A, PUD_UP);
pinMode (ENCODER_B, INPUT);
pullUpDnControl (ENCODER_B, PUD_UP);

encoderPos = pos;

// Setup ALSA access
snd_mixer_open(&handle, 0);
snd_mixer_attach(handle, card);
snd_mixer_selem_register(handle, NULL, NULL);
snd_mixer_load(handle);

snd_mixer_selem_id_alloca(&sid);
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, selem_name);
snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);

snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
if (DEBUG_PRINT) printf(“Returned card VOLUME range - min: %ld, max: %ld\n”, min, max);

// Minimum given is mute, we need the first real value
min++;

// Get current volume
if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, &currentVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf(“Current ALSA volume LEFT: %ld\n”, currentVolume);

if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_RIGHT, &currentVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf(“Current ALSA volume RIGHT: %ld\n”, currentVolume);

//Get current volumio volume level
long volumio_vol = 0;
FILE *fp = popen("/volumio/app/plugins/system_controller/volumio_command_line_client/volumio.sh volume",“r”);
fscanf(fp, “%ld”, &volumio_vol);
pclose(fp);
if( DEBUG_PRINT ) printf(“Volumio volume: %ld\n\n”, volumio_vol);

long sync_vol = 0; //This value will be calculated based on max volume value of ALSA

//Calculate volume level to synchronize
sync_vol = volumio_vol * ( max / 100);
snd_mixer_selem_set_playback_volume_all (elem, sync_vol);
//--------------------

/* monitor encoder level changes */
wiringPiISR (ENCODER_A, INT_EDGE_BOTH, &encoderPulse);
wiringPiISR (ENCODER_B, INT_EDGE_BOTH, &encoderPulse);

// Now sit and spin waiting for GPIO pins to go active…
while (1)
{
if (encoderPos != pos)
{
// get current volume
if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, &currentVolume))
{
printf("%d %s\n", x, snd_strerror(x));
}
else if (DEBUG_PRINT) printf(" Current ALSA volume: %ld\n", currentVolume);

		  // Adjust for MUTE
     if (currentVolume < min)
     {
        currentVolume = 0;
        if (DEBUG_PRINT) printf(" Current ALSA volume set to min: %ld\n", currentVolume);
     }

		  // What way did the encoder go?
     if (encoderPos > pos)
     {
        pos = encoderPos;
        currentVolume = currentVolume + 10;
        // Adjust for MAX volume
        if (currentVolume > max) currentVolume = max;
        if (DEBUG_PRINT) printf("Volume UP %d - %ld", pos, currentVolume);
     }
     else if (encoderPos < pos)
     {
        pos = encoderPos;
        currentVolume = currentVolume - 10;

        // Adjust for MUTE
        if (currentVolume < min) currentVolume = 0;
		   if (DEBUG_PRINT) printf("Volume DOWN %d - %ld", pos, currentVolume);
     }

        if (x = snd_mixer_selem_set_playback_volume_all(elem, currentVolume))
        {
				 printf(" ERROR %d %s\n", x, snd_strerror(x));
        } else if (DEBUG_PRINT){
           printf(" Volume successfully set to %ld using ALSA!\n", currentVolume);
        }

			sync_vol = currentVolume * (100 / (float)max);
			if(DEBUG_PRINT) printf("Sync volume: %ld\n", sync_vol);
        
        //Fork a new process to update volumio volume level
        pid_t pID = vfork();
           if(pID == 0){
           if(DEBUG_PRINT) printf("Setting volumio volume to %ld\n\n", sync_vol);
           char command[1000];
           char res[1000];

           //Build command to update volumio volume level
           sprintf(command, "/volumio/app/plugins/system_controller/volumio_command_line_client/volumio.sh volume %ld", sync_vol);

           //Execute command
           FILE *fp = popen(command, "r");
           fscanf(fp, "%s", &res);
           pclose(fp);
           _exit(0);
        }

}

// Check x times per second, MAY NEED TO ADJUST THS FREQUENCY FOR SOME ENCODERS /
delay(gpiodelay_value); /
check pos x times per second */
}

// We never get here but should close the sockets etc. on exit.
snd_mixer_close(handle);
}

// Called whenever there is GPIO activity on the defined pins.
void encoderPulse()
{
/*

         +---------+         +---------+      0
         |         |         |         |

A | | | |
| | | |
±--------+ ±--------+ ±---- 1

   +---------+         +---------+            0
   |         |         |         |

B | | | |
| | | |
----+ ±--------+ ±--------+ 1
*/

if (inCriticalSection==TRUE) return;

inCriticalSection = TRUE;

int MSB = digitalRead(ENCODER_A);
int LSB = digitalRead(ENCODER_B);

	int encoded = (MSB << 1) | LSB;
	int sum = (lastEncoded << 2) | encoded;

	if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderPos++;

else if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderPos–;

	lastEncoded = encoded;

inCriticalSection = FALSE;
}

[/code]

Hi folks,

I’ve got a python script that changes the volume with a rotary encoder.
I previously had this working with Moode and mpc, but updated it for Volumio (v2.246)

It uses RPi-GPIO and the rotary_class python module from Bob Rathbone (bobrathbone.com)

To install rpi.gpio:

sudo apt-get install python-rpi.gpio python3-rpi.gpio

The python code to change volume is below, I start this script up from /etc/rc.local.
When I was using Moode I could also get Mute working with the push button on the Rot-enc, but this is not working with Volumio.
I’ve not yet worked out why, so I had to comment out a line in the rotary_class.py file (line 53 in the version I have. [ #GPIO.add_event_detect(self.button, GPIO.BOTH, callback=self.button_event, bouncetime=200) as this was causing a python exception.]

By the way I’ve set the volume steps to 2 in the Playback Options.

[code]import time
import subprocess
from rotary_class import RotaryEncoder

VOL_UP = 23
VOL_DOWN = 24
MUTE = 22

def volume_event(event):
global volume_knob
if event == RotaryEncoder.CLOCKWISE:
print ‘vol up’
subprocess.call([’/usr/local/bin/volumio’, ‘volume’, ‘plus’ ])
elif event == RotaryEncoder.ANTICLOCKWISE:
print ‘vol down’
subprocess.call([’/usr/local/bin/volumio’, ‘volume’, ‘minus’ ])
elif event == RotaryEncoder.BUTTONDOWN:
print ‘mute’
elif event == RotaryEncoder.BUTTONUP:
print ‘unmute’
return

volume_knob = RotaryEncoder(VOL_UP, VOL_DOWN, MUTE, volume_event)
while True:
time.sleep(0.5)[/code]

Hello,
i have a PiDAC + and the rotary encoder from IQaudio as well, but i can’t get it work !
Is there a kind of step by step guide to do so?
I’ve already googled for it , but can’t find what i need
Someone would help me? I need just some basic (up down volume control and mute/unmute toggle using the push button. Also the shutoff when holding down).

thanks everyone.

F.