Unverified Commit c158b4ae authored by Wolle's avatar Wolle Committed by GitHub

first release

parent c3f8c981
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html
This diff is collapsed.
/*
* websrv.h
*
* Created on: 09.07.2017
* updated on: 11.04.2022
* Author: Wolle
*/
#ifndef WEBSRV_H_
#define WEBSRV_H_
#include "Arduino.h"
#include "WiFi.h"
#include "SD.h"
#include "FS.h"
#include "mbedtls/sha1.h"
#include "base64.h"
extern __attribute__((weak)) void WEBSRV_onInfo(const char*);
extern __attribute__((weak)) void WEBSRV_onCommand(const String cmd, const String param, const String arg);
extern __attribute__((weak)) void WEBSRV_onRequest(const String, uint32_t contentLength);
class WebSrv
{
protected:
WiFiClient cmdclient; // An instance of the client for commands
WiFiClient webSocketClient ;
WiFiServer cmdserver;
WiFiServer webSocketServer;
private:
bool http_reponse_flag = false ; // Response required
bool ws_conn_request_flag = false; // websocket connection attempt
bool hasclient_WS = false;
String http_rqfile ; // Requested file
String http_cmd ; // Content of command
String http_param; // Content of parameter
String http_arg; // Content of argument
String _Name;
String _Version;
String contenttype;
char buff[256];
uint8_t method;
String WS_sec_Key;
String WS_resp_Key;
String WS_sec_conKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
protected:
String calculateWebSocketResponseKey(String sec_WS_key);
void printWebSocketHeader(String wsRespKey);
String getContentType(String filename);
boolean handlehttp();
boolean handleWS();
void parseWsMessage(uint32_t len);
uint8_t inbyte();
String URLdecode(String str);
String UTF8toASCII(String str);
String responseCodeToString(int code);
public:
enum { HTTP_NONE = 0, HTTP_GET = 1, HTTP_PUT = 2 };
enum { Continuation_Frame = 0x00, Text_Frame = 0x01, Binary_Frame = 0x02, Connection_Close_Frame = 0x08,
Ping_Frame = 0x09, Pong_Frame = 0x0A };
WebSrv(String Name="WebSrv library", String Version="1.0");
void begin(uint16_t http_port = 80, uint16_t websocket_port = 81);
void stop();
boolean loop();
void show(const char* pagename, int16_t len=-1);
void show_not_found();
boolean streamfile(fs::FS &fs,const char* path);
boolean send(String msg, uint8_t opcode = Text_Frame);
boolean send(const char* msg, uint8_t opcode = Text_Frame);
void sendPing();
void sendPong();
boolean uploadfile(fs::FS &fs,const char* path, uint32_t contentLength);
boolean uploadB64image(fs::FS &fs,const char* path, uint32_t contentLength);
void reply(const String &response, boolean header=true);
const char* ASCIItoUTF8(const char* str);
private:
const int B64index[123] ={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63,
0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
};
#endif /* WEBSRV_H_ */
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x4000
phy_init, data, phy, 0xd000, 0x1000
factory, app, factory, 0x10000, 3M,
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/en/latest/platforms/espressif32.html
[env:esp32dev]
platform = https://github.com/platformio/platform-espressif32.git#v5.2.0 ; Arduino V2.0.5
board = esp32dev ;chipmodel ESP32, 4M FLASH, USBtoTTL
board_build.f_cpu = 240000000L
board_build.flash_size=4MB
board_build.flash_freq=80M
board_build.spiram_mode=2
framework = arduino
monitor_speed = 115200
monitor_filters = esp32_exception_decoder
board_build.partitions = default.csv
upload_speed = 460800 ; 921600, 512000, 460800, 256000, 115200
lib_deps =
https://github.com/schreibfaul1/ESP32-audioI2S.git#2.0.6
https://github.com/yellobyte/SoapESP32.git#1.1.4
https://github.com/arduino-libraries/Arduino_JSON.git
platform_packages =
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.5
board_upload.maximum_size = 3145728
board_upload.flash_size = 4MB
board_build.flash_mode = qio
board_build.bootloader = dio
;build_flags = -DCORE_DEBUG_LEVEL=0 ; None
;build_flags = -DCORE_DEBUG_LEVEL=1 ; Error
;build_flags = -DCORE_DEBUG_LEVEL=2 ; Warn
;build_flags = -DCORE_DEBUG_LEVEL=3 ; Info
;build_flags = -DCORE_DEBUG_LEVEL=4 ; Debug
;build_flags = -DCORE_DEBUG_LEVEL=5 ; Verbose
build_flags =
; -Wall
; -Wextra
-Wdouble-promotion ; double to float warnings
-Wimplicit-fallthrough ; switch case without break
-DCORE_DEBUG_LEVEL=3
-DCONFIG_ARDUHAL_LOG_COLORS
-DBOARD_HAS_PSRAM
-DARDUINO_RUNNING_CORE=3 ; Arduino Runs On Core (setup, loop)
-DARDUINO_EVENT_RUNNING_CORE=1 ; Events Run On Core
; -DAUDIO_LOG
build_unflags =
; -DARDUINO_USB_CDC_ON_BOOT=0 ; traditional log
; -DBOARD_HAS_PSRAM
/*
* index.h
*
* Created on: 13.12.2022
* Updated on:
* Author: Wolle
*
* ESP32 - DLNA
*
*/
#ifndef INDEX_H_
#define INDEX_H_
#include "Arduino.h"
// file in raw data format for PROGMEM
const char index_html[] PROGMEM = R"=====(
<!DOCTYPE HTML>
<html>
<head>
<title>ESP32 - DLNA</title>
<style type="text/css"> /* optimized with csstidy */
html { /* This is the groundplane */
font-family : serif;
height : 100%;
font-size: 16px;
color : DarkSlateGray;
background-color : navy;
margin : 0;
padding : 0;
}
#content {
min-height : 540px;
min-width : 725px;
overflow : hidden;
background-color : lightskyblue;
margin : 0;
padding : 5px;
}
#tab-content1 {
margin : 20px;
}
.boxstyle {
height : 36px;
padding-top : 0;
padding-left : 15px;
padding-bottom : 0;
background-color: white;
font-size : 16px;
line-height : normal;
border-color: black;
border-style: solid;
border-width: thin;
border-radius : 5px;
}
#BODY { display:block; }
</style>
</head>
<script>
// global variables and functions
// ---- websocket section------------------------
var socket = undefined
var host = location.hostname
var tm
function ping() {
if (socket.readyState == 1) { // reayState 'open'
socket.send("ping")
console.log("send ping")
tm = setTimeout(function () {
console.log('The connection to the ESP32 is interrupted! Please reload the page!')
}, 10000)
}
}
function connect() {
socket = new WebSocket('ws://'+window.location.hostname+':81/');
socket.onopen = function () {
console.log("Websocket connected")
socket.send('DLNA_getServer')
setInterval(ping, 20000)
};
socket.onclose = function (e) {
console.log(e)
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e)
socket = null
setTimeout(function () {
connect()
}, 1000)
}
socket.onerror = function (err) {
console.log(err)
}
socket.onmessage = function(event) {
var socketMsg = event.data
var n = socketMsg.indexOf('=')
var msg = ''
var val = ''
if (n >= 0) {
var msg = socketMsg.substring(0, n)
var val = socketMsg.substring(n + 1)
// console.log("para ",msg, " val ",val)
}
else {
msg = socketMsg
}
switch(msg) {
case "pong": clearTimeout(tm)
break
case "DLNA_Names": showServer(val)
break
case "Level0": show_DLNA_Content(val, 0)
break
case "Level1": show_DLNA_Content(val, 1)
break
case "Level2": show_DLNA_Content(val, 2)
break
case "Level3": show_DLNA_Content(val, 3)
break
default: console.log('unknown message', msg, val)
}
}
}
// ---- end websocket section------------------------
document.addEventListener('readystatechange', event => {
if (event.target.readyState === 'interactive') { // same as: document.addEventListener('DOMContentLoaded'...
// same as jQuery.ready
console.log('All HTML DOM elements are accessible')
// document.getElementById('dialog').style.display = 'none' // hide the div (its only a template)
}
if (event.target.readyState === 'complete') {
console.log('Now external resources are loaded too, like css,src etc... ')
connect(); // establish websocket connection
}
})
function showServer(val){
console.log(val)
var select = document.getElementById('server')
select.options.length = 0;
var server = val.split(",")
for (i = -1; i < (server.length); i++) {
opt = document.createElement('OPTION')
if(i == -1){
opt.value = ""
opt.text = "Select a DLNA Server here"
}
else{
console.log(server[i])
opt.value = server[i]
opt.text = server[i]
}
select.add(opt)
}
}
function show_DLNA_Content(val, level){
var select
if(level == 0) select = document.getElementById('level1')
if(level == 1) select = document.getElementById('level2')
if(level == 2) select = document.getElementById('level3')
if(level == 3) select = document.getElementById('level4')
content =JSON.parse(val)
//console.log(ct[1].name)
select.options.length = 0;
for (i = -1; i < (content.length); i++) {
opt = document.createElement('OPTION')
if(i == -1){
opt.value = ""
opt.text = "Select level " + level.toString()
}
else{
var n
var c
if(content[i].isDir == true){
n = content[i].name + ' <DIR>';
c = 'D=' + content[i].id // is directory
}
else{
n = content[i].name + ' ' + content[i].size;
c = 'F=' + content[i].id // is file
}
opt.value = c
opt.text = n
}
select.add(opt)
}
}
function selectserver (presctrl) { // preset, select a server
socket.send('DLNA_getContent0=' + presctrl.value)
select = document.getElementById('level1'); select.options.length = 0; // clear next level
select = document.getElementById('level2'); select.options.length = 0;
select = document.getElementById('level3'); select.options.length = 0;
select = document.getElementById('level4'); select.options.length = 0;
console.log('DLNA_getContent0=' + presctrl.value)
}
function select_l0 (presctrl) { // preset, select root
socket.send('DLNA_getContent1=' + presctrl.value)
select = document.getElementById('level2'); select.options.length = 0; // clear next level
select = document.getElementById('level3'); select.options.length = 0;
select = document.getElementById('level4'); select.options.length = 0;
console.log('DLNA_getContent1=' + presctrl.value)
}
function select_l1 (presctrl) { // preset, select level 1
socket.send('DLNA_getContent2=' + presctrl.value)
select = document.getElementById('level3'); select.options.length = 0;
select = document.getElementById('level4'); select.options.length = 0;
console.log('DLNA_getContent2=' + presctrl.value)
}
function select_l2 (presctrl) { // preset, select level 2
socket.send('DLNA_getContent3=' + presctrl.value)
select = document.getElementById('level4'); select.options.length = 0;
console.log('DLNA_getContent3=' + presctrl.value)
}
function select_l3 (presctrl) { // preset, select level 3
socket.send('DLNA_getContent4=' + presctrl.value)
console.log('DLNA_getContent4=' + presctrl.value)
}
</script>
<body id="BODY">
<!--==============================================================================================-->
<div id="content">
<div id="content1">
<div style="font-size: 50px; text-align: center; flex: 1;">
ESP32 - DLNA
</div>
<div style="display: flex;">
<div style="flex: 0 0 calc(100% - 0px);">
<select class="boxstyle" style="width: 100%;" onchange="selectserver(this)" id="server">
<option value="-1">Select a DLNA Server here</option>
</select>
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l0(this)" id="level1">
<option value="-1"> </option>
</select>
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l1(this)" id="level2">
<option value="-1"> </option>
</select>
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l2(this)" id="level3">
<option value="-1"> </option>
</select>
<select class="boxstyle" style="width: 100%; margin-top: 5px;" onchange="select_l3(this)" id="level4">
<option value="-1"> </option>
</select>
</div>
</div>
<hr>
</div>
</div>
<!--==============================================================================================-->
</body>
</html>
)=====";
#endif /* INDEX_H_ */
\ No newline at end of file
#include <Arduino.h>
#include <WiFi.h>
#include "websrv.h"
#include "index.h"
#include "Audio.h"
#include "SoapESP32.h"
#include "Arduino_JSON.h"
#include <vector>
using namespace std;
#define I2S_DOUT 25
#define I2S_BCLK 27
#define I2S_LRC 26
char SSID[] = "*****";
char PASS[] = "*****";
WebSrv webSrv;
WiFiClient client;
WiFiUDP udp;
SoapESP32 soap(&client, &udp);
Audio audio;
uint numServers = 0;
int currentServer = -1;
uint32_t media_downloadPort = 0;
String media_downloadIP = "";
vector<String> names{};
//----------------------------------------------------------------------------------------------------------------------
int DLNA_setCurrentServer(String serverName){
int serverNum = -1;
for(int i = 0; i < names.size(); i++){
if(names[i] == serverName) serverNum = i;
}
currentServer = serverNum;
return serverNum;
}
void DLNA_showServer(){ // Show connection details of all discovered, usable media servers
String msg = "DLNA_Names=";
soapServer_t srv;
names.clear();
for(int i = 0; i < numServers; i++){
soap.getServerInfo(i, &srv);
Serial.printf("Server[%d]: IP address: %s port: %d name: %s -> controlURL: %s\n",
i, srv.ip.toString().c_str(), srv.port, srv.friendlyName.c_str(), srv.controlURL.c_str());
msg += srv.friendlyName;
if(i < numServers - 1) msg += ',';
names.push_back(srv.friendlyName);
}
webSrv.send(msg);
}
void DLNA_browseServer(String objectId, uint8_t level){
JSONVar myObject;
soapObjectVect_t browseResult;
soapObject_t object;
// Here the user selects the DLNA server whose content he wants to see, level 0 is root
if(level == 0){
if(DLNA_setCurrentServer(objectId) < 0) {log_e("DLNA Server not found"); return;}
objectId = "0";
}
soap.browseServer(currentServer, objectId.c_str(), &browseResult);
if(browseResult.size() == 0){
log_i("no content!"); // then the directory is empty
return;
}
log_i("objectID: %s", objectId.c_str());
for (int i = 0; i < browseResult.size(); i++){
object = browseResult[i];
myObject[i]["name"]= object.name;
myObject[i]["isDir"] = object.isDirectory;
if(object.isDirectory){
myObject[i]["id"] = object.id;
}
else {
myObject[i]["id"] = object.uri;
media_downloadPort = object.downloadPort;
media_downloadIP = object.downloadIp.toString();
}
myObject[i]["size"] = (uint32_t)object.size;
myObject[i]["uri"] = object.id;
log_i("objectName %s", browseResult[i].name.c_str());
log_i("objectId %s", browseResult[i].artist.c_str());
}
String msg = "Level" + String(level,10) + "=" + JSON.stringify(myObject);
log_i("msg = %s", msg.c_str());
webSrv.send(msg);
browseResult.clear();
}
void DLNA_getFileItems(String uri){
soapObjectVect_t browseResult;
log_i("uri: %s", uri.c_str());
log_w("downloadIP: %s", media_downloadIP.c_str());
log_w("downloadport: %d", media_downloadPort);
String URL = "http://" + media_downloadIP + ":" + media_downloadPort + "/" + uri;
log_i("URL=%s", URL.c_str());
audio.connecttohost(URL.c_str());
}
void DLNA_showContent(String objectId, uint8_t level){
log_i("obkId=%s", objectId.c_str());
if(level == 0){
DLNA_browseServer(objectId, level);
}
if(objectId.startsWith("D=")) {
objectId = objectId.substring(2);
DLNA_browseServer(objectId, level);
}
if(objectId.startsWith("F=")) {
objectId = objectId.substring(2);
DLNA_getFileItems(objectId);
}
}
//----------------------------------------------------------------------------------------------------------------------
// S E T U P
//----------------------------------------------------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED) delay(1500);
log_i("connected, IP=%s", WiFi.localIP().toString().c_str());
webSrv.begin(80, 81); // HTTP port, WebSocket port
soap.seekServer();
numServers = soap.getServerCount();
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(21); // 0...21
}
//----------------------------------------------------------------------------------------------------------------------
// L O O P
//----------------------------------------------------------------------------------------------------------------------
void loop() {
if(webSrv.loop()) return; // if true: ignore all other for faster response to web
audio.loop();
}
//----------------------------------------------------------------------------------------------------------------------
// E V E N T S
//----------------------------------------------------------------------------------------------------------------------
void WEBSRV_onCommand(const String cmd, const String param, const String arg){ // called from html
log_d("WS_onCmd: cmd=\"%s\", params=\"%s\", arg=\"%s\"", cmd.c_str(),param.c_str(), arg.c_str());
if(cmd == "index.html"){ webSrv.show(index_html); return;}
if(cmd == "ping"){webSrv.send("pong"); return;}
if(cmd == "favicon.ico") return;
if(cmd == "DLNA_getServer") {DLNA_showServer(); return;}
if(cmd == "DLNA_getContent0"){DLNA_showContent(param, 0); return;}
if(cmd == "DLNA_getContent1"){DLNA_showContent(param, 1); return;} // search for level 1 content
if(cmd == "DLNA_getContent2"){DLNA_showContent(param, 2); return;} // search for level 2 content
if(cmd == "DLNA_getContent3"){DLNA_showContent(param, 3); return;} // search for level 3 content
if(cmd == "DLNA_getContent4"){DLNA_showContent(param, 4); return;} // search for level 4 content
log_e("unknown HTMLcommand %s, param=%s", cmd.c_str(), param.c_str());
}
void WEBSRV_onRequest(const String request, uint32_t contentLength){
log_d("WS_onReq: %s contentLength %d", request.c_str(), contentLength);
if(request.startsWith("------")) return; // uninteresting WebKitFormBoundaryString
if(request.indexOf("form-data") > 0) return; // uninteresting Info
log_e("unknown request: %s",request.c_str());
}
void WEBSRV_onInfo(const char* info){
log_v("HTML_info: %s", info); // infos for debug
}
void audio_info(const char *info){
Serial.print("info "); Serial.println(info);
}
void audio_id3data(const char *info){ //id3 metadata
Serial.print("id3data ");Serial.println(info);
}
void audio_eof_stream(const char* info){ // The webstream comes to an end
Serial.print("end of stream: ");Serial.println(info);
}
void audio_bitrate(const char *info){
Serial.print("bitrate ");Serial.println(info);
}
void audio_lasthost(const char *info){ //stream URL played
Serial.print("lasthost ");Serial.println(info);
}
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment