Arduino + OLED + LMS

I am loooking for some guidance. I am fond of music; computers and dyi. As a result I decided to build my own music streamer a few months back. I manged to get a nice chassis in which I implemeted a mini itx board , an home made ppower supply and an arduino and an oled screen.
The idea was to install daphile on the pc part. this would provide the music streaming part. The arduino + OLED was aiming at displaying song and artist infos. To achieve this, I had to covince the developer of daphile to add an option to allow some port redirection to the serial port. After several emails exchanges and tests the option was integrated in the daphile code as a hidden option.
I had then to work with the arduino. First get the arduino and the OLED to work together (fighting with pins :wink: ). Once done, i could start developing a bit of code to retrieve the necessary infos from the serial port. Indeed when connected to the pc, ther arduino board appears as a TTYACMx. With the help of my son, who is way brighter than I will ever be...we managed to develop some code that in the end is somehow producing what we want 85% of the time.
85% is fine but there is room for improvment ! however, i think that i reached my limits in terms of programming...I am not a developper and all that we did, we achieved it reading all the different online helps and googling all over to figure out how to do it...

here is the latest version of the code that lsitens on the serial port and parses some infos then displays it.

daphile here

//version daphilePlayer2Test1 : addon list caracteres UTF8-rework affichetexte maj et min (virer integer, delay en dur a 250) compliled with IDE1.6.5

// include the library code:
#include <LiquidCrystal.h>
LiquidCrystal lcd(6, 7, 8, 10, 11, 12, 13);

String playerId;
String statusRequest;
String artist;
String song;
int posArtist;
int posSong;
int dirArtist;
int dirSong;

String centered(String text)
{
int len = text.length(), numSpaces;
String centeredText;
if (len > 16)
{
 return text;
}
numSpaces = (16 - len)/2;
centeredText.reserve(16);
centeredText = text;
for (int i = 0; i < numSpaces; i++)
{
 centeredText = " " + centeredText + " ";
}
if (len % 2)
{
 centeredText = centeredText + " ";
}
return centeredText;
}

void afficheTexte(String *lines)
{
String lgnHaut = centered(lines[0]);
String lgnBas = centered(lines[1]);
int len = 16;
for (int i = len; i >= 0; i--)
{
 lcd.clear();
 lcd.setCursor(0,0);
 lcd.print(lgnHaut.substring(i));
 lcd.setCursor(0,1);
 lcd.print(lgnBas.substring(i));
 delay(300);
}
}

void setup()
{ 
delay(5000);
char serialAnswer[1024];
String initLines[] = { "V7", "DAPHILE PLAYER" }, lmsAnswer;
String DediLines[] = { " Arduino Code" , "by Thibault" };
String PlayLines[] = { " Let's listen to" , " some music now!"};
playerId.reserve(17);
lcd.begin(16, 2);
lcd.clear();
afficheTexte(initLines);
delay(4000);
afficheTexte(DediLines);
delay(2000);
afficheTexte(PlayLines);
delay(3000);
afficheTexte(initLines);
delay(4000);
Serial.begin(9600);
Serial.print("player id 0 ?\n");
while (!Serial.find("player id 0 ?\r\nplayer id 0 "))
{
 delay(500);
}
Serial.readBytes(serialAnswer,27);
lmsAnswer = serialAnswer;
playerId = lmsAnswer.substring(0,27);
playerId.replace("%3A",":");
statusRequest = playerId + " status - 1 tags:a subscribe:0";
Serial.print(statusRequest + "\n");
statusRequest.replace(":","%3A");

}

void loop()
//After creating a setup() function, which initializes and sets the initial values, the loop() function does precisely what its name suggests, and loops consecutively, allowing your program to change and respond. Use it to actively control the Arduino board.
{
char request[51];
char statusUpdate[2048];
String statusUpdateString = "";
String time, artist, song, duration, title, durationDisplay;
String topline, bottomline;
statusRequest.toCharArray(request,51);
int len, posTime, posRate, posArtist,  posSong, posDuration, posTitle, posCanSeek, durMin, durSec;
int artistlen, titlelen;
int myLongestLine = 0; // length of the longest line to display
int myStep = 0; // current step while looping
int myLength = 16;
while (!Serial.find(request) && !Serial.find("mode%3Aplay") && !Serial.find("time%3A"))
{
delay(500);
}
len = Serial.readBytesUntil('\n', statusUpdate, 2048);
statusUpdateString = statusUpdate;
statusUpdateString = statusUpdateString.substring(0,len);
posTime = statusUpdateString.indexOf("time%3A") + 7;
posRate = statusUpdateString.indexOf("rate%3A") - 1;
posDuration = statusUpdateString.indexOf("duration%3A") + 11;
posCanSeek = statusUpdateString.indexOf("can_seek%3A") - 1;
posTitle = statusUpdateString.indexOf("title%3A") + 8;
posArtist = statusUpdateString.indexOf("artist%3A");
time = statusUpdateString.substring(posTime, posRate);
duration = statusUpdateString.substring(posDuration, posCanSeek);
title = statusUpdateString.substring(posTitle, posArtist - 1);
//Returns the length of the String, in characters. (Note that this doesn't include a trailing null character.)
artistlen = artist.length();
titlelen = title.length();
title.replace("%20"," ");
title.replace("%21","!");
title.replace("%23","#");
title.replace("%24","$");
title.replace("%2C",",");
title.replace("%2D","-");
title.replace("%2E",".");
artist = statusUpdateString.substring(posArtist + 9);
artist.replace("%20"," ");
artist.replace("%21","!");
artist.replace("%C3%98","Ø");
artist.replace("%C3%99","Ù");
artist.replace("%C3%9A","Ú");
artist.replace("%C3%9B","Û");
artist.replace("%C3%9C","Ü");
artist.replace("%C3%9D","Ý");
artist.replace("%C3%9E","Þ");
artist.replace("%C3%9F","ß");
artist.replace("%C3%A0","À");
artist.replace("%C3%A1","Á");
artist.replace("%C3%A2","Â");
artist.replace("%C3%A3","Ã");
artist.replace("%C3%A4","Ä");
artist.replace("%C3%A5","Å");
artist.replace("%C3%A6","Æ");
artist.replace("%C3%A7","Ç");
artist.replace("%C3%A8","È");
artist.replace("%C3%A9","É");
artist.replace("%C3%AA","Ê");
artist.replace("%C3%AB","Ë");
artist.replace("%C3%AC","Ì");
artist.replace("%C3%AD","Í");
artist.replace("%C3%AE","Î");
artist.replace("%c3%bf","ÿ");
//Get a version of the String with any leading and trailing whitespace removed. As of 1.0, trim() modifies the string in place rather than returning a new one.
artist.trim();
title.trim(); 
lcd.clear();
if (artist.length() >= 16)
 {
   artist = artist.substring(0,10) + "...";
 }
 if (title.length() >=16)
 {
   title = title.substring(0,10) + "...";
 }
 String displayInfo[] = { artist, title };
 {
   afficheTexte(displayInfo);
 }      
}

