Thank you for answers and insights.
Some people mentioned the breadboard setup.
Offcourse that's not ideal, that's why it's a breadboard. I did not have one bad connection.
@madmark2150
I think I mentioned a TFT 320x240 display, parallel connected. I tried I2C LCD but don't like it.
I think I mentioned I'm building a preamplifier, narrowing down to the controlling section over here.
Allthough not completely click-free, the R2R design of volume regulating is what I want. There are other forums discussing the best possible volume controlling on analog audio. Many high-end (i mean really high-end) designs use R2R volume regulating. Yes that's with lots of relays.
The MEGA is too big in terms of pinouts, only using it for display. Space is not really restricted.
I am using just 2 encoders for volume, inputselect, balance, gainsettings, phonopreamp settings, setup menus, mute, etc. I think that's already enough narrowed down and fits the initial goal. No pots: these have a begin-and-end. and a pain when controlling volume from remote (preferably no motors/moving parts). Also in audio-terms there is no pot that beats the potential of a constant-load balanced (thus 4-way) R2R ladder design. The stepper switch comes close. As said, there are other forums for that. The R2R is completely made and tested, .1% resistors, pure silver leads,... , I heard it, I will keep it. I have no doubt you can learn me a lot about coding and arduinos, but you may not in terms of audiophile sound.
@gilshultz
Now that is an interesting path I already have seen. That may be a solution.
@TomGeorge
I am making a complete new preamp and phono preamp, based on existing (old) circuits.
The audio stuff is made, tested. That was the easy part (for me).
As a diy-er and enthousiast, I have no block diagrams. I do have circuits for the analog part.
The code for volume update (see below) is now running very smoothly. Next step is to embed the volume encoder in the same controller. If that works fine, I can move it all to one controller + the bluetooth controller, I have no problem having a seperate controller for bluetooth.
@TomGeorge
Citaat
What other Arduino projects have you done before this?
Can you please tell us your electronics, programming, arduino, hardware experience?
Please read previous posts. In short, no other arduino projects, respectively: basic, none, none and none. In real life, I'm a home automation engineer, working with existing products such as KNX. That's at most configuring, not programming.
@Grumpy_Mike
I certainly do not refuse to post my code, but it is not 'postable' yet ;-). See below for display code, now with smooth updating volume. It's all in seperate functions. So the display, no not updating all of the screen. As for the encoder, yes it is with polling and not with interrupts. I have not tried that. (For now I turn off interrupts only for volume updates, but I'm not sure if that's still necessary (it 'pends' the I2Creceive a bit without losing the information). Per % of displayed change, there are 2.55 times as much I2C 'receives'.
void tftPage1Update(bool Forced) {
//Serial.println("tftUpdate");
// PAGE SELECT
//++++++++++++page1 HOMEPAGE
if (DisplayLastPage != 1 && DisplayPage == 1 ||Forced == true) {
tft.setFont(&FreeSans12pt7b);
tft.fillScreen(ILI9341_BLACK); // background color
tft.drawRoundRect(0,0,320,24,8,ILI9341_WHITE); //bovenste rechthoek
tft.drawRoundRect(0,60,150,110,8,ILI9341_WHITE); //input rechthoek
tft.drawRoundRect(170,60,150,110,8,ILI9341_WHITE); //volume rechthoek
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(1);
tft.setCursor(10,20);
tft.println("ALEPH P1.7 XONO 2019");
tft.setFont(&FreeSansOblique9pt7b);
tft.setCursor(10,54);
tft.println("Input");
tft.setCursor(190,54);
tft.println("Volume");
DisplayLastPage = 1;
if (DisplayDebug == true)
Serial.println(F("main page drawn"));
}
}
//------------------------------MAIN PAGE VOLUME UPDATE---------------------------
void doMainPageVolume(bool Forced) {
if ((Volume != LCDDisplayedVolume && Volume<100) ||Forced == true) { //check op verandering
//++++eerst paar rechtzettingen/berekeningen
// if (Volume == 100) Volume = 99; //display te klein om 100 weer te geven
int volEenheid = Volume % 10; //opsplitsen in eenheden
int volTiental = Volume / 10; //en tientallen
//++++zet klaar voor screenupdate
noInterrupts(); //geen interrupts (door I2C) als volume geupdated wordt.
tft.setFont(&FreeSansBold24pt7b); // zet juiste font
tft.setTextSize(2); // zet juiste grootte
if (volTiental != lastVolTiental){ // tientallen veranderd?
tft.fillRect(VolumeNumbersX+2,VolumeNumbersY-67,48,71,ILI9341_BLACK); //verwijder cijfer
tft.setCursor(VolumeNumbersX,VolumeNumbersY); //zet positie/kleur
if (muteEnabled == true) {tft.setTextColor(ILI9341_RED);}else{tft.setTextColor(ILI9341_WHITE);}
tft.print(volTiental); //zet de waarde
lastVolTiental = volTiental; //onthoud de verandering
}
// eenheden hoeven niet vergeleken: veranderen altijdbij veranderd volume
tft.setCursor(VolumeNumbersX+50,VolumeNumbersY); // zet juiste plaats
if (muteEnabled == true) {
tft.setTextColor(ILI9341_RED); // zet juiste tekstkleur
} else {
tft.setTextColor(ILI9341_WHITE); // zet juiste tekstkleur
}
tft.fillRect(VolumeNumbersX+52,VolumeNumbersY-67,50,71,ILI9341_BLACK);
tft.print(volEenheid); // print waarde
interrupts();
doBargraph(); //zet de bargarph erop
BargraphShow = millis(); //en onthoud wanneer
LCDDisplayedVolume = Volume; // onthoud laatste waarde
if (DisplayDebug == true)
Serial.println(F("VolumeMainDisplay Updated")); //debug
}
if (millis() > BargraphShow + 2000){ //na verlopen van bargraphtijd
doIconBar(ForcedUpdate); //doe de iconbar
}
}
//-------------------------------main page--BARGRAPH VANUIT VOLUME UPDATE
void doBargraph() {
if (Volume != LCDDisplayedVolume) {
if (IconBar != "Volume") {
tft.fillRect(10,180,300,40,ILI9341_BLACK);
tft.drawRoundRect(10,180, 300, 40, 8, ILI9341_WHITE); //bargraph
IconBar = "Volume";
}
tft.fillRoundRect(Volume*3+11,181,289-Volume*3,40-2,8,ILI9341_BLACK);
tft.fillRoundRect(11,181,(Volume*3)-2,40-2,8,ILI9341_RED);
if (DisplayDebug == true)
Serial.println(F("BARGRAPH Updated")); //debug
}
}
//--------------------------------MAIN PAGE INPUT---------------------------
void doMainPageInput(bool Forced) {
if (InputSelect != InputSelectLast ||Forced == true) {
tft.setFont(&FreeSansBold24pt7b);
tft.setTextSize(2);
tft.fillRect(5,65,140,90,ILI9341_BLACK); //verwijder het vorige
tft.setCursor(5,150);
tft.setTextColor(ILI9341_WHITE);
tft.print(InputSelect);
tft.setFont(&FreeSans12pt7b);
tft.setTextSize(1);
tft.print(InputSelectText);
tft.fillRect(145-32,65,32,32,ILI9341_BLACK);
switch (InputSelect) {
case 1:
drawBitmap(145-40,73,myBitmapvinylrecordplayer,32,32,ILI9341_WHITE);
break;
case 2:
drawBitmap(145-40,73,myBitmapcdplayer,32,32,ILI9341_WHITE);
break;
case 3:
drawBitmap(145-40,73,myBitmapcdwithmusicnote,32,32,ILI9341_WHITE);
break;
case 4:
drawBitmap(145-40,73,myBitmapmobilephoneoff,32,32,ILI9341_WHITE);
break;
case 5:
drawBitmap(145-40,73,myBitmapmicrophonerecorder,32,32,ILI9341_WHITE);
break;
}
InputSelectLast = InputSelect;
if (DisplayDebug == true)
Serial.println(F("InputMainDisplay Updated"));
}
}
void doIconBar(bool Forced) {
int oldCurrentGainsetting;
bool oldBalanced;
// De layout van icons zetten
if (IconBar != "Icons" ||Forced){
tft.fillRect(10,180,300,40,ILI9341_BLACK);
for (int i=0 ; i < 4 ; i++) {
tft.drawRoundRect(10+i*75,180, 70, 40, 20, ILI9341_YELLOW);
IconBar = "Icons";
}
}
// ICON1 updaten
if (currentGainsetting != oldCurrentGainsetting) {
tft.setTextSize(1);
tft.setFont(&FreeSansBold9pt7b); // zet font
tft.setTextColor(ILI9341_WHITE);
tft.setCursor(12,208);
tft.print(currentGainsetting);tft.print("dB");
oldCurrentGainsetting = currentGainsetting;
}
//ICON 2 UPDATEN
if (balanced != oldBalanced) {
tft.setTextSize(1);
tft.setFont(&FreeSansBold9pt7b); // zet font
tft.setTextColor(ILI9341_WHITE);
if (balanced == true) {
tft.setCursor(95,208);
tft.print("XLR");
} else {
tft.setCursor(95,208);
tft.print("RCA");
}
oldBalanced = balanced;
}
//ICON 4 UPDATEN
if (BTConnected != oldBTConnected) {
tft.setTextSize(1);
tft.setFont(&FreeSansBold9pt7b); // zet font
tft.setTextColor(ILI9341_WHITE);
if (BTConnected == 1) {
tft.setCursor(250,208);
tft.print("BT");
} else {
tft.setCursor(250,208);
tft.fillRect(250,195,25,15,ILI9341_BLACK);
Serial.println(F("BT blank printed"));
}
oldBTConnected = BTConnected;
if (DisplayDebug){
Serial.print(F("Bluetooth icon set to new/old "));Serial.print(BTConnected);Serial.println(oldBTConnected);
}
}
}
Not everyting is completely ready: the bargraph works perfectly but there are some small pixels issues to resolve. The iconbar is to be done completely, but here is the basis.
Then this is in the loop, which is rather short:
if (DisplayPage == 1) {
Volume = I2CReceive[1];
muteEnabled = I2CReceive[2];
InputSelect = I2CReceive[3];
currentGainsetting = I2CReceive[4];
balanced = I2CReceive[5];
Alarm = I2CReceive[6];
tftPage1Update(ForcedUpdate); //Update het scherm met main page
doMainPageVolume(ForcedUpdate); // VAN HIERUIT DE VERSCHILLENDE ITEMS UPDATEN
doMainPageInput(ForcedUpdate);
if (IconBar != "Volume")
doIconBar(ForcedUpdate);
}
// wordt altijd uitgevoerd (onderkant, displaypaginas hierboven)
//Helderheid scherm reduced na verlopen van laatste IICreceive
if (millis() > lastReceiveStamp + 30000 ){
analogWrite(BacklightPin, reducedDisplayBrightness);
currentDisplayBrightness = reducedDisplayBrightness;
}
//Check of bluetooth remote connected is
if (digitalRead(BTConnectedPin) == HIGH){
BTConnected = true;
} else {
BTConnected = false;
}
Working but not finished in the loop are sleepandwakeroutines (both triggered from I2C), other than that there's the Wire.onReceive function updating 7 variables.
For the encoder part (for now in other 'master' arduino) this is just in the (loop) section: (and maybe that's what i'm doing wrong according your 'by interrupts' tip:
//====================
// Read the encoder and changevolume AND SEND TO DISPLAY
//====================
n = digitalRead(encoder0PinA);
if ((encoder0PinALast == LOW) && (n == HIGH)) {
if (digitalRead(encoder0PinB) == LOW) {
changeVolume(-encoderIncrement);
if (currentAttenuatorLevelDisplay != OldAttenuatorLevelDisplay) {
sendToDisplayPage1(DisplayPage, currentAttenuatorLevel, muteEnabled, InputSelect, currentGainsetting, Balanced, Alarm);
}
} else {
changeVolume(encoderIncrement);
if (currentAttenuatorLevelDisplay != OldAttenuatorLevelDisplay) {
sendToDisplayPage1(DisplayPage, currentAttenuatorLevel, muteEnabled, InputSelect, currentGainsetting, Balanced, Alarm);
}
}
}
encoder0PinALast = n;
Other than that the loop polls a 'slow' encoder, 1 encoder button, IR receive, a UNO-master-connected (very static) OLED display update if necessary, and - just for fun - a touchscreen press on the 'input' field. The encoder section above comes from an example preamp I found, but this was not incorporating a display.
@hammy
Thank you for your tips. I did not have wire or connection failure. That's stuff I'm good at. The whole setup is running as-is for two weeks now, without error. Offcourse it doesn't look the part. I have only a kitchentable, permanently divided into office, lab, and ... dining table.
You are right about tackling one aspect at a time. In the beginning I was way to overenthousiastic. Last few days I'm only bettering the display but will not change library anymore.
Thank everyone to the useful tips. Please hold off from the analog audio part. I know which volume control to use, the hardware is fixed. All of this is about the control part. The volume knob or remote has to go as smoothly as possible.