Welcome, Guest
Username: Password: Remember me
NOTE: This forum is community powered. Please be mindful that long time community members are here to help as part of a community effort. If you have a specific issue (e.g. hardware, failure), please use our tech support portal (Support menu - > Contact Us). Thanks a lot of your help in making a better community. :-)

TOPIC: Readout of Volume

Readout of Volume 3 years 8 months ago #23085

  • rhosch
  • rhosch's Avatar
  • Offline
  • Junior Boarder
  • Posts: 33
  • Thank you received: 6
  • Karma: 0
gbradley and mungum,

devteam pointed me this direction in response to a similar question I had posed. Trying to decide just what roles a miniDSP product could fill in the system I'm working toward.

I'm eyeing the 4x10Hd, and although it isn't SHARC based assume external communication and control would be very similar if not identical.

What are your thoughts on controlling 2 or more miniDSP units in this way with the goal of synchronized volume control? Can the I2C information be "duplicated" so that each board is getting identical commands, maybe just listening for return from one unit and assuming the others are responding equally? If that is a terrible idea, can you envision another method of doing this? I had wondered about simply making one Vol-FP master, and all other units slave to it, but again not sure what would happen with communication in the other direction. I assume each unit responding to identical device ID would not be uniquely addressable, which probably isn't something I would have much need of anyway.
The administrator has disabled public write access.

Readout of Volume 3 years 8 months ago #23086

  • gbradley
  • gbradley's Avatar
  • Offline
  • Fresh Boarder
  • Posts: 9
  • Karma: 0
Hi rhosch,

I've no doubt the control will be the same - but would be interested in your tests further down the line...

As for controlling two, I have the same problem - controlling two miniSHARCs from one controller. Because I need independent control (i.e. change filters on one but not the other for instance), I was going to use an I2C address translation device such as the Linear LTC4317. (www.linear.com/product/LTC4317).

I feel that it will take much of the complication away.

Hope that helps,
Gareth
The administrator has disabled public write access.

Readout of Volume 3 years 8 months ago #23087

  • rhosch
  • rhosch's Avatar
  • Offline
  • Junior Boarder
  • Posts: 33
  • Thank you received: 6
  • Karma: 0
Interesting. I don't anticipate "needing" to address each unit individually, but if that actually simplifies my end goal I'm all for it... especially if work is being done that direction already. :) I'm a bit off from needing to get this sorted out, but I think I'll probably purchase a 4x10Hd soon to start testing with. I'll let you know if I find any major differences from what you guys have encountered, and of course will probably have a million more question at that point too since microcontroller programming and I2C communication is something I'm only familiar with in principle, and have never had to actually work on.
The administrator has disabled public write access.

Readout of Volume 3 years 8 months ago #23088

  • Mungam
  • Mungam's Avatar
  • Offline
  • Fresh Boarder
  • Posts: 18
  • Thank you received: 18
  • Karma: 14
Hello rhosch,

assuming that the 8x10Hd works the same way, one solution could be:

Use one arduino (or chipkit, etc.) between the Vol-FP and the mainboard. The arduino should have three I2C busses, you connect one to the Vol-FP and the other two to the mainboards. Then you just write a small program to pass the Vol-FP commands to both mainboards simultaneously. Both mainboards will answer with the current settings (volume, input and filter) and so you can make sure both mainboards have the same setting. If there is a difference, you can send the Vol-FP command again to this mainboard until it has the same setting as the other one. You might need to do that, as the Vol-FP doesn't send an absolute volume setting, it just sends volume up or down. If one mainboard misses the volume command, they will not be synchronized anymore. With checking the answer of the mainboards and resending the command you can solve that problem.
The answer of at least one mainboard should be passed to the Vol-FP, as it needs it indicate the current input and filter setting.

I haven't tried it yet, but shouldn't be a to hard to do.

Now you juast need an arduino with three I2C busses.
The administrator has disabled public write access.
The following user(s) said Thank You: Cakir, rhosch

Readout of Volume 3 years 8 months ago #23089

  • Mungam
  • Mungam's Avatar
  • Offline
  • Fresh Boarder
  • Posts: 18
  • Thank you received: 18
  • Karma: 14
gbradley wrote:
Hi rhosch,

I've no doubt the control will be the same - but would be interested in your tests further down the line...

As for controlling two, I have the same problem - controlling two miniSHARCs from one controller. Because I need independent control (i.e. change filters on one but not the other for instance), I was going to use an I2C address translation device such as the Linear LTC4317. (www.linear.com/product/LTC4317).

I feel that it will take much of the complication away.

