Skip to content

Commit d01e3b4

Browse files
committed
add DLNA service
1 parent f057260 commit d01e3b4

File tree

5 files changed

+274
-12
lines changed

5 files changed

+274
-12
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,5 +306,6 @@
306306
"cinttypes": "cpp",
307307
"typeinfo": "cpp"
308308
},
309-
"C_Cpp_Runner.useMsvc": false
309+
"C_Cpp_Runner.useMsvc": false,
310+
"C_Cpp_Runner.useAddressSanitizer": false
310311
}

miniwebradio.csv

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
# Name, Type, SubType, Offset, Size, Flags
22
phy_init, data, phy, 0x9000, 0x7000,
3-
factory, app, factory, 0x10000, 0x300000,
4-
nvs, data, nvs, 0x310000, 0x32000,
5-
spiffs, data, spiffs, 0x342000, 0xB0000,
6-
eeprom, data, 0x99, 0x3F2000, 0xD000,
3+
factory, app, factory, 0x10000, 0x3A0000,
4+
nvs, data, nvs, 0x3B0000, 0x40000,

src/common.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// created: 10.Feb.2022
2-
// updated: 14.Mar.2023
2+
// updated: 23.Mar.2023
33

44
#pragma once
5+
#pragma GCC optimize("Os") // optimize for code size
56

67
#define _SSID "mySSID" // Your WiFi credentials here
78
#define _PW "myWiFiPassword"
@@ -193,6 +194,11 @@ void connecttohost(const char* host);
193194
void connecttoFS(const char* filename, uint32_t resumeFilePos = 0);
194195
void stopSong();
195196
void IRAM_ATTR headphoneDetect();
197+
int DLNA_setCurrentServer(String serverName);
198+
void DLNA_showServer();
199+
void DLNA_browseServer(String objectId, uint8_t level);
200+
void DLNA_getFileItems(String uri);
201+
void DLNA_showContent(String objectId, uint8_t level);
196202

197203
//prototypes (audiotask.cpp)
198204
void audioInit();

src/index.h

Lines changed: 151 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* index.h
33
*
44
* Created on: 04.10.2018
5-
* Updated on: 05.05.2022
5+
* Updated on: 24.03.2023
66
* Author: Wolle
77
*
88
* successfully tested with Chrome and Firefox
@@ -378,6 +378,19 @@ function connect() {
378378
if(val == '1') document.getElementById('chk_timeSpeech').checked = true;
379379
break
380380

381+
case "DLNA_Names": showServer(val)
382+
break
383+
case "Level1": show_DLNA_Content(val, 1)
384+
break
385+
case "Level2": show_DLNA_Content(val, 2)
386+
break
387+
case "Level3": show_DLNA_Content(val, 3)
388+
break
389+
case "Level4": show_DLNA_Content(val, 4)
390+
break
391+
case "Level5": show_DLNA_Content(val, 5)
392+
break
393+
381394
default: console.log('unknown message', msg, val)
382395
}
383396
}
@@ -464,8 +477,14 @@ function showTab3 () {
464477
document.getElementById('btn3').src = 'SD/png/MP3_Yellow.png'
465478
document.getElementById('btn4').src = 'SD/png/Search_Green.png'
466479
document.getElementById('btn5').src = 'SD/png/About_Green.png'
480+
document.getElementById('level1').options.length = 0
481+
document.getElementById('level2').options.length = 0
482+
document.getElementById('level3').options.length = 0
483+
document.getElementById('level4').options.length = 0
484+
document.getElementById('level5').options.length = 0
467485
socket.send("change_state=6")
468486
socket.send("audiolist") // Now get the audio file list from SD
487+
socket.send('DLNA_getServer')
469488
}
470489

