Go Back   Home > Forums > > >
Home Forums Rules Articles diyAudio Store Blogs Gallery Wiki Register Donations FAQ Calendar Search Today's Posts Mark Forums Read

Twisted Pear Superior quality electronic kits

Reply
 
Thread Tools Search this Thread
Old 7th May 2019, 10:12 AM   #1
Poni is offline Poni
diyAudio Member
 
Join Date: Jun 2013
Default C++ Easy and Scalable Software for BIII38Pro

Hi guys,

I have been looking for good, easy and scalable software for BIIIDAC, but couldn't find anything useful. I know, there are some guys writing the software, but it is mostly C or Microcontroller based and very difficult to follow or change.

I decided to write easy to follow software, based on C++ classes, which is as close to TPA Firmware as possible.
I use Atom + PlatformIO and Arduino DUE for this.
For remote control i use Apple Remote and the Display is 4,3" 480x272 TFT LCD mit Touchscreen SSD1963.

The software is based on TPA firmware, it behaves the same way: power up, reset, communication, configurateion, etc... is all based on TPA firmware.
The software gives you at this stage basic interaction, like volume change and input change, the rest of the configuration is read from the switches on the BIII board.
This software is easily scalable, you can replace any of the classes with your own or modify existing.
I have added some "interfaces" for editing, like eeprom or touch (didnt test the touch, but its easy to figure out how it works and correct it).
I also prepared the settings page for the tft screen so one can add it later on.


Main function is literaly 20 lines of code, it just takes the action from interface and apply to the DAC class.


As I said, the heart of the DAC Class is TPA Software.
The configuration file is the copy of TPA, ES9028_38.h file.
DAC class constructor is build of TPA functions, slightly modified for Arduino needs ( i use Wire.h for I2C communication ), but the funtions:
powerDACup();
initializeDAC();
configureDAC();
are practically the same you get with TPA firmware, those work the same way and are called the way TPA is doing it.


Simplified soft looks like this:
//--------------------------------------
setup:

dac = new DAC();
remoteInterface = new RemoteInterface();
touchInterface = new TouchInterface();
tftGraphics = new TFTGraphics();

//---------------------
loop:

action = remoteInterface->getAction();
if( action == NONE )
action = touchInterface->getAction( MAIN_MENU );


if( action == NONE ){
dac->readSwitchStates();
}


switch ( action ){
case NONE:
break;
case CHANNEL_LEFT:
tftGraphics->printChannel( dac->decreaseInput() );
break;
case CHANNEL_RIGHT:
tftGraphics->printChannel( dac->increaseInput() );
break;
case VOLUME_UP:
tftGraphics->printVolume( dac->increaseVolume() );
break;
case VOLUME_DOWN:
tftGraphics->printVolume( dac->decreaseVolume() );
break;
case ENTER:
tftGraphics->printVolume( dac->muteVolume() );
break;
default:
break;
}
action = NONE;

//-------------------------


All you need is Atom + Platform IO + ( Arduino Due, remote, TFT) and the repository:

GitHub - poninskit/TPA_Buffalo_IIIsePro38_Software: c++ Microcontroller Software for Buffalo III 9038 Pro DAC

happy coding.

here is how it looks like:

Click the image to open in full size.

Click the image to open in full size.

Click the image to open in full size.

Click the image to open in full size.

Optional DAC settings page could be written like this:

Click the image to open in full size.




and this is how it works:

VID_20190507_095456.mp4 - Google Drive

Last edited by Poni; 7th May 2019 at 04:45 PM.
  Reply With Quote
Old 8th May 2019, 02:54 PM   #2
pixelpusher is offline pixelpusher  United States
diyAudio Member
 
Join Date: Oct 2004
Location: Minneapolis
Very nice!
Thanks for posting. Could this work with the Beaglebone?
  Reply With Quote
Old 8th May 2019, 03:25 PM   #3
Poni is offline Poni
diyAudio Member
 
Join Date: Jun 2013
You welcome, lets see if people like it, maybe it will grow.

I dont have much time to write it till the end, but the idea is to public the core, after that anyone could contribute by writing new classes ( new interfaces, display classes and so on ).
Then just publish new class: library + *.cpp + *.h, so anyone can use it.
...maybe it will work.


I havent work with Beaglebone, but its C++/C, so the core should work everywhere...

Last edited by Poni; 8th May 2019 at 03:36 PM.
  Reply With Quote
Old 22nd July 2019, 09:05 PM   #4
drone is online now drone  Germany
diyAudio Member
 