Hope that helps,
Gareth

That chip is really cool, makes things easier.
The administrator has disabled public write access.

Readout of Volume 3 years 8 months ago #23094

  • rhosch
  • rhosch's Avatar
  • Offline
  • Junior Boarder
  • Posts: 33
  • Thank you received: 6
  • Karma: 0
OK, so for I2C control I'd need to listen to each minidsp to ensure it received the vol up/down command and responded accordingly, which requires a way to uniquely address them.

Don't shoot me (same thing I said to devteam), but what about a dozen minidsp devices? I looked at the address translator mentioned, seems it is designed for two devices and although this is way out of my comfort zone the scheme doesn't loom amenable to cascading. With three arduino busses that gets me to a vol-fp and four minidsp units.

Or am I wrong, and multiple translators can occupy the same bus but each with different pairs of device ID's?
The administrator has disabled public write access.

Readout of Volume 3 years 8 months ago #23098

  • Mungam
  • Mungam's Avatar
  • Offline
  • Fresh Boarder
  • Posts: 18
  • Thank you received: 18
  • Karma: 14
rhosch wrote:
OK, so for I2C control I'd need to listen to each minidsp to ensure it received the vol up/down command and responded accordingly, which requires a way to uniquely address them.

Don't shoot me (same thing I said to devteam), but what about a dozen minidsp devices? I looked at the address translator mentioned, seems it is designed for two devices and although this is way out of my comfort zone the scheme doesn't loom amenable to cascading. With three arduino busses that gets me to a vol-fp and four minidsp units.

Or am I wrong, and multiple translators can occupy the same bus but each with different pairs of device ID's?

You'll need one translator for every two MiniDSPs. So for a dozen you'll need 6, all on the same bus. My idea is the following: The arduino will have the address of the Vol-FP. With the translator the arduino can independently address all minidsps. The minidsps will then answer back to the arduino. You'll need two I2C buses, as the VOL-FP has to be separated from the bus with the minidsps. If you don't need the Vol-FP (you can attach the volume knob and the LEDs to the arduino, too, it just means a little bit more programming) you'll only need one I2C bus.
The administrator has disabled public write access.

Readout of Volume 3 years 8 months ago #23099

  • rhosch
  • rhosch's Avatar
  • Offline
  • Junior Boarder
  • Posts: 33
  • Thank you received: 6
  • Karma: 0
Awesome! So multiple address translators on one bus, vol-FP on another, and then all the miniDSPs I can dream of connected to the address translators. Sounds like some fun programming may be in my future, but will make for a cool system when done. I plan to have one 4x10Hd as crossover for each speaker location in an ambitious theater design, and being able to uniquely address might leave open the possibility to do some things "on the fly" I had figured I'd have to set and forget, like specific channel muting, maybe selection of certain groups of presets depending on source or music type, etc.
The administrator has disabled public write access.

Readout of Volume 3 years 7 months ago #23466

  • Cakir
  • Cakir's Avatar
  • Offline
  • Fresh Boarder
  • Posts: 1
  • Karma: 0
Mungam wrote:
I hooked up the logic analyzer.
When the volume is changed, the VOL-board sends 3 bytes: 3, 150,1 for volume up and 3, 150, 0 for volume down to 0x40.
The mainboard answers with 6,132,x,0,y,z where x is the volume (0-254), y is the input (0=SPDIF, 1=Optical, 2=AES) and z is the filter (0-3). The address of the VOL-board is 0x58.
Changing the filter: 3,138,z (z = 0-3), address again 0x40
Changing the input: 3,153,y (y same as above)

Ah this is worth gold. All I've been really wanting has been a toggle switch and no IR in order to switch the configuration between the crossovers for the monitors and the "I2S through" for multiple headphone outs at the studio preamp without putting switches on the I2S lines.
The administrator has disabled public write access.

Readout of Volume 3 years 4 months ago #25239

  • haggismonster
  • haggismonster's Avatar
  • Offline
  • Junior Boarder
  • Posts: 35
  • Thank you received: 17
  • Karma: 4
This really is gold. I've taken the ball and ran with it a bit. First I grabbed Mungam's code, but it didn't work for me. Then I grabbed my logic analyzer to see what was happening on the I2C bus between the MiniSHARC and the VOL-FP. My findings match those of Mungam except my I2C addresses are different.

Here's a captured "Volume down" command from the VOL-FP to the MiniSHARC, and the corresponding reply:



As Mungam already found, you can see three bytes are sent from VOL-FP to MiniSHARC: 3, 150, 0. Volume up is 3, 150, 1. You can also see that my MiniSHARC's I2C bus address is 0x20 and my VOL-FP is at 0x2C.