471490
function showTab4 () {
@@ -512,6 +531,103 @@ function uploadTextFile (fileName, content) {
512531
xhr.send(fd) // send
513532
}
514533

534+
// ----------------------------------- DLNA ------------------------------------
535+
function showServer(val){
536+
console.log(val)
537+
var select = document.getElementById('server')
538+
select.options.length = 0;
539+
var server = val.split(",")
540+
for (i = -1; i < (server.length); i++) {
541+
opt = document.createElement('OPTION')
542+
if(i == -1){
543+
opt.value = ""
544+
opt.text = "Select a DLNA Server here"
545+
}
546+
else{
547+
console.log(server[i])
548+
opt.value = server[i]
549+
opt.text = server[i]
550+
}
551+
select.add(opt)
552+
}
553+
}
554+
function show_DLNA_Content(val, level){
555+
var select
556+
if(level == 1) select = document.getElementById('level1')
557+
if(level == 2) select = document.getElementById('level2')
558+
if(level == 3) select = document.getElementById('level3')
559+
if(level == 4) select = document.getElementById('level4')
560+
if(level == 5) select = document.getElementById('level5')
561+
content =JSON.parse(val)
562+
//console.log(ct[1].name)
563+
select.options.length = 0;
564+
for (i = -1; i < (content.length); i++) {
565+
opt = document.createElement('OPTION')
566+
if(i == -1){
567+
opt.value = ""
568+
opt.text = "Select level " + level.toString()
569+
}
570+
else{
571+
var n
572+
var c
573+
if(content[i].isDir == true){
574+
n = content[i].name.concat('\xa0\xa0', '<DIR>'); // more than one space
575+
c = 'D=' + content[i].id // is directory
576+
}
577+
else{
578+
n = content[i].name + '\xa0\xa0' + content[i].size;
579+
c = 'F=' + content[i].id // is file
580+
}
581+
opt.value = c
582+
opt.text = n
583+
}
584+
select.add(opt)
585+
}
586+
}
587+
function selectserver (presctrl) { // preset, select a server, root, level0
588+
socket.send('DLNA_getContent0=' + presctrl.value)
589+
select = document.getElementById('level1'); select.options.length = 0; // clear next level
590+
select = document.getElementById('level2'); select.options.length = 0;
591+
select = document.getElementById('level3'); select.options.length = 0;
592+
select = document.getElementById('level4'); select.options.length = 0;
593+
select = document.getElementById('level5'); select.options.length = 0;
594+
console.log('DLNA_getContent0=' + presctrl.value)
595+
}
596+
function select_l1 (presctrl) { // preset, select root
597+
socket.send('DLNA_getContent1=' + presctrl.value)
598+
select = document.getElementById('level2'); select.options.length = 0; // clear next level
599+
select = document.getElementById('level3'); select.options.length = 0;
600+
select = document.getElementById('level4'); select.options.length = 0;
601+
select = document.getElementById('level5'); select.options.length = 0;
602+
console.log('DLNA_getContent1=' + presctrl.value)
603+
}
604+
function select_l2 (presctrl) { // preset, select level 1
605+
socket.send('DLNA_getContent2=' + presctrl.value)
606+
select = document.getElementById('level3'); select.options.length = 0;
607+
select = document.getElementById('level4'); select.options.length = 0;
608+
select = document.getElementById('level5'); select.options.length = 0;
609+
console.log('DLNA_getContent2=' + presctrl.value)
610+
}
611+
function select_l3 (presctrl) { // preset, select level 2
612+
socket.send('DLNA_getContent3=' + presctrl.value)
613+
select = document.getElementById('level4'); select.options.length = 0;
614+
select = document.getElementById('level5'); select.options.length = 0;
615+
console.log('DLNA_getContent3=' + presctrl.value)
616+
}
617+
function select_l4 (presctrl) { // preset, select level 3
618+
socket.send('DLNA_getContent4=' + presctrl.value)
619+
select = document.getElementById('level5'); select.options.length = 0;
620+
console.log('DLNA_getContent4=' + presctrl.value)
621+
}
622+
function select_l5 (presctrl) { // preset, select level 4
623+
socket.send('DLNA_getContent5=' + presctrl.value)
624+
console.log('DLNA_getContent5=' + presctrl.value)
625+
}
626+
627+
628+
629+
630+
515631
// ----------------------------------- TAB RADIO ------------------------------------
516632

517633
function showLabel (id, src) { // get the bitmap from SD, convert to URL first
@@ -1609,9 +1725,8 @@ function getnetworks () { // tab Config: load the connected WiFi network
16091725
</center>
16101726
</div>
16111727
<!--==============================================================================================-->
1612-
<div id="tab-content3">
1728+
<div id="tab-content3">
16131729
<center>
1614-
<br>
16151730
<label for="seltrack"><big>Audio files on SD card:</big></label>
16161731
<br>
16171732
<select class="boxstyle" style="width: calc(100% -280px)"; onchange="trackreq(this)" id="seltrack">
@@ -1620,9 +1735,40 @@ function getnetworks () { // tab Config: load the connected WiFi network
16201735
<br><br>
16211736
<button class="button" onclick="socket.send('stopfile')">STOP</button>
16221737
<button class="button" onclick="socket.send('resumefile')">RESUME</button>
1623-
<br><br>
1738+
<br>
16241739
<input type="text" class="boxstyle" style="width: calc(100% - 8px);" id="resultstr3" placeholder="Waiting for a command...."> <br>
1625-
<br><br>
1740+
<br>
1741+
<hr>
1742+
<br>
1743+
1744+
<div style="flex: 0 0 calc(100% - 0px);">
1745+
<select class="boxstyle" style="width: 100%;" onchange="selectserver(this)" id="server">
1746+
<option value="-1">Select a DLNA Server here</option>
1747+
</select>
1748+
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l1(this)" id="level1">
1749+
<option value="-1"> </option>
1750+
</select>
1751+
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l2(this)" id="level2">
1752+
<option value="-1"> </option>
1753+
</select>
1754+
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l3(this)" id="level3">
1755+
<option value="-1"> </option>
1756+
</select>
1757+
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l4(this)" id="level4">
1758+
<option value="-1"> </option>
1759+
</select>
1760+
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l5(this)" id="level5">
1761+
<option value="-1"> </option>
1762+
</select>
1763+
</div>
1764+
1765+
1766+
1767+
1768+
1769+
1770+
1771+
16261772
</center>
16271773
</div>
16281774
<!--==============================================================================================-->

src/main.cpp

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
MiniWebRadio -- Webradio receiver for ESP32
33
44
first release on 03/2017
5-
Version 2.5, Mar 14/2022
5+
Version 2.6, Mar 23/2022
66
77
2.8" color display (320x240px) with controller ILI9341 or HX8347D (SPI) or
88
3.5" color display (480x320px) wiht controller ILI9486 or ILI9488 (SPI)
@@ -84,6 +84,12 @@ String _stationName_air = "";
8484
String _homepage = "";
8585
String _filename = "";
8686

87+
uint numServers = 0;
88+
int currentServer = -1;
89+
uint32_t media_downloadPort = 0;
90+
String media_downloadIP = "";
91+
vector<String> names{};
92+
8793
char _hl_item[10][40]{ // Title in headline
8894
"** Internet Radio **", // "* интернет-радио *" "ραδιόφωνο Internet"
8995
"** Internet Radio **",
@@ -114,6 +120,9 @@ TP tp(TP_CS, TP_IRQ);
114120
File audioFile;
115121
File playlistFile;
116122
FtpServer ftpSrv;
123+
WiFiClient client;
124+
WiFiUDP udp;
125+
SoapESP32 soap(&client, &udp);
117126
#if DECODER == 2 // ac101
118127
AC101 dac;
119128
#endif
@@ -1176,6 +1185,8 @@ void setup(){
11761185
if(DECODER == 0) setTone(); // HW Decoder
11771186
else setI2STone(); // SW Decoder
11781187
showFooter();
1188+
soap.seekServer();
1189+
numServers = soap.getServerCount();
11791190
}
11801191
/***********************************************************************************************************************
11811192
* C O M M O N *
@@ -1715,6 +1726,98 @@ void changeState(int state){
17151726
_state = state;
17161727
}
17171728
/***********************************************************************************************************************
1729+
* D L N A *
1730+
***********************************************************************************************************************/
1731+
int DLNA_setCurrentServer(String serverName){
1732+
int serverNum = -1;
1733+
for(int i = 0; i < names.size(); i++){
1734+
if(names[i] == serverName) serverNum = i;
1735+
}
1736+
currentServer = serverNum;
1737+
return serverNum;
1738+
}
1739+
void DLNA_showServer(){ // Show connection details of all discovered, usable media servers
1740+
String msg = "DLNA_Names=";
1741+
soapServer_t srv;
1742+
names.clear();
1743+
for(int i = 0; i < numServers; i++){
1744+
soap.getServerInfo(i, &srv);
1745+
Serial.printf("Server[%d]: IP address: %s port: %d name: %s -> controlURL: %s\n",
1746+
i, srv.ip.toString().c_str(), srv.port, srv.friendlyName.c_str(), srv.controlURL.c_str());
1747+
msg += srv.friendlyName;
1748+
if(i < numServers - 1) msg += ',';
1749+
names.push_back(srv.friendlyName);
1750+
}
1751+
log_i("msg %s", msg.c_str());
1752+
webSrv.send(msg);
1753+
}
1754+
void DLNA_browseServer(String objectId, uint8_t level){
1755+
JSONVar myObject;
1756+
soapObjectVect_t browseResult;
1757+
soapObject_t object;
1758+
1759+
// Here the user selects the DLNA server whose content he wants to see, level 0 is root
1760+
if(level == 0){
1761+
if(DLNA_setCurrentServer(objectId) < 0) {log_e("DLNA Server not found"); return;}
1762+
objectId = "0";
1763+
}
1764+
1765+
soap.browseServer(currentServer, objectId.c_str(), &browseResult);
1766+
if(browseResult.size() == 0){
1767+
log_i("no content!"); // then the directory is empty
1768+
return;
1769+
}
1770+
log_v("objectID: %s", objectId.c_str());
1771+
for (int i = 0; i < browseResult.size(); i++){
1772+
object = browseResult[i];
1773+
myObject[i]["name"]= object.name;
1774+
myObject[i]["isDir"] = object.isDirectory;
1775+
if(object.isDirectory){
1776+
myObject[i]["id"] = object.id;
1777+
}
1778+
else {
1779+
myObject[i]["id"] = object.uri;
1780+
media_downloadPort = object.downloadPort;
1781+
media_downloadIP = object.downloadIp.toString();
1782+
}
1783+
myObject[i]["size"] = (uint32_t)object.size;
1784+
myObject[i]["uri"] = object.id;
1785+
log_v("objectName %s", browseResult[i].name.c_str());
1786+
log_v("objectId %s", browseResult[i].artist.c_str());
1787+
}
1788+
level++;
1789+
String msg = "Level" + String(level,10) + "=" + JSON.stringify(myObject);
1790+
1791+
log_v("msg = %s", msg.c_str());
1792+
webSrv.send(msg);
1793+
browseResult.clear();
1794+
}
1795+
1796+
void DLNA_getFileItems(String uri){
1797+
soapObjectVect_t browseResult;
1798+
1799+
log_v("uri: %s", uri.c_str());
1800+
log_v("downloadIP: %s", media_downloadIP.c_str());
1801+
log_v("downloadport: %d", media_downloadPort);
1802+
String URL = "http://" + media_downloadIP + ":" + media_downloadPort + "/" + uri;
1803+
log_i("URL=%s", URL.c_str());
1804+
audioConnecttohost(URL.c_str());
1805+
}
1806+
void DLNA_showContent(String objectId, uint8_t level){
1807+
log_v("obkId=%s", objectId.c_str());
1808+
if(level == 0){
1809+
DLNA_browseServer(objectId, level);
1810+
}
1811+
if(objectId.startsWith("D=")) {
1812+
objectId = objectId.substring(2);
1813+
DLNA_browseServer(objectId, level);
1814+
}
1815+
if(objectId.startsWith("F=")) {
1816+
objectId = objectId.substring(2);
1817+
DLNA_getFileItems(objectId);
1818+
}
1819+
}
1820+
/***********************************************************************************************************************
17181821
* L O O P *
17191822
***********************************************************************************************************************/
17201823
void loop() {
@@ -2397,6 +2500,14 @@ void WEBSRV_onCommand(const String cmd, const String param, const String arg){
23972500
if( param == "false") _f_timeAnnouncement = false;
23982501
pref.putBool("timeAnnouncing", _f_timeAnnouncement); return;}
23992502

2503+
if(cmd == "DLNA_getServer") {DLNA_showServer(); return;}
2504+
if(cmd == "DLNA_getContent0") {DLNA_showContent(param, 0); return;}
2505+
if(cmd == "DLNA_getContent1") {DLNA_showContent(param, 1); return;} // search for level 1 content
2506+
if(cmd == "DLNA_getContent2") {DLNA_showContent(param, 2); return;} // search for level 2 content
2507+
if(cmd == "DLNA_getContent3") {DLNA_showContent(param, 3); return;} // search for level 3 content
2508+
if(cmd == "DLNA_getContent4") {DLNA_showContent(param, 4); return;} // search for level 4 content
2509+
if(cmd == "DLNA_getContent5") {DLNA_showContent(param, 5); return;} // search for level 5 content
2510+
24002511
if(cmd == "test"){ sprintf(_chbuf, "free heap: %u, Inbuff filled: %u, Inbuff free: %u",
24012512
ESP.getFreeHeap(), audioInbuffFilled(), audioInbuffFree());
24022513
webSrv.reply(_chbuf);

0 commit comments

Comments
 (0)