Join Date: Jan 2006
Location: Germany Rheinarea
Hello Poni,

thank you for this project.

I have a lot of questions and hope it is not to much

First: I tried to figure out (by reading your code) how you did connect everything. Arduino Due , Eprom, Relais, TFT-Display etc.

Second I would like to connect/use a Led-Matrix for display to show only A: source and B: sampling rate. For the Display I have (Beam - beautiful 120 LED matrix — Hover Labs) there is a Arduino Library and it uses only few commands to display text (display() for static text and play() for scrolling text). So I would have to figure out how to read Sampling rate and convert it to small text bits (44.1k; 96k ...; DSD 64, DSD 128 ...). Also I would need advise how to implement a "class" of LED-Matrix to you code.

thx

Branko
  Reply With Quote
Old 26th July 2019, 02:48 PM   #5
Poni is offline Poni
diyAudio Member
 
Join Date: Jun 2013
Hi Drone,

I will try to put some light on how i did it.

There is a main file, where everything happens in loop. The purpose is to keep this file simple.
To go deeper I created classes, which i wrapped on Arduino classes. I suggest to inherit the class and in your inherited class you can make all the configuration and write your specific functions.

So, on the beginning you will find declarations (pointers to the objects that doesnt exists yet, to be specific) of the clsses I use later on:
...
//REMOTE CONTROLL
RemoteInterface* remoteInterface;
//TOUCH INTERFACE
TouchInterface* touchInterface;
//TFT SCREEN
TFTGraphics* tftGraphics;
...

to go deeper i will takte TFTGraphics class, that basically makes all the display stuff. This is the class you would like to replace if you use diffrent LCD.

So in setup I create instance of the class:
tftGraphics = new TFTGraphics();

that calls the constructor of the class, which is inheritance of UTFT class.
TFTGraphics:UTFT

My constructor looks like this (header):
TFTGraphics(byte model = SSD1963_480, int RS = 38, int WR=39, int CS = 40, int RST=41, int SER = 0);

if you have a look on UTFT constructor it take exact same parameters as my TFTGraphics class:
UTFT::UTFT(byte model, int RS, int WR, int CS, int RST, int SER)

With the difference, that I put default values in my class (header file), so by calling my constructor i also call constructor of UTFT with my specific values:

(header)
TFTGraphics(byte model = SSD1963_480, int RS = 38, int WR=39, int CS = 40, int RST=41, int SER = 0);
(cpp)
TFTGraphics::TFTGraphics(byte model, int RS, int WR, int CS, int RST, int SER)
:UTFT(model, RS, WR, CS, RST, SER)

So, now i have TFTGraphics class, that is specific case of UTFT class.
You can use any class you want... if you have LED-Matrix class, just inherit the class and put your values in constructor.
In inheritance of your "Led-Matrix" class you want to put all the methods you will possibly need. You can use all the functions of inherited class, and probably want to write methods like "printVolume", "printChannel", "printSampleRate"...

Now when you have your class MyLedMatrix:LedMatrix, which inherits the LedMatrix.
You just use your methods in main.cpp file.

for example, in case when user increses volume:

case VOLUME_UP:

//increase the volume of DAC
int volume = dac->increaseVolume();

//print the volume of the DAC in your specific class
MyLedMatrix->printVolume( volume );

break;

... and so you move on.... take the class that matches your hardware, inherit in your class...write mehods you need.... connect... next class...


So in your specific case, the BEAM class have two constructors....you normally take one and inherit it in your class ...


BEAM:
/*
This constructor used when multiple Beams behave like one long Beam
*/
Beam::Beam ( int rstpin, int irqpin, int numberOfBeams){
_rst = rstpin;
_irq = irqpin;
_beamCount = numberOfBeams;
activeBeams = numberOfBeams;
_gblMode = 1;
}

/*
This constructor used when multiple Beams behave like single Beam units
*/
Beam::Beam ( int rstpin, int irqpin, uint8_t syncMode, uint8_t beamAddress){
_rst = rstpin;
_irq = irqpin;
_syncMode = 0;
activeBeams = 1;
_currBeam = beamAddress;
_gblMode = 0;
}


Just as example I will take the first constructor... it would look something like this:

header:
MyBeam ( int rstpin = 1, int irqpin = 2, int numberOfBeams = 3) // here you put your values...i just put fake values 1,2,3...