Here's a captured "change config" command:



The command in this case is 3, 138, X where "X" is 0 - 3, representing configurations 1 through 4.

Putting all this together I wrote a basic but functional Arduino-based VOL-FP emulator. Just unplug the VOL-FP from the MiniSHARC and in its place connect your Arduino's SDA to pin 1 on the MiniSHARC's VOL-FP header; Ardunio SDC pin to pin 2 on the MiniSHARC; Arduino GND pin to pin 5 on the MiniSHARC.

With that, use this code:

Edit: new code is pasted a few posts down.

Voila! A working, programmable VOL-FP replacement that lets you query the current config, the current volume, set the volume, and set the config :)
Last Edit: 3 years 4 months ago by haggismonster.
The administrator has disabled public write access.

Readout of Volume 3 years 4 months ago #25240

  • bwaslo
  • bwaslo's Avatar
  • Offline
  • Junior Boarder
  • Posts: 31
  • Thank you received: 8
  • Karma: 6
Thanks for your efforts on this.

Does your code by chance help with the irregular dB step sizes of volume up/down (where the changes might be anything between 0.5dB and 2dB apparently randomly)?
The administrator has disabled public write access.

Readout of Volume 3 years 4 months ago #25241

  • rhosch
  • rhosch's Avatar
  • Offline
  • Junior Boarder
  • Posts: 33
  • Thank you received: 6
  • Karma: 0
Awsesome!

I'm still not to that point, but I will, eventually, be able to try this out and post my feedback. Looks like the forum is headed towards a generic external microcontroller scheme for those who need it.
The administrator has disabled public write access.

Readout of Volume 3 years 4 months ago #25242

  • haggismonster
  • haggismonster's Avatar
  • Offline
  • Junior Boarder
  • Posts: 35
  • Thank you received: 17
  • Karma: 4
I'm not sure - I haven't checked. I won't actually be using the VOL-FP now that I've got a working microcontroller replacement.
The administrator has disabled public write access.

Readout of Volume 3 years 4 months ago #25243

  • haggismonster
  • haggismonster's Avatar
  • Offline
  • Junior Boarder
  • Posts: 35
  • Thank you received: 17
  • Karma: 4
Mungam wrote:
Hello,
I am using a OpenDRC-DI and attached two Curryman Dacs to use it as crossover.
Is it possible to get a readout of the selected volume somewhere?
I would like to connect a Pic board and drive a display to show the volume. So I can handle I2C, SPI and Serial.

The Vol-FP only seems to convert the volume increase / decrease in IR code, so I would have to get it from the Minisharc board.
Is there any port that has the volume information?

Many thanks for any info.

Magnus

This is possible by replacing the VOL_FP with an Arduino/MCU and sending a junk I2C command to the MiniSHARC, which elicits the standard 6-byte response containing the volume level. For example, my code above sends 3,150,255 (which does nothing) and receives a response containing the current volume and selected config.

It's also useful during MiniSHARC initialization. I need to be able to detect the state change from "MiniSHARC is booting" to "MiniSHARC is ready". This is super easy because the MiniSHARC doesn't respond to I2C commands until it's finished booting; as such, you can just keep sending junk commands to it every few hundred milliseconds until it replies, indicating that it's all initialized.
Last Edit: 3 years 4 months ago by haggismonster.
The administrator has disabled public write access.

Readout of Volume 3 years 4 months ago #25244

  • haggismonster
  • haggismonster's Avatar
  • Offline
  • Junior Boarder
  • Posts: 35
  • Thank you received: 17
  • Karma: 4
I revamped the code to be object oriented and more complete. This version has the follow features:

  • Tracks volume state so you can always get the current volume with Sharc.getVolume()
  • Tracks muted state so you can always get the current mute state with Sharc.isMuted()
  • Gives you full control of muting with: Sharc.toggleMute(), Sharc.muteOn(), and Sharc.muteOff()
  • Allows you to set a specific volume level with Sharc.setVolume(123); // in the range 0-255 where 0=no attenuation, 255=full attenuation
  • Allows you to increase/decrease the volume by X number of 0.5dB increments with Sharc.volumeUp(100) or Sharc.volumeDown(123);
  • Change preconfigured configuration using Sharc.changeConfig(2); // in range 1 - 4
  • By default it waits for the MiniSHARC to initialize before proceeding.

Here's the updated code:

/*
	MiniSHARC / Arduino integration via I2C.
	haggismonster @ https://www.minidsp.com/forum/

	This is open source with a "do what the fuck you want" license.
*/
#include <Wire.h>

#define VOLFP_ADDR 0x2C
#define SHARC_ADDR 0x20
#define POS_VOLUME	2
#define POS_MUTE		3
#define POS_CONFIG	5
#define serprintf(fmt, ... ) { char ___b[1024]; snprintf(___b, 1023, fmt, ##__VA_ARGS__ ); Serial.println(___b); }

typedef unsigned char BOOL;

/*****************************************************************
 * This is the MiniSHARC class. Do NOT instantiate this.
 * You automatically get an instance of this Class called "Sharc".
 * See "loop()", below for more usage.
 *****************************************************************/
class MiniSHARC {
public:
	MiniSHARC();
	BOOL isInitialized();
	void waitForMiniSHARCToInitialize();
	void setConfig(byte config);
	int getConfig();
	int getVolume();
	void volumeUp();
	void volumeDown();
	void volumeUp(int steps);
	void volumeDown(int steps);
	void setVolume(byte targetVolume);
	void resetToDefaults();
	void printStatus();
	void setIsCallbackRegistered(BOOL value);
	BOOL isCallbackRegistered();
	void I2CReceiveCallback(int numBytes);
	void check_Sharc();
	void toggleMute();
	BOOL isMuted();
	void muteOn();
	void muteOff();

protected:
	byte _currentVolume;
	byte _currentConfig;
	byte _state[6];
	BOOL _isCallbackRegistered;
	byte _muted;

	void sendI2CBytes(byte major, byte minor);
	void forceMiniSHARCRefresh();
} Sharc;

MiniSHARC::MiniSHARC() {
	resetToDefaults();
	if(!Sharc.isCallbackRegistered()) {
		Wire.onReceive([&Sharc](int i) { return Sharc.I2CReceiveCallback(i); }); // hehe
		Sharc.setIsCallbackRegistered(true);
	}
}

BOOL MiniSHARC::isInitialized() {
	printStatus();
	return _state[0] == 0x06;
}

void MiniSHARC::resetToDefaults() {
	_isCallbackRegistered = false;
	
	memset(_state, 0, 6);

	Wire.begin(VOLFP_ADDR);
}

void MiniSHARC::sendI2CBytes(byte major, byte minor) {
	Wire.beginTransmission(SHARC_ADDR);
	Wire.write(3);
	Wire.write(major);
	Wire.write(minor);
	Wire.endTransmission();
	delay(50);
}

void MiniSHARC::forceMiniSHARCRefresh() {
	sendI2CBytes(150, 0xff); // junk command does nothing except elicit a response from MiniSHARC
}

void MiniSHARC::waitForMiniSHARCToInitialize() {
	Serial.println("Waiting for MiniSHARC to initialize...");
	while(isInitialized() == false) {
		forceMiniSHARCRefresh();
		delay(250);
	}
	Serial.println("Initialized!");
}

void MiniSHARC::printStatus() {
	serprintf("Volume: %u  //  Input: %u  //  State: %02x-%02x-%02x-%02x-%02x-%02x", getVolume(), getConfig(), _state[0]&0xff, _state[1]&0xff, _state[2]&0xff, _state[3]&0xff, _state[4]&0xff, _state[5&0xff]);
}

void MiniSHARC::volumeUp() {
	if(_currentVolume == 255)
		return;

	sendI2CBytes(150, 0);
}

void MiniSHARC::volumeDown() {
	if(_currentVolume == 0)
		return;

	sendI2CBytes(150, 1);
}

void MiniSHARC::volumeUp(int steps) {
	for(int i = 0; i < steps; i++) {
		volumeUp();
	}
}

void MiniSHARC::volumeDown(int steps) {
	for(int i = 0; i < steps; i++) {
		volumeDown();
	}
}

#define MAX(x,y) ( (x > y) ? x : y)
#define MIN(x,y) ( (x < y) ? x : y)
#define UP 1
#define DOWN -1

void MiniSHARC::setVolume(byte targetVolume) {
	if(targetVolume > 255)
		targetVolume = 255;

	if(targetVolume < 0)
		targetVolume = 0;

	int diff = MAX(targetVolume, getVolume()) - MIN(targetVolume, getVolume());
	if(diff == 0)
		return;

	int direction = (getVolume() < targetVolume) ? UP : DOWN;

	if(direction == UP)
		volumeUp(diff);
	else
		volumeDown(diff);
}