note : I had to retrieve a lot of the replace lines as the it went beyond the 9000 caracters limit of the post. Consider that there are around 150 lines of replace

Now what kind of help do I need?

well any :slight_smile:

What i really would like to achieve is the following:

Any time the arduino starts, it has to synch with the LMS and retrieve the infos (the display offers two lines 16*2) I use the top line for artist name and the bottom one for the song right now. But ultimately, I would love to display additionnal infos like sample rate that comes from the LMS cli.

I also get issues with UTF-8 caracters. That 's why I added the replace list in the end...It eats some memory and i saw that somehow it get the arduino to crash...so any code optimization is also welcome :slight_smile:

I know it seems a lot but really I don't think I can go further and it is frustrating...

thanks for your help

pattdepanick

daphile player v1.0.jpg

I never used Strings, don't know whether they can leak memory, what might be (one of) the reasons for crashes. Does your code work without crashes when you omit the UTF conversion part?

As a general note, the string length changes after every applied replace(), so it's quite useless to determine the string length before the conversion. No bug so far, these values seem not be used later.

When all multi-byte UTF-8 sequences start with %CF, the conversion can be optimized. Then you'll need only a table to map the second value into the display code. I'd go the traditional (PCHAR) way, and copy the strings char by char. On every occurence of '%' decode the next two chars (always case-insensitive!), and copy the result for values < 0x80. In case of 0xCF decode the following sequence, and look up the replacement in a CF conversion table. You can add more tables for other first-values.

When your display covers the low 256 entries of the Unicode BMP, you can replace the tables by straight calculation of the resulting value (see UTF-8 encoding formulas). Otherwise you'll have to find the replacement display code for all Unicode values >= 0x80.

Thanks for thé inputs...as isaid earlier,i 'am not a developper,.. How do I create the table? And howcanIimprove / rewrite thé code to allow multiple display of strings on a single loop?
Thanks.

Hi,
You can attach the sketch ino file like you did the jpg, if it is too big for the code tags.

Tom.... :slight_smile:

Thé main part is there anyhow as i only removed some artistes.replace and title.replace lines

Hi @pattdepanick I used your code as base for my work. You can find it here: GitHub - ikxx/Liquid-dap-duino: Daphile (Squeezebox) control with arduino and LCD . Hope it is OK (you are tributed in readme file)

Hi

Well have I seen this on the day you posted it; I would I say it is a great birthday gift you re making me ;-).I was born on the 12th of december in 1970 I 'd never thought this code would be useful to anyone...

I am currently working on a new solution. I removed the arduino from my current setup as I am now using a USB-LCD from adafruit. It is powered by USB and as a usb to serial board attatched to the LCD. It therefore appears as a TTYACM0 on my daphile.

I am rebuilding some simple shell scripts to perform the display of information leveraging some python scripts coming from here : Server Class (pylms.player.Server) — PyLMS 1.00 documentation .

I'll find another usage for the arduino board I am sure!

Anyway. No worries on my side for this! Quite the contrary in fact :slight_smile:

By the way, in my latest version of the code, I change the order of initialization in the setup part. I was doing lcd then serial...but on my setup; this was creating some garbage characters on the lcd from time to time due to the serial port I suppose...

I changed it to:
void setup()
{
delay(1000);
char serialAnswer[1024];
String initLines[] = { "V1", "STREAMER" }, lmsAnswer;
String DediLines[] = { " Initialisation" , "en cours" };
String PlayLines[] = { " Let's listen to" , " some music now!"};
playerId.reserve(17);
Serial.begin(9600);
while (Serial.available())
{
Serial.print("player id 0 ?\n");
}
while (!Serial.find("player id 0 ?\r\nplayer id 0 "))
{
delay(500);
}
Serial.readBytes(serialAnswer,27);
lmsAnswer = serialAnswer;
playerId = lmsAnswer.substring(0,27);
playerId.replace("%3A",":");
statusRequest = playerId + " status - 1 tags:a subscribe:0";
Serial.print(statusRequest + "\n");
statusRequest.replace(":","%3A");
lcd.begin(16, 2);
lcd.clear();
lcd.print(" LMS OK... ");
delay(900);
afficheTexte(initLines);
delay(4000);
afficheTexte(DediLines);
delay(2000);
afficheTexte(PlayLines);
delay(3000);
afficheTexte(initLines);
delay(4000);
}

then the problem was gone.