.cpp:
MyBeam::MyBeam(int rstpin, int irqpin, int numberOfBeams)
:Beam( rstpin, irqpin, numberOfBeams)
{
//here you call the initial methods of class Beam, like
begin();
initBeam();
print("willkommen");
}


...then you can proceed with your methods:
MyBeam:: printVolume(int volume){

//here you call Beam mehod print like so:

print (volume);
or
play();
or
printFrame(...);

}
...



... after that you just put MyBeam class in main file....

MyBeam *myBeam;


in Setup
myBeam = new MyBeam(); // that calls default parameters you put in constructor of the class MyBeam (optinally you could enter parameters here in setup as well)


then you move to loop() and use your methods in given case... like VOLUME_UP, or CHANNEL_LEFT

case VOLUME_UP:

//increase the volume of DAC
int volume = dac->increaseVolume();

//print the volume of the DAC in your specific class
MyBeam->printVolume( volume );

break;


hope that helps... have a fun

Last edited by Poni; 26th July 2019 at 02:58 PM.
  Reply With Quote
Old 27th July 2019, 05:58 PM   #6
drone is online now drone  Germany
diyAudio Member
 
Join Date: Jan 2006
Location: Germany Rheinarea
Quote:
Originally Posted by Poni View Post
Hi Drone,

I will try to put some light on how i did it.

There is a main file, where everything happens in loop. The purpose is to keep this file simple.
To go deeper I created classes, which i wrapped on Arduino classes. I suggest to inherit the class and in your inherited class you can make all the configuration and write your specific functions.

So, on the beginning you will find declarations (pointers to the objects that doesnt exists yet, to be specific) of the clsses I use later on:
...
//REMOTE CONTROLL
RemoteInterface* remoteInterface;
//TOUCH INTERFACE
TouchInterface* touchInterface;
//TFT SCREEN
TFTGraphics* tftGraphics;
...

to go deeper i will takte TFTGraphics class, that basically makes all the display stuff. This is the class you would like to replace if you use diffrent LCD.

So in setup I create instance of the class:
tftGraphics = new TFTGraphics();

that calls the constructor of the class, which is inheritance of UTFT class.
TFTGraphics:UTFT

My constructor looks like this (header):
TFTGraphics(byte model = SSD1963_480, int RS = 38, int WR=39, int CS = 40, int RST=41, int SER = 0);

if you have a look on UTFT constructor it take exact same parameters as my TFTGraphics class:
UTFT::UTFT(byte model, int RS, int WR, int CS, int RST, int SER)

With the difference, that I put default values in my class (header file), so by calling my constructor i also call constructor of UTFT with my specific values:

(header)
TFTGraphics(byte model = SSD1963_480, int RS = 38, int WR=39, int CS = 40, int RST=41, int SER = 0);
(cpp)
TFTGraphics::TFTGraphics(byte model, int RS, int WR, int CS, int RST, int SER)
:UTFT(model, RS, WR, CS, RST, SER)

So, now i have TFTGraphics class, that is specific case of UTFT class.
You can use any class you want... if you have LED-Matrix class, just inherit the class and put your values in constructor.
In inheritance of your "Led-Matrix" class you want to put all the methods you will possibly need. You can use all the functions of inherited class, and probably want to write methods like "printVolume", "printChannel", "printSampleRate"...

Now when you have your class MyLedMatrix:LedMatrix, which inherits the LedMatrix.
You just use your methods in main.cpp file.

for example, in case when user increses volume:

case VOLUME_UP:

//increase the volume of DAC
int volume = dac->increaseVolume();

//print the volume of the DAC in your specific class
MyLedMatrix->printVolume( volume );

break;

... and so you move on.... take the class that matches your hardware, inherit in your class...write mehods you need.... connect... next class...


So in your specific case, the BEAM class have two constructors....you normally take one and inherit it in your class ...


BEAM:
/*
This constructor used when multiple Beams behave like one long Beam
*/
Beam::Beam ( int rstpin, int irqpin, int numberOfBeams){
_rst = rstpin;
_irq = irqpin;
_beamCount = numberOfBeams;
activeBeams = numberOfBeams;
_gblMode = 1;
}

/*
This constructor used when multiple Beams behave like single Beam units
*/
Beam::Beam ( int rstpin, int irqpin, uint8_t syncMode, uint8_t beamAddress){
_rst = rstpin;
_irq = irqpin;
_syncMode = 0;
activeBeams = 1;
_currBeam = beamAddress;
_gblMode = 0;
}


Just as example I will take the first constructor... it would look something like this:

