mirror of
https://github.com/luc-github/ESP3D.git
synced 2025-10-31 11:56:48 -07:00
Idf 5.1.4/Arduino 3.0.4 porting for esp32 (#1046)
* Update WebSocket library * Update SSDP library * Update TFT_eSPI library * Update EspLuaEngine library * Update SDFat library * Change to pioarduino * Make ESP3DMessageFIFO and ESP3DMessage more thread safe * Fix sanity checks for BT * Add some C6 support * Refactor ethernet code * Split Ethernet Sta / WiFi sta ESP Commands and settings * Simplify wait and wdtFeed code * Set C3 with 4MB by default in platformio.ini * Apply Disable brown out only on ESP32 to avoid crash e.g:ESP32S3 * Add missing entries in platformio.ini
This commit is contained in:
11
libraries/ESP32SSDP-1.2.1/.github/FUNDING.yml
vendored
11
libraries/ESP32SSDP-1.2.1/.github/FUNDING.yml
vendored
@@ -1,11 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
#github: luc-github
|
||||
#open_collective: # Replace with a single Open Collective username
|
||||
#ko_fi: # Replace with a single Ko-fi username
|
||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: ESP3D
|
||||
#issuehunt: # Replace with a single IssueHunt username
|
||||
#otechie: # Replace with a single Otechie username
|
||||
#custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y8FFE7NA4LJWQ
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]<Enter comprehensive title>"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Firmware:**
|
||||
- ESP core version: [2.5.2]
|
||||
- Library Version:[e.g 2.1b4]
|
||||
- Wifi mode:[e.g. STA]
|
||||
|
||||
**Board used (please complete the following information):**
|
||||
- MCU: [e.g. ESP32]
|
||||
- Name:[e.g. NodeMCU2s]
|
||||
- Flash size: [e.g. 4M: 2M/2M]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
Wiring, decoded stack, etc...
|
||||
@@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Question template
|
||||
about: Ask your question, if not bug neither feature request.
|
||||
title: "[Question]<Enter comprehensive title>"
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
What is your question ?
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
# Enable the globstar shell option
|
||||
shopt -s globstar
|
||||
|
||||
#arduino or PlatformIO
|
||||
ide=$1
|
||||
|
||||
# Make sure we are inside the github workspace
|
||||
cd $GITHUB_WORKSPACE
|
||||
#export paths
|
||||
export PATH="$HOME/arduino_ide:$PATH"
|
||||
export ARDUINO_IDE_PATH="$HOME/arduino_ide"
|
||||
if [[ "$ide" == "arduino" ]];
|
||||
then
|
||||
echo "Arduino"
|
||||
fqbn=esp32:esp32:esp32:PartitionScheme=min_spiffs,FlashFreq=80,PSRAM=disabled,CPUFreq=240,FlashMode=qio,FlashSize=4M,DebugLevel=none
|
||||
arduino-builder -hardware "$ARDUINO_IDE_PATH/hardware" -tools "$ARDUINO_IDE_PATH/tools-builder" -tools "$ARDUINO_IDE_PATH/tools" -libraries "$ARDUINO_IDE_PATH/libraries" -fqbn=$fqbn -compile -logger=human -core-api-version=10810 ./examples/SSDP/SSDP.ino
|
||||
else
|
||||
echo "PlatformIO"
|
||||
cp -r ./src/ESP32SSDP.cpp ./examples/SSDP/
|
||||
cp -r ./src/ESP32SSDP.h ./examples/SSDP/
|
||||
cp ./test/platformio.ini ./examples/
|
||||
cd examples
|
||||
platformio run -e esp32dev
|
||||
fi
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
# Enable the globstar shell option
|
||||
shopt -s globstar
|
||||
# Make sure we are inside the github workspace
|
||||
cd $GITHUB_WORKSPACE
|
||||
echo $STEPS_CONTEXT
|
||||
step=$1
|
||||
status=$2
|
||||
|
||||
if [[ "$status" == "success" ]];
|
||||
then
|
||||
echo "Success build"
|
||||
exit 0
|
||||
else
|
||||
echo "Build failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
# Enable the globstar shell option
|
||||
shopt -s globstar
|
||||
|
||||
wget http://downloads.arduino.cc/arduino-1.8.13-linux64.tar.xz
|
||||
|
||||
tar xf arduino-1.8.13-linux64.tar.xz
|
||||
|
||||
mv arduino-1.8.13 $HOME/arduino_ide
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
# Enable the globstar shell option
|
||||
shopt -s globstar
|
||||
|
||||
#install pyserial
|
||||
echo "Installing Python Serial ..."
|
||||
pip install pyserial
|
||||
|
||||
echo "Clone esp32 core"
|
||||
cd $HOME/arduino_ide/hardware
|
||||
mkdir esp32
|
||||
cd esp32
|
||||
git clone https://github.com/espressif/arduino-esp32.git esp32
|
||||
cd esp32
|
||||
git submodule update --init
|
||||
cd tools
|
||||
python get.py
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
# Enable the globstar shell option
|
||||
shopt -s globstar
|
||||
|
||||
pip install -U platformio
|
||||
platformio update
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
# Enable the globstar shell option
|
||||
shopt -s globstar
|
||||
ls $HOME
|
||||
# Make sure we are inside the github workspace
|
||||
cd $GITHUB_WORKSPACE
|
||||
mkdir -p $HOME/arduino_ide/libraries/SSDP
|
||||
cp -R ./src $HOME/arduino_ide/libraries/SSDP
|
||||
cp ./library.properties $HOME/arduino_ide/libraries/SSDP
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
name: build-ci-dev
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- Dev
|
||||
push:
|
||||
branches:
|
||||
- Dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.x"
|
||||
architecture: "x64"
|
||||
- name: Install Arduino
|
||||
run: bash ./.github/ci/install-arduino.sh
|
||||
- name: Install platformIO
|
||||
run: bash ./.github/ci/install-platformio.sh
|
||||
- name: Install ESP32
|
||||
run: bash ./.github/ci/install-esp32.sh
|
||||
- name: Setup libraries
|
||||
run: bash ./.github/ci/prepare-libs.sh
|
||||
- name: Build ESP32 arduino
|
||||
id: esp32_1
|
||||
run: bash ./.github/ci/build-ssdp.sh arduino
|
||||
continue-on-error: true
|
||||
- name: Build platformIO
|
||||
id: pio_1
|
||||
run: bash ./.github/ci/build-ssdp.sh pio
|
||||
continue-on-error: true
|
||||
- name: Final check
|
||||
env:
|
||||
STEPS_CONTEXT: ${{ toJson(steps) }}
|
||||
if: steps.pio_1.outcome == 'failure' || steps.esp32_1.outcome == 'failure'
|
||||
run: bash ./.github/ci/final-check.sh "GLOBAL" "failure"
|
||||
- name: Final confirmation
|
||||
env:
|
||||
STEPS_CONTEXT: ${{ toJson(steps) }}
|
||||
if: steps.pio_1.outcome == 'success' && steps.esp32_1.outcome == 'success'
|
||||
run: bash ./.github/ci/final-check.sh "GLOBAL" "success"
|
||||
@@ -1,46 +0,0 @@
|
||||
name: build-ci
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.x"
|
||||
architecture: "x64"
|
||||
- name: Install Arduino
|
||||
run: bash ./.github/ci/install-arduino.sh
|
||||
- name: Install platformIO
|
||||
run: bash ./.github/ci/install-platformio.sh
|
||||
- name: Install ESP32
|
||||
run: bash ./.github/ci/install-esp32.sh
|
||||
- name: Setup libraries
|
||||
run: bash ./.github/ci/prepare-libs.sh
|
||||
- name: Build ESP32 arduino
|
||||
id: esp32_1
|
||||
run: bash ./.github/ci/build-ssdp.sh arduino
|
||||
continue-on-error: true
|
||||
- name: Build platformIO
|
||||
id: pio_1
|
||||
run: bash ./.github/ci/build-ssdp.sh pio
|
||||
continue-on-error: true
|
||||
- name: Final check
|
||||
env:
|
||||
STEPS_CONTEXT: ${{ toJson(steps) }}
|
||||
if: steps.pio_1.outcome == 'failure' || steps.esp32_1.outcome == 'failure'
|
||||
run: bash ./.github/ci/final-check.sh "GLOBAL" "failure"
|
||||
- name: Final confirmation
|
||||
env:
|
||||
STEPS_CONTEXT: ${{ toJson(steps) }}
|
||||
if: steps.pio_1.outcome == 'success' && steps.esp32_1.outcome == 'success'
|
||||
run: bash ./.github/ci/final-check.sh "GLOBAL" "success"
|
||||
@@ -1,13 +0,0 @@
|
||||
name: Greetings
|
||||
|
||||
on: [issues]
|
||||
|
||||
jobs:
|
||||
greeting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: 'Thank your for submiting, please be sure you followed template or your issue may be dismissed.'
|
||||
pr-message: 'Thank you for your contribution, be patient, review can take a time.'
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Deploy Wiki
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
# Trigger only when wiki directory changes
|
||||
- 'wiki/**'
|
||||
branches:
|
||||
# And only on master branch
|
||||
- wiki
|
||||
|
||||
jobs:
|
||||
deploy-wiki:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Push Wiki Changes
|
||||
uses: Andrew-Chen-Wang/github-wiki-action@v2
|
||||
env:
|
||||
# Make sure you have that / at the end. We use rsync
|
||||
# WIKI_DIR's default is wiki/
|
||||
WIKI_DIR: wiki/
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_MAIL: ${{ secrets.MY_EMAIL }}
|
||||
GH_NAME: ${{ github.repository_owner }}
|
||||
EXCLUDED_FILES: "a/ b.md"
|
||||
@@ -1,105 +0,0 @@
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
#include <ESP32SSDP.h>
|
||||
|
||||
const char* ssid = "********";
|
||||
const char* password = "********";
|
||||
|
||||
WebServer HTTP(80);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Starting WiFi...");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if(WiFi.waitForConnectResult() == WL_CONNECTED) {
|
||||
|
||||
Serial.printf("Starting HTTP...\n");
|
||||
HTTP.on("/index.html", HTTP_GET, []() {
|
||||
HTTP.send(200, "text/plain", "Hello World!");
|
||||
});
|
||||
HTTP.on("/description.xml", HTTP_GET, []() {
|
||||
SSDP.schema(HTTP.client());
|
||||
});
|
||||
HTTP.begin();
|
||||
|
||||
//set schema xml url, nees to match http handler
|
||||
//"ssdp/schema.xml" if not set
|
||||
SSDP.setSchemaURL("description.xml");
|
||||
//set port
|
||||
//80 if not set
|
||||
SSDP.setHTTPPort(80);
|
||||
//set device name
|
||||
//Null string if not set
|
||||
SSDP.setName("Philips hue clone");
|
||||
//set Serial Number
|
||||
//Null string if not set
|
||||
SSDP.setSerialNumber("001788102201");
|
||||
//set device url
|
||||
//Null string if not set
|
||||
SSDP.setURL("index.html");
|
||||
//set model name
|
||||
//Null string if not set
|
||||
SSDP.setModelName("Philips hue bridge 2012");
|
||||
//set model description
|
||||
//Null string if not set
|
||||
SSDP.setModelDescription("This device can be controled by WiFi");
|
||||
//set model number
|
||||
//Null string if not set
|
||||
SSDP.setModelNumber("929000226503");
|
||||
//set model url
|
||||
//Null string if not set
|
||||
SSDP.setModelURL("http://www.meethue.com");
|
||||
//set model manufacturer name
|
||||
//Null string if not set
|
||||
SSDP.setManufacturer("Royal Philips Electronics");
|
||||
//set model manufacturer url
|
||||
//Null string if not set
|
||||
SSDP.setManufacturerURL("http://www.philips.com");
|
||||
//set device type
|
||||
//"urn:schemas-upnp-org:device:Basic:1" if not set
|
||||
SSDP.setDeviceType("rootdevice"); //to appear as root device, other examples: MediaRenderer, MediaServer ...
|
||||
//set server name
|
||||
//"Arduino/1.0" if not set
|
||||
SSDP.setServerName("SSDPServer/1.0");
|
||||
//set UUID, you can use https://www.uuidgenerator.net/
|
||||
//use 38323636-4558-4dda-9188-cda0e6 + 4 last bytes of mac address if not set
|
||||
//use SSDP.setUUID("daa26fa3-d2d4-4072-bc7a-a1b88ab4234a", false); for full UUID
|
||||
SSDP.setUUID("daa26fa3-d2d4-4072-bc7a");
|
||||
//Set icons list, NB: optional, this is ignored under windows
|
||||
SSDP.setIcons( "<icon>"
|
||||
"<mimetype>image/png</mimetype>"
|
||||
"<height>48</height>"
|
||||
"<width>48</width>"
|
||||
"<depth>24</depth>"
|
||||
"<url>icon48.png</url>"
|
||||
"</icon>");
|
||||
//Set service list, NB: optional for simple device
|
||||
SSDP.setServices( "<service>"
|
||||
"<serviceType>urn:schemas-upnp-org:service:SwitchPower:1</serviceType>"
|
||||
"<serviceId>urn:upnp-org:serviceId:SwitchPower:1</serviceId>"
|
||||
"<SCPDURL>/SwitchPower1.xml</SCPDURL>"
|
||||
"<controlURL>/SwitchPower/Control</controlURL>"
|
||||
"<eventSubURL>/SwitchPower/Event</eventSubURL>"
|
||||
"</service>");
|
||||
|
||||
Serial.printf("Starting SSDP...\n");
|
||||
SSDP.begin();
|
||||
|
||||
Serial.printf("Ready!\n");
|
||||
} else {
|
||||
Serial.printf("WiFi Failed\n");
|
||||
while(1) {
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
HTTP.handleClient();
|
||||
delay(1);
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "ESP32SSDP.h"
|
||||
|
||||
const char* ssid = "********";
|
||||
const char* password = "********";
|
||||
|
||||
AsyncWebServer webserver(80);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Starting WiFi...");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if(WiFi.waitForConnectResult() == WL_CONNECTED) {
|
||||
|
||||
Serial.printf("Starting HTTP...\n");
|
||||
webserver.on("/index.html", HTTP_GET, [&](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/plain", "Hello World!");
|
||||
});
|
||||
webserver.on("/description.xml", HTTP_GET, [&](AsyncWebServerRequest *request) {
|
||||
request->send(200, "text/xml", SSDP.schema(false));
|
||||
});
|
||||
webserver.begin();
|
||||
|
||||
//set schema xml url, nees to match http handler
|
||||
//"ssdp/schema.xml" if not set
|
||||
SSDP.setSchemaURL("description.xml");
|
||||
//set port
|
||||
//80 if not set
|
||||
SSDP.setHTTPPort(80);
|
||||
//set device name
|
||||
//Null string if not set
|
||||
SSDP.setName("Philips hue clone");
|
||||
//set Serial Number
|
||||
//Null string if not set
|
||||
SSDP.setSerialNumber("001788102201");
|
||||
//set device url
|
||||
//Null string if not set
|
||||
SSDP.setURL("index.html");
|
||||
//set model name
|
||||
//Null string if not set
|
||||
SSDP.setModelName("Philips hue bridge 2012");
|
||||
//set model description
|
||||
//Null string if not set
|
||||
SSDP.setModelDescription("This device can be controled by WiFi");
|
||||
//set model number
|
||||
//Null string if not set
|
||||
SSDP.setModelNumber("929000226503");
|
||||
//set model url
|
||||
//Null string if not set
|
||||
SSDP.setModelURL("http://www.meethue.com");
|
||||
//set model manufacturer name
|
||||
//Null string if not set
|
||||
SSDP.setManufacturer("Royal Philips Electronics");
|
||||
//set model manufacturer url
|
||||
//Null string if not set
|
||||
SSDP.setManufacturerURL("http://www.philips.com");
|
||||
//set device type
|
||||
//"urn:schemas-upnp-org:device:Basic:1" if not set
|
||||
SSDP.setDeviceType("rootdevice"); //to appear as root device, other examples: MediaRenderer, MediaServer ...
|
||||
//set server name
|
||||
//"Arduino/1.0" if not set
|
||||
SSDP.setServerName("SSDPServer/1.0");
|
||||
//set UUID, you can use https://www.uuidgenerator.net/
|
||||
//use 38323636-4558-4dda-9188-cda0e6 + 4 last bytes of mac address if not set
|
||||
//use SSDP.setUUID("daa26fa3-d2d4-4072-bc7a-a1b88ab4234a", false); for full UUID
|
||||
SSDP.setUUID("daa26fa3-d2d4-4072-bc7a");
|
||||
//Set icons list, NB: optional, this is ignored under windows
|
||||
SSDP.setIcons( "<icon>"
|
||||
"<mimetype>image/png</mimetype>"
|
||||
"<height>48</height>"
|
||||
"<width>48</width>"
|
||||
"<depth>24</depth>"
|
||||
"<url>icon48.png</url>"
|
||||
"</icon>");
|
||||
//Set service list, NB: optional for simple device
|
||||
SSDP.setServices( "<service>"
|
||||
"<serviceType>urn:schemas-upnp-org:service:SwitchPower:1</serviceType>"
|
||||
"<serviceId>urn:upnp-org:serviceId:SwitchPower:1</serviceId>"
|
||||
"<SCPDURL>/SwitchPower1.xml</SCPDURL>"
|
||||
"<controlURL>/SwitchPower/Control</controlURL>"
|
||||
"<eventSubURL>/SwitchPower/Event</eventSubURL>"
|
||||
"</service>");
|
||||
|
||||
Serial.printf("Starting SSDP...\n");
|
||||
SSDP.begin();
|
||||
|
||||
Serial.printf("Ready!\n");
|
||||
} else {
|
||||
Serial.printf("WiFi Failed\n");
|
||||
while(1) {
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
delay(1);
|
||||
}
|
||||
@@ -1,734 +0,0 @@
|
||||
/*
|
||||
ESP32 Simple Service Discovery
|
||||
Copyright (c) 2015 Hristo Gochkov
|
||||
|
||||
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
|
||||
Can be found at: https://github.com/nomadnt/uSSDP
|
||||
|
||||
License (MIT license):
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <functional>
|
||||
#include "ESP32SSDP.h"
|
||||
#include <AsyncUDP.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
|
||||
//#define DEBUG_SSDP Serial
|
||||
//#define DEBUG_VERBOSE_SSDP
|
||||
//#define DEBUG_WITH_MARLIN
|
||||
#if defined (DEBUG_WITH_MARLIN)
|
||||
class FlushableHardwareSerial : public HardwareSerial
|
||||
{
|
||||
public:
|
||||
FlushableHardwareSerial(int uart_nr) : HardwareSerial(uart_nr) {}
|
||||
};
|
||||
extern FlushableHardwareSerial flushableSerial;
|
||||
#define DEBUG_SSDP flushableSerial
|
||||
#endif //endif DEBUG_WITH_MARLIN
|
||||
|
||||
|
||||
#define SSDP_INTERVAL 1200
|
||||
#define SSDP_PORT 1900
|
||||
#define SSDP_METHOD_SIZE 10
|
||||
#define SSDP_URI_SIZE 2
|
||||
#define SSDP_BUFFER_SIZE 64
|
||||
#define SSDP_MULTICAST_TTL 2
|
||||
static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250);
|
||||
#define SSDP_UUID_ROOT "38323636-4558-4dda-9188-cda0e6"
|
||||
|
||||
|
||||
static const char _ssdp_response_template[] PROGMEM =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"EXT:\r\n";
|
||||
|
||||
static const char _ssdp_notify_template[] PROGMEM =
|
||||
"NOTIFY * HTTP/1.1\r\n"
|
||||
"HOST: 239.255.255.250:1900\r\n"
|
||||
"NTS: ssdp:alive\r\n";
|
||||
|
||||
static const char _ssdp_packet_template[] PROGMEM =
|
||||
"%s" // _ssdp_response_template / _ssdp_notify_template
|
||||
"CACHE-CONTROL: max-age=%u\r\n" // _interval
|
||||
"SERVER: %s UPNP/1.1 %s/%s\r\n" // _servername, _modelName, _modelNumber
|
||||
"USN: uuid:%s%s\r\n" // _uuid, _usn_suffix
|
||||
"%s: %s\r\n" // "NT" or "ST", _deviceType
|
||||
"LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL
|
||||
"\r\n";
|
||||
|
||||
/*This need to be removed as part as deprecated, headers should be handled outside of library*/
|
||||
static const char _ssdp_schema_header[] PROGMEM =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"\r\n";
|
||||
|
||||
static const char _ssdp_schema_template[] PROGMEM =
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
||||
"<specVersion>"
|
||||
"<major>1</major>"
|
||||
"<minor>0</minor>"
|
||||
"</specVersion>"
|
||||
"<URLBase>http://%u.%u.%u.%u:%u/</URLBase>" // WiFi.localIP(), _port
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:%s:1</deviceType>"
|
||||
"<friendlyName>%s</friendlyName>"
|
||||
"<presentationURL>%s</presentationURL>"
|
||||
"<serialNumber>%s</serialNumber>"
|
||||
"<modelName>%s</modelName>"
|
||||
"<modelDescription>%s</modelDescription>"
|
||||
"<modelNumber>%s</modelNumber>"
|
||||
"<modelURL>%s</modelURL>"
|
||||
"<manufacturer>%s</manufacturer>"
|
||||
"<manufacturerURL>%s</manufacturerURL>"
|
||||
"<UDN>uuid:%s</UDN>"
|
||||
"<serviceList>%s</serviceList>"
|
||||
"<iconList>%s</iconList>"
|
||||
"</device>"
|
||||
"</root>\r\n"
|
||||
"\r\n";
|
||||
|
||||
SSDPClass::SSDPClass() :
|
||||
_replySlots{NULL},
|
||||
_respondToAddr{0,0,0,0}
|
||||
{
|
||||
_port = 80;
|
||||
_ttl = SSDP_MULTICAST_TTL;
|
||||
_interval = SSDP_INTERVAL;
|
||||
_respondToPort = 0;
|
||||
_pending = false;
|
||||
_stmatch = false;
|
||||
_delay=0;
|
||||
_process_time = 0;
|
||||
_notify_time = 0;
|
||||
_uuid[0] = '\0';
|
||||
_usn_suffix[0] = '\0';
|
||||
_respondType[0] = '\0';
|
||||
_modelNumber[0] = '\0';
|
||||
sprintf(_deviceType, "Basic");
|
||||
_friendlyName[0] = '\0';
|
||||
_presentationURL[0] = '\0';
|
||||
_serialNumber[0] = '\0';
|
||||
_modelName[0] = '\0';
|
||||
_modelURL[0] = '\0';
|
||||
_manufacturer[0] = '\0';
|
||||
_manufacturerURL[0] = '\0';
|
||||
_servername = "Arduino/1.0";
|
||||
sprintf(_schemaURL, "ssdp/schema.xml");
|
||||
_schema = nullptr;
|
||||
}
|
||||
|
||||
SSDPClass::~SSDPClass()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
void SSDPClass::end()
|
||||
{
|
||||
if (_schema) {
|
||||
free(_schema);
|
||||
_schema = nullptr;
|
||||
}
|
||||
#if defined( DEBUG_SSDP) && defined (DEBUG_VERBOSE_SSDP)
|
||||
DEBUG_SSDP.printf_P(PSTR("SSDP end ... "));
|
||||
#endif
|
||||
}
|
||||
|
||||
IPAddress SSDPClass::localIP()
|
||||
{
|
||||
tcpip_adapter_ip_info_t ip;
|
||||
if (WiFi.getMode() == WIFI_STA) {
|
||||
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
} else if (WiFi.getMode() == WIFI_OFF) {
|
||||
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
}
|
||||
return IPAddress(ip.ip.addr);
|
||||
}
|
||||
|
||||
void SSDPClass::setUUID(const char *uuid, bool rootonly)
|
||||
{
|
||||
//no sanity check is done - TBD
|
||||
if (rootonly) {
|
||||
uint32_t chipId = ((uint16_t) (ESP.getEfuseMac() >> 32));
|
||||
sprintf(_uuid, "%s%02x%02x%02x",
|
||||
uuid,
|
||||
(uint16_t) ((chipId >> 16) & 0xff),
|
||||
(uint16_t) ((chipId >> 8) & 0xff),
|
||||
(uint16_t) chipId & 0xff );
|
||||
} else {
|
||||
strlcpy(_uuid, uuid,sizeof(_uuid));
|
||||
}
|
||||
}
|
||||
|
||||
bool SSDPClass::begin()
|
||||
{
|
||||
_pending = false;
|
||||
_stmatch = false;
|
||||
end();
|
||||
if (strlen(_uuid) == 0) {
|
||||
setUUID(SSDP_UUID_ROOT);
|
||||
}
|
||||
#if defined (DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid);
|
||||
#endif
|
||||
if(_udp.connected()) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Already connected, abort begin");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
_udp.onPacket([](void * arg, AsyncUDPPacket& packet) {
|
||||
((SSDPClass*)(arg))->_onPacket(packet);
|
||||
}, this);
|
||||
|
||||
if (!_udp.listenMulticast(IPAddress(SSDP_MULTICAST_ADDR),SSDP_PORT, _ttl)) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Error begin");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSDPClass::_send(ssdp_method_t method)
|
||||
{
|
||||
char buffer[1460];
|
||||
IPAddress ip = localIP();
|
||||
|
||||
char * valueBuffer = (char *)malloc(strlen_P(_ssdp_notify_template)+1);
|
||||
if (!valueBuffer) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Error not enough memory for valueBuffer creation");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
strcpy_P(valueBuffer, (method == NONE)?_ssdp_response_template:_ssdp_notify_template);
|
||||
|
||||
int len = snprintf_P(buffer, sizeof(buffer)-1,
|
||||
_ssdp_packet_template,
|
||||
valueBuffer,
|
||||
_interval,
|
||||
_servername.c_str(),
|
||||
_modelName, _modelNumber,
|
||||
_uuid, _usn_suffix,
|
||||
(method == NONE)?"ST":"NT",
|
||||
_respondType,
|
||||
ip[0], ip[1], ip[2], ip[3], _port, _schemaURL
|
||||
);
|
||||
if(len <= 0) {
|
||||
free(valueBuffer);
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Error not enough memory for using valueBuffer");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
IPAddress remoteAddr;
|
||||
uint16_t remotePort;
|
||||
if(method == NONE) {
|
||||
remoteAddr = _respondToAddr;
|
||||
remotePort = _respondToPort;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.print("Sending Response to ");
|
||||
#endif
|
||||
} else {
|
||||
remoteAddr = IPAddress(SSDP_MULTICAST_ADDR);
|
||||
remotePort = SSDP_PORT;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.print("Sending Notify to ");
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.print(remoteAddr);
|
||||
DEBUG_SSDP.print(":");
|
||||
DEBUG_SSDP.println(remotePort);
|
||||
#endif
|
||||
_udp.writeTo((const uint8_t *)buffer, len, remoteAddr, remotePort);
|
||||
#if defined (DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
DEBUG_SSDP.println("*************************TX*************************");
|
||||
DEBUG_SSDP.println(buffer);
|
||||
DEBUG_SSDP.println("****************************************************");
|
||||
#endif
|
||||
free(valueBuffer);
|
||||
}
|
||||
|
||||
const char * SSDPClass::getSchema()
|
||||
{
|
||||
uint len = strlen(_ssdp_schema_template)
|
||||
+ 21 //(IP = 15) + 1 (:) + 5 (port)
|
||||
+ SSDP_DEVICE_TYPE_SIZE
|
||||
+ SSDP_FRIENDLY_NAME_SIZE
|
||||
+ SSDP_SCHEMA_URL_SIZE
|
||||
+ SSDP_SERIAL_NUMBER_SIZE
|
||||
+ SSDP_MODEL_NAME_SIZE
|
||||
+ _modelDescription.length()
|
||||
+ SSDP_MODEL_VERSION_SIZE
|
||||
+ SSDP_MODEL_URL_SIZE
|
||||
+ SSDP_MANUFACTURER_SIZE
|
||||
+ SSDP_MANUFACTURER_URL_SIZE
|
||||
+ SSDP_UUID_SIZE
|
||||
+ _services.length()
|
||||
+ _icons.length();
|
||||
if (_schema) {
|
||||
free (_schema);
|
||||
_schema = nullptr;
|
||||
}
|
||||
_schema = (char *)malloc(len+1);
|
||||
if (_schema) {
|
||||
IPAddress ip = localIP();
|
||||
sprintf(_schema, _ssdp_schema_template,
|
||||
ip[0], ip[1], ip[2], ip[3], _port,
|
||||
_deviceType,
|
||||
_friendlyName,
|
||||
_presentationURL,
|
||||
_serialNumber,
|
||||
_modelName,
|
||||
_modelDescription.c_str(),
|
||||
_modelNumber,
|
||||
_modelURL,
|
||||
_manufacturer,
|
||||
_manufacturerURL,
|
||||
_uuid,
|
||||
_services.c_str(),
|
||||
_icons.c_str()
|
||||
);
|
||||
} else {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("not enough memory for schema");
|
||||
#endif
|
||||
}
|
||||
return _schema;
|
||||
}
|
||||
/*This function is now deprecated and will be removed in future release*/
|
||||
/*Please use getSchema() instead */
|
||||
void SSDPClass::schema(WiFiClient client, bool sendHeaders)
|
||||
{
|
||||
if(sendHeaders) {
|
||||
client.print(_ssdp_schema_header);
|
||||
}
|
||||
client.print(getSchema());
|
||||
}
|
||||
|
||||
/*This function is now deprecated and will be removed in future release*/
|
||||
/*Please use getSchema() instead */
|
||||
const char * SSDPClass::schema(bool includeheader)
|
||||
{
|
||||
return getSchema();
|
||||
}
|
||||
|
||||
void SSDPClass::_onPacket(AsyncUDPPacket& packet)
|
||||
{
|
||||
if (packet.length()== 0) {
|
||||
return;
|
||||
}
|
||||
int nbBytes =0;
|
||||
char * packetBuffer = nullptr;
|
||||
|
||||
if(!_pending ) {
|
||||
ssdp_method_t method = NONE;
|
||||
nbBytes= packet.length();
|
||||
|
||||
typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states;
|
||||
states state = METHOD;
|
||||
typedef enum {STRIP, START, SKIP, MAN, ST, MX} headers;
|
||||
headers header = STRIP;
|
||||
|
||||
uint8_t cursor = 0;
|
||||
uint8_t cr = 0;
|
||||
|
||||
char buffer[SSDP_BUFFER_SIZE] = {0};
|
||||
packetBuffer = new char[nbBytes +1];
|
||||
if (packetBuffer == nullptr) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("not enough memory for the packet");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
int process_pos = 0;
|
||||
strncpy(packetBuffer,(const char*)packet.data(),nbBytes);
|
||||
packetBuffer[nbBytes]='\0';
|
||||
_respondToAddr = packet.remoteIP();
|
||||
_respondToPort = packet.remotePort();
|
||||
|
||||
#if defined( DEBUG_SSDP) && defined (DEBUG_VERBOSE_SSDP)
|
||||
if (nbBytes) {
|
||||
DEBUG_SSDP.println("*************************RX*************************");
|
||||
DEBUG_SSDP.print(packet.remoteIP());
|
||||
DEBUG_SSDP.print(":");
|
||||
DEBUG_SSDP.println(packet.remotePort());
|
||||
DEBUG_SSDP.println(packetBuffer);
|
||||
DEBUG_SSDP.println("****************************************************");
|
||||
}
|
||||
#endif
|
||||
while(process_pos < nbBytes) {
|
||||
|
||||
char c = packetBuffer[process_pos];
|
||||
process_pos++;
|
||||
(c == '\r' || c == '\n') ? cr++ : cr = 0;
|
||||
switch(state) {
|
||||
case METHOD:
|
||||
if(c == ' ') {
|
||||
if(strcmp(buffer, "M-SEARCH") == 0) {
|
||||
method = SEARCH;
|
||||
}
|
||||
|
||||
if(method == NONE) {
|
||||
state = ABORT;
|
||||
} else {
|
||||
state = URI;
|
||||
}
|
||||
cursor = 0;
|
||||
|
||||
} else if(cursor < SSDP_METHOD_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
break;
|
||||
case URI:
|
||||
if(c == ' ') {
|
||||
if(strcmp(buffer, "*")) {
|
||||
state = ABORT;
|
||||
} else {
|
||||
state = PROTO;
|
||||
}
|
||||
cursor = 0;
|
||||
} else if(cursor < SSDP_URI_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
break;
|
||||
case PROTO:
|
||||
if(cr == 2) {
|
||||
state = KEY;
|
||||
cursor = 0;
|
||||
}
|
||||
break;
|
||||
case KEY:
|
||||
// end of HTTP request parsing. If we find a match start reply delay.
|
||||
if(cr == 4) {
|
||||
if (_stmatch) {
|
||||
_pending = true;
|
||||
_process_time = millis();
|
||||
}
|
||||
} else if(c == ':') {
|
||||
cursor = 0;
|
||||
state = VALUE;
|
||||
} else if(c != '\r' && c != '\n' && c != ' ' && cursor < SSDP_BUFFER_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
break;
|
||||
case VALUE:
|
||||
if(cr == 2) {
|
||||
switch(header) {
|
||||
case START:
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("***********************");
|
||||
#endif
|
||||
case STRIP:
|
||||
case SKIP:
|
||||
break;
|
||||
case MAN:
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer);
|
||||
#endif
|
||||
break;
|
||||
case ST:
|
||||
// save the search term for the reply and clear usn suffix.
|
||||
strlcpy(_respondType, buffer, sizeof(_respondType));
|
||||
_usn_suffix[0] = '\0';
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("ST: '%s'\n",buffer);
|
||||
#endif
|
||||
// if looking for all or root reply with upnp:rootdevice
|
||||
if(strcmp(buffer, "ssdp:all")==0 || strcmp(buffer, "upnp:rootdevice")==0) {
|
||||
_stmatch = true;
|
||||
// set USN suffix
|
||||
strlcpy(_usn_suffix, "::upnp:rootdevice", sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("the search type matches all and root");
|
||||
#endif
|
||||
state = KEY;
|
||||
} else
|
||||
// if the search type matches our type, we should respond instead of ABORT
|
||||
if(strcasecmp(buffer, _deviceType) == 0) {
|
||||
_stmatch = true;
|
||||
// set USN suffix to the device type
|
||||
strlcpy(_usn_suffix, "::", sizeof(_usn_suffix));
|
||||
strlcat(_usn_suffix, _deviceType, sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("the search type matches our type");
|
||||
#endif
|
||||
state = KEY;
|
||||
} else {
|
||||
state = ABORT;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("REJECT. The search type does not match our type");
|
||||
DEBUG_SSDP.println("***********************");
|
||||
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case MX:
|
||||
// delay in ms from 0 to MX*1000 where MX is in seconds with limits.
|
||||
_delay = (short)random(0, atoi(buffer) * 1000L);
|
||||
if (_delay > SSDP_MAX_DELAY) {
|
||||
_delay = SSDP_MAX_DELAY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(state != ABORT) {
|
||||
state = KEY;
|
||||
header = STRIP;
|
||||
cursor = 0;
|
||||
}
|
||||
} else if(c != '\r' && c != '\n') {
|
||||
if(header == STRIP) {
|
||||
if(c == ' ') {
|
||||
break;
|
||||
} else {
|
||||
header = START;
|
||||
}
|
||||
}
|
||||
if(header == START) {
|
||||
if(strncmp(buffer, "MA", 2) == 0) {
|
||||
header = MAN;
|
||||
} else if(strcmp(buffer, "ST") == 0) {
|
||||
header = ST;
|
||||
} else if(strcmp(buffer, "MX") == 0) {
|
||||
header = MX;
|
||||
} else {
|
||||
header = SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
if(cursor < SSDP_BUFFER_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABORT:
|
||||
_pending = false;
|
||||
_delay = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(packetBuffer) {
|
||||
delete packetBuffer;
|
||||
}
|
||||
// save reply in reply queue if one is pending
|
||||
if(_pending) {
|
||||
int i;
|
||||
// Many UPNP hosts send out mulitple M-SEARCH packets at the same time to mitigate
|
||||
// packet loss. Just reply to one for a given host:port.
|
||||
for (i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
if (_replySlots[i]) {
|
||||
if (_replySlots[i]->_respondToPort == _respondToPort &&
|
||||
_replySlots[i]->_respondToAddr == _respondToAddr
|
||||
) {
|
||||
// keep original delay
|
||||
_delay = _replySlots[i]->_delay;
|
||||
_process_time = _replySlots[i]->_process_time;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("Remove duplicate SSDP reply in slot %i.\n", i);
|
||||
#endif
|
||||
delete _replySlots[i];
|
||||
_replySlots[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// save packet to available reply queue slot
|
||||
for (i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
if (!_replySlots[i]) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("Saving deferred SSDP reply to queue slot %i.\n", i);
|
||||
DEBUG_SSDP.println("***********************");
|
||||
|
||||
#endif
|
||||
_replySlots[i] = new ssdp_reply_slot_item_t;
|
||||
if (_replySlots[i]) {
|
||||
_replySlots[i]->_process_time = _process_time;
|
||||
_replySlots[i]->_delay = _delay;
|
||||
_replySlots[i]->_respondToAddr = _respondToAddr;
|
||||
_replySlots[i]->_respondToPort = _respondToPort;
|
||||
strlcpy(_replySlots[i]->_respondType, _respondType, sizeof(_replySlots[i]->_respondType));
|
||||
strlcpy(_replySlots[i]->_usn_suffix, _usn_suffix, sizeof(_replySlots[i]->_usn_suffix));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_SSDP
|
||||
if (i == SSDP_MAX_REPLY_SLOTS) {
|
||||
DEBUG_SSDP.println("SSDP reply queue is full dropping packet.");
|
||||
}
|
||||
#endif
|
||||
_pending = false;
|
||||
_delay = 0;
|
||||
}
|
||||
// send any packets that are pending and overdue.
|
||||
unsigned long t = millis();
|
||||
bool sent = false;
|
||||
for (int i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
if (_replySlots[i]) {
|
||||
// millis delay with overflow protection.
|
||||
if (t - _replySlots[i]->_process_time > _replySlots[i]->_delay) {
|
||||
// reply ready. restore and send.
|
||||
_respondToAddr = _replySlots[i]->_respondToAddr;
|
||||
_respondToPort = _replySlots[i]->_respondToPort;
|
||||
strlcpy(_respondType, _replySlots[i]->_respondType, sizeof(_respondType));
|
||||
strlcpy(_usn_suffix, _replySlots[i]->_usn_suffix, sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("Slot(%d) ", i);
|
||||
DEBUG_SSDP.println("Send None");
|
||||
#endif
|
||||
_send(NONE);
|
||||
sent = true;
|
||||
delete _replySlots[i];
|
||||
_replySlots[i] = 0;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("***********************");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined (DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
uint8_t rcount = 0;
|
||||
DEBUG_SSDP.print("SSDP reply queue status: [");
|
||||
for (int i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
DEBUG_SSDP.print(_replySlots[i] ? "X" : "-" );
|
||||
}
|
||||
DEBUG_SSDP.println("]");
|
||||
#endif
|
||||
if(_notify_time == 0 || (millis() - _notify_time) > (_interval * 1000L)) {
|
||||
_notify_time = millis();
|
||||
// send notify with our root device type
|
||||
strlcpy(_respondType, "upnp:rootdevice", sizeof(_respondType));
|
||||
strlcpy(_usn_suffix, "::upnp:rootdevice", sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Send Notify");
|
||||
#endif
|
||||
_send(NOTIFY);
|
||||
sent = true;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("***********************");
|
||||
#endif
|
||||
}
|
||||
if (!sent) {
|
||||
#if defined (DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
DEBUG_SSDP.println("Do not sent");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void SSDPClass::setSchemaURL(const char *url)
|
||||
{
|
||||
strlcpy(_schemaURL, url, sizeof(_schemaURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setHTTPPort(uint16_t port)
|
||||
{
|
||||
_port = port;
|
||||
}
|
||||
|
||||
void SSDPClass::setDeviceType(const char *deviceType)
|
||||
{
|
||||
strlcpy(_deviceType, deviceType, sizeof(_deviceType));
|
||||
}
|
||||
|
||||
void SSDPClass::setName(const char *name)
|
||||
{
|
||||
strlcpy(_friendlyName, name, sizeof(_friendlyName));
|
||||
}
|
||||
|
||||
void SSDPClass::setURL(const char *url)
|
||||
{
|
||||
strlcpy(_presentationURL, url, sizeof(_presentationURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setSerialNumber(const char *serialNumber)
|
||||
{
|
||||
strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber));
|
||||
}
|
||||
|
||||
void SSDPClass::setSerialNumber(const uint32_t serialNumber)
|
||||
{
|
||||
snprintf(_serialNumber, sizeof(uint32_t)*2+1, "%08X", serialNumber);
|
||||
}
|
||||
|
||||
void SSDPClass::setModelName(const char *name)
|
||||
{
|
||||
strlcpy(_modelName, name, sizeof(_modelName));
|
||||
}
|
||||
|
||||
void SSDPClass::setModelDescription(const char *desc)
|
||||
{
|
||||
_modelDescription = desc;
|
||||
}
|
||||
void SSDPClass::setServerName(const char *name)
|
||||
{
|
||||
_servername = name;
|
||||
}
|
||||
|
||||
void SSDPClass::setModelNumber(const char *num)
|
||||
{
|
||||
strlcpy(_modelNumber, num, sizeof(_modelNumber));
|
||||
}
|
||||
|
||||
void SSDPClass::setModelURL(const char *url)
|
||||
{
|
||||
strlcpy(_modelURL, url, sizeof(_modelURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setManufacturer(const char *name)
|
||||
{
|
||||
strlcpy(_manufacturer, name, sizeof(_manufacturer));
|
||||
}
|
||||
|
||||
void SSDPClass::setManufacturerURL(const char *url)
|
||||
{
|
||||
strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setTTL(const uint8_t ttl)
|
||||
{
|
||||
_ttl = ttl;
|
||||
}
|
||||
|
||||
void SSDPClass::setInterval(uint32_t interval)
|
||||
{
|
||||
_interval = interval;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP)
|
||||
SSDPClass SSDP;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
ESP32 Simple Service Discovery
|
||||
Copyright (c) 2015 Hristo Gochkov
|
||||
|
||||
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
|
||||
Can be found at: https://github.com/nomadnt/uSSDP
|
||||
|
||||
License (MIT license):
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifndef ESP32SSDP_H
|
||||
#define ESP32SSDP_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <AsyncUDP.h>
|
||||
|
||||
#define SSDP_UUID_SIZE 37
|
||||
#define SSDP_SCHEMA_URL_SIZE 64
|
||||
#define SSDP_DEVICE_TYPE_SIZE 64
|
||||
#define SSDP_FRIENDLY_NAME_SIZE 64
|
||||
#define SSDP_SERIAL_NUMBER_SIZE 32
|
||||
#define SSDP_PRESENTATION_URL_SIZE 128
|
||||
#define SSDP_MODEL_NAME_SIZE 64
|
||||
#define SSDP_MODEL_URL_SIZE 128
|
||||
#define SSDP_MODEL_VERSION_SIZE 32
|
||||
#define SSDP_MANUFACTURER_SIZE 64
|
||||
#define SSDP_MANUFACTURER_URL_SIZE 128
|
||||
#define SSDP_MAX_REPLY_SLOTS 5
|
||||
#define SSDP_MAX_DELAY 10000
|
||||
|
||||
typedef enum {
|
||||
NONE,
|
||||
SEARCH,
|
||||
NOTIFY
|
||||
} ssdp_method_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned long _process_time;
|
||||
short _delay;
|
||||
IPAddress _respondToAddr;
|
||||
uint16_t _respondToPort;
|
||||
char _respondType[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _usn_suffix[SSDP_DEVICE_TYPE_SIZE];
|
||||
} ssdp_reply_slot_item_t;
|
||||
|
||||
class SSDPClass
|
||||
{
|
||||
public:
|
||||
SSDPClass();
|
||||
~SSDPClass();
|
||||
|
||||
bool begin();
|
||||
void end();
|
||||
|
||||
void schema(WiFiClient client, bool sendHeaders = true) __attribute__((deprecated));
|
||||
const char * schema(bool includeheader = true) __attribute__((deprecated));
|
||||
const char * getSchema();
|
||||
|
||||
void setDeviceType(const String& deviceType)
|
||||
{
|
||||
setDeviceType(deviceType.c_str());
|
||||
}
|
||||
void setDeviceType(const char *deviceType);
|
||||
void setName(const String& name)
|
||||
{
|
||||
setName(name.c_str());
|
||||
}
|
||||
void setName(const char *name);
|
||||
void setURL(const String& url)
|
||||
{
|
||||
setURL(url.c_str());
|
||||
}
|
||||
void setURL(const char *url);
|
||||
void setSchemaURL(const String& url)
|
||||
{
|
||||
setSchemaURL(url.c_str());
|
||||
}
|
||||
void setSchemaURL(const char *url);
|
||||
void setSerialNumber(const String& serialNumber)
|
||||
{
|
||||
setSerialNumber(serialNumber.c_str());
|
||||
}
|
||||
void setSerialNumber(const char *serialNumber);
|
||||
void setSerialNumber(const uint32_t serialNumber);
|
||||
void setModelName(const String& name)
|
||||
{
|
||||
setModelName(name.c_str());
|
||||
}
|
||||
void setModelName(const char *name);
|
||||
void setModelNumber(const String& num)
|
||||
{
|
||||
setModelNumber(num.c_str());
|
||||
}
|
||||
void setModelNumber(const char *num);
|
||||
void setModelURL(const String& url)
|
||||
{
|
||||
setModelURL(url.c_str());
|
||||
}
|
||||
void setModelDescription(const String& desc)
|
||||
{
|
||||
setModelDescription(desc.c_str());
|
||||
}
|
||||
void setModelDescription(const char *desc);
|
||||
void setServerName(const String& name)
|
||||
{
|
||||
setServerName(name.c_str());
|
||||
}
|
||||
void setServerName(const char *name);
|
||||
void setModelURL(const char *url);
|
||||
void setManufacturer(const String& name)
|
||||
{
|
||||
setManufacturer(name.c_str());
|
||||
}
|
||||
void setManufacturer(const char *name);
|
||||
void setManufacturerURL(const String& url)
|
||||
{
|
||||
setManufacturerURL(url.c_str());
|
||||
}
|
||||
void setManufacturerURL(const char *url);
|
||||
void setHTTPPort(uint16_t port);
|
||||
void setTTL(uint8_t ttl);
|
||||
void setInterval(uint32_t interval);
|
||||
void setUUID(const char * uuid, bool rootonly = true);
|
||||
void setServices(const char * services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
void setIcons(const char * icons)
|
||||
{
|
||||
_icons = icons;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void _onPacket(AsyncUDPPacket& packet);
|
||||
void _send(ssdp_method_t method);
|
||||
IPAddress localIP();
|
||||
uint16_t _port;
|
||||
uint32_t _ttl;
|
||||
uint32_t _interval;
|
||||
AsyncUDP _udp;
|
||||
ssdp_reply_slot_item_t *_replySlots[SSDP_MAX_REPLY_SLOTS];
|
||||
IPAddress _respondToAddr;
|
||||
uint16_t _respondToPort;
|
||||
|
||||
bool _pending;
|
||||
bool _stmatch;
|
||||
short _delay;
|
||||
unsigned long _process_time;
|
||||
unsigned long _notify_time;
|
||||
|
||||
char _respondType[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _schemaURL[SSDP_SCHEMA_URL_SIZE];
|
||||
char _uuid[SSDP_UUID_SIZE];
|
||||
char _usn_suffix[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _deviceType[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _friendlyName[SSDP_FRIENDLY_NAME_SIZE];
|
||||
char _serialNumber[SSDP_SERIAL_NUMBER_SIZE];
|
||||
char _presentationURL[SSDP_PRESENTATION_URL_SIZE];
|
||||
char _manufacturer[SSDP_MANUFACTURER_SIZE];
|
||||
char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE];
|
||||
char _modelName[SSDP_MODEL_NAME_SIZE];
|
||||
char _modelURL[SSDP_MODEL_URL_SIZE];
|
||||
char _modelNumber[SSDP_MODEL_VERSION_SIZE];
|
||||
String _modelDescription;
|
||||
String _servername;
|
||||
char * _schema;
|
||||
String _services;
|
||||
String _icons;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP)
|
||||
extern SSDPClass SSDP;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -3,11 +3,12 @@ ESP32 Simple Service Discovery Copyright (c) 2015 Hristo Gochkov
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
Original (Arduino) version by Filippo Sallemi, July 23, 2014. Can be
|
||||
found at: https://github.com/nomadnt/uSSDP
|
||||
found at: https://github.com/nomadnt/uSSDP
|
||||
|
||||
[Latest stable release  ](https://github.com/luc-github/ESP32SSDP/releases/latest/) [](https://github.com/luc-github/ESP32SSDP/actions/workflows/build-ci.yml)
|
||||
Version 2.x for ESP32 V3.0.0 and later.
|
||||
|
||||
[Latest stable release  ](https://github.com/luc-github/ESP32SSDP/releases/latest/) [](https://github.com/luc-github/ESP32SSDP/actions/workflows/build-ci-v2.yml) [](https://github.com/espressif/arduino-esp32/releases/tag/3.0.0)
|
||||
|
||||
[Latest development version  ](https://github.com/luc-github/ESP32SSDP/tree/devt) [](https://github.com/luc-github/ESP32SSDP/actions/workflows/build-ci-dev.yml)
|
||||
|
||||
The IDF component version can be found here: https://github.com/luc-github/SSDP_IDF
|
||||
|
||||
@@ -22,16 +23,21 @@ the following conditions:
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
> [!WARNING]
|
||||
>### Disclaimer
|
||||
> The software is provided 'as is,' without any warranty of any kind, expressed or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
|
||||
>It is essential that you carefully read and understand this disclaimer before using this software and its components. If you do not agree with any part of this disclaimer, please refrain from using the software.
|
||||
|
||||
> [!NOTE]
|
||||
>### Be Noted
|
||||
> This version is an update for ESP32 V3.0.0 and later which is a breaking change, so this version of the library is not compatible with the previous versions of the ESP32 core. The previous version of the library is available in the branch [V1.x](https://github.com/luc-github/ESP32SSDP/tree/V1.x).
|
||||
> The deprecated functions of 1.x version have been removed.
|
||||
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
@@ -52,4 +58,4 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
104
libraries/ESP32SSDP-2.0.2/examples/SSDP/SSDP.ino
Normal file
104
libraries/ESP32SSDP-2.0.2/examples/SSDP/SSDP.ino
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <ESP32SSDP.h>
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
const char* ssid = "********";
|
||||
const char* password = "********";
|
||||
|
||||
WebServer HTTP(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Starting WiFi...");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
|
||||
Serial.printf("Starting HTTP...\n");
|
||||
HTTP.on("/index.html", HTTP_GET,
|
||||
[]() { HTTP.send(200, "text/plain", "Hello World!"); });
|
||||
HTTP.on("/description.xml", HTTP_GET, []() { HTTP.send(200, "text/xml", SSDP.getSchema());});
|
||||
HTTP.begin();
|
||||
|
||||
// set schema xml url, nees to match http handler
|
||||
//"ssdp/schema.xml" if not set
|
||||
SSDP.setSchemaURL("description.xml");
|
||||
// set port
|
||||
// 80 if not set
|
||||
SSDP.setHTTPPort(80);
|
||||
// set device name
|
||||
// Null string if not set
|
||||
SSDP.setName("Philips hue clone");
|
||||
// set Serial Number
|
||||
// Null string if not set
|
||||
SSDP.setSerialNumber("001788102201");
|
||||
// set device url
|
||||
// Null string if not set
|
||||
SSDP.setURL("index.html");
|
||||
// set model name
|
||||
// Null string if not set
|
||||
SSDP.setModelName("Philips hue bridge 2012");
|
||||
// set model description
|
||||
// Null string if not set
|
||||
SSDP.setModelDescription("This device can be controled by WiFi");
|
||||
// set model number
|
||||
// Null string if not set
|
||||
SSDP.setModelNumber("929000226503");
|
||||
// set model url
|
||||
// Null string if not set
|
||||
SSDP.setModelURL("http://www.meethue.com");
|
||||
// set model manufacturer name
|
||||
// Null string if not set
|
||||
SSDP.setManufacturer("Royal Philips Electronics");
|
||||
// set model manufacturer url
|
||||
// Null string if not set
|
||||
SSDP.setManufacturerURL("http://www.philips.com");
|
||||
// set device type
|
||||
//"urn:schemas-upnp-org:device:Basic:1" if not set
|
||||
SSDP.setDeviceType(
|
||||
"rootdevice"); // to appear as root device, other examples:
|
||||
// MediaRenderer, MediaServer ...
|
||||
// set server name
|
||||
//"Arduino/1.0" if not set
|
||||
SSDP.setServerName("SSDPServer/1.0");
|
||||
// set UUID, you can use https://www.uuidgenerator.net/
|
||||
// use 38323636-4558-4dda-9188-cda0e6 + 4 last bytes of mac address if not
|
||||
// set use SSDP.setUUID("daa26fa3-d2d4-4072-bc7a-a1b88ab4234a", false); for
|
||||
// full UUID
|
||||
SSDP.setUUID("daa26fa3-d2d4-4072-bc7a");
|
||||
// Set icons list, NB: optional, this is ignored under windows
|
||||
SSDP.setIcons(
|
||||
"<icon>"
|
||||
"<mimetype>image/png</mimetype>"
|
||||
"<height>48</height>"
|
||||
"<width>48</width>"
|
||||
"<depth>24</depth>"
|
||||
"<url>icon48.png</url>"
|
||||
"</icon>");
|
||||
// Set service list, NB: optional for simple device
|
||||
SSDP.setServices(
|
||||
"<service>"
|
||||
"<serviceType>urn:schemas-upnp-org:service:SwitchPower:1</serviceType>"
|
||||
"<serviceId>urn:upnp-org:serviceId:SwitchPower:1</serviceId>"
|
||||
"<SCPDURL>/SwitchPower1.xml</SCPDURL>"
|
||||
"<controlURL>/SwitchPower/Control</controlURL>"
|
||||
"<eventSubURL>/SwitchPower/Event</eventSubURL>"
|
||||
"</service>");
|
||||
|
||||
Serial.printf("Starting SSDP...\n");
|
||||
SSDP.begin();
|
||||
|
||||
Serial.printf("Ready!\n");
|
||||
} else {
|
||||
Serial.printf("WiFi Failed\n");
|
||||
while (1) {
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
HTTP.handleClient();
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "ESP32SSDP.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
|
||||
const char* ssid = "********";
|
||||
const char* password = "********";
|
||||
|
||||
AsyncWebServer webserver(80);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Starting WiFi...");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
if (WiFi.waitForConnectResult() == WL_CONNECTED) {
|
||||
Serial.printf("Starting HTTP...\n");
|
||||
webserver.on("/index.html", HTTP_GET, [&](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/plain", "Hello World!");
|
||||
});
|
||||
webserver.on("/description.xml", HTTP_GET,
|
||||
[&](AsyncWebServerRequest* request) {
|
||||
request->send(200, "text/xml", SSDP.getSchema());
|
||||
});
|
||||
webserver.begin();
|
||||
|
||||
// set schema xml url, nees to match http handler
|
||||
//"ssdp/schema.xml" if not set
|
||||
SSDP.setSchemaURL("description.xml");
|
||||
// set port
|
||||
// 80 if not set
|
||||
SSDP.setHTTPPort(80);
|
||||
// set device name
|
||||
// Null string if not set
|
||||
SSDP.setName("Philips hue clone");
|
||||
// set Serial Number
|
||||
// Null string if not set
|
||||
SSDP.setSerialNumber("001788102201");
|
||||
// set device url
|
||||
// Null string if not set
|
||||
SSDP.setURL("index.html");
|
||||
// set model name
|
||||
// Null string if not set
|
||||
SSDP.setModelName("Philips hue bridge 2012");
|
||||
// set model description
|
||||
// Null string if not set
|
||||
SSDP.setModelDescription("This device can be controled by WiFi");
|
||||
// set model number
|
||||
// Null string if not set
|
||||
SSDP.setModelNumber("929000226503");
|
||||
// set model url
|
||||
// Null string if not set
|
||||
SSDP.setModelURL("http://www.meethue.com");
|
||||
// set model manufacturer name
|
||||
// Null string if not set
|
||||
SSDP.setManufacturer("Royal Philips Electronics");
|
||||
// set model manufacturer url
|
||||
// Null string if not set
|
||||
SSDP.setManufacturerURL("http://www.philips.com");
|
||||
// set device type
|
||||
//"urn:schemas-upnp-org:device:Basic:1" if not set
|
||||
SSDP.setDeviceType(
|
||||
"rootdevice"); // to appear as root device, other examples:
|
||||
// MediaRenderer, MediaServer ...
|
||||
// set server name
|
||||
//"Arduino/1.0" if not set
|
||||
SSDP.setServerName("SSDPServer/1.0");
|
||||
// set UUID, you can use https://www.uuidgenerator.net/
|
||||
// use 38323636-4558-4dda-9188-cda0e6 + 4 last bytes of mac address if not
|
||||
// set use SSDP.setUUID("daa26fa3-d2d4-4072-bc7a-a1b88ab4234a", false); for
|
||||
// full UUID
|
||||
SSDP.setUUID("daa26fa3-d2d4-4072-bc7a");
|
||||
// Set icons list, NB: optional, this is ignored under windows
|
||||
SSDP.setIcons(
|
||||
"<icon>"
|
||||
"<mimetype>image/png</mimetype>"
|
||||
"<height>48</height>"
|
||||
"<width>48</width>"
|
||||
"<depth>24</depth>"
|
||||
"<url>icon48.png</url>"
|
||||
"</icon>");
|
||||
// Set service list, NB: optional for simple device
|
||||
SSDP.setServices(
|
||||
"<service>"
|
||||
"<serviceType>urn:schemas-upnp-org:service:SwitchPower:1</serviceType>"
|
||||
"<serviceId>urn:upnp-org:serviceId:SwitchPower:1</serviceId>"
|
||||
"<SCPDURL>/SwitchPower1.xml</SCPDURL>"
|
||||
"<controlURL>/SwitchPower/Control</controlURL>"
|
||||
"<eventSubURL>/SwitchPower/Event</eventSubURL>"
|
||||
"</service>");
|
||||
|
||||
Serial.printf("Starting SSDP...\n");
|
||||
SSDP.begin();
|
||||
|
||||
Serial.printf("Ready!\n");
|
||||
} else {
|
||||
Serial.printf("WiFi Failed\n");
|
||||
while (1) {
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop() { delay(1); }
|
||||
@@ -1,5 +1,5 @@
|
||||
name=ESP32SSDP
|
||||
version=1.2.1
|
||||
version=2.0.2
|
||||
author=Me-No-Dev
|
||||
maintainer=luc-github
|
||||
sentence=Simple SSDP library for ESP32
|
||||
687
libraries/ESP32SSDP-2.0.2/src/ESP32SSDP.cpp
Normal file
687
libraries/ESP32SSDP-2.0.2/src/ESP32SSDP.cpp
Normal file
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
ESP32 Simple Service Discovery
|
||||
Copyright (c) 2015 Hristo Gochkov
|
||||
|
||||
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
|
||||
Can be found at: https://github.com/nomadnt/uSSDP
|
||||
|
||||
License (MIT license):
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "ESP32SSDP.h"
|
||||
|
||||
#include <AsyncUDP.h>
|
||||
#include <lwip/ip_addr.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
// #define DEBUG_SSDP Serial
|
||||
// #define DEBUG_VERBOSE_SSDP
|
||||
// #define DEBUG_WITH_MARLIN
|
||||
#if defined(DEBUG_WITH_MARLIN)
|
||||
class FlushableHardwareSerial : public HardwareSerial {
|
||||
public:
|
||||
FlushableHardwareSerial(int uart_nr) : HardwareSerial(uart_nr) {}
|
||||
};
|
||||
extern FlushableHardwareSerial flushableSerial;
|
||||
#define DEBUG_SSDP flushableSerial
|
||||
#endif // endif DEBUG_WITH_MARLIN
|
||||
|
||||
#define SSDP_INTERVAL 1200
|
||||
#define SSDP_PORT 1900
|
||||
#define SSDP_METHOD_SIZE 10
|
||||
#define SSDP_URI_SIZE 2
|
||||
#define SSDP_BUFFER_SIZE 64
|
||||
#define SSDP_MULTICAST_TTL 2
|
||||
static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250);
|
||||
#define SSDP_UUID_ROOT "38323636-4558-4dda-9188-cda0e6"
|
||||
|
||||
esp_netif_t *get_esp_interface_netif(esp_interface_t interface);
|
||||
|
||||
static const char _ssdp_response_template[] PROGMEM =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"EXT:\r\n";
|
||||
|
||||
static const char _ssdp_notify_template[] PROGMEM =
|
||||
"NOTIFY * HTTP/1.1\r\n"
|
||||
"HOST: 239.255.255.250:1900\r\n"
|
||||
"NTS: ssdp:alive\r\n";
|
||||
|
||||
static const char _ssdp_packet_template[] PROGMEM =
|
||||
"%s" // _ssdp_response_template / _ssdp_notify_template
|
||||
#if (ESP_ARDUINO_VERSION_MAJOR < 3)
|
||||
"CACHE-CONTROL: max-age=%u\r\n" // _interval
|
||||
#else
|
||||
"CACHE-CONTROL: max-age=%lu\r\n" // _interval
|
||||
#endif //(ESP_ARDUINO_VERSION_MAJOR < 3)
|
||||
"SERVER: %s UPNP/1.1 %s/%s\r\n" // _servername, _modelName, _modelNumber
|
||||
"USN: uuid:%s%s\r\n" // _uuid, _usn_suffix
|
||||
"%s: %s\r\n" // "NT" or "ST", _deviceType
|
||||
"LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port,
|
||||
// _schemaURL
|
||||
"\r\n";
|
||||
|
||||
/*This need to be removed as part as deprecated, headers should be handled
|
||||
* outside of library*/
|
||||
static const char _ssdp_schema_header[] PROGMEM =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"\r\n";
|
||||
|
||||
static const char _ssdp_schema_template[] PROGMEM =
|
||||
"<?xml version=\"1.0\"?>"
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
||||
"<specVersion>"
|
||||
"<major>1</major>"
|
||||
"<minor>0</minor>"
|
||||
"</specVersion>"
|
||||
"<URLBase>http://%u.%u.%u.%u:%u/</URLBase>" // WiFi.localIP(), _port
|
||||
"<device>"
|
||||
"<deviceType>urn:schemas-upnp-org:device:%s:1</deviceType>"
|
||||
"<friendlyName>%s</friendlyName>"
|
||||
"<presentationURL>%s</presentationURL>"
|
||||
"<serialNumber>%s</serialNumber>"
|
||||
"<modelName>%s</modelName>"
|
||||
"<modelDescription>%s</modelDescription>"
|
||||
"<modelNumber>%s</modelNumber>"
|
||||
"<modelURL>%s</modelURL>"
|
||||
"<manufacturer>%s</manufacturer>"
|
||||
"<manufacturerURL>%s</manufacturerURL>"
|
||||
"<UDN>uuid:%s</UDN>"
|
||||
"<serviceList>%s</serviceList>"
|
||||
"<iconList>%s</iconList>"
|
||||
"</device>"
|
||||
"</root>\r\n"
|
||||
"\r\n";
|
||||
|
||||
SSDPClass::SSDPClass() : _replySlots{NULL}, _respondToAddr{0, 0, 0, 0} {
|
||||
_port = 80;
|
||||
_ttl = SSDP_MULTICAST_TTL;
|
||||
_interval = SSDP_INTERVAL;
|
||||
_respondToPort = 0;
|
||||
_pending = false;
|
||||
_stmatch = false;
|
||||
_delay = 0;
|
||||
_process_time = 0;
|
||||
_notify_time = 0;
|
||||
_uuid[0] = '\0';
|
||||
_usn_suffix[0] = '\0';
|
||||
_respondType[0] = '\0';
|
||||
_modelNumber[0] = '\0';
|
||||
sprintf(_deviceType, "Basic");
|
||||
_friendlyName[0] = '\0';
|
||||
_presentationURL[0] = '\0';
|
||||
_serialNumber[0] = '\0';
|
||||
_modelName[0] = '\0';
|
||||
_modelURL[0] = '\0';
|
||||
_manufacturer[0] = '\0';
|
||||
_manufacturerURL[0] = '\0';
|
||||
_servername = "Arduino/1.0";
|
||||
sprintf(_schemaURL, "ssdp/schema.xml");
|
||||
_schema = nullptr;
|
||||
}
|
||||
|
||||
SSDPClass::~SSDPClass() { end(); }
|
||||
|
||||
void SSDPClass::end() {
|
||||
if (_schema) {
|
||||
free(_schema);
|
||||
_schema = nullptr;
|
||||
}
|
||||
if (_udp.connected()) {
|
||||
_udp.close();
|
||||
}
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf_P(PSTR("SSDP end ... "));
|
||||
#endif
|
||||
}
|
||||
|
||||
IPAddress SSDPClass::localIP() {
|
||||
|
||||
#if (ESP_ARDUINO_VERSION_MAJOR < 3)
|
||||
// Arduino ESP32 2.x board version
|
||||
tcpip_adapter_ip_info_t ip;
|
||||
if (WiFi.getMode() == WIFI_STA) {
|
||||
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
} else if (WiFi.getMode() == WIFI_OFF) {
|
||||
if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
// Arduino ESP32 3.x board version
|
||||
esp_netif_ip_info_t ip;
|
||||
if (WiFi.getMode() == WIFI_STA) {
|
||||
if (esp_netif_get_ip_info(get_esp_interface_netif(ESP_IF_WIFI_STA), &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
} else if (WiFi.getMode() == WIFI_OFF) {
|
||||
if (esp_netif_get_ip_info(get_esp_interface_netif(ESP_IF_ETH), &ip)) {
|
||||
return IPAddress();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return IPAddress(ip.ip.addr);
|
||||
}
|
||||
|
||||
|
||||
void SSDPClass::setUUID(const char *uuid, bool rootonly) {
|
||||
// no sanity check is done - TBD
|
||||
if (rootonly) {
|
||||
uint32_t chipId = ((uint16_t)(ESP.getEfuseMac() >> 32));
|
||||
sprintf(_uuid, "%s%02x%02x%02x", uuid, (uint16_t)((chipId >> 16) & 0xff),
|
||||
(uint16_t)((chipId >> 8) & 0xff), (uint16_t)chipId & 0xff);
|
||||
} else {
|
||||
strlcpy(_uuid, uuid, sizeof(_uuid));
|
||||
}
|
||||
}
|
||||
|
||||
bool SSDPClass::begin() {
|
||||
_pending = false;
|
||||
_stmatch = false;
|
||||
end();
|
||||
if (strlen(_uuid) == 0) {
|
||||
setUUID(SSDP_UUID_ROOT);
|
||||
}
|
||||
#if defined(DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid);
|
||||
#endif
|
||||
if (_udp.connected()) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Already connected, abort begin");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
_udp.onPacket(
|
||||
[](void *arg, AsyncUDPPacket &packet) {
|
||||
((SSDPClass *)(arg))->_onPacket(packet);
|
||||
},
|
||||
this);
|
||||
|
||||
if (!_udp.listenMulticast(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT, _ttl)) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Error begin");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SSDPClass::_send(ssdp_method_t method) {
|
||||
char buffer[1460];
|
||||
IPAddress ip = localIP();
|
||||
|
||||
char valueBuffer[strlen_P(_ssdp_notify_template) + 1];
|
||||
strcpy_P(valueBuffer,
|
||||
(method == NONE) ? _ssdp_response_template : _ssdp_notify_template);
|
||||
|
||||
int len =
|
||||
snprintf_P(buffer, sizeof(buffer), _ssdp_packet_template, valueBuffer,
|
||||
_interval, _servername.c_str(), _modelName, _modelNumber,
|
||||
_uuid, _usn_suffix, (method == NONE) ? "ST" : "NT",
|
||||
_respondType, ip[0], ip[1], ip[2], ip[3], _port, _schemaURL);
|
||||
if (len < 0) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Error not enough memory for using valueBuffer");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
IPAddress remoteAddr;
|
||||
uint16_t remotePort;
|
||||
if (method == NONE) {
|
||||
remoteAddr = _respondToAddr;
|
||||
remotePort = _respondToPort;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.print("Sending Response to ");
|
||||
#endif
|
||||
} else {
|
||||
remoteAddr = IPAddress(SSDP_MULTICAST_ADDR);
|
||||
remotePort = SSDP_PORT;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.print("Sending Notify to ");
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.print(remoteAddr);
|
||||
DEBUG_SSDP.print(":");
|
||||
DEBUG_SSDP.println(remotePort);
|
||||
#endif
|
||||
_udp.writeTo((const uint8_t *)buffer, len, remoteAddr, remotePort);
|
||||
#if defined(DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
DEBUG_SSDP.println("*************************TX*************************");
|
||||
DEBUG_SSDP.println(buffer);
|
||||
DEBUG_SSDP.println("****************************************************");
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *SSDPClass::getSchema() {
|
||||
uint len = strlen(_ssdp_schema_template) + 21 //(IP = 15) + 1 (:) + 5 (port)
|
||||
+ SSDP_DEVICE_TYPE_SIZE + SSDP_FRIENDLY_NAME_SIZE +
|
||||
SSDP_SCHEMA_URL_SIZE + SSDP_SERIAL_NUMBER_SIZE +
|
||||
SSDP_MODEL_NAME_SIZE + _modelDescription.length() +
|
||||
SSDP_MODEL_VERSION_SIZE + SSDP_MODEL_URL_SIZE +
|
||||
SSDP_MANUFACTURER_SIZE + SSDP_MANUFACTURER_URL_SIZE +
|
||||
SSDP_UUID_SIZE + _services.length() + _icons.length();
|
||||
if (_schema) {
|
||||
free(_schema);
|
||||
_schema = nullptr;
|
||||
}
|
||||
_schema = (char *)malloc(len + 1);
|
||||
if (_schema) {
|
||||
IPAddress ip = localIP();
|
||||
sprintf(_schema, _ssdp_schema_template, ip[0], ip[1], ip[2], ip[3], _port,
|
||||
_deviceType, _friendlyName, _presentationURL, _serialNumber,
|
||||
_modelName, _modelDescription.c_str(), _modelNumber, _modelURL,
|
||||
_manufacturer, _manufacturerURL, _uuid, _services.c_str(),
|
||||
_icons.c_str());
|
||||
} else {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("not enough memory for schema");
|
||||
#endif
|
||||
}
|
||||
return _schema;
|
||||
}
|
||||
|
||||
void SSDPClass::_onPacket(AsyncUDPPacket &packet) {
|
||||
if (packet.length() == 0) {
|
||||
return;
|
||||
}
|
||||
int nbBytes = 0;
|
||||
char *packetBuffer = nullptr;
|
||||
|
||||
if (!_pending) {
|
||||
ssdp_method_t method = NONE;
|
||||
nbBytes = packet.length();
|
||||
|
||||
typedef enum { METHOD, URI, PROTO, KEY, VALUE, ABORT } states;
|
||||
states state = METHOD;
|
||||
typedef enum { STRIP, START, SKIP, MAN, ST, MX } headers;
|
||||
headers header = STRIP;
|
||||
|
||||
uint8_t cursor = 0;
|
||||
uint8_t cr = 0;
|
||||
|
||||
char buffer[SSDP_BUFFER_SIZE] = {0};
|
||||
packetBuffer = new char[nbBytes + 1];
|
||||
if (packetBuffer == nullptr) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("not enough memory for the packet");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
int process_pos = 0;
|
||||
strncpy(packetBuffer, (const char *)packet.data(), nbBytes);
|
||||
packetBuffer[nbBytes] = '\0';
|
||||
_respondToAddr = packet.remoteIP();
|
||||
_respondToPort = packet.remotePort();
|
||||
|
||||
#if defined(DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
if (nbBytes) {
|
||||
DEBUG_SSDP.println(
|
||||
"*************************RX*************************");
|
||||
DEBUG_SSDP.print(packet.remoteIP());
|
||||
DEBUG_SSDP.print(":");
|
||||
DEBUG_SSDP.println(packet.remotePort());
|
||||
DEBUG_SSDP.println(packetBuffer);
|
||||
DEBUG_SSDP.println(
|
||||
"****************************************************");
|
||||
}
|
||||
#endif
|
||||
while (process_pos < nbBytes) {
|
||||
char c = packetBuffer[process_pos];
|
||||
process_pos++;
|
||||
(c == '\r' || c == '\n') ? cr++ : cr = 0;
|
||||
switch (state) {
|
||||
case METHOD:
|
||||
if (c == ' ') {
|
||||
if (strcmp(buffer, "M-SEARCH") == 0) {
|
||||
method = SEARCH;
|
||||
}
|
||||
|
||||
if (method == NONE) {
|
||||
state = ABORT;
|
||||
} else {
|
||||
state = URI;
|
||||
}
|
||||
cursor = 0;
|
||||
|
||||
} else if (cursor < SSDP_METHOD_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
break;
|
||||
case URI:
|
||||
if (c == ' ') {
|
||||
if (strcmp(buffer, "*")) {
|
||||
state = ABORT;
|
||||
} else {
|
||||
state = PROTO;
|
||||
}
|
||||
cursor = 0;
|
||||
} else if (cursor < SSDP_URI_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
break;
|
||||
case PROTO:
|
||||
if (cr == 2) {
|
||||
state = KEY;
|
||||
cursor = 0;
|
||||
}
|
||||
break;
|
||||
case KEY:
|
||||
// end of HTTP request parsing. If we find a match start reply delay.
|
||||
if (cr == 4) {
|
||||
if (_stmatch) {
|
||||
_pending = true;
|
||||
_process_time = millis();
|
||||
}
|
||||
} else if (c == ':') {
|
||||
cursor = 0;
|
||||
state = VALUE;
|
||||
} else if (c != '\r' && c != '\n' && c != ' ' &&
|
||||
cursor < SSDP_BUFFER_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
break;
|
||||
case VALUE:
|
||||
if (cr == 2) {
|
||||
switch (header) {
|
||||
case START:
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("***********************");
|
||||
#endif
|
||||
case STRIP:
|
||||
case SKIP:
|
||||
break;
|
||||
case MAN:
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer);
|
||||
#endif
|
||||
break;
|
||||
case ST:
|
||||
// save the search term for the reply and clear usn suffix.
|
||||
strlcpy(_respondType, buffer, sizeof(_respondType));
|
||||
_usn_suffix[0] = '\0';
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("ST: '%s'\n", buffer);
|
||||
#endif
|
||||
// if looking for all or root reply with upnp:rootdevice
|
||||
if (strcmp(buffer, "ssdp:all") == 0 ||
|
||||
strcmp(buffer, "upnp:rootdevice") == 0) {
|
||||
_stmatch = true;
|
||||
// set USN suffix
|
||||
strlcpy(_usn_suffix, "::upnp:rootdevice",
|
||||
sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("the search type matches all and root");
|
||||
#endif
|
||||
state = KEY;
|
||||
} else
|
||||
// if the search type matches our type, we should respond
|
||||
// instead of ABORT
|
||||
if (strcasecmp(buffer, _deviceType) == 0) {
|
||||
_stmatch = true;
|
||||
// set USN suffix to the device type
|
||||
strlcpy(_usn_suffix, "::", sizeof(_usn_suffix));
|
||||
strlcat(_usn_suffix, _deviceType, sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("the search type matches our type");
|
||||
#endif
|
||||
state = KEY;
|
||||
} else {
|
||||
state = ABORT;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println(
|
||||
"REJECT. The search type does not match our type");
|
||||
DEBUG_SSDP.println("***********************");
|
||||
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case MX:
|
||||
// delay in ms from 0 to MX*1000 where MX is in seconds with
|
||||
// limits.
|
||||
_delay = (short)random(0, atoi(buffer) * 1000L);
|
||||
if (_delay > SSDP_MAX_DELAY) {
|
||||
_delay = SSDP_MAX_DELAY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (state != ABORT) {
|
||||
state = KEY;
|
||||
header = STRIP;
|
||||
cursor = 0;
|
||||
}
|
||||
} else if (c != '\r' && c != '\n') {
|
||||
if (header == STRIP) {
|
||||
if (c == ' ') {
|
||||
break;
|
||||
} else {
|
||||
header = START;
|
||||
}
|
||||
}
|
||||
if (header == START) {
|
||||
if (strncmp(buffer, "MA", 2) == 0) {
|
||||
header = MAN;
|
||||
} else if (strcmp(buffer, "ST") == 0) {
|
||||
header = ST;
|
||||
} else if (strcmp(buffer, "MX") == 0) {
|
||||
header = MX;
|
||||
} else {
|
||||
header = SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor < SSDP_BUFFER_SIZE - 1) {
|
||||
buffer[cursor++] = c;
|
||||
buffer[cursor] = '\0';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABORT:
|
||||
_pending = false;
|
||||
_delay = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packetBuffer) {
|
||||
delete[] packetBuffer;
|
||||
}
|
||||
// save reply in reply queue if one is pending
|
||||
if (_pending) {
|
||||
int i;
|
||||
// Many UPNP hosts send out mulitple M-SEARCH packets at the same time to
|
||||
// mitigate packet loss. Just reply to one for a given host:port.
|
||||
for (i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
if (_replySlots[i]) {
|
||||
if (_replySlots[i]->_respondToPort == _respondToPort &&
|
||||
_replySlots[i]->_respondToAddr == _respondToAddr) {
|
||||
// keep original delay
|
||||
_delay = _replySlots[i]->_delay;
|
||||
_process_time = _replySlots[i]->_process_time;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("Remove duplicate SSDP reply in slot %i.\n", i);
|
||||
#endif
|
||||
delete _replySlots[i];
|
||||
_replySlots[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// save packet to available reply queue slot
|
||||
for (i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
if (!_replySlots[i]) {
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("Saving deferred SSDP reply to queue slot %i.\n", i);
|
||||
DEBUG_SSDP.println("***********************");
|
||||
|
||||
#endif
|
||||
_replySlots[i] = new ssdp_reply_slot_item_t;
|
||||
if (_replySlots[i]) {
|
||||
_replySlots[i]->_process_time = _process_time;
|
||||
_replySlots[i]->_delay = _delay;
|
||||
_replySlots[i]->_respondToAddr = _respondToAddr;
|
||||
_replySlots[i]->_respondToPort = _respondToPort;
|
||||
strlcpy(_replySlots[i]->_respondType, _respondType,
|
||||
sizeof(_replySlots[i]->_respondType));
|
||||
strlcpy(_replySlots[i]->_usn_suffix, _usn_suffix,
|
||||
sizeof(_replySlots[i]->_usn_suffix));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_SSDP
|
||||
if (i == SSDP_MAX_REPLY_SLOTS) {
|
||||
DEBUG_SSDP.println("SSDP reply queue is full dropping packet.");
|
||||
}
|
||||
#endif
|
||||
_pending = false;
|
||||
_delay = 0;
|
||||
}
|
||||
// send any packets that are pending and overdue.
|
||||
unsigned long t = millis();
|
||||
bool sent = false;
|
||||
for (int i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
if (_replySlots[i]) {
|
||||
// millis delay with overflow protection.
|
||||
if (t - _replySlots[i]->_process_time > _replySlots[i]->_delay) {
|
||||
// reply ready. restore and send.
|
||||
_respondToAddr = _replySlots[i]->_respondToAddr;
|
||||
_respondToPort = _replySlots[i]->_respondToPort;
|
||||
strlcpy(_respondType, _replySlots[i]->_respondType,
|
||||
sizeof(_respondType));
|
||||
strlcpy(_usn_suffix, _replySlots[i]->_usn_suffix, sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.printf("Slot(%d) ", i);
|
||||
DEBUG_SSDP.println("Send None");
|
||||
#endif
|
||||
_send(NONE);
|
||||
sent = true;
|
||||
delete _replySlots[i];
|
||||
_replySlots[i] = 0;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("***********************");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
uint8_t rcount = 0;
|
||||
DEBUG_SSDP.print("SSDP reply queue status: [");
|
||||
for (int i = 0; i < SSDP_MAX_REPLY_SLOTS; i++) {
|
||||
DEBUG_SSDP.print(_replySlots[i] ? "X" : "-");
|
||||
}
|
||||
DEBUG_SSDP.println("]");
|
||||
#endif
|
||||
if (_notify_time == 0 || (millis() - _notify_time) > (_interval * 1000L)) {
|
||||
_notify_time = millis();
|
||||
// send notify with our root device type
|
||||
strlcpy(_respondType, "upnp:rootdevice", sizeof(_respondType));
|
||||
strlcpy(_usn_suffix, "::upnp:rootdevice", sizeof(_usn_suffix));
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("Send Notify");
|
||||
#endif
|
||||
_send(NOTIFY);
|
||||
sent = true;
|
||||
#ifdef DEBUG_SSDP
|
||||
DEBUG_SSDP.println("***********************");
|
||||
#endif
|
||||
}
|
||||
if (!sent) {
|
||||
#if defined(DEBUG_SSDP) && defined(DEBUG_VERBOSE_SSDP)
|
||||
DEBUG_SSDP.println("Do not sent");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void SSDPClass::setSchemaURL(const char *url) {
|
||||
strlcpy(_schemaURL, url, sizeof(_schemaURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setHTTPPort(uint16_t port) { _port = port; }
|
||||
|
||||
void SSDPClass::setDeviceType(const char *deviceType) {
|
||||
strlcpy(_deviceType, deviceType, sizeof(_deviceType));
|
||||
}
|
||||
|
||||
void SSDPClass::setName(const char *name) {
|
||||
strlcpy(_friendlyName, name, sizeof(_friendlyName));
|
||||
}
|
||||
|
||||
void SSDPClass::setURL(const char *url) {
|
||||
strlcpy(_presentationURL, url, sizeof(_presentationURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setSerialNumber(const char *serialNumber) {
|
||||
strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber));
|
||||
}
|
||||
|
||||
void SSDPClass::setSerialNumber(const uint32_t serialNumber) {
|
||||
snprintf(_serialNumber, sizeof(uint32_t) * 2 + 1, "%08X",
|
||||
(unsigned int)serialNumber);
|
||||
}
|
||||
|
||||
void SSDPClass::setModelName(const char *name) {
|
||||
strlcpy(_modelName, name, sizeof(_modelName));
|
||||
}
|
||||
|
||||
void SSDPClass::setModelDescription(const char *desc) {
|
||||
_modelDescription = desc;
|
||||
}
|
||||
void SSDPClass::setServerName(const char *name) { _servername = name; }
|
||||
|
||||
void SSDPClass::setModelNumber(const char *num) {
|
||||
strlcpy(_modelNumber, num, sizeof(_modelNumber));
|
||||
}
|
||||
|
||||
void SSDPClass::setModelURL(const char *url) {
|
||||
strlcpy(_modelURL, url, sizeof(_modelURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setManufacturer(const char *name) {
|
||||
strlcpy(_manufacturer, name, sizeof(_manufacturer));
|
||||
}
|
||||
|
||||
void SSDPClass::setManufacturerURL(const char *url) {
|
||||
strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL));
|
||||
}
|
||||
|
||||
void SSDPClass::setTTL(const uint8_t ttl) { _ttl = ttl; }
|
||||
|
||||
void SSDPClass::setInterval(uint32_t interval) { _interval = interval; }
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP)
|
||||
SSDPClass SSDP;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
154
libraries/ESP32SSDP-2.0.2/src/ESP32SSDP.h
Normal file
154
libraries/ESP32SSDP-2.0.2/src/ESP32SSDP.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
ESP32 Simple Service Discovery
|
||||
Copyright (c) 2015 Hristo Gochkov
|
||||
|
||||
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
|
||||
Can be found at: https://github.com/nomadnt/uSSDP
|
||||
|
||||
License (MIT license):
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifndef ESP32SSDP_H
|
||||
#define ESP32SSDP_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <AsyncUDP.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#define SSDP_UUID_SIZE 37
|
||||
#define SSDP_SCHEMA_URL_SIZE 64
|
||||
#define SSDP_DEVICE_TYPE_SIZE 64
|
||||
#define SSDP_FRIENDLY_NAME_SIZE 64
|
||||
#define SSDP_SERIAL_NUMBER_SIZE 32
|
||||
#define SSDP_PRESENTATION_URL_SIZE 128
|
||||
#define SSDP_MODEL_NAME_SIZE 64
|
||||
#define SSDP_MODEL_URL_SIZE 128
|
||||
#define SSDP_MODEL_VERSION_SIZE 32
|
||||
#define SSDP_MANUFACTURER_SIZE 64
|
||||
#define SSDP_MANUFACTURER_URL_SIZE 128
|
||||
#define SSDP_MAX_REPLY_SLOTS 5
|
||||
#define SSDP_MAX_DELAY 10000
|
||||
|
||||
typedef struct {
|
||||
unsigned long _process_time;
|
||||
short _delay;
|
||||
IPAddress _respondToAddr;
|
||||
uint16_t _respondToPort;
|
||||
char _respondType[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _usn_suffix[SSDP_DEVICE_TYPE_SIZE];
|
||||
} ssdp_reply_slot_item_t;
|
||||
|
||||
class SSDPClass {
|
||||
public:
|
||||
SSDPClass();
|
||||
~SSDPClass();
|
||||
|
||||
bool begin();
|
||||
void end();
|
||||
|
||||
const char* getSchema();
|
||||
|
||||
void setDeviceType(const String& deviceType) {
|
||||
setDeviceType(deviceType.c_str());
|
||||
}
|
||||
void setDeviceType(const char* deviceType);
|
||||
void setName(const String& name) { setName(name.c_str()); }
|
||||
void setName(const char* name);
|
||||
void setURL(const String& url) { setURL(url.c_str()); }
|
||||
void setURL(const char* url);
|
||||
void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); }
|
||||
void setSchemaURL(const char* url);
|
||||
void setSerialNumber(const String& serialNumber) {
|
||||
setSerialNumber(serialNumber.c_str());
|
||||
}
|
||||
void setSerialNumber(const char* serialNumber);
|
||||
void setSerialNumber(const uint32_t serialNumber);
|
||||
void setModelName(const String& name) { setModelName(name.c_str()); }
|
||||
void setModelName(const char* name);
|
||||
void setModelNumber(const String& num) { setModelNumber(num.c_str()); }
|
||||
void setModelNumber(const char* num);
|
||||
void setModelURL(const String& url) { setModelURL(url.c_str()); }
|
||||
void setModelDescription(const String& desc) {
|
||||
setModelDescription(desc.c_str());
|
||||
}
|
||||
void setModelDescription(const char* desc);
|
||||
void setServerName(const String& name) { setServerName(name.c_str()); }
|
||||
void setServerName(const char* name);
|
||||
void setModelURL(const char* url);
|
||||
void setManufacturer(const String& name) { setManufacturer(name.c_str()); }
|
||||
void setManufacturer(const char* name);
|
||||
void setManufacturerURL(const String& url) {
|
||||
setManufacturerURL(url.c_str());
|
||||
}
|
||||
void setManufacturerURL(const char* url);
|
||||
void setHTTPPort(uint16_t port);
|
||||
void setTTL(uint8_t ttl);
|
||||
void setInterval(uint32_t interval);
|
||||
void setUUID(const char* uuid, bool rootonly = true);
|
||||
void setServices(const char* services) { _services = services; }
|
||||
void setIcons(const char* icons) { _icons = icons; }
|
||||
|
||||
protected:
|
||||
typedef enum { NONE, SEARCH, NOTIFY } ssdp_method_t;
|
||||
|
||||
void _onPacket(AsyncUDPPacket& packet);
|
||||
void _send(ssdp_method_t method);
|
||||
IPAddress localIP();
|
||||
uint16_t _port;
|
||||
uint32_t _ttl;
|
||||
uint32_t _interval;
|
||||
AsyncUDP _udp;
|
||||
ssdp_reply_slot_item_t* _replySlots[SSDP_MAX_REPLY_SLOTS];
|
||||
IPAddress _respondToAddr;
|
||||
uint16_t _respondToPort;
|
||||
|
||||
bool _pending;
|
||||
bool _stmatch;
|
||||
short _delay;
|
||||
unsigned long _process_time;
|
||||
unsigned long _notify_time;
|
||||
|
||||
char _respondType[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _schemaURL[SSDP_SCHEMA_URL_SIZE];
|
||||
char _uuid[SSDP_UUID_SIZE];
|
||||
char _usn_suffix[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _deviceType[SSDP_DEVICE_TYPE_SIZE];
|
||||
char _friendlyName[SSDP_FRIENDLY_NAME_SIZE];
|
||||
char _serialNumber[SSDP_SERIAL_NUMBER_SIZE];
|
||||
char _presentationURL[SSDP_PRESENTATION_URL_SIZE];
|
||||
char _manufacturer[SSDP_MANUFACTURER_SIZE];
|
||||
char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE];
|
||||
char _modelName[SSDP_MODEL_NAME_SIZE];
|
||||
char _modelURL[SSDP_MODEL_URL_SIZE];
|
||||
char _modelNumber[SSDP_MODEL_VERSION_SIZE];
|
||||
String _modelDescription;
|
||||
String _servername;
|
||||
char* _schema;
|
||||
String _services;
|
||||
String _icons;
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP)
|
||||
extern SSDPClass SSDP;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
50
libraries/ESP32SSDP-2.0.2/tools/format_sources.py
Normal file
50
libraries/ESP32SSDP-2.0.2/tools/format_sources.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
def format_sources():
|
||||
"""
|
||||
Formats the source code files in the ESP3D project using clang-format with Google style.
|
||||
|
||||
This script recursively searches for C, C++, H, and INO files in the ESP3D project directory
|
||||
and its subdirectories. It then applies the clang-format tool to each file, using the Google
|
||||
style for formatting.
|
||||
|
||||
Note: Make sure you have clang-format installed and available in your system's PATH.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
# Base directory of the script
|
||||
script_path = os.path.abspath(__file__)
|
||||
|
||||
# Extract dir path
|
||||
script_dir = os.path.dirname(script_path)
|
||||
|
||||
# Build path of sources dir: ../esp3d
|
||||
src_dir = os.path.abspath(os.path.normpath(os.path.join(script_dir, '..', 'src')))
|
||||
examples_dir = os.path.abspath(os.path.normpath(os.path.join(script_dir, '..', 'examples')))
|
||||
|
||||
# Parse all c, h, cpp, and ino files in all directories and subdirectories
|
||||
file_paths = []
|
||||
for base_dir in [src_dir, examples_dir]:
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for file in files:
|
||||
if file.endswith(('.c', '.cpp', '.h', '.ino')):
|
||||
file_path = os.path.join(root, file)
|
||||
file_paths.append(os.path.abspath(os.path.normpath(file_path)))
|
||||
|
||||
# Now format all files one by one with clang-format
|
||||
for file_path in file_paths:
|
||||
tmpPath = '"' + file_path + '"'
|
||||
print("Formatting " + tmpPath, end="")
|
||||
try:
|
||||
command = ['clang-format', '-i', '--style=Google', file_path]
|
||||
subprocess.run(command, check=False)
|
||||
print(" => Ok")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f'=> Error: {e}')
|
||||
|
||||
# Call the format_sources function to format the source code
|
||||
format_sources()
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE REQUEST]<Enter comprehensive title>"
|
||||
labels: Feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
49
libraries/EspLuaEngine-1.0.1/.github/stale.yml
vendored
49
libraries/EspLuaEngine-1.0.1/.github/stale.yml
vendored
@@ -1,49 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 21
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 3
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- "Under Investigation"
|
||||
- "Work in progress"
|
||||
- Planned
|
||||
- "Feedback Welcome"
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when removing the stale label.
|
||||
unmarkComment: >
|
||||
This issue has been removed from the stale queue. Please ensure activity to keep it openin the future.
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
closeComment: >
|
||||
This stale issue has been automatically closed. Thank you for your contributions.
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
@@ -1,53 +0,0 @@
|
||||
# EspLuaEngine for ESP32
|
||||
|
||||
## 📚 API Reference
|
||||
|
||||
### `EspLuaEngine()`
|
||||
Constructor. Initializes a new Lua state and loads standard libraries.
|
||||
|
||||
### `~EspLuaEngine()`
|
||||
Destructor. Closes the Lua state and frees resources.
|
||||
|
||||
### `bool executeScript(const char* script)`
|
||||
Executes a Lua script.
|
||||
- **Parameters:**
|
||||
- `script`: A null-terminated string containing the Lua code to execute.
|
||||
- **Returns:** `true` if the script executes successfully, `false` otherwise.
|
||||
- **Note:** This is a blocking function. For long-running scripts, consider integrating it into a separate task to allow the rest of your firmware to continue running.
|
||||
|
||||
|
||||
### `bool registerFunction(const char* name, lua_CFunction function)`
|
||||
Registers a C function to be callable from Lua scripts.
|
||||
- **Parameters:**
|
||||
- `name`: The name to use for the function in Lua.
|
||||
- `function`: A pointer to the C function to register.
|
||||
- **Returns:** `true` if the function is successfully registered, `false` otherwise.
|
||||
|
||||
### `template<typename T> bool registerConstant(const char* name, T value)`
|
||||
Registers a constant value that can be accessed from Lua scripts.
|
||||
- **Parameters:**
|
||||
- `name`: The name to use for the constant in Lua.
|
||||
- `value`: The value of the constant. Supported types include `lua_Number`, `const char*`, `bool`, `int`, and `unsigned char`.
|
||||
- **Returns:** `true` if the constant is successfully registered, `false` otherwise.
|
||||
|
||||
## 🔧 Usage Examples
|
||||
|
||||
```cpp
|
||||
EspLuaEngine lua;
|
||||
|
||||
// Register a custom function
|
||||
lua.registerFunction("myFunction", l_myCustomFunction);
|
||||
|
||||
// Register constants
|
||||
lua.registerConstant("PI", 3.14159);
|
||||
lua.registerConstant("PROJECT_NAME", "MyESP32Project");
|
||||
lua.registerConstant("DEBUG_MODE", true);
|
||||
|
||||
// Execute a Lua script
|
||||
lua.executeScript(R"(
|
||||
print("PI is: " .. PI)
|
||||
print("Project: " .. PROJECT_NAME)
|
||||
myFunction()
|
||||
)");
|
||||
```
|
||||
|
||||
142
libraries/EspLuaEngine-1.0.2/API.md
Normal file
142
libraries/EspLuaEngine-1.0.2/API.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# EspLuaEngine for ESP32
|
||||
|
||||
## 📚 API Reference
|
||||
|
||||
### `EspLuaEngine()`
|
||||
Constructor. Initializes a new Lua state and loads standard libraries.
|
||||
|
||||
### `~EspLuaEngine()`
|
||||
Destructor. Closes the Lua state and frees resources.
|
||||
|
||||
### `bool executeScript(const char* script)`
|
||||
Executes a Lua script.
|
||||
- **Parameters:**
|
||||
- `script`: A null-terminated string containing the Lua code to execute.
|
||||
- **Returns:** `true` if the script executes successfully, `false` otherwise.
|
||||
- **Note:** This function is non-blocking and supports pause and stop operations.
|
||||
|
||||
### `bool registerFunction(const char* name, lua_CFunction function, void* userData = nullptr)`
|
||||
Registers a C function to be callable from Lua scripts.
|
||||
- **Parameters:**
|
||||
- `name`: The name to use for the function in Lua.
|
||||
- `function`: A pointer to the C function to register.
|
||||
- `userData`: Optional user data to be passed to the function.
|
||||
- **Returns:** `true` if the function is successfully registered, `false` otherwise.
|
||||
|
||||
### `template<typename T> bool registerConstant(const char* name, T value)`
|
||||
Registers a constant value that can be accessed from Lua scripts.
|
||||
- **Parameters:**
|
||||
- `name`: The name to use for the constant in Lua.
|
||||
- `value`: The value of the constant. Supported types include `lua_Number`, `const char*`, `bool`, `int`, and `unsigned char`.
|
||||
- **Returns:** `true` if the constant is successfully registered, `false` otherwise.
|
||||
|
||||
### `void resetState()`
|
||||
Resets the Lua state, clearing all registered functions and constants.
|
||||
|
||||
### `void setPauseFunction(PauseFunction func)`
|
||||
Sets a custom function to be called when execution is paused.
|
||||
- **Parameters:**
|
||||
- `func`: A function of type `std::function<void(EspLuaEngine*)>` to be called during pauses.
|
||||
|
||||
### `void pauseExecution()`
|
||||
Pauses the execution of the current script.
|
||||
|
||||
### `void resumeExecution()`
|
||||
Resumes the execution of a paused script.
|
||||
|
||||
### `void stopExecution()`
|
||||
Stops the execution of the current script.
|
||||
|
||||
### `bool isPaused()`
|
||||
Checks if the script execution is currently paused.
|
||||
- **Returns:** `true` if paused, `false` otherwise.
|
||||
|
||||
### `bool isRunning()`
|
||||
Checks if a script is currently running.
|
||||
- **Returns:** `true` if running, `false` otherwise.
|
||||
|
||||
### `Status getStatus()`
|
||||
Gets the current status of the EspLuaEngine.
|
||||
- **Returns:** An enum of type `EspLuaEngine::Status` with possible values:
|
||||
- `Idle`: No script is currently running.
|
||||
- `Running`: A script is currently executing.
|
||||
- `Paused`: Script execution is paused.
|
||||
|
||||
### `bool hasError()`
|
||||
Checks if an error occurred during the last script execution.
|
||||
- **Returns:** `true` if an error occurred, `false` otherwise.
|
||||
|
||||
### `const char* getLastError()`
|
||||
Gets the last error message.
|
||||
- **Returns:** A string containing the last error message, or an empty string if no error occurred.
|
||||
|
||||
### `lua_State* getLuaState()`
|
||||
Gets the underlying Lua state.
|
||||
- **Returns:** A pointer to the `lua_State` object.
|
||||
|
||||
## 🔧 Usage Examples
|
||||
|
||||
```cpp
|
||||
EspLuaEngine lua;
|
||||
|
||||
// Register a custom function
|
||||
lua.registerFunction("myFunction", l_myCustomFunction);
|
||||
|
||||
// Register constants
|
||||
lua.registerConstant("PI", 3.14159);
|
||||
lua.registerConstant("PROJECT_NAME", "MyESP32Project");
|
||||
lua.registerConstant("DEBUG_MODE", true);
|
||||
|
||||
// Execute a Lua script
|
||||
if (lua.executeScript(R"(
|
||||
print("PI is: " .. PI)
|
||||
print("Project: " .. PROJECT_NAME)
|
||||
myFunction()
|
||||
)")) {
|
||||
Serial.println("Script executed successfully");
|
||||
} else {
|
||||
Serial.print("Error executing script: ");
|
||||
Serial.println(lua.getLastError());
|
||||
}
|
||||
|
||||
// Pause and resume execution
|
||||
lua.pauseExecution();
|
||||
// Do something while paused
|
||||
lua.resumeExecution();
|
||||
|
||||
// Check status
|
||||
if (lua.getStatus() == EspLuaEngine::Status::Running) {
|
||||
Serial.println("Script is running");
|
||||
}
|
||||
|
||||
// Stop execution
|
||||
lua.stopExecution();
|
||||
|
||||
// Reset state
|
||||
lua.resetState();
|
||||
```
|
||||
|
||||
|
||||
## 📝 Notes
|
||||
|
||||
- The EspLuaEngine now supports non-blocking script execution with pause and stop capabilities.
|
||||
- Use `setPauseFunction()` to define custom behavior during pauses.
|
||||
- Always check for errors after executing a script using `hasError()` and `getLastError()`.
|
||||
- The engine supports multitasking environments, making it suitable for complex ESP32 projects.
|
||||
- **Important note for ESP8266 users:** While state monitoring and control are straightforward on ESP32 due to its task management capabilities, implementation on ESP8266 may require additional libraries or the use of an interrupt system. Currently, this functionality is not fully supported on ESP8266 platforms.
|
||||
|
||||
## 🚀 Platform-Specific Considerations
|
||||
|
||||
### ESP32
|
||||
On ESP32, the EspLuaEngine takes full advantage of the FreeRTOS task management system, allowing for efficient multitasking and state control without additional setup.
|
||||
|
||||
### ESP8266
|
||||
For ESP8266 users:
|
||||
|
||||
- The current implementation may not fully support all state monitoring and control features.
|
||||
- To achieve similar functionality as on ESP32, you might need to:
|
||||
1. Implement a custom interrupt-based system for state checks.
|
||||
2. Use additional libraries for task management (e.g., `TaskScheduler`).
|
||||
3. Carefully manage your main loop to prevent blocking while allowing for state checks.
|
||||
|
||||
Please note that these advanced features on ESP8266 are not officially supported in the current version of EspLuaEngine and may require custom modifications to the library.
|
||||
@@ -1,5 +1,5 @@
|
||||
name=EspLuaEngine
|
||||
version=1.0.1
|
||||
version=1.0.2
|
||||
author=Luc LEBOSSE <luc.lebosse@tech-hunters.com>
|
||||
maintainer=Luc LEBOSSE <luc.lebosse@tech-hunters.com>
|
||||
sentence=Lua engine for ESP
|
||||
@@ -19,9 +19,28 @@
|
||||
*/
|
||||
|
||||
#include "EspLuaEngine.h"
|
||||
#define ESP_LUA_NB_LINES_BEFORE_HOOK 1000
|
||||
|
||||
#include <Arduino.h>
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#endif // defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
// #define DEBUG_ESP_LUA_ENGINE Serial
|
||||
#if defined(DEBUG_ESP_LUA_ENGINE)
|
||||
#define log_e(format, ...) \
|
||||
DEBUG_ESP_LUA_ENGINE.printf("E: " format "\n", ##__VA_ARGS__)
|
||||
#define log_v(format, ...) \
|
||||
DEBUG_ESP_LUA_ENGINE.printf("V: " format "\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define log_e(format, ...)
|
||||
#define log_v(format, ...)
|
||||
#endif // defined(DEBUG_ESP_LUA_ENGINE)
|
||||
#endif // defined(ARDUINO_ARCH_ESP8266)
|
||||
|
||||
EspLuaEngine::PauseFunction EspLuaEngine::_pauseFunction = nullptr;
|
||||
String EspLuaEngine::_lastError;
|
||||
|
||||
/*Public methods*/
|
||||
|
||||
@@ -40,15 +59,112 @@ EspLuaEngine::~EspLuaEngine() {
|
||||
}
|
||||
}
|
||||
|
||||
bool EspLuaEngine::executeScript(const char* script) {
|
||||
if (luaL_dostring(_lua_state, script) != LUA_OK) {
|
||||
log_e("%s", lua_tostring(_lua_state, -1));
|
||||
lua_pop(_lua_state, 1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
void EspLuaEngine::setPauseFunction(PauseFunction func) {
|
||||
_pauseFunction = func;
|
||||
}
|
||||
|
||||
void EspLuaEngine::_defaultPauseFunction() {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
vTaskDelay(ESP_LUA_CHECK_INTERVAL);
|
||||
#endif // defined(ARDUINO_ARCH_ESP32)
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
delay(ESP_LUA_CHECK_INTERVAL);
|
||||
#endif // defined(ARDUINO_ARCH_ESP8266)
|
||||
}
|
||||
|
||||
void EspLuaEngine::hookFunction(lua_State* L, lua_Debug* ar) {
|
||||
if (_isPaused.load()) {
|
||||
while (_isPaused.load() && _isRunning.load()) {
|
||||
if (_pauseFunction) {
|
||||
_pauseFunction();
|
||||
} else {
|
||||
_defaultPauseFunction();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_isRunning.load()) {
|
||||
if (_lastError.length() == 0) _lastError = "Execution stopped";
|
||||
luaL_error(L, "Execution stopped");
|
||||
}
|
||||
}
|
||||
|
||||
void EspLuaEngine::resetState() {
|
||||
if (_lua_state) {
|
||||
lua_close(_lua_state);
|
||||
_lua_state = luaL_newstate();
|
||||
if (_lua_state) {
|
||||
_loadLibraries();
|
||||
} else {
|
||||
log_e("Error: Impossible to create a new Lua state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EspLuaEngine::executeScript(const char* script) {
|
||||
_lastError=""; // Clear the error message
|
||||
_isPaused.store(false);
|
||||
_isRunning.store(true);
|
||||
|
||||
// Configure the hook function
|
||||
lua_sethook(_lua_state, hookFunction, LUA_MASKCOUNT,
|
||||
ESP_LUA_NB_LINES_BEFORE_HOOK);
|
||||
|
||||
bool success = true;
|
||||
|
||||
// Compile the script
|
||||
if (luaL_loadstring(_lua_state, script) != LUA_OK) {
|
||||
if (_lastError.length() == 0) _lastError = lua_tostring(_lua_state, -1);
|
||||
log_e("Error loading script: %s", _lastError.c_str());
|
||||
lua_pop(_lua_state, 1);
|
||||
success = false;
|
||||
} else {
|
||||
// Execute the script
|
||||
if (lua_pcall(_lua_state, 0, 0, 0) != LUA_OK) {
|
||||
if (_lastError.length() == 0) _lastError = lua_tostring(_lua_state, -1);
|
||||
log_e("Error executing script: %s", _lastError.c_str());
|
||||
lua_pop(_lua_state, 1);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable the hook function
|
||||
lua_sethook(_lua_state, nullptr, 0, 0);
|
||||
_isRunning.store(false);
|
||||
_isPaused.store(false);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void EspLuaEngine::pauseExecution() {
|
||||
if (_isRunning.load()) {
|
||||
_isPaused.store(true);
|
||||
}
|
||||
}
|
||||
|
||||
void EspLuaEngine::resumeExecution() { _isPaused.store(false); }
|
||||
|
||||
void EspLuaEngine::stopExecution() {
|
||||
_isRunning.store(false);
|
||||
_isPaused.store(false);
|
||||
if (_lastError.length() == 0) _lastError = "Execution stopped by user";
|
||||
}
|
||||
|
||||
bool EspLuaEngine::isRunning() { return _isRunning.load(); }
|
||||
|
||||
bool EspLuaEngine::isPaused() { return _isPaused.load(); }
|
||||
|
||||
EspLuaEngine::Status EspLuaEngine::getStatus() {
|
||||
if (_isPaused.load()) {
|
||||
return Status::Paused;
|
||||
} else if (_isRunning.load()) {
|
||||
return Status::Running;
|
||||
} else {
|
||||
return Status::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
bool EspLuaEngine::hasError() { return _lastError.length() > 0; }
|
||||
|
||||
bool EspLuaEngine::registerFunction(const char* name, lua_CFunction function,
|
||||
void* userData) {
|
||||
if (!_checkPreconditions(name) || !function) {
|
||||
@@ -222,4 +338,4 @@ bool EspLuaEngine::_makeReadOnly(const char* name) {
|
||||
lua_setmetatable(_lua_state, -2);
|
||||
lua_pop(_lua_state, 2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -20,22 +20,62 @@
|
||||
|
||||
#pragma once
|
||||
#define LUA_USE_C89
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
#include "lua-5.4.7/src/lua.hpp"
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#define ESP_LUA_CHECK_INTERVAL pdMS_TO_TICKS(10)
|
||||
#endif // defined(ARDUINO_ARCH_ESP32)
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP8266)
|
||||
#define ESP_LUA_CHECK_INTERVAL 10
|
||||
#endif // defined(ARDUINO_ARCH_ESP8266)
|
||||
|
||||
|
||||
|
||||
class EspLuaEngine {
|
||||
public:
|
||||
enum class Status {
|
||||
Idle,
|
||||
Running,
|
||||
Paused,
|
||||
};
|
||||
EspLuaEngine();
|
||||
~EspLuaEngine();
|
||||
|
||||
using PauseFunction = std::function<void(void)>;
|
||||
|
||||
bool executeScript(const char* script);
|
||||
bool registerFunction(const char* name, lua_CFunction function,
|
||||
void* userData = nullptr);
|
||||
template <typename T>
|
||||
bool registerConstant(const char* name, T value);
|
||||
lua_State* getLuaState() { return _lua_state; }
|
||||
const char* getLastError() { return _lastError.c_str(); }
|
||||
void resetState();
|
||||
|
||||
void setPauseFunction(PauseFunction func);
|
||||
|
||||
static void pauseExecution();
|
||||
static void resumeExecution();
|
||||
static void stopExecution();
|
||||
bool isPaused();
|
||||
bool isRunning();
|
||||
Status getStatus();
|
||||
bool hasError();
|
||||
|
||||
private:
|
||||
lua_State* _lua_state;
|
||||
static PauseFunction _pauseFunction;
|
||||
static String _lastError;
|
||||
static inline std::atomic<bool> _isPaused{false};
|
||||
static inline std::atomic<bool> _isRunning{false};
|
||||
|
||||
static void hookFunction(lua_State* L, lua_Debug* ar);
|
||||
static void _defaultPauseFunction();
|
||||
void _loadLibraries();
|
||||
bool _checkPreconditions(const char* name);
|
||||
bool _verifyGlobal(const char* name, int type);
|
||||
@@ -46,3 +86,5 @@ class EspLuaEngine {
|
||||
bool _registerConstantImpl(const char* name, bool value);
|
||||
bool _registerConstantImpl(const char* name, int value);
|
||||
};
|
||||
|
||||
using EspLuaStatus = EspLuaEngine::Status;
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user