void MiniSHARC::setConfig(byte config) {
	if(config < 1 || config > 4)
		return;

	config--; // The caller uses the range 1-4 but MiniSHARC I2C expects 0-3
		
	sendI2CBytes(138, config);
	
	while(getConfig() != config) {
		sendI2CBytes(138, config);
		delay(250);
	}
}

int MiniSHARC::getConfig() {
	return _currentConfig;
}

int MiniSHARC::getVolume() {
	return _currentVolume;
}

BOOL MiniSHARC::isCallbackRegistered() {
	return _isCallbackRegistered;
}

void MiniSHARC::setIsCallbackRegistered(BOOL value) {
	_isCallbackRegistered = value;
}

void MiniSHARC::I2CReceiveCallback(int numBytes) {
	int pos = 0;

	while (0 < Wire.available()) {
		byte c = Wire.read();
		if(numBytes ==6 && pos < 6)
			_state[pos++] = c;
	}

	// All the responses we're interested in begin with 0x06
	if(_state[0] == 0x06) {
		_currentConfig =	_state[POS_CONFIG];
		_currentVolume =	_state[POS_VOLUME];
		_muted =					_state[POS_MUTE];
		//Sharc.printStatus();
	}
}

void MiniSHARC::toggleMute() {
	Wire.beginTransmission(SHARC_ADDR);
	Wire.write(9);
	Wire.write(149);
	Wire.write(135);
	Wire.write(1);
	Wire.write(0);
	Wire.write(255);
	Wire.write(2);
	Wire.write(253);
	Wire.write(246);
	Wire.endTransmission();
	delay(50);
	Wire.beginTransmission(SHARC_ADDR);
	Wire.write(5);
	Wire.write(149);
	Wire.write(131);
	Wire.write(1);
	Wire.write(0);
	Wire.endTransmission();
	delay(50);
}

BOOL MiniSHARC::isMuted() {
	return _muted;
}

void MiniSHARC::muteOn() {
	if(!_muted)
		toggleMute();
}

void MiniSHARC::muteOff() {
	if(_muted)
		toggleMute();
}

void MiniSHARC::check_Sharc() {
	while(!Sharc.isInitialized()) {
		Sharc.waitForMiniSHARCToInitialize();
	}
}


/**********************************************
 * This is how an example of how Sharc is used.
 **********************************************/

void setup() {
	Serial.begin(115200);
}

void loop() {
	// Always run this first to make sure things are sane
	Sharc.check_Sharc();

	// Dump internal state (volume, config, state)
	Sharc.printStatus();

	// Toggle mute on and off a few times
	serprintf("Muted: %d", Sharc.isMuted());
	
	Sharc.toggleMute();
	serprintf("Muted: %d", Sharc.isMuted());
	delay(500);

	Sharc.toggleMute();
	serprintf("Muted: %d", Sharc.isMuted());
	delay(500);

	Sharc.toggleMute();
	serprintf("Muted: %d", Sharc.isMuted());
	delay(2000);

	// Force mute on/off a few times
	Sharc.muteOn();
	serprintf("Muted: %d", Sharc.isMuted());
	delay(500);

	Sharc.muteOff();
	serprintf("Muted: %d", Sharc.isMuted());
	delay(500);

	Sharc.muteOn();
	serprintf("Muted: %d", Sharc.isMuted());
	delay(500);

	Sharc.muteOff();
	serprintf("Muted: %d", Sharc.isMuted());
	delay(2000);

	// Forcibly set the volume to a specific value
	serprintf("Setting volume to 50");
	Sharc.setVolume(50);
	Sharc.printStatus();
	delay(2000);

	// Forcibly set the volume to a specific value
	serprintf("Setting volume to 100");
	Sharc.setVolume(100);
	Sharc.printStatus();
	delay(2000);

	// Decrease the volume 10 times in .5dB decrements
	serprintf("Decreasing volume by 10 0.5dB decrements");
	Sharc.volumeDown(10);
	Sharc.printStatus();
	delay(2000);

	// Increase the volume 10 times in .5dB increments
	serprintf("Increasing volume by 10 0.5dB increments");
	Sharc.volumeUp(10);
	Sharc.printStatus();

	// Change the configuration (in range 1 - 4)
	serprintf("Changing config to 2");
	Sharc.setConfig(2); 
	Sharc.printStatus();

	// Change the configuration (in range 1 - 4)
	serprintf("Changing config to 1");
	Sharc.setConfig(1); 

	delay(2000);
}
Last Edit: 3 years 4 months ago by haggismonster.
The administrator has disabled public write access.
The following user(s) said Thank You: Richard, avddreamr, rhosch, Umlautica
Moderators: devteam