header:
MyBeam ( int rstpin = 1, int irqpin = 2, int numberOfBeams = 3) // here you put your values...i just put fake values 1,2,3...

.cpp:
MyBeam::MyBeam(int rstpin, int irqpin, int numberOfBeams)
:Beam( rstpin, irqpin, numberOfBeams)
{
//here you call the initial methods of class Beam, like
begin();
initBeam();
print("willkommen");
}


...then you can proceed with your methods:
MyBeam:: printVolume(int volume){

//here you call Beam mehod print like so:

print (volume);
or
play();
or
printFrame(...);

}
...



... after that you just put MyBeam class in main file....

MyBeam *myBeam;


in Setup
myBeam = new MyBeam(); // that calls default parameters you put in constructor of the class MyBeam (optinally you could enter parameters here in setup as well)


then you move to loop() and use your methods in given case... like VOLUME_UP, or CHANNEL_LEFT

case VOLUME_UP:

//increase the volume of DAC
int volume = dac->increaseVolume();

//print the volume of the DAC in your specific class
MyBeam->printVolume( volume );

break;


hope that helps... have a fun

Thank you for taking so much time. Will try to make my way through

BR

Branko
  Reply With Quote
Old 30th July 2019, 04:20 PM   #7
drone is online now drone  Germany
diyAudio Member
 
Join Date: Jan 2006
Location: Germany Rheinarea
Making progress with the "LED_MATRIX" display class )

How would be the best way to read the sampling rate of the actual playing music from DAC and convert it to i.e. "96k" or "DSD 64"

thank you again

Branko
  Reply With Quote
Old 31st July 2019, 08:59 AM   #8
Poni is offline Poni
diyAudio Member
 
Join Date: Jun 2013
Hi

I havent done sample rate yet, but "HiFiDuino" and "DimDim" have...

You can download the project here...its huge, but you will find "void getSR()", you could use it as a basis.
TFT HiFiDuino Pro Project | Dimdim's Blog
H i F i D U I N O | Lot of Value, Little Money


I would proceed a little bit diffrent way here.

Anyway, you could use it as guide.... add new function to the DAC class. What I would change on the beginning, is that the function should not print anything.
Just take the input from DAC and return the result... String would be the easiest way to return the result, but the propper way would be to create like enum...with codes, and then define strings that describe the codes. You can print the strings after all as info text.
If that method works and returns proper results, anyone could change the strings and display whatever they want.

have a look on what I have done in getLockStatus... its empty example on how you could do this

LOCK_STATUS DAC::getLockStatus()

... this returns lock status enum

enum LOCK_STATUS
{
Unknown = 0,
Locked,
No_Lock
};

then you can write "translator" to human redable string

// Returns a text description of an ErrorCode
// keep at 28 characters
static char* dacLockString(LOCK_STATUS lock)
{
switch (lock)
{
case Unknown: return (char*)("");
case Locked: return (char*)("Locked");
case No_Lock: return (char*)("Unlocked");
default: return (char*)("");
}
}


at the end you just display string on screen:

tftGraphics->printInfoText( dacLockString( dac->getLockStatus() ) );

alternatively you could write last line like this:

LOCK_STATUS status = dac->getLockStatus();
String status_string = dacLockString( status );
tftGraphics->printInfoText(status_string);



I also think that this function should be divided...dont make it to big... maybe use signal type as parameter or write couple of functions for each signal type and then one funtion that manages it. Just to keep it simple.


have a fun!

Last edited by Poni; 31st July 2019 at 09:02 AM.
  Reply With Quote

Reply


C++ Easy and Scalable Software for BIII38ProHide this!Advertise here!
Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Scalable PSU/regulator GB BobEllis Group Buys 655 2nd June 2015 09:52 PM
Easy, simple to use software for measuring FR, impulse, etc? makingmoney Multi-Way 7 6th July 2010 09:45 AM
Easy PCB Software Bonanza Software Tools 3 31st May 2010 10:23 AM


New To Site? Need Help?

All times are GMT. The time now is 08:27 PM.


Search Engine Optimisation provided by DragonByte SEO (Pro) - vBulletin Mods & Addons Copyright © 2019 DragonByte Technologies Ltd.
Resources saved on this page: MySQL 15.00%
vBulletin Optimisation provided by vB Optimise (Pro) - vBulletin Mods & Addons Copyright © 2019 DragonByte Technologies Ltd.
Copyright ©1999-2019 diyAudio
Wiki