marius
6 months ago
1105 changed files with 143944 additions and 0 deletions
Binary file not shown.
@ -0,0 +1,5 @@ |
|||||||
|
.pio |
||||||
|
.vscode/.browse.c_cpp.db* |
||||||
|
.vscode/c_cpp_properties.json |
||||||
|
.vscode/launch.json |
||||||
|
.vscode/ipch |
@ -0,0 +1,10 @@ |
|||||||
|
{ |
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846 |
||||||
|
// for the documentation about the extensions.json format |
||||||
|
"recommendations": [ |
||||||
|
"platformio.platformio-ide" |
||||||
|
], |
||||||
|
"unwantedRecommendations": [ |
||||||
|
"ms-vscode.cpptools-extension-pack" |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
|
||||||
|
This directory is intended for project header files. |
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions |
||||||
|
to be shared between several project source files. You request the use of a |
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder |
||||||
|
by including it, with the C preprocessing directive `#include'. |
||||||
|
|
||||||
|
```src/main.c |
||||||
|
|
||||||
|
#include "header.h" |
||||||
|
|
||||||
|
int main (void) |
||||||
|
{ |
||||||
|
... |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file |
||||||
|
into each source file that needs it. Such copying would be time-consuming |
||||||
|
and error-prone. With a header file, the related declarations appear |
||||||
|
in only one place. If they need to be changed, they can be changed in one |
||||||
|
place, and programs that include the header file will automatically use the |
||||||
|
new version when next recompiled. The header file eliminates the labor of |
||||||
|
finding and changing all the copies as well as the risk that a failure to |
||||||
|
find one copy will result in inconsistencies within a program. |
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'. |
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in |
||||||
|
header file names, and at most one dot. |
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation: |
||||||
|
|
||||||
|
* Include Syntax |
||||||
|
* Include Operation |
||||||
|
* Once-Only Headers |
||||||
|
* Computed Includes |
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html |
@ -0,0 +1,46 @@ |
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries. |
||||||
|
PlatformIO will compile them to static libraries and link into executable file. |
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory |
||||||
|
("lib/your_library_name/[here are source files]"). |
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`: |
||||||
|
|
||||||
|
|--lib |
||||||
|
| | |
||||||
|
| |--Bar |
||||||
|
| | |--docs |
||||||
|
| | |--examples |
||||||
|
| | |--src |
||||||
|
| | |- Bar.c |
||||||
|
| | |- Bar.h |
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html |
||||||
|
| | |
||||||
|
| |--Foo |
||||||
|
| | |- Foo.c |
||||||
|
| | |- Foo.h |
||||||
|
| | |
||||||
|
| |- README --> THIS FILE |
||||||
|
| |
||||||
|
|- platformio.ini |
||||||
|
|--src |
||||||
|
|- main.c |
||||||
|
|
||||||
|
and a contents of `src/main.c`: |
||||||
|
``` |
||||||
|
#include <Foo.h> |
||||||
|
#include <Bar.h> |
||||||
|
|
||||||
|
int main (void) |
||||||
|
{ |
||||||
|
... |
||||||
|
} |
||||||
|
|
||||||
|
``` |
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent |
||||||
|
libraries scanning project source files. |
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder |
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html |
@ -0,0 +1,14 @@ |
|||||||
|
; PlatformIO Project Configuration File |
||||||
|
; |
||||||
|
; Build options: build flags, source filter |
||||||
|
; Upload options: custom upload port, speed and extra flags |
||||||
|
; Library options: dependencies, extra library storages |
||||||
|
; Advanced options: extra scripting |
||||||
|
; |
||||||
|
; Please visit documentation for the other options and examples |
||||||
|
; https://docs.platformio.org/page/projectconf.html |
||||||
|
|
||||||
|
[env:nanoatmega328new] |
||||||
|
platform = atmelavr |
||||||
|
board = nanoatmega328new |
||||||
|
framework = arduino |
@ -0,0 +1,162 @@ |
|||||||
|
|
||||||
|
#include <Wire.h> // I2C |
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#define CHIPSET ARDUINO_NANO |
||||||
|
#define DEVMODE true |
||||||
|
|
||||||
|
#define I2C_SCL 19 // A5
|
||||||
|
#define I2C_SDA 18 // A4
|
||||||
|
#define I2C_INTERRUPT_REQUEST 2 // D2 // D3?
|
||||||
|
|
||||||
|
#define BUTTON_PIN0 12 // D12 S10
|
||||||
|
#define BUTTON_PIN1 13 // D13 S11
|
||||||
|
#define BUTTON_PIN2 14 // A0 S12
|
||||||
|
#define BUTTON_PIN3 15 // A1 S13
|
||||||
|
#define BUTTON_PIN4 16 // A2 S14
|
||||||
|
#define BUTTON_PIN5 17 // A3 S15
|
||||||
|
#define BUTTON_PIN6 20 // A6 S16
|
||||||
|
#define BUTTON_PIN7 21 // A7 S17
|
||||||
|
|
||||||
|
#define LED_PIN0 4 // D4 S10
|
||||||
|
#define LED_PIN1 5 // D5 S11
|
||||||
|
#define LED_PIN2 6 // D6 S12
|
||||||
|
#define LED_PIN3 7 // D7 S13
|
||||||
|
#define LED_PIN4 8 // D8 S14
|
||||||
|
#define LED_PIN5 9 // D9 S15
|
||||||
|
#define LED_PIN6 10 // D10 S16
|
||||||
|
#define LED_PIN7 11 // D11 S17
|
||||||
|
|
||||||
|
#define I2C_SLAVE1_ADR 56 |
||||||
|
|
||||||
|
uint8_t ledPins[8] = {LED_PIN0, LED_PIN1, LED_PIN2, LED_PIN3, LED_PIN4, LED_PIN5, LED_PIN6, LED_PIN7}; |
||||||
|
|
||||||
|
uint8_t buttonLastState[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |
||||||
|
uint8_t buttonNextState[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |
||||||
|
|
||||||
|
uint8_t buttonExternalState[8] = {0}; |
||||||
|
|
||||||
|
bool changeDetected = false; |
||||||
|
|
||||||
|
void requestEvent() |
||||||
|
{ |
||||||
|
|
||||||
|
Serial.print("requestEvent"); |
||||||
|
Wire.write(buttonLastState, 8); // respond with message of 8 bytes
|
||||||
|
|
||||||
|
// as expected by master
|
||||||
|
changeDetected = false; |
||||||
|
} |
||||||
|
|
||||||
|
void setup() |
||||||
|
{ |
||||||
|
// put your setup code here, to run once:
|
||||||
|
if (DEVMODE) |
||||||
|
{ |
||||||
|
// pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
Serial.begin(9600); // start serial for output
|
||||||
|
} |
||||||
|
// initialize the LED pin as an output:
|
||||||
|
|
||||||
|
pinMode(LED_PIN0, OUTPUT); |
||||||
|
pinMode(LED_PIN1, OUTPUT); |
||||||
|
pinMode(LED_PIN2, OUTPUT); |
||||||
|
pinMode(LED_PIN3, OUTPUT); |
||||||
|
pinMode(LED_PIN4, OUTPUT); |
||||||
|
pinMode(LED_PIN5, OUTPUT); |
||||||
|
pinMode(LED_PIN6, OUTPUT); |
||||||
|
pinMode(LED_PIN7, OUTPUT); |
||||||
|
|
||||||
|
// initialize the pushbutton pin as an input:
|
||||||
|
// pinMode(BUTTON_PIN0, INPUT);
|
||||||
|
pinMode(BUTTON_PIN0, INPUT_PULLUP); |
||||||
|
pinMode(BUTTON_PIN1, INPUT_PULLUP); |
||||||
|
pinMode(BUTTON_PIN2, INPUT_PULLUP); |
||||||
|
pinMode(BUTTON_PIN3, INPUT_PULLUP); |
||||||
|
pinMode(BUTTON_PIN4, INPUT_PULLUP); |
||||||
|
pinMode(BUTTON_PIN5, INPUT_PULLUP); |
||||||
|
pinMode(BUTTON_PIN6, INPUT); |
||||||
|
pinMode(BUTTON_PIN7, INPUT); |
||||||
|
// digitalWrite(BUTTON_PIN6, INPUT_PULLUP);
|
||||||
|
// digitalWrite(BUTTON_PIN7, INPUT_PULLUP);
|
||||||
|
|
||||||
|
// Interrupt line open drain
|
||||||
|
pinMode(I2C_INTERRUPT_REQUEST, INPUT); |
||||||
|
digitalWrite(I2C_INTERRUPT_REQUEST, LOW); |
||||||
|
|
||||||
|
Wire.begin(I2C_SLAVE1_ADR); // join i2c bus
|
||||||
|
// Write to low for voltage compatibility
|
||||||
|
digitalWrite(SDA, LOW); |
||||||
|
digitalWrite(SCL, LOW); |
||||||
|
Wire.onRequest(requestEvent); // register event
|
||||||
|
} |
||||||
|
|
||||||
|
int resetIRQCount = 0; |
||||||
|
|
||||||
|
void loop() |
||||||
|
{ |
||||||
|
buttonNextState[0] = digitalRead(BUTTON_PIN0) ? 0 : 255; |
||||||
|
buttonNextState[1] = digitalRead(BUTTON_PIN1) ? 0 : 255; |
||||||
|
buttonNextState[2] = digitalRead(BUTTON_PIN2) ? 0 : 255; |
||||||
|
buttonNextState[3] = digitalRead(BUTTON_PIN3) ? 0 : 255; |
||||||
|
buttonNextState[4] = digitalRead(BUTTON_PIN4) ? 0 : 255; |
||||||
|
buttonNextState[5] = digitalRead(BUTTON_PIN5) ? 0 : 255; |
||||||
|
|
||||||
|
int buttonNextState6 = analogRead(BUTTON_PIN6); |
||||||
|
int buttonNextState7 = analogRead(BUTTON_PIN7); |
||||||
|
if (buttonNextState6 > 500) |
||||||
|
{ |
||||||
|
buttonNextState[6] = 0; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
buttonNextState[6] = 255; |
||||||
|
} |
||||||
|
if (buttonNextState7 > 500) |
||||||
|
{ |
||||||
|
buttonNextState[7] = 0; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
buttonNextState[7] = 255; |
||||||
|
} |
||||||
|
// WIRE I2C
|
||||||
|
|
||||||
|
// better only on change?
|
||||||
|
for (int i = 0; i < 8; i++) |
||||||
|
{ |
||||||
|
if (buttonNextState[i] != buttonLastState[i]) |
||||||
|
{ |
||||||
|
buttonLastState[i] = buttonNextState[i]; |
||||||
|
Serial.print("B"); |
||||||
|
Serial.print(i); |
||||||
|
Serial.print(": "); |
||||||
|
Serial.println(buttonNextState[i]); |
||||||
|
changeDetected = true; |
||||||
|
} |
||||||
|
if (buttonLastState[i] == 255 || buttonExternalState[i] == 255) |
||||||
|
{ |
||||||
|
digitalWrite(ledPins[i], HIGH); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
digitalWrite(ledPins[i], LOW); |
||||||
|
} |
||||||
|
} |
||||||
|
if (changeDetected) |
||||||
|
{ |
||||||
|
pinMode(I2C_INTERRUPT_REQUEST, OUTPUT); |
||||||
|
resetIRQCount++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
pinMode(I2C_INTERRUPT_REQUEST, INPUT); |
||||||
|
resetIRQCount = 0; |
||||||
|
} |
||||||
|
if (resetIRQCount > 1000) |
||||||
|
{ |
||||||
|
changeDetected = false; |
||||||
|
resetIRQCount = 0; |
||||||
|
} |
||||||
|
delayMicroseconds(5); |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests. |
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of |
||||||
|
source code, sets of one or more MCU program modules together with associated |
||||||
|
control data, usage procedures, and operating procedures, are tested to |
||||||
|
determine whether they are fit for use. Unit testing finds problems early |
||||||
|
in the development cycle. |
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing: |
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html |
@ -0,0 +1,39 @@ |
|||||||
|
|
||||||
|
This directory is intended for project header files. |
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions |
||||||
|
to be shared between several project source files. You request the use of a |
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder |
||||||
|
by including it, with the C preprocessing directive `#include'. |
||||||
|
|
||||||
|
```src/main.c |
||||||
|
|
||||||
|
#include "header.h" |
||||||
|
|
||||||
|
int main (void) |
||||||
|
{ |
||||||
|
... |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file |
||||||
|
into each source file that needs it. Such copying would be time-consuming |
||||||
|
and error-prone. With a header file, the related declarations appear |
||||||
|
in only one place. If they need to be changed, they can be changed in one |
||||||
|
place, and programs that include the header file will automatically use the |
||||||
|
new version when next recompiled. The header file eliminates the labor of |
||||||
|
finding and changing all the copies as well as the risk that a failure to |
||||||
|
find one copy will result in inconsistencies within a program. |
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'. |
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in |
||||||
|
header file names, and at most one dot. |
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation: |
||||||
|
|
||||||
|
* Include Syntax |
||||||
|
* Include Operation |
||||||
|
* Once-Only Headers |
||||||
|
* Computed Includes |
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html |
@ -0,0 +1,46 @@ |
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries. |
||||||
|
PlatformIO will compile them to static libraries and link into executable file. |
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory |
||||||
|
("lib/your_library_name/[here are source files]"). |
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`: |
||||||
|
|
||||||
|
|--lib |
||||||
|
| | |
||||||
|
| |--Bar |
||||||
|
| | |--docs |
||||||
|
| | |--examples |
||||||
|
| | |--src |
||||||
|
| | |- Bar.c |
||||||
|
| | |- Bar.h |
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html |
||||||
|
| | |
||||||
|
| |--Foo |
||||||
|
| | |- Foo.c |
||||||
|
| | |- Foo.h |
||||||
|
| | |
||||||
|
| |- README --> THIS FILE |
||||||
|
| |
||||||
|
|- platformio.ini |
||||||
|
|--src |
||||||
|
|- main.c |
||||||
|
|
||||||
|
and a contents of `src/main.c`: |
||||||
|
``` |
||||||
|
#include <Foo.h> |
||||||
|
#include <Bar.h> |
||||||
|
|
||||||
|
int main (void) |
||||||
|
{ |
||||||
|
... |
||||||
|
} |
||||||
|
|
||||||
|
``` |
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent |
||||||
|
libraries scanning project source files. |
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder |
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html |
@ -0,0 +1,15 @@ |
|||||||
|
; PlatformIO Project Configuration File |
||||||
|
; |
||||||
|
; Build options: build flags, source filter |
||||||
|
; Upload options: custom upload port, speed and extra flags |
||||||
|
; Library options: dependencies, extra library storages |
||||||
|
; Advanced options: extra scripting |
||||||
|
; |
||||||
|
; Please visit documentation for the other options and examples |
||||||
|
; https://docs.platformio.org/page/projectconf.html |
||||||
|
|
||||||
|
[env:teensy36] |
||||||
|
platform = teensy |
||||||
|
board = teensy36 |
||||||
|
framework = arduino |
||||||
|
lib_deps = arduinogetstarted/ezButton@^1.0.4 |
@ -0,0 +1,290 @@ |
|||||||
|
/* Belegung Pin Name Pin Number Spice Number
|
||||||
|
VBAT 1 1 |
||||||
|
5V 2 2 |
||||||
|
USBD2- 3 3 |
||||||
|
USBD2+ 4 4 |
||||||
|
USBD1- 5 5 |
||||||
|
USBD1+ 6 6 |
||||||
|
Fader 5 64/A10 7 7 |
||||||
|
Fader 6 65/A11 8 8 |
||||||
|
GND 9 9 |
||||||
|
Btn 1 0 10 10 |
||||||
|
Btn 2 1 11 11 |
||||||
|
Btn LED 1 2 12 12 |
||||||
|
Btn LED 2 3 13 13 |
||||||
|
Btn LED 3 4 14 14 |
||||||
|
Btn LED 4 5 15 15 |
||||||
|
Btn LED 5 6 16 16 |
||||||
|
Btn LED 6 7 17 17 |
||||||
|
Btn LED 7 8 18 18 |
||||||
|
Btn LED 8 9 19 19 |
||||||
|
Btn LED 9 10 20 20 |
||||||
|
Motor 1 B 11 21 21 |
||||||
|
Motor 1 A 12 22 22 |
||||||
|
Motor 5 B 40 23 23 |
||||||
|
Motor 2 B 24 24 24 |
||||||
|
Motor 3 B 25 25 25 |
||||||
|
Motor 3 A 26 26 26 |
||||||
|
Motor 4 A 27 27 27 |
||||||
|
Motor 4 B 28 28 28 |
||||||
|
Touch 8 29 29 29 |
||||||
|
Touch 9 30 30 30 |
||||||
|
Fader 1 31/A12 31 31 |
||||||
|
Fader 2 32/A13 32 32 |
||||||
|
Motor 5 A 41 33 33 |
||||||
|
Motor 6 A 42 34 34 |
||||||
|
Motor 6 B 43 35 35 |
||||||
|
Motor 7 B 44 36 36 |
||||||
|
Motor 7 B 45 37 37 |
||||||
|
Motor 8 A 46 38 38 |
||||||
|
Motor 8 B 47 39 39 |
||||||
|
Motor 9 B 48 40 40 |
||||||
|
Btn 3 49/A23 41 41 |
||||||
|
Btn 4 50/A24 42 42 |
||||||
|
Motor 9 A 51 43 43 |
||||||
|
Btn 5 52 44 44 |
||||||
|
Btn 6 53 45 45 |
||||||
|
Btn 7 54 46 46 |
||||||
|
Btn 8 55 47 47 |
||||||
|
I2C 56 48 48 |
||||||
|
Standby 33/A14 49 49 |
||||||
|
Motor 2 A 34/A15 50 50 |
||||||
|
35/A16 51 51 |
||||||
|
Motor Speed 36/A17 52 52 |
||||||
|
I2C 37/A18 53 53 |
||||||
|
I2C 38/A19 54 54 |
||||||
|
Fader 7 39/A20 55 55 |
||||||
|
Fader 3 66/A21 56 56 |
||||||
|
Fader 4 67/A22 57 57 |
||||||
|
I2C 57 58 58 |
||||||
|
Sys LED 13 (LED) 59 59 |
||||||
|
Fader 8 14/A0 60 60 |
||||||
|
Touch 1 15/A1 61 61 |
||||||
|
Touch 2 16/A2 62 62 |
||||||
|
Touch 3 17/A3 63 63 |
||||||
|
Touch 4 18/A4 64 64 |
||||||
|
Touch 5 19/A5 65 65 |
||||||
|
Fader 9 20/A6 66 66 |
||||||
|
Btn 9 21/A7 67 67 |
||||||
|
Touch 6 22/A8 68 68 |
||||||
|
Touch 7 23/A9 69 69 |
||||||
|
3V3 70 70 |
||||||
|
AGND 71 71 |
||||||
|
VIN 72 72 |
||||||
|
GND 73 73 |
||||||
|
VUSB 74 74 |
||||||
|
PRGM 75 75 |
||||||
|
RESET 76 76 |
||||||
|
AREF 77 77 |
||||||
|
DBGEN 78 78 |
||||||
|
SWCLK 79 79 |
||||||
|
SWDIO 80 80 |
||||||
|
MT1 81 81 |
||||||
|
MT2 82 82 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Wire.h> // I2C |
||||||
|
#include <Arduino.h> |
||||||
|
#include <ezButton.h> |
||||||
|
|
||||||
|
#define CHIPSET ARDUINO_NANO |
||||||
|
#define DEVMODE true |
||||||
|
|
||||||
|
// #define I2C_SCL 37 //
|
||||||
|
// #define I2C_SDA 38 //
|
||||||
|
#define I2C_INTERRUPT_REQUEST 57 //
|
||||||
|
|
||||||
|
#define FADER_PIN0 31 |
||||||
|
#define FADER_PIN1 32 |
||||||
|
#define FADER_PIN2 66 |
||||||
|
#define FADER_PIN3 67 |
||||||
|
#define FADER_PIN4 64 //
|
||||||
|
#define FADER_PIN5 65 //
|
||||||
|
#define FADER_PIN6 39 //
|
||||||
|
#define FADER_PIN7 14 //
|
||||||
|
#define FADER_PIN8 20 //
|
||||||
|
// sensibility 0 is highest
|
||||||
|
// fader 4 has problems so i need to set to 3 to not getting signals all the time the others worked good with 1 // todo invetigate
|
||||||
|
#define FADER_SENIBILITY 3 //
|
||||||
|
|
||||||
|
#define BUTTON_PIN0 0 |
||||||
|
#define BUTTON_PIN1 1 |
||||||
|
#define BUTTON_PIN2 49 |
||||||
|
#define BUTTON_PIN3 50 |
||||||
|
#define BUTTON_PIN4 52 //
|
||||||
|
#define BUTTON_PIN5 53 //
|
||||||
|
#define BUTTON_PIN6 54 //
|
||||||
|
#define BUTTON_PIN7 55 //
|
||||||
|
#define BUTTON_PIN8 21 //
|
||||||
|
|
||||||
|
#define BUTTON_DEBOUNCE 20 |
||||||
|
|
||||||
|
#define LED_PIN0 2 //
|
||||||
|
#define LED_PIN1 3 //
|
||||||
|
#define LED_PIN2 4 //
|
||||||
|
#define LED_PIN3 5 //
|
||||||
|
#define LED_PIN4 6 //
|
||||||
|
#define LED_PIN5 7 //
|
||||||
|
#define LED_PIN6 8 //
|
||||||
|
#define LED_PIN7 9 //
|
||||||
|
#define LED_PIN8 10 //
|
||||||
|
|
||||||
|
#define SYS_LED 13 //
|
||||||
|
|
||||||
|
#define I2C_SLAVE1_ADR 55 |
||||||
|
|
||||||
|
uint8_t ledPins[9] = {LED_PIN0, LED_PIN1, LED_PIN2, LED_PIN3, LED_PIN4, LED_PIN5, LED_PIN6, LED_PIN7, LED_PIN8}; |
||||||
|
|
||||||
|
uint8_t faderPins[9] = {FADER_PIN0, FADER_PIN1, FADER_PIN2, FADER_PIN3, FADER_PIN4, FADER_PIN5, FADER_PIN6, FADER_PIN7, FADER_PIN8}; |
||||||
|
|
||||||
|
uint8_t buttonPins[9] = {BUTTON_PIN0, BUTTON_PIN1, BUTTON_PIN2, BUTTON_PIN3, BUTTON_PIN4, BUTTON_PIN5, BUTTON_PIN6, BUTTON_PIN7, BUTTON_PIN8}; |
||||||
|
|
||||||
|
uint8_t faderLastState[9] = {0, 0, 0, 0, 0, 0, 0, 0}; |
||||||
|
uint8_t faderNextState[9] = {0, 0, 0, 0, 0, 0, 0, 0}; |
||||||
|
|
||||||
|
uint8_t buttonLastState[9] = {0, 0, 0, 0, 0, 0, 0, 0}; |
||||||
|
uint8_t buttonNextState[9] = {0, 0, 0, 0, 0, 0, 0, 0}; |
||||||
|
|
||||||
|
uint8_t buttonExternalState[9] = {0}; |
||||||
|
|
||||||
|
bool changeDetected = false; |
||||||
|
|
||||||
|
ezButton button[9] = {{BUTTON_PIN0}, {BUTTON_PIN1}, {BUTTON_PIN2}, {BUTTON_PIN3}, {BUTTON_PIN4}, {BUTTON_PIN5}, {BUTTON_PIN6}, {BUTTON_PIN7}, {BUTTON_PIN8}}; |
||||||
|
|
||||||
|
void requestEvent() |
||||||
|
{ |
||||||
|
|
||||||
|
Serial.print("requestEvent"); |
||||||
|
Wire1.write(faderLastState, 9); // respond with message of 8 bytes
|
||||||
|
Wire1.write(buttonLastState, 9); // respond with message of 8 bytes
|
||||||
|
// as expected by master
|
||||||
|
changeDetected = false; |
||||||
|
} |
||||||
|
|
||||||
|
void setup() |
||||||
|
{ |
||||||
|
// put your setup code here, to run once:
|
||||||
|
if (DEVMODE) |
||||||
|
{ |
||||||
|
Serial.begin(9600); // start serial for output
|
||||||
|
} |
||||||
|
// INIT PINS
|
||||||
|
// SYS LED
|
||||||
|
pinMode(SYS_LED, OUTPUT); |
||||||
|
digitalWrite(SYS_LED, HIGH); |
||||||
|
|
||||||
|
for (int i = 0; i < 9; i++) |
||||||
|
{ |
||||||
|
// BTN LED
|
||||||
|
pinMode(ledPins[i], OUTPUT); |
||||||
|
// BTN PIN
|
||||||
|
// pinMode(buttonPins[i], INPUT_PULLUP);
|
||||||
|
// button[i] = *new ezButton(buttonPins[i]);
|
||||||
|
button[i].setDebounceTime(BUTTON_DEBOUNCE); |
||||||
|
// FADER
|
||||||
|
pinMode(faderPins[i], INPUT); |
||||||
|
} |
||||||
|
|
||||||
|
// Interrupt line open drain
|
||||||
|
pinMode(I2C_INTERRUPT_REQUEST, INPUT); |
||||||
|
digitalWrite(I2C_INTERRUPT_REQUEST, LOW); |
||||||
|
// I2C
|
||||||
|
Wire1.begin(I2C_SLAVE1_ADR); |
||||||
|
// INTERUPTTRIGGER
|
||||||
|
// Write to low for voltage compatibility
|
||||||
|
// digitalWrite(SDA, LOW);
|
||||||
|
// digitalWrite(SCL, LOW);
|
||||||
|
Wire1.onRequest(requestEvent); // register event
|
||||||
|
} |
||||||
|
|
||||||
|
int resetIRQCount = 0; |
||||||
|
|
||||||
|
void loop() |
||||||
|
{ |
||||||
|
for (int i = 0; i < 9; i++) |
||||||
|
{ |
||||||
|
// uint8_t buttonValue = digitalRead(buttonPins[i]);
|
||||||
|
// buttonNextState[i] = buttonValue ? 0 : 255;
|
||||||
|
button[i].loop(); |
||||||
|
if (button[i].isPressed()) |
||||||
|
{ |
||||||
|
buttonNextState[i] = 255; |
||||||
|
} |
||||||
|
else if (button[i].isReleased()) |
||||||
|
{ |
||||||
|
buttonNextState[i] = 0; |
||||||
|
} |
||||||
|
int read = analogRead(faderPins[i]); |
||||||
|
faderNextState[i] = read >> 2; |
||||||
|
// Serial.print("r");
|
||||||
|
// Serial.print(read);
|
||||||
|
// Serial.print(": ");
|
||||||
|
// Serial.println(faderNextState[i]);
|
||||||
|
} |
||||||
|
|
||||||
|
// WIRE I2C
|
||||||
|
bool nextChangeDetected = false; |
||||||
|
// better only on change?
|
||||||
|
for (int i = 0; i < 9; i++) |
||||||
|
{ |
||||||
|
// FADERS
|
||||||
|
if (abs(faderNextState[i] - faderLastState[i]) > 3) // > 3
|
||||||
|
{ |
||||||
|
if (faderNextState[i] <= 3) |
||||||
|
{ |
||||||
|
faderNextState[i] = 0; |
||||||
|
} |
||||||
|
else if (faderNextState[i] >= (255 - 3)) |
||||||
|
{ |
||||||
|
faderNextState[i] = 255; |
||||||
|
} |
||||||
|
faderLastState[i] = faderNextState[i]; |
||||||
|
Serial.print("F"); |
||||||
|
Serial.print(i); |
||||||
|
Serial.print(": "); |
||||||
|
Serial.println(faderNextState[i]); |
||||||
|
// Serial.println(analogRead(faderPins[i]));
|
||||||
|
nextChangeDetected = true; |
||||||
|
} |
||||||
|
// Buttons
|
||||||
|
if (buttonNextState[i] != buttonLastState[i]) |
||||||
|
{ |
||||||
|
buttonLastState[i] = buttonNextState[i]; |
||||||
|
Serial.print("B"); |
||||||
|
Serial.print(i); |
||||||
|
Serial.print(": "); |
||||||
|
Serial.println(buttonNextState[i]); |
||||||
|
nextChangeDetected = true; |
||||||
|
} |
||||||
|
if (buttonLastState[i] == 255 || buttonExternalState[i] == 255) |
||||||
|
{ |
||||||
|
digitalWrite(ledPins[i], HIGH); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
digitalWrite(ledPins[i], LOW); |
||||||
|
} |
||||||
|
} |
||||||
|
if (nextChangeDetected && !changeDetected) |
||||||
|
{ |
||||||
|
changeDetected = nextChangeDetected; |
||||||
|
pinMode(I2C_INTERRUPT_REQUEST, OUTPUT); |
||||||
|
// resetIRQCount++;
|
||||||
|
} |
||||||
|
else if (!changeDetected) |
||||||
|
{ |
||||||
|
pinMode(I2C_INTERRUPT_REQUEST, INPUT); |
||||||
|
} |
||||||
|
// else
|
||||||
|
// {
|
||||||
|
|
||||||
|
// // resetIRQCount = 0;
|
||||||
|
// }
|
||||||
|
// if (resetIRQCount > 1000)
|
||||||
|
// {
|
||||||
|
// changeDetected = false;
|
||||||
|
// resetIRQCount = 0;
|
||||||
|
// Serial.println(buttonNextState[i]);
|
||||||
|
// }
|
||||||
|
// delayMicroseconds(5);
|
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests. |
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of |
||||||
|
source code, sets of one or more MCU program modules together with associated |
||||||
|
control data, usage procedures, and operating procedures, are tested to |
||||||
|
determine whether they are fit for use. Unit testing finds problems early |
||||||
|
in the development cycle. |
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing: |
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html |
@ -0,0 +1,4 @@ |
|||||||
|
.pio |
||||||
|
.vscode |
||||||
|
build |
||||||
|
managed_components |
@ -0,0 +1,6 @@ |
|||||||
|
# The following five lines of boilerplate have to be in your project's |
||||||
|
# CMakeLists in this exact order for cmake to work correctly |
||||||
|
cmake_minimum_required(VERSION 3.16) |
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake) |
||||||
|
project(tusb_midi) |
@ -0,0 +1,83 @@ |
|||||||
|
| Supported Targets | ESP32-S2 | ESP32-S3 | |
||||||
|
| ----------------- | -------- | -------- | |
||||||
|
|
||||||
|
# TinyUSB MIDI Device Example |
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.) |
||||||
|
|
||||||
|
This example shows how to set up ESP chip to work as a USB MIDI Device. |
||||||
|
It outputs a MIDI note sequence via the native USB port. |
||||||
|
|
||||||
|
As a USB stack, a TinyUSB component is used. |
||||||
|
|
||||||
|
## How to use example |
||||||
|
|
||||||
|
### Hardware Required |
||||||
|
|
||||||
|
Any ESP board that have USB-OTG supported. |
||||||
|
|
||||||
|
#### Pin Assignment |
||||||
|
|
||||||
|
_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. |
||||||
|
|
||||||
|
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments). |
||||||
|
|
||||||
|
### Build and Flash |
||||||
|
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output: |
||||||
|
|
||||||
|
```bash |
||||||
|
idf.py -p PORT flash monitor |
||||||
|
``` |
||||||
|
|
||||||
|
(Replace PORT with the name of the serial port to use.) |
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.) |
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. |
||||||
|
|
||||||
|
## MIDI output |
||||||
|
|
||||||
|
You can use several programs on your computer to listen to the ESP's MIDI output depending on your operating system, e.g.: |
||||||
|
|
||||||
|
* Windows: `MIDI-OX` |
||||||
|
* Linux: `qsynth` with `qjackctl` |
||||||
|
* macOS: `SimpleSynth` |
||||||
|
|
||||||
|
## Example Output |
||||||
|
|
||||||
|
After the flashing you should see the output at idf monitor: |
||||||
|
|
||||||
|
``` |
||||||
|
I (285) example: USB initialization |
||||||
|
I (285) tusb_desc: |
||||||
|
┌─────────────────────────────────┐ |
||||||
|
│ USB Device Descriptor Summary │ |
||||||
|
├───────────────────┬─────────────┤ |
||||||
|
│bDeviceClass │ 0 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│bDeviceSubClass │ 0 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│bDeviceProtocol │ 0 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│bMaxPacketSize0 │ 64 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│idVendor │ 0x303a │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│idProduct │ 0x4008 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│bcdDevice │ 0x100 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│iManufacturer │ 0x1 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│iProduct │ 0x2 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│iSerialNumber │ 0x3 │ |
||||||
|
├───────────────────┼─────────────┤ |
||||||
|
│bNumConfigurations │ 0x1 │ |
||||||
|
└───────────────────┴─────────────┘ |
||||||
|
I (455) TinyUSB: TinyUSB Driver installed |
||||||
|
I (465) example: USB initialization DONE |
||||||
|
``` |
||||||
|
|
||||||
|
Disconnect UART-to-USB port and connect the native USB port to a computer then the device should show up as a USB MIDI Device while outputting notes. |
@ -0,0 +1 @@ |
|||||||
|
3f3297a0c07a511d5b455c3806d53f23fa65cf75833270daa89f35564e9375a3 |
@ -0,0 +1,217 @@ |
|||||||
|
#include "Adafruit_Keypad.h" |
||||||
|
|
||||||
|
#define _KEY_PRESSED_POS (1) |
||||||
|
#define _KEY_PRESSED (1UL << _KEY_PRESSED_POS) |
||||||
|
|
||||||
|
#define _JUST_PRESSED_POS (2) |
||||||
|
#define _JUST_PRESSED (1UL << _JUST_PRESSED_POS) |
||||||
|
|
||||||
|
#define _JUST_RELEASED_POS (3) |
||||||
|
#define _JUST_RELEASED (1UL << _JUST_RELEASED_POS) |
||||||
|
|
||||||
|
#define _KEYPAD_SETTLING_DELAY 20 |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief default constructor |
||||||
|
@param userKeymap a multidimensional array of key characters |
||||||
|
@param row an array of GPIO pins that are connected to each row of the |
||||||
|
keypad |
||||||
|
@param col an array of GPIO pins that are connected to each column of the |
||||||
|
keypad |
||||||
|
@param numRows the number of rows on the keypad |
||||||
|
@param numCols the number of columns on the keypad |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
Adafruit_Keypad::Adafruit_Keypad(byte *userKeymap, byte *row, byte *col, |
||||||
|
int numRows, int numCols) { |
||||||
|
_userKeymap = userKeymap; |
||||||
|
_row = row; |
||||||
|
_col = col; |
||||||
|
_numRows = numRows; |
||||||
|
_numCols = numCols; |
||||||
|
|
||||||
|
_keystates = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief default destructor |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
Adafruit_Keypad::~Adafruit_Keypad() { |
||||||
|
if (_keystates != NULL) { |
||||||
|
free((void *)_keystates); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief get the state of a key with the given name |
||||||
|
@param key the name of the key to be checked |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
volatile byte *Adafruit_Keypad::getKeyState(byte key) { |
||||||
|
for (int i = 0; i < _numRows * _numCols; i++) { |
||||||
|
if (_userKeymap[i] == key) { |
||||||
|
return _keystates + i; |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief read the array of switches and place any events in the buffer. |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
void Adafruit_Keypad::tick() { |
||||||
|
uint8_t evt; |
||||||
|
for (int i = 0; i < _numCols; i++) { |
||||||
|
digitalWrite(_col[i], HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
int i = 0; |
||||||
|
for (int c = 0; c < _numCols; c++) { |
||||||
|
digitalWrite(_col[c], LOW); |
||||||
|
delayMicroseconds(_KEYPAD_SETTLING_DELAY); |
||||||
|
for (int r = 0; r < _numRows; r++) { |
||||||
|
i = r * _numCols + c; |
||||||
|
bool pressed = !digitalRead(_row[r]); |
||||||
|
// Serial.print((int)pressed);
|
||||||
|
volatile byte *state = _keystates + i; |
||||||
|
byte currentState = *state; |
||||||
|
if (pressed && !(currentState & _KEY_PRESSED)) { |
||||||
|
currentState |= (_JUST_PRESSED | _KEY_PRESSED); |
||||||
|
evt = KEY_JUST_PRESSED; |
||||||
|
_eventbuf.store_char(evt); |
||||||
|
_eventbuf.store_char(*(_userKeymap + i)); |
||||||
|
_eventbuf.store_char(r); |
||||||
|
_eventbuf.store_char(c); |
||||||
|
} else if (!pressed && (currentState & _KEY_PRESSED)) { |
||||||
|
currentState |= _JUST_RELEASED; |
||||||
|
currentState &= ~(_KEY_PRESSED); |
||||||
|
evt = KEY_JUST_RELEASED; |
||||||
|
_eventbuf.store_char(evt); |
||||||
|
_eventbuf.store_char(*(_userKeymap + i)); |
||||||
|
_eventbuf.store_char(r); |
||||||
|
_eventbuf.store_char(c); |
||||||
|
} |
||||||
|
*state = currentState; |
||||||
|
} |
||||||
|
// Serial.println("");
|
||||||
|
digitalWrite(_col[c], HIGH); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief set all the pin modes and set up variables. |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
void Adafruit_Keypad::begin() { |
||||||
|
_keystates = (volatile byte *)malloc(_numRows * _numCols); |
||||||
|
memset((void *)_keystates, 0, _numRows * _numCols); |
||||||
|
|
||||||
|
for (int i = 0; i < _numCols; i++) { |
||||||
|
pinMode(_col[i], OUTPUT); |
||||||
|
digitalWrite(_col[i], HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < _numRows; i++) { |
||||||
|
pinMode(_row[i], INPUT_PULLUP); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief check if the given key has just been pressed since the last tick. |
||||||
|
@param key the name of the key to be checked |
||||||
|
@param clear whether to reset the state (default yes) post-check |
||||||
|
@returns true if it has been pressed, false otherwise. |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
bool Adafruit_Keypad::justPressed(byte key, bool clear) { |
||||||
|
volatile byte *state = getKeyState(key); |
||||||
|
bool val = (*state & _JUST_PRESSED) != 0; |
||||||
|
|
||||||
|
if (clear) |
||||||
|
*state &= ~(_JUST_PRESSED); |
||||||
|
|
||||||
|
return val; |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief check if the given key has just been released since the last tick. |
||||||
|
@param key the name of the key to be checked |
||||||
|
@returns true if it has been released, false otherwise. |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
bool Adafruit_Keypad::justReleased(byte key) { |
||||||
|
volatile byte *state = getKeyState(key); |
||||||
|
bool val = (*state & _JUST_RELEASED) != 0; |
||||||
|
|
||||||
|
*state &= ~(_JUST_RELEASED); |
||||||
|
|
||||||
|
return val; |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief check if the given key is currently pressed |
||||||
|
@param key the name of the key to be checked |
||||||
|
@returns true if it is currently pressed, false otherwise. |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
bool Adafruit_Keypad::isPressed(byte key) { |
||||||
|
return (*getKeyState(key) & _KEY_PRESSED) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief check if the given key is currently released |
||||||
|
@param key the name of the key to be checked |
||||||
|
@returns true if it is currently released, false otherwise. |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
bool Adafruit_Keypad::isReleased(byte key) { |
||||||
|
return (*getKeyState(key) & _KEY_PRESSED) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief check how many events are in the keypads buffer |
||||||
|
@returns the number of events currently in the buffer |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
int Adafruit_Keypad::available() { |
||||||
|
return (_eventbuf.available() / sizeof(keypadEvent)); |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief pop the next event off of the FIFO |
||||||
|
@returns the next event in the FIFO |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
keypadEvent Adafruit_Keypad::read() { |
||||||
|
keypadEvent k; |
||||||
|
k.bit.EVENT = _eventbuf.read_char(); |
||||||
|
k.bit.KEY = _eventbuf.read_char(); |
||||||
|
k.bit.ROW = _eventbuf.read_char(); |
||||||
|
k.bit.COL = _eventbuf.read_char(); |
||||||
|
|
||||||
|
return k; |
||||||
|
} |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief Clear out the event buffer and all the key states |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
void Adafruit_Keypad::clear() { |
||||||
|
_eventbuf.clear(); |
||||||
|
for (int i = 0; i < _numRows * _numCols; i++) |
||||||
|
*(_keystates + i) = 0; |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
#ifndef _ADAFRUIT_KEYPAD_H_ |
||||||
|
#define _ADAFRUIT_KEYPAD_H_ |
||||||
|
|
||||||
|
#include "Adafruit_Keypad_Ringbuffer.h" |
||||||
|
#include "Arduino.h" |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#define makeKeymap(x) ((byte *)x) ///< cast the passed key characters to bytes
|
||||||
|
|
||||||
|
#define KEY_JUST_RELEASED (0) ///< key has been released
|
||||||
|
#define KEY_JUST_PRESSED (1) ///< key has been pressed
|
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief key event structure |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
union keypadEvent { |
||||||
|
struct { |
||||||
|
uint8_t KEY : 8; ///< the keycode
|
||||||
|
uint8_t EVENT : 8; ///< the edge
|
||||||
|
uint8_t ROW : 8; ///< the row number
|
||||||
|
uint8_t COL : 8; ///< the col number
|
||||||
|
} bit; ///< bitfield format
|
||||||
|
uint32_t reg; ///< register format
|
||||||
|
}; |
||||||
|
|
||||||
|
/**************************************************************************/ |
||||||
|
/*!
|
||||||
|
@brief Class for interfacing GPIO with a diode-multiplexed keypad |
||||||
|
*/ |
||||||
|
/**************************************************************************/ |
||||||
|
class Adafruit_Keypad { |
||||||
|
public: |
||||||
|
Adafruit_Keypad(byte *userKeymap, byte *row, byte *col, int numRows, |
||||||
|
int numCols); |
||||||
|
~Adafruit_Keypad(); |
||||||
|
void begin(); |
||||||
|
|
||||||
|
void tick(); |
||||||
|
|
||||||
|
bool justPressed(byte key, bool clear = true); |
||||||
|
bool justReleased(byte key); |
||||||
|
bool isPressed(byte key); |
||||||
|
bool isReleased(byte key); |
||||||
|
int available(); |
||||||
|
keypadEvent read(); |
||||||
|
void clear(); |
||||||
|
|
||||||
|
private: |
||||||
|
byte *_userKeymap; |
||||||
|
byte *_row; |
||||||
|
byte *_col; |
||||||
|
volatile byte *_keystates; |
||||||
|
Adafruit_Keypad_Ringbuffer _eventbuf; |
||||||
|
|
||||||
|
int _numRows; |
||||||
|
int _numCols; |
||||||
|
|
||||||
|
volatile byte *getKeyState(byte key); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,126 @@ |
|||||||
|
/*
|
||||||
|
Copyright (c) 2014 Arduino. All right reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||||||
|
See the GNU Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
|
||||||
|
#ifndef _ADAFRUIT_KEYPAD_RING_BUFFER_ |
||||||
|
#define _ADAFRUIT_KEYPAD_RING_BUFFER_ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
// Define constants and variables for buffering incoming serial data. We're
|
||||||
|
// using a ring buffer (I think), in which head is the index of the location
|
||||||
|
// to which to write the next incoming character and tail is the index of the
|
||||||
|
// location from which to read.
|
||||||
|
|
||||||
|
#ifndef SERIAL_BUFFER_SIZE |
||||||
|
#define SERIAL_BUFFER_SIZE 256 |
||||||
|
#endif |
||||||
|
|
||||||
|
template <int N> class Adafruit_Keypad_RingbufferN { |
||||||
|
public: |
||||||
|
uint8_t _aucBuffer[N]; |
||||||
|
volatile int _iHead; |
||||||
|
volatile int _iTail; |
||||||
|
|
||||||
|
public: |
||||||
|
Adafruit_Keypad_RingbufferN(void); |
||||||
|
void store_char(uint8_t c); |
||||||
|
void clear(); |
||||||
|
int read_char(); |
||||||
|
int available(); |
||||||
|
int availableForStore(); |
||||||
|
int peek(); |
||||||
|
bool isFull(); |
||||||
|
|
||||||
|
private: |
||||||
|
int nextIndex(int index); |
||||||
|
}; |
||||||
|
|
||||||
|
typedef Adafruit_Keypad_RingbufferN<SERIAL_BUFFER_SIZE> |
||||||
|
Adafruit_Keypad_Ringbuffer; |
||||||
|
|
||||||
|
template <int N> |
||||||
|
Adafruit_Keypad_RingbufferN<N>::Adafruit_Keypad_RingbufferN(void) { |
||||||
|
memset(_aucBuffer, 0, N); |
||||||
|
clear(); |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> void Adafruit_Keypad_RingbufferN<N>::store_char(uint8_t c) { |
||||||
|
int i = nextIndex(_iHead); |
||||||
|
|
||||||
|
// if we should be storing the received character into the location
|
||||||
|
// just before the tail (meaning that the head would advance to the
|
||||||
|
// current location of the tail), we're about to overflow the buffer
|
||||||
|
// and so we don't write the character or advance the head.
|
||||||
|
if (i != _iTail) { |
||||||
|
_aucBuffer[_iHead] = c; |
||||||
|
_iHead = i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> void Adafruit_Keypad_RingbufferN<N>::clear() { |
||||||
|
_iHead = 0; |
||||||
|
_iTail = 0; |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> int Adafruit_Keypad_RingbufferN<N>::read_char() { |
||||||
|
if (_iTail == _iHead) |
||||||
|
return -1; |
||||||
|
|
||||||
|
uint8_t value = _aucBuffer[_iTail]; |
||||||
|
_iTail = nextIndex(_iTail); |
||||||
|
|
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> int Adafruit_Keypad_RingbufferN<N>::available() { |
||||||
|
int delta = _iHead - _iTail; |
||||||
|
|
||||||
|
if (delta < 0) |
||||||
|
return N + delta; |
||||||
|
else |
||||||
|
return delta; |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> int Adafruit_Keypad_RingbufferN<N>::availableForStore() { |
||||||
|
if (_iHead >= _iTail) |
||||||
|
return N - 1 - _iHead + _iTail; |
||||||
|
else |
||||||
|
return _iTail - _iHead - 1; |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> int Adafruit_Keypad_RingbufferN<N>::peek() { |
||||||
|
if (_iTail == _iHead) |
||||||
|
return -1; |
||||||
|
|
||||||
|
return _aucBuffer[_iTail]; |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> int Adafruit_Keypad_RingbufferN<N>::nextIndex(int index) { |
||||||
|
return (uint32_t)(index + 1) % N; |
||||||
|
} |
||||||
|
|
||||||
|
template <int N> bool Adafruit_Keypad_RingbufferN<N>::isFull() { |
||||||
|
return (nextIndex(_iHead) == _iTail); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* _ADAFRUIT_KEYPAD_RING_BUFFER_ */ |
||||||
|
|
||||||
|
#endif /* __cplusplus */ |
@ -0,0 +1,5 @@ |
|||||||
|
cmake_minimum_required(VERSION 3.5) |
||||||
|
idf_component_register(SRCS "Adafruit_Keypad.cpp" |
||||||
|
REQUIRES "arduino-esp32" "spi_flash" "esp_partition" |
||||||
|
INCLUDE_DIRS ".") |
||||||
|
project(Adafruit_Keypad) |
@ -0,0 +1,10 @@ |
|||||||
|
# Adafruit Keypad Library [![Build Status](https://github.com/adafruit/Adafruit_Keypad/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_Keypad/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_Keypad/html/index.html) |
||||||
|
|
||||||
|
<img src="https://cdn-shop.adafruit.com/970x728/4020-04.jpg" height="300"/> |
||||||
|
|
||||||
|
This is a library for using diode multiplexed keypads with GPIO pins on Arduino. |
||||||
|
|
||||||
|
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! |
||||||
|
|
||||||
|
Written by Dean Miller for Adafruit Industries. |
||||||
|
MIT license, all text above must be included in any redistribution |
@ -0,0 +1,100 @@ |
|||||||
|
#include <Adafruit_NeoPixel.h> |
||||||
|
#include "Adafruit_Keypad.h" |
||||||
|
|
||||||
|
#define ROWS 5 // rows
|
||||||
|
#define COLS 6 // columns
|
||||||
|
|
||||||
|
#define NEOPIXEL_PIN A0 |
||||||
|
#define NUM_PIXELS (ROWS * COLS) |
||||||
|
|
||||||
|
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); |
||||||
|
|
||||||
|
|
||||||
|
//define the symbols on the buttons of the keypads
|
||||||
|
char keys[ROWS][COLS] = { |
||||||
|
{'1','2','3','4','5','6'}, |
||||||
|
{'7','8','9','A','B','C'}, |
||||||
|
{'D','E','F','G','H','I'}, |
||||||
|
{'J','K','L','M','N','O'}, |
||||||
|
{'P','Q','R','S','T','U'} |
||||||
|
}; |
||||||
|
|
||||||
|
uint8_t rowPins[ROWS] = {6, 5, 4, 3, 2}; //connect to the row pinouts of the keypad
|
||||||
|
uint8_t colPins[COLS] = {7, 8, 9, 10, 11, 12}; //connect to the column pinouts of the keypad
|
||||||
|
|
||||||
|
//initialize an instance of class NewKeypad
|
||||||
|
Adafruit_Keypad customKeypad = Adafruit_Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS); |
||||||
|
|
||||||
|
bool lit[ROWS*COLS] = {0}; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
//while (!Serial);
|
||||||
|
Serial.println("Ortho 5x6 keypad demo"); |
||||||
|
strip.begin(); |
||||||
|
strip.setBrightness(40); |
||||||
|
strip.show(); // Initialize all pixels to 'off'
|
||||||
|
|
||||||
|
customKeypad.begin(); |
||||||
|
for (int i=0; i<ROWS*COLS; i++) { |
||||||
|
lit[i] = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t j=0; // color ticker
|
||||||
|
|
||||||
|
void loop() { |
||||||
|
//Serial.println("Test NeoPixels");
|
||||||
|
customKeypad.tick(); |
||||||
|
|
||||||
|
while(customKeypad.available()){ |
||||||
|
keypadEvent e = customKeypad.read(); |
||||||
|
Serial.print((char)e.bit.KEY); |
||||||
|
if (e.bit.EVENT == KEY_JUST_PRESSED) { |
||||||
|
Serial.println(" pressed"); |
||||||
|
uint8_t row = e.bit.ROW; |
||||||
|
uint8_t col = e.bit.COL; |
||||||
|
Serial.print("Row: "); Serial.print(row); |
||||||
|
Serial.print(" col: "); Serial.print(col); |
||||||
|
Serial.print(" -> "); |
||||||
|
uint16_t keynum; |
||||||
|
if (row % 2 == 0) { // even row
|
||||||
|
keynum = row * COLS + col; |
||||||
|
} else { // odd row the neopixels go BACKWARDS!
|
||||||
|
keynum = row * COLS + (5 - col); |
||||||
|
} |
||||||
|
Serial.println(keynum); |
||||||
|
lit[keynum] = !lit[keynum]; // invert neopixel status
|
||||||
|
} |
||||||
|
else if(e.bit.EVENT == KEY_JUST_RELEASED) { |
||||||
|
Serial.println(" released"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for(int i=0; i< strip.numPixels(); i++) { |
||||||
|
if (lit[i]) { |
||||||
|
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); |
||||||
|
} else { |
||||||
|
strip.setPixelColor(i, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
strip.show(); |
||||||
|
j++; |
||||||
|
|
||||||
|
delay(10); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Input a value 0 to 255 to get a color value.
|
||||||
|
// The colours are a transition r - g - b - back to r.
|
||||||
|
uint32_t Wheel(byte WheelPos) { |
||||||
|
if(WheelPos < 85) { |
||||||
|
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); |
||||||
|
} else if(WheelPos < 170) { |
||||||
|
WheelPos -= 85; |
||||||
|
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); |
||||||
|
} else { |
||||||
|
WheelPos -= 170; |
||||||
|
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
#include "Adafruit_Keypad.h" |
||||||
|
|
||||||
|
const byte ROWS = 4; // rows
|
||||||
|
const byte COLS = 4; // columns
|
||||||
|
//define the symbols on the buttons of the keypads
|
||||||
|
char keys[ROWS][COLS] = { |
||||||
|
{'1','2','3','A'}, |
||||||
|
{'4','5','6','B'}, |
||||||
|
{'7','8','9','C'}, |
||||||
|
{'*','0','#','D'} |
||||||
|
}; |
||||||
|
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
|
||||||
|
byte colPins[COLS] = {11, 10, 9, 8}; //connect to the column pinouts of the keypad
|
||||||
|
|
||||||
|
//initialize an instance of class NewKeypad
|
||||||
|
Adafruit_Keypad customKeypad = Adafruit_Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS); |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(9600); |
||||||
|
customKeypad.begin(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// put your main code here, to run repeatedly:
|
||||||
|
customKeypad.tick(); |
||||||
|
|
||||||
|
while(customKeypad.available()){ |
||||||
|
keypadEvent e = customKeypad.read(); |
||||||
|
Serial.print((char)e.bit.KEY); |
||||||
|
if(e.bit.EVENT == KEY_JUST_PRESSED) Serial.println(" pressed"); |
||||||
|
else if(e.bit.EVENT == KEY_JUST_RELEASED) Serial.println(" released"); |
||||||
|
} |
||||||
|
|
||||||
|
delay(10); |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
// This file contains predefined setup for various Adafruit Matrix Keypads.
|
||||||
|
#ifndef __KEYPAD_CONFIG_H__ |
||||||
|
#define __KEYPAD_CONFIG_H__ |
||||||
|
|
||||||
|
#if defined(KEYPAD_PID3844) |
||||||
|
const byte ROWS = 4; // rows
|
||||||
|
const byte COLS = 4; // columns
|
||||||
|
// define the symbols on the buttons of the keypads
|
||||||
|
char keys[ROWS][COLS] = {{'1', '2', '3', 'A'}, |
||||||
|
{'4', '5', '6', 'B'}, |
||||||
|
{'7', '8', '9', 'C'}, |
||||||
|
{'*', '0', '#', 'D'}}; |
||||||
|
byte rowPins[ROWS] = {R1, R2, R3, |
||||||
|
R4}; // connect to the row pinouts of the keypad
|
||||||
|
byte colPins[COLS] = {C1, C2, C3, |
||||||
|
C4}; // connect to the column pinouts of the keypad
|
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(KEYPAD_PID1824) || defined(KEYPAD_PID3845) || defined(KEYPAD_PID419) |
||||||
|
const byte ROWS = 4; // rows
|
||||||
|
const byte COLS = 3; // columns
|
||||||
|
// define the symbols on the buttons of the keypads
|
||||||
|
char keys[ROWS][COLS] = { |
||||||
|
{'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}, {'*', '0', '#'}}; |
||||||
|
byte rowPins[ROWS] = {R1, R2, R3, |
||||||
|
R4}; // connect to the row pinouts of the keypad
|
||||||
|
byte colPins[COLS] = {C1, C2, C3}; // connect to the column pinouts of the
|
||||||
|
// keypad
|
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(KEYPAD_PID1332) |
||||||
|
const byte ROWS = 1; // rows
|
||||||
|
const byte COLS = 4; // columns
|
||||||
|
// define the symbols on the buttons of the keypads
|
||||||
|
char keys[ROWS][COLS] = { |
||||||
|
{'1', '2', '3', '4'}, |
||||||
|
}; |
||||||
|
byte rowPins[ROWS] = {R1}; // connect to the row pinouts of the keypad
|
||||||
|
byte colPins[COLS] = {C1, C2, C3, |
||||||
|
C4}; // connect to the column pinouts of the keypad
|
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,47 @@ |
|||||||
|
// Use this example with the Adafruit Keypad products.
|
||||||
|
// You'll need to know the Product ID for your keypad.
|
||||||
|
// Here's a summary:
|
||||||
|
// * PID3844 4x4 Matrix Keypad
|
||||||
|
// * PID3845 3x4 Matrix Keypad
|
||||||
|
// * PID1824 3x4 Phone-style Matrix Keypad
|
||||||
|
// * PID1332 Membrane 1x4 Keypad
|
||||||
|
// * PID419 Membrane 3x4 Matrix Keypad
|
||||||
|
|
||||||
|
#include "Adafruit_Keypad.h" |
||||||
|
|
||||||
|
// define your specific keypad here via PID
|
||||||
|
#define KEYPAD_PID3844 |
||||||
|
// define your pins here
|
||||||
|
// can ignore ones that don't apply
|
||||||
|
#define R1 2 |
||||||
|
#define R2 3 |
||||||
|
#define R3 4 |
||||||
|
#define R4 5 |
||||||
|
#define C1 8 |
||||||
|
#define C2 9 |
||||||
|
#define C3 10 |
||||||
|
#define C4 11 |
||||||
|
// leave this import after the above configuration
|
||||||
|
#include "keypad_config.h" |
||||||
|
|
||||||
|
//initialize an instance of class NewKeypad
|
||||||
|
Adafruit_Keypad customKeypad = Adafruit_Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS); |
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(9600); |
||||||
|
customKeypad.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// put your main code here, to run repeatedly:
|
||||||
|
customKeypad.tick(); |
||||||
|
|
||||||
|
while(customKeypad.available()){ |
||||||
|
keypadEvent e = customKeypad.read(); |
||||||
|
Serial.print((char)e.bit.KEY); |
||||||
|
if(e.bit.EVENT == KEY_JUST_PRESSED) Serial.println(" pressed"); |
||||||
|
else if(e.bit.EVENT == KEY_JUST_RELEASED) Serial.println(" released"); |
||||||
|
} |
||||||
|
|
||||||
|
delay(10); |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
#include "Adafruit_Keypad.h" |
||||||
|
|
||||||
|
const byte ROWS = 4; // four rows
|
||||||
|
const byte COLS = 8; // eight columns
|
||||||
|
//define the symbols on the buttons of the keypads
|
||||||
|
byte trellisKeys[ROWS][COLS] = { |
||||||
|
{1, 2, 3, 4, 5, 6, 7, 8}, |
||||||
|
{9, 10, 11, 12, 13, 14, 15, 16}, |
||||||
|
{17, 18, 19, 20, 21, 22, 23, 24}, |
||||||
|
{25, 26, 27, 28, 29, 30, 31, 32} |
||||||
|
}; |
||||||
|
byte rowPins[ROWS] = {14, 15, 16, 17}; //connect to the row pinouts of the keypad
|
||||||
|
byte colPins[COLS] = {2, 3, 4, 5, 6, 7, 8, 9}; //connect to the column pinouts of the keypad
|
||||||
|
|
||||||
|
//initialize an instance of class NewKeypad
|
||||||
|
Adafruit_Keypad customKeypad = Adafruit_Keypad( makeKeymap(trellisKeys), rowPins, colPins, ROWS, COLS);
|
||||||
|
|
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
customKeypad.begin(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// put your main code here, to run repeatedly:
|
||||||
|
customKeypad.tick(); |
||||||
|
|
||||||
|
while(customKeypad.available()){ |
||||||
|
keypadEvent e = customKeypad.read(); |
||||||
|
Serial.print((int)e.bit.KEY); |
||||||
|
if(e.bit.EVENT == KEY_JUST_PRESSED) Serial.println(" pressed"); |
||||||
|
else if(e.bit.EVENT == KEY_JUST_RELEASED) Serial.println(" released"); |
||||||
|
} |
||||||
|
|
||||||
|
delay(10); |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
name=Adafruit Keypad |
||||||
|
version=1.3.2 |
||||||
|
author=Adafruit |
||||||
|
maintainer=Adafruit <info@adafruit.com> |
||||||
|
sentence=Diode-multiplexed keypad support for Arduino |
||||||
|
paragraph=Diode-multiplexed keypad support for Arduino |
||||||
|
category=Signal Input/Output |
||||||
|
url=https://github.com/adafruit/Adafruit_Keypad |
||||||
|
architectures=* |
||||||
|
depends=Adafruit NeoPixel |
@ -0,0 +1,5 @@ |
|||||||
|
cmake_minimum_required(VERSION 3.5) |
||||||
|
idf_component_register(SRCS "ESPRotary.cpp" |
||||||
|
REQUIRES "arduino-esp32" "spi_flash" "esp_partition" |
||||||
|
INCLUDE_DIRS ".") |
||||||
|
project(ESPRotary) |
@ -0,0 +1,435 @@ |
|||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
/*
|
||||||
|
ESP8266/Arduino Library for reading rotary encoder values. |
||||||
|
Copyright 2017-2022 Lennart Hennigs. |
||||||
|
*/ |
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "ESPRotary.h" |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// initialize static counter
|
||||||
|
|
||||||
|
int ESPRotary::_nextID = 0; |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::_setID() |
||||||
|
{ |
||||||
|
id = _nextID; |
||||||
|
_nextID++; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
ESPRotary::ESPRotary() |
||||||
|
{ |
||||||
|
_setID(); |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
ESPRotary::ESPRotary(byte pin1, byte pin2, byte steps_per_click /* = 1 */, int lower_bound /* = INT16_MIN */, int upper_bound /* = INT16_MAX */, int inital_pos /* = 0 */, int increment /* = 1 */) |
||||||
|
{ |
||||||
|
ESPRotary(); |
||||||
|
begin(pin1, pin2, steps_per_click, lower_bound, upper_bound, inital_pos, increment); |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::begin(byte pin1, byte pin2, byte steps_per_click /* = 1 */, int lower_bound /* = INT16_MIN */, int upper_bound /* = INT16_MAX */, int inital_pos /* = 0 */, int increment /* = 1 */) |
||||||
|
{ |
||||||
|
this->pin1 = pin1; |
||||||
|
this->pin2 = pin2; |
||||||
|
pinMode(pin1, INPUT_PULLUP); |
||||||
|
pinMode(pin2, INPUT_PULLUP); |
||||||
|
|
||||||
|
setUpperBound(upper_bound); |
||||||
|
setLowerBound(lower_bound); |
||||||
|
setIncrement(increment); |
||||||
|
setStepsPerClick(steps_per_click); |
||||||
|
|
||||||
|
loop(); |
||||||
|
steps = inital_pos * steps_per_click; |
||||||
|
last_event = rotary_event::none; |
||||||
|
dir = rotary_direction::undefined; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setUpperBound(int upper) |
||||||
|
{ |
||||||
|
upper_bound = (lower_bound < upper) ? upper : lower_bound; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setLowerBound(int lower) |
||||||
|
{ |
||||||
|
lower_bound = (lower < upper_bound) ? lower : upper_bound; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getUpperBound() const |
||||||
|
{ |
||||||
|
return upper_bound; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getLowerBound() const |
||||||
|
{ |
||||||
|
return lower_bound; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setChangedHandler(CallbackFunction f) |
||||||
|
{ |
||||||
|
change_cb = f; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setRightRotationHandler(CallbackFunction f) |
||||||
|
{ |
||||||
|
right_cb = f; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setLeftRotationHandler(CallbackFunction f) |
||||||
|
{ |
||||||
|
left_cb = f; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setUpperOverflowHandler(CallbackFunction f) |
||||||
|
{ |
||||||
|
upper_cb = f; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setLowerOverflowHandler(CallbackFunction f) |
||||||
|
{ |
||||||
|
lower_cb = f; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setSpeedupStartedHandler(CallbackFunction f) |
||||||
|
{ |
||||||
|
speedup_start_cb = f; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setSpeedupEndedHandler(CallbackFunction f) |
||||||
|
{ |
||||||
|
speedup_end_cb = f; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::resetPosition(int p /* = 0 */, bool fireCallback /* = true */) |
||||||
|
{ |
||||||
|
// change position?
|
||||||
|
if (p == getPosition()) |
||||||
|
return; |
||||||
|
// yes...
|
||||||
|
steps = p * steps_per_click; |
||||||
|
_isWithinBounds(); |
||||||
|
if (fireCallback) |
||||||
|
_callCallback(change_cb); |
||||||
|
last_event = rotary_event::none; |
||||||
|
dir = rotary_direction::undefined; |
||||||
|
in_speedup = false; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setIncrement(int increment) |
||||||
|
{ |
||||||
|
this->increment = increment; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getIncrement() const |
||||||
|
{ |
||||||
|
return increment; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setStepsPerClick(int steps) |
||||||
|
{ |
||||||
|
steps_per_click = (steps < 1) ? 1 : steps; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getStepsPerClick() const |
||||||
|
{ |
||||||
|
return steps_per_click; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
rotary_direction ESPRotary::getDirection() const |
||||||
|
{ |
||||||
|
return dir; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
String ESPRotary::directionToString(rotary_direction dir) const |
||||||
|
{ |
||||||
|
return (dir == rotary_direction::right) ? "right" : "left"; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getPosition() const |
||||||
|
{ |
||||||
|
return steps / steps_per_click; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getID() const |
||||||
|
{ |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setID(int newID) |
||||||
|
{ |
||||||
|
id = newID; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool ESPRotary::operator==(ESPRotary &rhs) |
||||||
|
{ |
||||||
|
return (this == &rhs); |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::loop() |
||||||
|
{ |
||||||
|
unsigned long now = millis(); |
||||||
|
// did it change (enough)?
|
||||||
|
if (!_wasRotated()) |
||||||
|
return; |
||||||
|
dir = (steps > last_steps) ? rotary_direction::right : rotary_direction::left; |
||||||
|
// shall I speedup things
|
||||||
|
if (enable_speedup) |
||||||
|
_checkForSpeedup(now); |
||||||
|
// are we out of bounds?
|
||||||
|
if (_isWithinBounds(true)) |
||||||
|
{ |
||||||
|
// trigger rotation event
|
||||||
|
_setEvent((dir == rotary_direction::right) ? rotary_event::right_rotation : rotary_event::left_rotation); |
||||||
|
last_turn = now; |
||||||
|
} |
||||||
|
last_steps = steps; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool ESPRotary::_wasRotated() |
||||||
|
{ |
||||||
|
static const int8_t factors[] = {0, 1, -1, 2, -1, 0, -2, 1, 1, -2, 0, -1, 2, -1, 1, 0}; |
||||||
|
int encoderState = (state & 3) | digitalRead(pin1) << 2 | digitalRead(pin2) << 3; |
||||||
|
steps += factors[encoderState] * increment; |
||||||
|
state = (encoderState >> 2); |
||||||
|
int stepDifference = abs(steps - last_steps); |
||||||
|
return stepDifference >= (steps_per_click * increment); |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::_checkForSpeedup(unsigned long now) |
||||||
|
{ |
||||||
|
if (now - last_turn > speedup_interval) |
||||||
|
{ |
||||||
|
if (in_speedup) |
||||||
|
_setEvent(rotary_event::speedup_ended); |
||||||
|
return; |
||||||
|
} |
||||||
|
steps += ((dir == rotary_direction::right ? 1 : -1) * (speedup_increment - increment) * steps_per_click); |
||||||
|
int pos = getPosition(); |
||||||
|
// only trigger speedup when you are not "on a wall"
|
||||||
|
if (pos > lower_bound && pos < upper_bound) |
||||||
|
{ |
||||||
|
if (!in_speedup) |
||||||
|
_setEvent(rotary_event::speedup_started); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::triggerOnBounds(bool triggerEvents /* = true */) |
||||||
|
{ |
||||||
|
boundsTrigger = triggerEvents; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::_callCallback(CallbackFunction callback) |
||||||
|
{ |
||||||
|
if (callback != NULL) |
||||||
|
callback(*this); |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool ESPRotary::_isWithinBounds(bool triggerAlerts /* = false */) |
||||||
|
{ |
||||||
|
int pos = getPosition(); |
||||||
|
if (pos > lower_bound && pos < upper_bound) |
||||||
|
return true; |
||||||
|
|
||||||
|
if (pos >= upper_bound) |
||||||
|
{ |
||||||
|
steps = upper_bound * steps_per_click; |
||||||
|
if (in_speedup) |
||||||
|
_setEvent(rotary_event::speedup_ended); |
||||||
|
if (triggerAlerts) |
||||||
|
_setEvent(rotary_event::upper_bound_hit); |
||||||
|
} |
||||||
|
else if (pos <= lower_bound) |
||||||
|
{ |
||||||
|
steps = lower_bound * steps_per_click; |
||||||
|
if (in_speedup) |
||||||
|
_setEvent(rotary_event::speedup_ended); |
||||||
|
if (triggerAlerts) |
||||||
|
_setEvent(rotary_event::lower_bound_hit); |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::_setEvent(rotary_event event) |
||||||
|
{ |
||||||
|
switch (event) |
||||||
|
{ |
||||||
|
case rotary_event::left_rotation: |
||||||
|
_callCallback(left_cb); |
||||||
|
_callCallback(change_cb); |
||||||
|
break; |
||||||
|
|
||||||
|
case rotary_event::right_rotation: |
||||||
|
_callCallback(right_cb); |
||||||
|
_callCallback(change_cb); |
||||||
|
break; |
||||||
|
|
||||||
|
case rotary_event::speedup_started: |
||||||
|
_callCallback(speedup_start_cb); |
||||||
|
in_speedup = true; |
||||||
|
break; |
||||||
|
|
||||||
|
case rotary_event::speedup_ended: |
||||||
|
_callCallback(speedup_end_cb); |
||||||
|
in_speedup = false; |
||||||
|
break; |
||||||
|
|
||||||
|
case rotary_event::upper_bound_hit: |
||||||
|
if (last_event == rotary_event::upper_bound_hit && !retrigger_event) |
||||||
|
return; |
||||||
|
if (boundsTrigger) |
||||||
|
{ |
||||||
|
_callCallback(right_cb); |
||||||
|
_callCallback(change_cb); |
||||||
|
} |
||||||
|
_callCallback(upper_cb); |
||||||
|
break; |
||||||
|
|
||||||
|
case rotary_event::lower_bound_hit: |
||||||
|
if (last_event == rotary_event::lower_bound_hit && !retrigger_event) |
||||||
|
return; |
||||||
|
if (boundsTrigger) |
||||||
|
{ |
||||||
|
_callCallback(left_cb); |
||||||
|
_callCallback(change_cb); |
||||||
|
} |
||||||
|
_callCallback(lower_cb); |
||||||
|
break; |
||||||
|
|
||||||
|
case rotary_event::none: |
||||||
|
break; |
||||||
|
} |
||||||
|
last_event = event; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setSpeedupInterval(int interval) |
||||||
|
{ |
||||||
|
speedup_interval = interval; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getSpeedupInterval() const |
||||||
|
{ |
||||||
|
return speedup_interval; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::setSpeedupIncrement(int increment) |
||||||
|
{ |
||||||
|
speedup_increment = increment; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int ESPRotary::getSpeedupIncrement() const |
||||||
|
{ |
||||||
|
return speedup_increment; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::enableSpeedup(bool enable) |
||||||
|
{ |
||||||
|
enable_speedup = enable; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool ESPRotary::isSpeedupEnabled() const |
||||||
|
{ |
||||||
|
return enable_speedup; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
rotary_event ESPRotary::getLastEvent() const |
||||||
|
{ |
||||||
|
return last_event; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void ESPRotary::retriggerEvent(bool retrigger) |
||||||
|
{ |
||||||
|
retrigger_event = retrigger; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool ESPRotary::isInSpeedup() const |
||||||
|
{ |
||||||
|
return in_speedup; |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
@ -0,0 +1,136 @@ |
|||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
/*
|
||||||
|
ESP8266/Arduino Library for reading rotary encoder values. |
||||||
|
Copyright 2017-2022 Lennart Hennigs |
||||||
|
*/ |
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#ifndef ESPRotary_h |
||||||
|
#define ESPRotary_h |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
enum class rotary_direction |
||||||
|
{ |
||||||
|
undefined = 0, |
||||||
|
right = 1, |
||||||
|
left = 255 |
||||||
|
}; |
||||||
|
|
||||||
|
enum class rotary_event |
||||||
|
{ |
||||||
|
left_rotation, |
||||||
|
right_rotation, |
||||||
|
speedup_started, |
||||||
|
speedup_ended, |
||||||
|
upper_bound_hit, |
||||||
|
lower_bound_hit, |
||||||
|
none |
||||||
|
}; |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class ESPRotary |
||||||
|
{ |
||||||
|
protected: |
||||||
|
int id; |
||||||
|
byte pin1, pin2; |
||||||
|
byte steps_per_click; |
||||||
|
int lower_bound; |
||||||
|
int upper_bound; |
||||||
|
byte state; |
||||||
|
int increment; |
||||||
|
int steps = 0; |
||||||
|
int last_steps = 0; |
||||||
|
rotary_event last_event; |
||||||
|
rotary_direction dir; |
||||||
|
|
||||||
|
bool boundsTrigger = true; |
||||||
|
bool retrigger_event = true; |
||||||
|
bool enable_speedup = false; |
||||||
|
unsigned int speedup_increment = 5; |
||||||
|
unsigned int speedup_interval = 75; |
||||||
|
int in_speedup = false; |
||||||
|
unsigned long last_turn = 0; |
||||||
|
|
||||||
|
using CallbackFunction = void (*)(ESPRotary &); |
||||||
|
|
||||||
|
CallbackFunction change_cb = NULL; |
||||||
|
CallbackFunction right_cb = NULL; |
||||||
|
CallbackFunction left_cb = NULL; |
||||||
|
CallbackFunction lower_cb = NULL; |
||||||
|
CallbackFunction upper_cb = NULL; |
||||||
|
|
||||||
|
CallbackFunction speedup_start_cb = NULL; |
||||||
|
CallbackFunction speedup_end_cb = NULL; |
||||||
|
|
||||||
|
public: |
||||||
|
ESPRotary(); |
||||||
|
ESPRotary(byte pin1, byte pin2, byte steps_per_click = 1, int lower_bound = INT16_MIN, int upper_bound = INT16_MAX, int inital_pos = 0, int increment = 1); |
||||||
|
|
||||||
|
void begin(byte pin1, byte pin2, byte steps_per_click = 1, int lower_bound = INT16_MIN, int upper_bound = INT16_MAX, int inital_pos = 0, int increment = 1); |
||||||
|
|
||||||
|
int getPosition() const; |
||||||
|
void resetPosition(int p = 0, bool fireCallback = true); |
||||||
|
|
||||||
|
void triggerOnBounds(bool triggerEvents = true); |
||||||
|
rotary_direction getDirection() const; |
||||||
|
String directionToString(rotary_direction dir) const; |
||||||
|
|
||||||
|
void setIncrement(int increment); |
||||||
|
int getIncrement() const; |
||||||
|
|
||||||
|
void enableSpeedup(bool enable); |
||||||
|
void setSpeedupInterval(int interval); |
||||||
|
void setSpeedupIncrement(int increment); |
||||||
|
|
||||||
|
bool isSpeedupEnabled() const; |
||||||
|
int getSpeedupInterval() const; |
||||||
|
int getSpeedupIncrement() const; |
||||||
|
bool isInSpeedup() const; |
||||||
|
|
||||||
|
rotary_event getLastEvent() const; |
||||||
|
void retriggerEvent(bool retrigger); |
||||||
|
|
||||||
|
void setUpperBound(int upper_bound); |
||||||
|
void setLowerBound(int lower_bound); |
||||||
|
int getUpperBound() const; |
||||||
|
int getLowerBound() const; |
||||||
|
|
||||||
|
void setStepsPerClick(int steps); |
||||||
|
int getStepsPerClick() const; |
||||||
|
|
||||||
|
void setChangedHandler(CallbackFunction f); |
||||||
|
void setRightRotationHandler(CallbackFunction f); |
||||||
|
void setLeftRotationHandler(CallbackFunction f); |
||||||
|
void setUpperOverflowHandler(CallbackFunction f); |
||||||
|
void setLowerOverflowHandler(CallbackFunction f); |
||||||
|
void setSpeedupStartedHandler(CallbackFunction f); |
||||||
|
void setSpeedupEndedHandler(CallbackFunction f); |
||||||
|
|
||||||
|
int getID() const; |
||||||
|
void setID(int newID); |
||||||
|
|
||||||
|
bool operator==(ESPRotary &rhs); |
||||||
|
|
||||||
|
void loop(); |
||||||
|
|
||||||
|
private: |
||||||
|
static int _nextID; |
||||||
|
|
||||||
|
void _callCallback(CallbackFunction callback); |
||||||
|
void _setEvent(rotary_event e); |
||||||
|
bool _wasRotated(); |
||||||
|
bool _isWithinBounds(bool triggerAlerts = false); |
||||||
|
void _checkForSpeedup(unsigned long now); |
||||||
|
void _setID(); |
||||||
|
}; |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
#endif |
||||||
|
/////////////////////////////////////////////////////////////////
|
@ -0,0 +1,5 @@ |
|||||||
|
cmake_minimum_required(VERSION 3.5) |
||||||
|
idf_component_register(SRCS "esp_lan.c" |
||||||
|
PRIV_REQUIRES esp_netif driver esp_wifi vfs |
||||||
|
INCLUDE_DIRS ".") |
||||||
|
project(esp_lan) |
@ -0,0 +1,51 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" |
||||||
|
{ |
||||||
|
#endif |
||||||
|
typedef enum |
||||||
|
{ |
||||||
|
W5500, |
||||||
|
DM9051 |
||||||
|
} lan_eth_driver; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
bool enabled; |
||||||
|
uint16_t reset_io; |
||||||
|
uint16_t miso_io; |
||||||
|
uint16_t mosi_io; |
||||||
|
uint16_t sclk_io; |
||||||
|
uint16_t cs_io; |
||||||
|
uint16_t phy_addr; |
||||||
|
uint16_t spi_host; |
||||||
|
uint16_t spi_mhz; |
||||||
|
uint16_t stack_size; |
||||||
|
uint16_t driver; |
||||||
|
} lan_eth_config; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
int rssi; |
||||||
|
uint16_t authmode; |
||||||
|
} wifi_config_threshold; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
bool enabled; |
||||||
|
char *ssid; |
||||||
|
char *password; |
||||||
|
uint16_t scan_method; |
||||||
|
uint16_t sort_method; |
||||||
|
struct wifi_config_threshold *threshold; |
||||||
|
} wifi_config; |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
struct lan_eth_config *eth; |
||||||
|
struct wifi_config *wifi; |
||||||
|
} network_config; |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,17 @@ |
|||||||
|
cmake_minimum_required(VERSION 3.5) |
||||||
|
# set(COMPONENT_REQUIRES "esp_timer") |
||||||
|
# set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) |
||||||
|
set(COMPONENT_REQUIRES "protocol_examples_common") |
||||||
|
idf_component_register(SRCS "esp_server.c" "keep_alive.c" "udp_server.c" "websocket_client.c" |
||||||
|
|
||||||
|
REQUIRES "esp_timer" "esp_event" "nvs_flash" "esp_netif" "esp_eth" "esp_wifi" "protocol_examples_common" "esp_https_server" "json_parser" "esp_websocket_client" |
||||||
|
INCLUDE_DIRS "." |
||||||
|
EMBED_TXTFILES "certs/servercert.pem" |
||||||
|
"certs/prvtkey.pem" |
||||||
|
"esp_vue/dist/index.html" |
||||||
|
"esp_vue/dist/favicon.ico" |
||||||
|
"esp_vue/dist/assets/SettingsView.css" |
||||||
|
"esp_vue/dist/assets/SettingsView.js" |
||||||
|
"esp_vue/dist/assets/index.css" |
||||||
|
"esp_vue/dist/assets/index.js") |
||||||
|
project(server) |
@ -0,0 +1,39 @@ |
|||||||
|
menu "Example Configuration" |
||||||
|
|
||||||
|
config EXAMPLE_IPV4 |
||||||
|
bool "IPV4" |
||||||
|
default y |
||||||
|
|
||||||
|
config EXAMPLE_IPV6 |
||||||
|
bool "IPV6" |
||||||
|
default n |
||||||
|
select EXAMPLE_CONNECT_IPV6 |
||||||
|
|
||||||
|
config EXAMPLE_PORT |
||||||
|
int "Port" |
||||||
|
range 0 65535 |
||||||
|
default 9003 |
||||||
|
help |
||||||
|
Local port the example server will listen on. |
||||||
|
|
||||||
|
choice WEBSOCKET_URI_SOURCE |
||||||
|
prompt "Websocket URI source" |
||||||
|
default WEBSOCKET_URI_FROM_STRING |
||||||
|
help |
||||||
|
Selects the source of the URI used in the example. |
||||||
|
|
||||||
|
config WEBSOCKET_URI_FROM_STRING |
||||||
|
bool "From string" |
||||||
|
|
||||||
|
config WEBSOCKET_URI_FROM_STDIN |
||||||
|
bool "From stdin" |
||||||
|
endchoice |
||||||
|
|
||||||
|
config WEBSOCKET_URI |
||||||
|
string "Websocket endpoint URI" |
||||||
|
depends on WEBSOCKET_URI_FROM_STRING |
||||||
|
default "ws://localhost:9999/qlcplusWS" |
||||||
|
help |
||||||
|
URL of websocket endpoint this example connects to and sends echo |
||||||
|
|
||||||
|
endmenu |
@ -0,0 +1,28 @@ |
|||||||
|
-----BEGIN PRIVATE KEY----- |
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwYp7epz++0QkH |
||||||
|
JioMD7U7BitLgpcYPi8Cid1l7snt6Kp546iQsDBJ3l8xnRtPU7ANEsjT8KxIHmyw |
||||||
|
h/NGp94FlOKRw3ahh3yUGtowS9vdHv+S+TAfuj07NjSnKIyv5KnGZJ+fDFl4Q1tT |
||||||
|
aQJybY1Z4itirL6/2CGEm8g/iYhLNDBsRMfpDpfXe4URyWiM3Rhf7ztqZdveb9al |
||||||
|
3pAJZIDTLWCFQI1MvQjKamkAQkES/gZj0iUZFwbGJPBj54nkuLFLKedw7DbwgrVg |
||||||
|
0+n3fQ9b/gQepw5PxQjyobY2DsDgGZV+MFjUmaUTa+XX68SrG4wJ+DwrkdmpHReB |
||||||
|
vFi1Hg1hAgMBAAECggEAaTCnZkl/7qBjLexIryC/CBBJyaJ70W1kQ7NMYfniWwui |
||||||
|
f0aRxJgOdD81rjTvkINsPp+xPRQO6oOadjzdjImYEuQTqrJTEUnntbu924eh+2D9 |
||||||
|
Mf2CAanj0mglRnscS9mmljZ0KzoGMX6Z/EhnuS40WiJTlWlH6MlQU/FDnwC6U34y |
||||||
|
JKy6/jGryfsx+kGU/NRvKSru6JYJWt5v7sOrymHWD62IT59h3blOiP8GMtYKeQlX |
||||||
|
49om9Mo1VTIFASY3lrxmexbY+6FG8YO+tfIe0tTAiGrkb9Pz6tYbaj9FjEWOv4Vc |
||||||
|
+3VMBUVdGJjgqvE8fx+/+mHo4Rg69BUPfPSrpEg7sQKBgQDlL85G04VZgrNZgOx6 |
||||||
|
pTlCCl/NkfNb1OYa0BELqWINoWaWQHnm6lX8YjrUjwRpBF5s7mFhguFjUjp/NW6D |
||||||
|
0EEg5BmO0ePJ3dLKSeOA7gMo7y7kAcD/YGToqAaGljkBI+IAWK5Su5yldrECTQKG |
||||||
|
YnMKyQ1MWUfCYEwHtPvFvE5aPwKBgQDFBWXekpxHIvt/B41Cl/TftAzE7/f58JjV |
||||||
|
MFo/JCh9TDcH6N5TMTRS1/iQrv5M6kJSSrHnq8pqDXOwfHLwxetpk9tr937VRzoL |
||||||
|
CuG1Ar7c1AO6ujNnAEmUVC2DppL/ck5mRPWK/kgLwZSaNcZf8sydRgphsW1ogJin |
||||||
|
7g0nGbFwXwKBgQCPoZY07Pr1TeP4g8OwWTu5F6dSvdU2CAbtZthH5q98u1n/cAj1 |
||||||
|
noak1Srpa3foGMTUn9CHu+5kwHPIpUPNeAZZBpq91uxa5pnkDMp3UrLIRJ2uZyr8 |
||||||
|
4PxcknEEh8DR5hsM/IbDcrCJQglM19ZtQeW3LKkY4BsIxjDf45ymH407IQKBgE/g |
||||||
|
Ul6cPfOxQRlNLH4VMVgInSyyxWx1mODFy7DRrgCuh5kTVh+QUVBM8x9lcwAn8V9/ |
||||||
|
nQT55wR8E603pznqY/jX0xvAqZE6YVPcw4kpZcwNwL1RhEl8GliikBlRzUL3SsW3 |
||||||
|
q30AfqEViHPE3XpE66PPo6Hb1ymJCVr77iUuC3wtAoGBAIBrOGunv1qZMfqmwAY2 |
||||||
|
lxlzRgxgSiaev0lTNxDzZkmU/u3dgdTwJ5DDANqPwJc6b8SGYTp9rQ0mbgVHnhIB |
||||||
|
jcJQBQkTfq6Z0H6OoTVi7dPs3ibQJFrtkoyvYAbyk36quBmNRjVh6rc8468bhXYr |
||||||
|
v/t+MeGJP/0Zw8v/X2CFll96 |
||||||
|
-----END PRIVATE KEY----- |
@ -0,0 +1,19 @@ |
|||||||
|
-----BEGIN CERTIFICATE----- |
||||||
|
MIIDKzCCAhOgAwIBAgIUBxM3WJf2bP12kAfqhmhhjZWv0ukwDQYJKoZIhvcNAQEL |
||||||
|
BQAwJTEjMCEGA1UEAwwaRVNQMzIgSFRUUFMgc2VydmVyIGV4YW1wbGUwHhcNMTgx |
||||||
|
MDE3MTEzMjU3WhcNMjgxMDE0MTEzMjU3WjAlMSMwIQYDVQQDDBpFU1AzMiBIVFRQ |
||||||
|
UyBzZXJ2ZXIgZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB |
||||||
|
ALBint6nP77RCQcmKgwPtTsGK0uClxg+LwKJ3WXuye3oqnnjqJCwMEneXzGdG09T |
||||||
|
sA0SyNPwrEgebLCH80an3gWU4pHDdqGHfJQa2jBL290e/5L5MB+6PTs2NKcojK/k |
||||||
|
qcZkn58MWXhDW1NpAnJtjVniK2Ksvr/YIYSbyD+JiEs0MGxEx+kOl9d7hRHJaIzd |
||||||
|
GF/vO2pl295v1qXekAlkgNMtYIVAjUy9CMpqaQBCQRL+BmPSJRkXBsYk8GPnieS4 |
||||||
|
sUsp53DsNvCCtWDT6fd9D1v+BB6nDk/FCPKhtjYOwOAZlX4wWNSZpRNr5dfrxKsb |
||||||
|
jAn4PCuR2akdF4G8WLUeDWECAwEAAaNTMFEwHQYDVR0OBBYEFMnmdJKOEepXrHI/ |
||||||
|
ivM6mVqJgAX8MB8GA1UdIwQYMBaAFMnmdJKOEepXrHI/ivM6mVqJgAX8MA8GA1Ud |
||||||
|
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADiXIGEkSsN0SLSfCF1VNWO3 |
||||||
|
emBurfOcDq4EGEaxRKAU0814VEmU87btIDx80+z5Dbf+GGHCPrY7odIkxGNn0DJY |
||||||
|
W1WcF+DOcbiWoUN6DTkAML0SMnp8aGj9ffx3x+qoggT+vGdWVVA4pgwqZT7Ybntx |
||||||
|
bkzcNFW0sqmCv4IN1t4w6L0A87ZwsNwVpre/j6uyBw7s8YoJHDLRFT6g7qgn0tcN |
||||||
|
ZufhNISvgWCVJQy/SZjNBHSpnIdCUSJAeTY2mkM4sGxY0Widk8LnjydxZUSxC3Nl |
||||||
|
hb6pnMh3jRq4h0+5CZielA4/a+TdrNPv/qok67ot/XJdY3qHCCd8O2b14OVq9jo= |
||||||
|
-----END CERTIFICATE----- |
@ -0,0 +1,559 @@ |
|||||||
|
/* Simple HTTP + SSL + WS Server Example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.) |
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this |
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||||
|
CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <esp_event.h> |
||||||
|
#include <esp_log.h> |
||||||
|
#include <esp_system.h> |
||||||
|
#include <nvs_flash.h> |
||||||
|
#include <sys/param.h> |
||||||
|
#include "esp_netif.h" |
||||||
|
#include "esp_eth.h" |
||||||
|
#include "esp_wifi.h" |
||||||
|
#include "protocol_examples_common.h" |
||||||
|
#include "lwip/sockets.h" |
||||||
|
#include <esp_https_server.h> |
||||||
|
#include "keep_alive.h" |
||||||
|
#include "sdkconfig.h" |
||||||
|
#include "json_parser.h" |
||||||
|
|
||||||
|
#if !CONFIG_HTTPD_WS_SUPPORT |
||||||
|
#error This example cannot be used unless HTTPD_WS_SUPPORT is enabled in esp-http-server component configuration |
||||||
|
#endif |
||||||
|
|
||||||
|
struct async_resp_arg |
||||||
|
{ |
||||||
|
httpd_handle_t hd; |
||||||
|
int fd; |
||||||
|
}; |
||||||
|
|
||||||
|
static const char *TAG_SERVER = "wss_echo_server"; |
||||||
|
static const size_t max_clients = 4; |
||||||
|
/* An HTTP GET handler */ |
||||||
|
extern const uint8_t file_index_html_start[] asm("_binary_index_html_start"); |
||||||
|
// extern const uint8_t file_index_html_end[] asm("_binary_index_html_end");
|
||||||
|
// static esp_err_t index_html_handler(httpd_req_t *req)
|
||||||
|
// {
|
||||||
|
// httpd_resp_set_type(req, "text/html");
|
||||||
|
// httpd_resp_send(req, (char *)file_index_html_start, HTTPD_RESP_USE_STRLEN);
|
||||||
|
// return ESP_OK;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// static const httpd_uri_t index_html = {
|
||||||
|
// .uri = "/",
|
||||||
|
// .method = HTTP_GET,
|
||||||
|
// .handler = index_html_handler};
|
||||||
|
httpd_handle_t *gserver = NULL; |
||||||
|
|
||||||
|
int parse_json(char *payload, int length) |
||||||
|
{ |
||||||
|
jparse_ctx_t jctx; |
||||||
|
int ret = json_parse_start(&jctx, payload, length); |
||||||
|
if (ret != OS_SUCCESS) |
||||||
|
{ |
||||||
|
printf("Parser failed\n"); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
int bf00, bf01; |
||||||
|
|
||||||
|
if (json_obj_get_object(&jctx, "controls") == OS_SUCCESS) |
||||||
|
{ |
||||||
|
printf("Found controls\n"); |
||||||
|
if (json_obj_get_int(&jctx, "bf00", &bf00) == OS_SUCCESS) |
||||||
|
printf("bf00 %d\n", bf00); |
||||||
|
if (json_obj_get_int(&jctx, "bf01", &bf01) == OS_SUCCESS) |
||||||
|
printf("bf01 %d\n", bf01); |
||||||
|
json_obj_leave_object(&jctx); |
||||||
|
} |
||||||
|
|
||||||
|
// char str_val[64];
|
||||||
|
// int int_val, num_elem;
|
||||||
|
// int64_t int64_val;
|
||||||
|
// bool bool_val;
|
||||||
|
// float float_val;
|
||||||
|
|
||||||
|
// if (json_obj_get_string(&jctx, "str_val", str_val, sizeof(str_val)) == OS_SUCCESS)
|
||||||
|
// printf("str_val %s\n", str_val);
|
||||||
|
|
||||||
|
// if (json_obj_get_float(&jctx, "float_val", &float_val) == OS_SUCCESS)
|
||||||
|
// printf("float_val %f\n", float_val);
|
||||||
|
|
||||||
|
// if (json_obj_get_int(&jctx, "int_val", &int_val) == OS_SUCCESS)
|
||||||
|
// printf("int_val %d\n", int_val);
|
||||||
|
|
||||||
|
// if (json_obj_get_bool(&jctx, "bool_val", &bool_val) == OS_SUCCESS)
|
||||||
|
// printf("bool_val %s\n", bool_val ? "true" : "false");
|
||||||
|
|
||||||
|
// if (json_obj_get_array(&jctx, "supported_el", &num_elem) == OS_SUCCESS)
|
||||||
|
// {
|
||||||
|
// printf("Array has %d elements\n", num_elem);
|
||||||
|
// int i;
|
||||||
|
// for (i = 0; i < num_elem; i++)
|
||||||
|
// {
|
||||||
|
// json_arr_get_string(&jctx, i, str_val, sizeof(str_val));
|
||||||
|
// printf("index %d: %s\n", i, str_val);
|
||||||
|
// }
|
||||||
|
// json_obj_leave_array(&jctx);
|
||||||
|
// }
|
||||||
|
// if (json_obj_get_object(&jctx, "features") == OS_SUCCESS)
|
||||||
|
// {
|
||||||
|
// printf("Found object\n");
|
||||||
|
// if (json_obj_get_bool(&jctx, "objects", &bool_val) == OS_SUCCESS)
|
||||||
|
// printf("objects %s\n", bool_val ? "true" : "false");
|
||||||
|
// if (json_obj_get_string(&jctx, "arrays", str_val, sizeof(str_val)) == OS_SUCCESS)
|
||||||
|
// printf("arrays %s\n", str_val);
|
||||||
|
// json_obj_leave_object(&jctx);
|
||||||
|
// }
|
||||||
|
// if (json_obj_get_int64(&jctx, "int_64", &int64_val) == OS_SUCCESS)
|
||||||
|
// printf("int64_val %lld\n", int64_val);
|
||||||
|
|
||||||
|
json_parse_end(&jctx); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static esp_err_t all_to_index_handler(httpd_req_t *req) |
||||||
|
{ |
||||||
|
httpd_resp_set_type(req, "text/html"); |
||||||
|
httpd_resp_send(req, (char *)file_index_html_start, HTTPD_RESP_USE_STRLEN); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
static const httpd_uri_t all_to_index = { |
||||||
|
.uri = "*", |
||||||
|
.method = HTTP_GET, |
||||||
|
.handler = all_to_index_handler}; |
||||||
|
|
||||||
|
extern const uint8_t file_favicon_ico_start[] asm("_binary_favicon_ico_start"); |
||||||
|
static esp_err_t favicon_ico_handler(httpd_req_t *req) |
||||||
|
{ |
||||||
|
httpd_resp_set_type(req, "image/x-icon"); |
||||||
|
httpd_resp_send(req, (char *)file_favicon_ico_start, HTTPD_RESP_USE_STRLEN); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
static const httpd_uri_t favicon_ico = { |
||||||
|
.uri = "/favicon.ico", |
||||||
|
.method = HTTP_GET, |
||||||
|
.handler = favicon_ico_handler}; |
||||||
|
|
||||||
|
extern const uint8_t file_SettingsView_css_start[] asm("_binary_SettingsView_css_start"); |
||||||
|
static esp_err_t SettingsView_css_handler(httpd_req_t *req) |
||||||
|
{ |
||||||
|
httpd_resp_set_type(req, "text/css"); |
||||||
|
httpd_resp_send(req, (char *)file_SettingsView_css_start, HTTPD_RESP_USE_STRLEN); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
static const httpd_uri_t SettingsView_css = { |
||||||
|
.uri = "/assets/SettingsView.css", |
||||||
|
.method = HTTP_GET, |
||||||
|
.handler = SettingsView_css_handler}; |
||||||
|
|
||||||
|
//
|
||||||
|
extern const uint8_t file_SettingsView_js_start[] asm("_binary_SettingsView_js_start"); // application/javascript
|
||||||
|
static esp_err_t SettingsView_js_handler(httpd_req_t *req) |
||||||
|
{ |
||||||
|
httpd_resp_set_type(req, "application/javascript"); |
||||||
|
httpd_resp_send(req, (char *)file_SettingsView_js_start, HTTPD_RESP_USE_STRLEN); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
static const httpd_uri_t SettingsView_js = { |
||||||
|
.uri = "/assets/SettingsView.js", |
||||||
|
.method = HTTP_GET, |
||||||
|
.handler = SettingsView_js_handler}; |
||||||
|
//
|
||||||
|
extern const uint8_t file_index_css_start[] asm("_binary_index_css_start"); |
||||||
|
static esp_err_t index_css_handler(httpd_req_t *req) |
||||||
|
{ |
||||||
|
httpd_resp_set_type(req, "text/css"); |
||||||
|
httpd_resp_send(req, (char *)file_index_css_start, HTTPD_RESP_USE_STRLEN); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
static const httpd_uri_t index_css = { |
||||||
|
.uri = "/assets/index.css", |
||||||
|
.method = HTTP_GET, |
||||||
|
.handler = index_css_handler}; |
||||||
|
//
|
||||||
|
extern const uint8_t file_index_js_start[] asm("_binary_index_js_start"); |
||||||
|
static esp_err_t index_js_handler(httpd_req_t *req) |
||||||
|
{ |
||||||
|
httpd_resp_set_type(req, "application/javascript"); |
||||||
|
httpd_resp_send(req, (char *)file_index_js_start, HTTPD_RESP_USE_STRLEN); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
static const httpd_uri_t index_js = { |
||||||
|
.uri = "/assets/index.js", |
||||||
|
.method = HTTP_GET, |
||||||
|
.handler = index_js_handler}; |
||||||
|
//
|
||||||
|
// extern const uint8_t file_logo_svg_start[] asm("_binary_logo_svg_start");
|
||||||
|
// static esp_err_t logo_svg_handler(httpd_req_t *req)
|
||||||
|
// {
|
||||||
|
// httpd_resp_set_type(req, "image/svg");
|
||||||
|
// httpd_resp_send(req, (char *)file_logo_svg_start, HTTPD_RESP_USE_STRLEN);
|
||||||
|
// return ESP_OK;
|
||||||
|
// }
|
||||||
|
// static const httpd_uri_t logo_svg = {
|
||||||
|
// .uri = "/assets/logo.svg",
|
||||||
|
// .method = HTTP_GET,
|
||||||
|
// .handler = logo_svg_handler};
|
||||||
|
|
||||||
|
static void send_hello(void *arg) |
||||||
|
{ |
||||||
|
static const char *data = "Hello client"; |
||||||
|
struct async_resp_arg *resp_arg = arg; |
||||||
|
httpd_handle_t hd = resp_arg->hd; |
||||||
|
int fd = resp_arg->fd; |
||||||
|
httpd_ws_frame_t ws_pkt; |
||||||
|
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); |
||||||
|
ws_pkt.payload = (uint8_t *)data; |
||||||
|
ws_pkt.len = strlen(data); |
||||||
|
ws_pkt.type = HTTPD_WS_TYPE_TEXT; |
||||||
|
|
||||||
|
httpd_ws_send_frame_async(hd, fd, &ws_pkt); |
||||||
|
free(resp_arg); |
||||||
|
} |
||||||
|
static esp_err_t ws_handler(httpd_req_t *req) |
||||||
|
{ |
||||||
|
if (req->method == HTTP_GET) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG_SERVER, "Handshake done, the new connection was opened"); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
httpd_ws_frame_t ws_pkt; |
||||||
|
uint8_t *buf = NULL; |
||||||
|
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); |
||||||
|
|
||||||
|
// First receive the full ws message
|
||||||
|
/* Set max_len = 0 to get the frame len */ |
||||||
|
esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, 0); |
||||||
|
if (ret != ESP_OK) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "httpd_ws_recv_frame failed to get frame len with %d", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
ESP_LOGI(TAG_SERVER, "frame len is %d", ws_pkt.len); |
||||||
|
if (ws_pkt.len) |
||||||
|
{ |
||||||
|
/* ws_pkt.len + 1 is for NULL termination as we are expecting a string */ |
||||||
|
buf = calloc(1, ws_pkt.len + 1); |
||||||
|
if (buf == NULL) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "Failed to calloc memory for buf"); |
||||||
|
return ESP_ERR_NO_MEM; |
||||||
|
} |
||||||
|
ws_pkt.payload = buf; |
||||||
|
/* Set max_len = ws_pkt.len to get the frame payload */ |
||||||
|
ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len); |
||||||
|
if (ret != ESP_OK) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "httpd_ws_recv_frame failed with %d", ret); |
||||||
|
free(buf); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
// If it was a PONG, update the keep-alive
|
||||||
|
if (ws_pkt.type == HTTPD_WS_TYPE_PONG) |
||||||
|
{ |
||||||
|
ESP_LOGD(TAG_SERVER, "Received PONG message"); |
||||||
|
free(buf); |
||||||
|
return wss_keep_alive_client_is_active(httpd_get_global_user_ctx(req->handle), |
||||||
|
httpd_req_to_sockfd(req)); |
||||||
|
|
||||||
|
// If it was a TEXT message, just echo it back
|
||||||
|
} |
||||||
|
else if (ws_pkt.type == HTTPD_WS_TYPE_TEXT || ws_pkt.type == HTTPD_WS_TYPE_PING || ws_pkt.type == HTTPD_WS_TYPE_CLOSE) |
||||||
|
{ |
||||||
|
if (ws_pkt.type == HTTPD_WS_TYPE_TEXT) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG_SERVER, "Received packet with message: %s", ws_pkt.payload); |
||||||
|
parse_json((char *)ws_pkt.payload, strlen((char *)ws_pkt.payload)); |
||||||
|
if (*gserver) |
||||||
|
{ // httpd might not have been created by now
|
||||||
|
size_t clients = max_clients; |
||||||
|
int client_fds[max_clients]; |
||||||
|
if (httpd_get_client_list(*gserver, &clients, client_fds) == ESP_OK) |
||||||
|
{ |
||||||
|
for (size_t i = 0; i < clients; ++i) |
||||||
|
{ |
||||||
|
int sock = client_fds[i]; |
||||||
|
if (httpd_ws_get_fd_info(*gserver, sock) == HTTPD_WS_CLIENT_WEBSOCKET) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG_SERVER, "Active client (fd=%d) -> sending async message", sock); |
||||||
|
// struct async_resp_arg *resp_arg = malloc(sizeof(struct async_resp_arg));
|
||||||
|
// resp_arg->hd = *gserver;
|
||||||
|
// resp_arg->fd = sock;
|
||||||
|
// if (httpd_queue_work(resp_arg->hd, send_hello, resp_arg) != ESP_OK)
|
||||||
|
// {
|
||||||
|
// ESP_LOGE(TAG_SERVER, "httpd_queue_work failed!");
|
||||||
|
// // send_messages = false;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "httpd_get_client_list failed!"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else if (ws_pkt.type == HTTPD_WS_TYPE_PING) |
||||||
|
{ |
||||||
|
// Response PONG packet to peer
|
||||||
|
ESP_LOGI(TAG_SERVER, "Got a WS PING frame, Replying PONG"); |
||||||
|
ws_pkt.type = HTTPD_WS_TYPE_PONG; |
||||||
|
} |
||||||
|
else if (ws_pkt.type == HTTPD_WS_TYPE_CLOSE) |
||||||
|
{ |
||||||
|
// Response CLOSE packet with no payload to peer
|
||||||
|
ws_pkt.len = 0; |
||||||
|
ws_pkt.payload = NULL; |
||||||
|
} |
||||||
|
ret = httpd_ws_send_frame(req, &ws_pkt); |
||||||
|
if (ret != ESP_OK) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "httpd_ws_send_frame failed with %d", ret); |
||||||
|
} |
||||||
|
ESP_LOGI(TAG_SERVER, "ws_handler: httpd_handle_t=%p, sockfd=%d, client_info:%d", req->handle, |
||||||
|
httpd_req_to_sockfd(req), httpd_ws_get_fd_info(req->handle, httpd_req_to_sockfd(req))); |
||||||
|
free(buf); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
free(buf); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t wss_open_fd(httpd_handle_t hd, int sockfd) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG_SERVER, "New client connected %d", sockfd); |
||||||
|
wss_keep_alive_t h = httpd_get_global_user_ctx(hd); |
||||||
|
return wss_keep_alive_add_client(h, sockfd); |
||||||
|
} |
||||||
|
|
||||||
|
void wss_close_fd(httpd_handle_t hd, int sockfd) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG_SERVER, "Client disconnected %d", sockfd); |
||||||
|
wss_keep_alive_t h = httpd_get_global_user_ctx(hd); |
||||||
|
wss_keep_alive_remove_client(h, sockfd); |
||||||
|
close(sockfd); |
||||||
|
} |
||||||
|
|
||||||
|
static const httpd_uri_t ws = { |
||||||
|
.uri = "/ws", |
||||||
|
.method = HTTP_GET, |
||||||
|
.handler = ws_handler, |
||||||
|
.user_ctx = NULL, |
||||||
|
.is_websocket = true, |
||||||
|
.handle_ws_control_frames = true}; |
||||||
|
|
||||||
|
static void send_ping(void *arg) |
||||||
|
{ |
||||||
|
struct async_resp_arg *resp_arg = arg; |
||||||
|
httpd_handle_t hd = resp_arg->hd; |
||||||
|
int fd = resp_arg->fd; |
||||||
|
httpd_ws_frame_t ws_pkt; |
||||||
|
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); |
||||||
|
ws_pkt.payload = NULL; |
||||||
|
ws_pkt.len = 0; |
||||||
|
ws_pkt.type = HTTPD_WS_TYPE_PING; |
||||||
|
|
||||||
|
httpd_ws_send_frame_async(hd, fd, &ws_pkt); |
||||||
|
free(resp_arg); |
||||||
|
} |
||||||
|
|
||||||
|
bool client_not_alive_cb(wss_keep_alive_t h, int fd) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "Client not alive, closing fd %d", fd); |
||||||
|
httpd_sess_trigger_close(wss_keep_alive_get_user_ctx(h), fd); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool check_client_alive_cb(wss_keep_alive_t h, int fd) |
||||||
|
{ |
||||||
|
ESP_LOGD(TAG_SERVER, "Checking if client (fd=%d) is alive", fd); |
||||||
|
struct async_resp_arg *resp_arg = malloc(sizeof(struct async_resp_arg)); |
||||||
|
resp_arg->hd = wss_keep_alive_get_user_ctx(h); |
||||||
|
resp_arg->fd = fd; |
||||||
|
|
||||||
|
if (httpd_queue_work(resp_arg->hd, send_ping, resp_arg) == ESP_OK) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
static httpd_handle_t start_wss_echo_server(void) |
||||||
|
{ |
||||||
|
// Prepare keep-alive engine
|
||||||
|
wss_keep_alive_config_t keep_alive_config = KEEP_ALIVE_CONFIG_DEFAULT(); |
||||||
|
keep_alive_config.max_clients = max_clients; |
||||||
|
keep_alive_config.client_not_alive_cb = client_not_alive_cb; |
||||||
|
keep_alive_config.check_client_alive_cb = check_client_alive_cb; |
||||||
|
wss_keep_alive_t keep_alive = wss_keep_alive_start(&keep_alive_config); |
||||||
|
|
||||||
|
// Start the httpd server
|
||||||
|
httpd_handle_t server = NULL; |
||||||
|
ESP_LOGI(TAG_SERVER, "Starting server"); |
||||||
|
|
||||||
|
httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT(); |
||||||
|
conf.transport_mode = HTTPD_SSL_TRANSPORT_INSECURE; |
||||||
|
conf.httpd.max_open_sockets = max_clients; |
||||||
|
conf.httpd.global_user_ctx = keep_alive; |
||||||
|
conf.httpd.open_fn = wss_open_fd; |
||||||
|
conf.httpd.close_fn = wss_close_fd; |
||||||
|
// wildcard matcher
|
||||||
|
conf.httpd.uri_match_fn = httpd_uri_match_wildcard; |
||||||
|
// Certificates
|
||||||
|
extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start"); |
||||||
|
extern const unsigned char servercert_end[] asm("_binary_servercert_pem_end"); |
||||||
|
conf.servercert = servercert_start; |
||||||
|
conf.servercert_len = servercert_end - servercert_start; |
||||||
|
|
||||||
|
extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); |
||||||
|
extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); |
||||||
|
conf.prvtkey_pem = prvtkey_pem_start; |
||||||
|
conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start; |
||||||
|
|
||||||
|
esp_err_t ret = httpd_ssl_start(&server, &conf); |
||||||
|
if (ESP_OK != ret) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG_SERVER, "Error starting server!"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// Set URI handlers
|
||||||
|
ESP_LOGI(TAG_SERVER, "Registering URI handlers"); |
||||||
|
httpd_register_uri_handler(server, &ws); |
||||||
|
wss_keep_alive_set_user_ctx(keep_alive, server); |
||||||
|
|
||||||
|
ESP_LOGI(TAG_SERVER, "Registering site URI handlers"); |
||||||
|
// httpd_register_uri_handler(server, &index_html);
|
||||||
|
httpd_register_uri_handler(server, &SettingsView_css); |
||||||
|
httpd_register_uri_handler(server, &SettingsView_js); |
||||||
|
|
||||||
|
httpd_register_uri_handler(server, &index_css); |
||||||
|
// httpd_register_uri_handler(server, &logo_svg);
|
||||||
|
httpd_register_uri_handler(server, &index_js); |
||||||
|
httpd_register_uri_handler(server, &favicon_ico); |
||||||
|
|
||||||
|
httpd_register_uri_handler(server, &all_to_index); |
||||||
|
return server; |
||||||
|
} |
||||||
|
|
||||||
|
static esp_err_t stop_wss_echo_server(httpd_handle_t server) |
||||||
|
{ |
||||||
|
// Stop keep alive thread
|
||||||
|
wss_keep_alive_stop(httpd_get_global_user_ctx(server)); |
||||||
|
// Stop the httpd server
|
||||||
|
return httpd_ssl_stop(server); |
||||||
|
} |
||||||
|
|
||||||
|
void disconnect_handler(void *arg, esp_event_base_t event_base, |
||||||
|
int32_t event_id, void *event_data) |
||||||
|
{ |
||||||
|
httpd_handle_t *server = (httpd_handle_t *)arg; |
||||||
|
if (*server) |
||||||
|
{ |
||||||
|
if (stop_wss_echo_server(*server) == ESP_OK) |
||||||
|
{ |
||||||
|
*server = NULL; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "Failed to stop https server"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void connect_handler(void *arg, esp_event_base_t event_base, |
||||||
|
int32_t event_id, void *event_data) |
||||||
|
{ |
||||||
|
httpd_handle_t *server = (httpd_handle_t *)arg; |
||||||
|
ESP_LOGI(TAG_SERVER, "server %i", *server == NULL); |
||||||
|
if (*server == NULL) |
||||||
|
{ |
||||||
|
*server = start_wss_echo_server(); |
||||||
|
gserver = server; |
||||||
|
ESP_LOGI(TAG_SERVER, "start_wss_echo_server started"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Get all clients and send async message
|
||||||
|
static void wss_server_send_messages(httpd_handle_t *server) |
||||||
|
{ |
||||||
|
bool send_messages = true; |
||||||
|
|
||||||
|
// Send async message to all connected clients that use websocket protocol every 10 seconds
|
||||||
|
while (send_messages) |
||||||
|
{ |
||||||
|
vTaskDelay(10000 / portTICK_PERIOD_MS); |
||||||
|
|
||||||
|
if (!*server) |
||||||
|
{ // httpd might not have been created by now
|
||||||
|
continue; |
||||||
|
} |
||||||
|
size_t clients = max_clients; |
||||||
|
int client_fds[max_clients]; |
||||||
|
if (httpd_get_client_list(*server, &clients, client_fds) == ESP_OK) |
||||||
|
{ |
||||||
|
for (size_t i = 0; i < clients; ++i) |
||||||
|
{ |
||||||
|
int sock = client_fds[i]; |
||||||
|
if (httpd_ws_get_fd_info(*server, sock) == HTTPD_WS_CLIENT_WEBSOCKET) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG_SERVER, "Active client (fd=%d) -> sending async message", sock); |
||||||
|
struct async_resp_arg *resp_arg = malloc(sizeof(struct async_resp_arg)); |
||||||
|
resp_arg->hd = *server; |
||||||
|
resp_arg->fd = sock; |
||||||
|
if (httpd_queue_work(resp_arg->hd, send_hello, resp_arg) != ESP_OK) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "httpd_queue_work failed!"); |
||||||
|
send_messages = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG_SERVER, "httpd_get_client_list failed!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// void app_main(void)
|
||||||
|
// {
|
||||||
|
// static httpd_handle_t server = NULL;
|
||||||
|
|
||||||
|
// ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
// ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
// ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
|
// /* Register event handlers to start server when Wi-Fi or Ethernet is connected,
|
||||||
|
// * and stop server when disconnection happens.
|
||||||
|
// */
|
||||||
|
|
||||||
|
// #ifdef CONFIG_EXAMPLE_CONNECT_WIFI
|
||||||
|
// ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
|
||||||
|
// ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));
|
||||||
|
// #endif // CONFIG_EXAMPLE_CONNECT_WIFI
|
||||||
|
// #ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||||
|
// ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &connect_handler, &server));
|
||||||
|
// ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &disconnect_handler, &server));
|
||||||
|
// #endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
|
||||||
|
|
||||||
|
// /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||||
|
// * Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||||
|
// * examples/protocols/README.md for more information about this function.
|
||||||
|
// */
|
||||||
|
// ESP_ERROR_CHECK(example_connect());
|
||||||
|
|
||||||
|
// /* This function demonstrates periodic sending Websocket messages
|
||||||
|
// * to all connected clients to this server
|
||||||
|
// */
|
||||||
|
// wss_server_send_messages(&server);
|
||||||
|
// }
|
@ -0,0 +1,21 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
// #include "lwip/sys.h"
|
||||||
|
// #include <lwip/netdb.h>
|
||||||
|
// #include <arpa/inet.h>
|
||||||
|
#include <esp_event.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" |
||||||
|
{ |
||||||
|
#endif |
||||||
|
|
||||||
|
void disconnect_handler(void *arg, esp_event_base_t event_base, |
||||||
|
int32_t event_id, void *event_data); |
||||||
|
|
||||||
|
void connect_handler(void *arg, esp_event_base_t event_base, |
||||||
|
int32_t event_id, void *event_data); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,15 @@ |
|||||||
|
/* eslint-env node */ |
||||||
|
require('@rushstack/eslint-patch/modern-module-resolution') |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
root: true, |
||||||
|
'extends': [ |
||||||
|
'plugin:vue/vue3-essential', |
||||||
|
'eslint:recommended', |
||||||
|
'@vue/eslint-config-typescript', |
||||||
|
'@vue/eslint-config-prettier/skip-formatting' |
||||||
|
], |
||||||
|
parserOptions: { |
||||||
|
ecmaVersion: 'latest' |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
# Logs |
||||||
|
logs |
||||||
|
*.log |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
pnpm-debug.log* |
||||||
|
lerna-debug.log* |
||||||
|
|
||||||
|
node_modules |
||||||
|
.DS_Store |
||||||
|
dist |
||||||
|
dist-ssr |
||||||
|
coverage |
||||||
|
*.local |
||||||
|
|
||||||
|
/cypress/videos/ |
||||||
|
/cypress/screenshots/ |
||||||
|
|
||||||
|
# Editor directories and files |
||||||
|
.vscode/* |
||||||
|
!.vscode/extensions.json |
||||||
|
.idea |
||||||
|
*.suo |
||||||
|
*.ntvs* |
||||||
|
*.njsproj |
||||||
|
*.sln |
||||||
|
*.sw? |
@ -0,0 +1,8 @@ |
|||||||
|
{ |
||||||
|
"$schema": "https://json.schemastore.org/prettierrc", |
||||||
|
"semi": false, |
||||||
|
"tabWidth": 2, |
||||||
|
"singleQuote": true, |
||||||
|
"printWidth": 100, |
||||||
|
"trailingComma": "none" |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
# esp-vue |
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite. |
||||||
|
|
||||||
|
## Recommended IDE Setup |
||||||
|
|
||||||
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). |
||||||
|
|
||||||
|
## Type Support for `.vue` Imports in TS |
||||||
|
|
||||||
|
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. |
||||||
|
|
||||||
|
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: |
||||||
|
|
||||||
|
1. Disable the built-in TypeScript Extension |
||||||
|
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette |
||||||
|
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` |
||||||
|
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. |
||||||
|
|
||||||
|
## Customize configuration |
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vitejs.dev/config/). |
||||||
|
|
||||||
|
## Project Setup |
||||||
|
|
||||||
|
```sh |
||||||
|
npm install |
||||||
|
``` |
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development |
||||||
|
|
||||||
|
```sh |
||||||
|
npm run dev |
||||||
|
``` |
||||||
|
|
||||||
|
### Type-Check, Compile and Minify for Production |
||||||
|
|
||||||
|
```sh |
||||||
|
npm run build |
||||||
|
``` |
||||||
|
|
||||||
|
### Lint with [ESLint](https://eslint.org/) |
||||||
|
|
||||||
|
```sh |
||||||
|
npm run lint |
||||||
|
``` |
@ -0,0 +1 @@ |
|||||||
|
/// <reference types="vite/client" />
|
@ -0,0 +1,13 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8"> |
||||||
|
<link rel="icon" href="/favicon.ico"> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||||
|
<title>Vite App</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="app"></div> |
||||||
|
<script type="module" src="/src/main.ts"></script> |
||||||
|
</body> |
||||||
|
</html> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,39 @@ |
|||||||
|
{ |
||||||
|
"name": "esp-vue", |
||||||
|
"version": "0.0.0", |
||||||
|
"private": true, |
||||||
|
"scripts": { |
||||||
|
"dev": "vite", |
||||||
|
"build": "run-p type-check \"build-only {@}\" --", |
||||||
|
"preview": "vite preview", |
||||||
|
"build-only": "vite build", |
||||||
|
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false", |
||||||
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", |
||||||
|
"format": "prettier --write src/" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@kyvg/vue3-notification": "^3.0.2", |
||||||
|
"pinia": "^2.1.6", |
||||||
|
"vue": "^3.3.4", |
||||||
|
"vue-router": "^4.2.4" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@rushstack/eslint-patch": "^1.3.3", |
||||||
|
"@tsconfig/node18": "^18.2.2", |
||||||
|
"@types/node": "^18.18.7", |
||||||
|
"@vitejs/plugin-vue": "^4.3.4", |
||||||
|
"@vitejs/plugin-vue-jsx": "^3.0.2", |
||||||
|
"@vue/eslint-config-prettier": "^8.0.0", |
||||||
|
"@vue/eslint-config-typescript": "^12.0.0", |
||||||
|
"@vue/tsconfig": "^0.4.0", |
||||||
|
"eslint": "^8.49.0", |
||||||
|
"eslint-plugin-vue": "^9.17.0", |
||||||
|
"naive-ui": "^2.35.0", |
||||||
|
"npm-run-all2": "^6.0.6", |
||||||
|
"prettier": "^3.0.3", |
||||||
|
"typescript": "~5.2.0", |
||||||
|
"vfonts": "^0.0.3", |
||||||
|
"vite": "^4.4.9", |
||||||
|
"vue-tsc": "^1.8.11" |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,134 @@ |
|||||||
|
<script lang="ts"> |
||||||
|
import { useNotification } from '@kyvg/vue3-notification' |
||||||
|
import { RouterLink, RouterView } from 'vue-router' |
||||||
|
const { notify } = useNotification() |
||||||
|
// const connect = () => { |
||||||
|
// console.log(this) |
||||||
|
// } |
||||||
|
export default { |
||||||
|
test: console.log(this), |
||||||
|
data() { |
||||||
|
return { |
||||||
|
ws: this.$ws, |
||||||
|
notify |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
// toggleWs() { |
||||||
|
// this.$ws.connected ? this.$ws.connect() : this.$ws.disconnect() |
||||||
|
// console.log(this) |
||||||
|
// } |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<header> |
||||||
|
<!-- <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" /> --> |
||||||
|
|
||||||
|
<!-- <div class="wrapper"> --> |
||||||
|
<!-- <HelloWorld msg="You did it!" /> --> |
||||||
|
|
||||||
|
<nav> |
||||||
|
<RouterLink to="/">Home</RouterLink> |
||||||
|
<RouterLink to="/settings">Settings</RouterLink> |
||||||
|
<RouterLink to="/qlc">qlc Iframe</RouterLink> |
||||||
|
<RouterLink to="/qlc-ws">qlc Websocket</RouterLink> |
||||||
|
<button @click="ws.connected ? ws.disconnect() : ws.connect()"> |
||||||
|
{{ ws.connected ? 'disconnect' : 'connect' }} |
||||||
|
</button> |
||||||
|
<button @click="ws.sendMessage({ controls: { bf00: 0 }, action: 'setControls' })"> |
||||||
|
setControl |
||||||
|
</button> |
||||||
|
<button @click="ws.sendMessage({ settings: { ssid: '', pass: '' }, action: 'setSettings' })"> |
||||||
|
setSettings |
||||||
|
</button> |
||||||
|
<button |
||||||
|
@click="ws.sendMessage({ settings: { ssid: '', pass: '' }, action: 'applyAndReload' })" |
||||||
|
> |
||||||
|
applyAndReload |
||||||
|
</button> |
||||||
|
<button |
||||||
|
@click=" |
||||||
|
notify({ |
||||||
|
title: 'Authorization', |
||||||
|
text: 'You have been logged in!' |
||||||
|
}) |
||||||
|
" |
||||||
|
> |
||||||
|
show note |
||||||
|
</button> |
||||||
|
<!-- { title: 'testTitle', text: 'testText' } --> |
||||||
|
</nav> |
||||||
|
<!-- </div> --> |
||||||
|
</header> |
||||||
|
<main> |
||||||
|
<!-- <TheWelcome /> --> |
||||||
|
<RouterView /> |
||||||
|
</main> |
||||||
|
<notifications position="bottom right" /> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
header { |
||||||
|
line-height: 1.5; |
||||||
|
max-height: 100vh; |
||||||
|
} |
||||||
|
|
||||||
|
.logo { |
||||||
|
display: block; |
||||||
|
margin: 0 auto 2rem; |
||||||
|
} |
||||||
|
|
||||||
|
nav { |
||||||
|
height: 24px; |
||||||
|
font-size: 12px; |
||||||
|
text-align: center; |
||||||
|
/* margin-top: 2rem; */ |
||||||
|
} |
||||||
|
|
||||||
|
nav a.router-link-exact-active { |
||||||
|
color: var(--color-text); |
||||||
|
} |
||||||
|
|
||||||
|
nav a.router-link-exact-active:hover { |
||||||
|
background-color: transparent; |
||||||
|
} |
||||||
|
|
||||||
|
nav a { |
||||||
|
display: inline-block; |
||||||
|
padding: 0 1rem; |
||||||
|
border-left: 1px solid var(--color-border); |
||||||
|
} |
||||||
|
|
||||||
|
nav a:first-of-type { |
||||||
|
border: 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* @media (min-width: 1024px) { |
||||||
|
header { |
||||||
|
display: flex; |
||||||
|
place-items: center; |
||||||
|
padding-right: calc(var(--section-gap) / 2); |
||||||
|
} |
||||||
|
|
||||||
|
.logo { |
||||||
|
margin: 0 2rem 0 0; |
||||||
|
} |
||||||
|
|
||||||
|
header .wrapper { |
||||||
|
display: flex; |
||||||
|
place-items: flex-start; |
||||||
|
flex-wrap: wrap; |
||||||
|
} |
||||||
|
|
||||||
|
nav { |
||||||
|
text-align: left; |
||||||
|
margin-left: -1rem; |
||||||
|
font-size: 1rem; |
||||||
|
|
||||||
|
padding: 1rem 0; |
||||||
|
margin-top: 1rem; |
||||||
|
} |
||||||
|
} */ |
||||||
|
</style> |
@ -0,0 +1,86 @@ |
|||||||
|
/* color palette from <https://github.com/vuejs/theme> */ |
||||||
|
:root { |
||||||
|
--vt-c-white: #ffffff; |
||||||
|
--vt-c-white-soft: #f8f8f8; |
||||||
|
--vt-c-white-mute: #f2f2f2; |
||||||
|
|
||||||
|
--vt-c-black: #181818; |
||||||
|
--vt-c-black-soft: #222222; |
||||||
|
--vt-c-black-mute: #282828; |
||||||
|
|
||||||
|
--vt-c-indigo: #2c3e50; |
||||||
|
|
||||||
|
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29); |
||||||
|
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12); |
||||||
|
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); |
||||||
|
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); |
||||||
|
|
||||||
|
--vt-c-text-light-1: var(--vt-c-indigo); |
||||||
|
--vt-c-text-light-2: rgba(60, 60, 60, 0.66); |
||||||
|
--vt-c-text-dark-1: var(--vt-c-white); |
||||||
|
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64); |
||||||
|
} |
||||||
|
|
||||||
|
/* semantic color variables for this project */ |
||||||
|
:root { |
||||||
|
--color-background: var(--vt-c-white); |
||||||
|
--color-background-soft: var(--vt-c-white-soft); |
||||||
|
--color-background-mute: var(--vt-c-white-mute); |
||||||
|
|
||||||
|
--color-border: var(--vt-c-divider-light-2); |
||||||
|
--color-border-hover: var(--vt-c-divider-light-1); |
||||||
|
|
||||||
|
--color-heading: var(--vt-c-text-light-1); |
||||||
|
--color-text: var(--vt-c-text-light-1); |
||||||
|
|
||||||
|
--section-gap: 160px; |
||||||
|
} |
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) { |
||||||
|
:root { |
||||||
|
--color-background: var(--vt-c-black); |
||||||
|
--color-background-soft: var(--vt-c-black-soft); |
||||||
|
--color-background-mute: var(--vt-c-black-mute); |
||||||
|
|
||||||
|
--color-border: var(--vt-c-divider-dark-2); |
||||||
|
--color-border-hover: var(--vt-c-divider-dark-1); |
||||||
|
|
||||||
|
--color-heading: var(--vt-c-text-dark-1); |
||||||
|
--color-text: var(--vt-c-text-dark-2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
*, |
||||||
|
*::before, |
||||||
|
*::after { |
||||||
|
box-sizing: border-box; |
||||||
|
margin: 0; |
||||||
|
font-weight: normal; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
min-height: 100vh; |
||||||
|
color: var(--color-text); |
||||||
|
background: var(--color-background); |
||||||
|
transition: |
||||||
|
color 0.5s, |
||||||
|
background-color 0.5s; |
||||||
|
line-height: 1.6; |
||||||
|
font-family: |
||||||
|
Inter, |
||||||
|
-apple-system, |
||||||
|
BlinkMacSystemFont, |
||||||
|
'Segoe UI', |
||||||
|
Roboto, |
||||||
|
Oxygen, |
||||||
|
Ubuntu, |
||||||
|
Cantarell, |
||||||
|
'Fira Sans', |
||||||
|
'Droid Sans', |
||||||
|
'Helvetica Neue', |
||||||
|
sans-serif; |
||||||
|
font-size: 15px; |
||||||
|
text-rendering: optimizeLegibility; |
||||||
|
-webkit-font-smoothing: antialiased; |
||||||
|
-moz-osx-font-smoothing: grayscale; |
||||||
|
} |
After Width: | Height: | Size: 276 B |
@ -0,0 +1,35 @@ |
|||||||
|
@import './base.css'; |
||||||
|
|
||||||
|
#app { |
||||||
|
/* max-width: 1280px; */ |
||||||
|
/* margin: 0 auto; */ |
||||||
|
/* padding: 2rem; */ |
||||||
|
|
||||||
|
font-weight: normal; |
||||||
|
} |
||||||
|
|
||||||
|
a, |
||||||
|
.green { |
||||||
|
text-decoration: none; |
||||||
|
color: hsla(160, 100%, 37%, 1); |
||||||
|
transition: 0.4s; |
||||||
|
} |
||||||
|
|
||||||
|
@media (hover: hover) { |
||||||
|
a:hover { |
||||||
|
background-color: hsla(160, 100%, 37%, 0.2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* @media (min-width: 1024px) { |
||||||
|
body { |
||||||
|
display: flex; |
||||||
|
place-items: center; |
||||||
|
} |
||||||
|
|
||||||
|
#app { |
||||||
|
display: grid; |
||||||
|
grid-template-columns: 1fr 1fr; |
||||||
|
padding: 0 2rem; |
||||||
|
} |
||||||
|
} */ |
@ -0,0 +1,41 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
defineProps<{ |
||||||
|
msg: string |
||||||
|
}>() |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<div class="greetings"> |
||||||
|
<h1 class="green">{{ msg }}</h1> |
||||||
|
<h3> |
||||||
|
You’ve successfully created a project with |
||||||
|
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> + |
||||||
|
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next? |
||||||
|
</h3> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
h1 { |
||||||
|
font-weight: 500; |
||||||
|
font-size: 2.6rem; |
||||||
|
position: relative; |
||||||
|
top: -10px; |
||||||
|
} |
||||||
|
|
||||||
|
h3 { |
||||||
|
font-size: 1.2rem; |
||||||
|
} |
||||||
|
|
||||||
|
.greetings h1, |
||||||
|
.greetings h3 { |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
|
||||||
|
@media (min-width: 1024px) { |
||||||
|
.greetings h1, |
||||||
|
.greetings h3 { |
||||||
|
text-align: left; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,88 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
import WelcomeItem from './WelcomeItem.vue' |
||||||
|
import DocumentationIcon from './icons/IconDocumentation.vue' |
||||||
|
import ToolingIcon from './icons/IconTooling.vue' |
||||||
|
import EcosystemIcon from './icons/IconEcosystem.vue' |
||||||
|
import CommunityIcon from './icons/IconCommunity.vue' |
||||||
|
import SupportIcon from './icons/IconSupport.vue' |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<WelcomeItem> |
||||||
|
<template #icon> |
||||||
|
<DocumentationIcon /> |
||||||
|
</template> |
||||||
|
<template #heading>Documentation</template> |
||||||
|
|
||||||
|
Vue’s |
||||||
|
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a> |
||||||
|
provides you with all information you need to get started. |
||||||
|
</WelcomeItem> |
||||||
|
|
||||||
|
<WelcomeItem> |
||||||
|
<template #icon> |
||||||
|
<ToolingIcon /> |
||||||
|
</template> |
||||||
|
<template #heading>Tooling</template> |
||||||
|
|
||||||
|
This project is served and bundled with |
||||||
|
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The |
||||||
|
recommended IDE setup is |
||||||
|
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> + |
||||||
|
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If |
||||||
|
you need to test your components and web pages, check out |
||||||
|
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and |
||||||
|
<a href="https://on.cypress.io/component" target="_blank" rel="noopener" |
||||||
|
>Cypress Component Testing</a |
||||||
|
>. |
||||||
|
|
||||||
|
<br /> |
||||||
|
|
||||||
|
More instructions are available in <code>README.md</code>. |
||||||
|
</WelcomeItem> |
||||||
|
|
||||||
|
<WelcomeItem> |
||||||
|
<template #icon> |
||||||
|
<EcosystemIcon /> |
||||||
|
</template> |
||||||
|
<template #heading>Ecosystem</template> |
||||||
|
|
||||||
|
Get official tools and libraries for your project: |
||||||
|
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>, |
||||||
|
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>, |
||||||
|
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and |
||||||
|
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If |
||||||
|
you need more resources, we suggest paying |
||||||
|
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a> |
||||||
|
a visit. |
||||||
|
</WelcomeItem> |
||||||
|
|
||||||
|
<WelcomeItem> |
||||||
|
<template #icon> |
||||||
|
<CommunityIcon /> |
||||||
|
</template> |
||||||
|
<template #heading>Community</template> |
||||||
|
|
||||||
|
Got stuck? Ask your question on |
||||||
|
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official |
||||||
|
Discord server, or |
||||||
|
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener" |
||||||
|
>StackOverflow</a |
||||||
|
>. You should also subscribe to |
||||||
|
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow |
||||||
|
the official |
||||||
|
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a> |
||||||
|
twitter account for latest news in the Vue world. |
||||||
|
</WelcomeItem> |
||||||
|
|
||||||
|
<WelcomeItem> |
||||||
|
<template #icon> |
||||||
|
<SupportIcon /> |
||||||
|
</template> |
||||||
|
<template #heading>Support Vue</template> |
||||||
|
|
||||||
|
As an independent project, Vue relies on community backing for its sustainability. You can help |
||||||
|
us by |
||||||
|
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>. |
||||||
|
</WelcomeItem> |
||||||
|
</template> |
@ -0,0 +1,87 @@ |
|||||||
|
<template> |
||||||
|
<div class="item"> |
||||||
|
<i> |
||||||
|
<slot name="icon"></slot> |
||||||
|
</i> |
||||||
|
<div class="details"> |
||||||
|
<h3> |
||||||
|
<slot name="heading"></slot> |
||||||
|
</h3> |
||||||
|
<slot></slot> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.item { |
||||||
|
margin-top: 2rem; |
||||||
|
display: flex; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.details { |
||||||
|
flex: 1; |
||||||
|
margin-left: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
i { |
||||||
|
display: flex; |
||||||
|
place-items: center; |
||||||
|
place-content: center; |
||||||
|
width: 32px; |
||||||
|
height: 32px; |
||||||
|
|
||||||
|
color: var(--color-text); |
||||||
|
} |
||||||
|
|
||||||
|
h3 { |
||||||
|
font-size: 1.2rem; |
||||||
|
font-weight: 500; |
||||||
|
margin-bottom: 0.4rem; |
||||||
|
color: var(--color-heading); |
||||||
|
} |
||||||
|
|
||||||
|
@media (min-width: 1024px) { |
||||||
|
.item { |
||||||
|
margin-top: 0; |
||||||
|
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2); |
||||||
|
} |
||||||
|
|
||||||
|
i { |
||||||
|
top: calc(50% - 25px); |
||||||
|
left: -26px; |
||||||
|
position: absolute; |
||||||
|
border: 1px solid var(--color-border); |
||||||
|
background: var(--color-background); |
||||||
|
border-radius: 8px; |
||||||
|
width: 50px; |
||||||
|
height: 50px; |
||||||
|
} |
||||||
|
|
||||||
|
.item:before { |
||||||
|
content: ' '; |
||||||
|
border-left: 1px solid var(--color-border); |
||||||
|
position: absolute; |
||||||
|
left: 0; |
||||||
|
bottom: calc(50% + 25px); |
||||||
|
height: calc(50% - 25px); |
||||||
|
} |
||||||
|
|
||||||
|
.item:after { |
||||||
|
content: ' '; |
||||||
|
border-left: 1px solid var(--color-border); |
||||||
|
position: absolute; |
||||||
|
left: 0; |
||||||
|
top: calc(50% + 25px); |
||||||
|
height: calc(50% - 25px); |
||||||
|
} |
||||||
|
|
||||||
|
.item:first-of-type:before { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
.item:last-of-type:after { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,7 @@ |
|||||||
|
<template> |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"> |
||||||
|
<path |
||||||
|
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z" |
||||||
|
/> |
||||||
|
</svg> |
||||||
|
</template> |
@ -0,0 +1,7 @@ |
|||||||
|
<template> |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor"> |
||||||
|
<path |
||||||
|
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z" |
||||||
|
/> |
||||||
|
</svg> |
||||||
|
</template> |
@ -0,0 +1,7 @@ |
|||||||
|
<template> |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor"> |
||||||
|
<path |
||||||
|
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z" |
||||||
|
/> |
||||||
|
</svg> |
||||||
|
</template> |
@ -0,0 +1,7 @@ |
|||||||
|
<template> |
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor"> |
||||||
|
<path |
||||||
|
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z" |
||||||
|
/> |
||||||
|
</svg> |
||||||
|
</template> |
@ -0,0 +1,19 @@ |
|||||||
|
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license--> |
||||||
|
<template> |
||||||
|
<svg |
||||||
|
xmlns="http://www.w3.org/2000/svg" |
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" |
||||||
|
aria-hidden="true" |
||||||
|
role="img" |
||||||
|
class="iconify iconify--mdi" |
||||||
|
width="24" |
||||||
|
height="24" |
||||||
|
preserveAspectRatio="xMidYMid meet" |
||||||
|
viewBox="0 0 24 24" |
||||||
|
> |
||||||
|
<path |
||||||
|
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z" |
||||||
|
fill="currentColor" |
||||||
|
></path> |
||||||
|
</svg> |
||||||
|
</template> |
@ -0,0 +1,23 @@ |
|||||||
|
import './assets/main.css' |
||||||
|
|
||||||
|
import Notifications from '@kyvg/vue3-notification' |
||||||
|
import { createPinia } from 'pinia' |
||||||
|
import { createApp } from 'vue' |
||||||
|
import App from './App.vue' |
||||||
|
import { WebsocketPlugin, type WebsocketPlugInterface } from './plugins/WebsocketPlugin' |
||||||
|
import router from './router' |
||||||
|
|
||||||
|
//declare the property so that it can be used by ts
|
||||||
|
declare module '@vue/runtime-core' { |
||||||
|
interface ComponentCustomProperties { |
||||||
|
$ws: WebsocketPlugInterface |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const app = createApp(App) |
||||||
|
|
||||||
|
app.use(createPinia()) |
||||||
|
app.use(router) |
||||||
|
app.use(Notifications) |
||||||
|
app.use(WebsocketPlugin) |
||||||
|
app.mount('#app') |
@ -0,0 +1,60 @@ |
|||||||
|
import { useNotification } from '@kyvg/vue3-notification' |
||||||
|
import { reactive, type App, type Plugin } from 'vue' |
||||||
|
const { notify } = useNotification() |
||||||
|
// plugins/mebsocket.js
|
||||||
|
// const ws: Ref<WebSocket | undefined> = ref()
|
||||||
|
export interface WebsocketPluginOptions { |
||||||
|
retryDelayTime?: string |
||||||
|
addNotification?(): void |
||||||
|
} |
||||||
|
export interface WebsocketPlugInterface { |
||||||
|
connected: boolean |
||||||
|
socket?: WebSocket |
||||||
|
connect(): void |
||||||
|
disconnect(): void |
||||||
|
sendMessage(payload: Object): void |
||||||
|
} |
||||||
|
export const WebsocketPlugin: Plugin = { |
||||||
|
install(app: App, options: WebsocketPluginOptions) { |
||||||
|
app.config.globalProperties.$ws = reactive({ |
||||||
|
connected: false, |
||||||
|
socket: undefined, |
||||||
|
connect: () => { |
||||||
|
const url = 'ws://' + window.location.host + '/ws' |
||||||
|
// if (process.env.NODE_ENV === 'development') {
|
||||||
|
// url = 'ws://192.168.1.206/ws'
|
||||||
|
// }
|
||||||
|
console.log('Starting connection to WebSocket Server', url) |
||||||
|
const websocket = new WebSocket(url) |
||||||
|
websocket.onmessage = (event) => { |
||||||
|
console.log('onmessage', event) |
||||||
|
} |
||||||
|
|
||||||
|
websocket.onopen = (event) => { |
||||||
|
notify({ text: 'Successfully connected', type: 'success' }) |
||||||
|
console.log(event) |
||||||
|
app.config.globalProperties.$ws.connected = true |
||||||
|
console.log('Successfully connected to the echo websocket server...') |
||||||
|
} |
||||||
|
|
||||||
|
websocket.onclose = () => { |
||||||
|
app.config.globalProperties.$ws.connected = false |
||||||
|
console.log('onclose') |
||||||
|
} |
||||||
|
addEventListener('unload', (event) => { |
||||||
|
websocket.close() |
||||||
|
console.log('unload') |
||||||
|
}) |
||||||
|
app.config.globalProperties.$ws.socket = websocket |
||||||
|
}, |
||||||
|
disconnect: () => { |
||||||
|
console.log('disconnect WebSocket Server') |
||||||
|
app.config.globalProperties.$ws.socket?.close() |
||||||
|
}, |
||||||
|
sendMessage: (payload: Object) => { |
||||||
|
console.log('sendMessage') |
||||||
|
app.config.globalProperties.$ws.socket?.send(JSON.stringify(payload)) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
import { createRouter, createWebHistory } from 'vue-router' |
||||||
|
import HomeView from '../views/HomeView.vue' |
||||||
|
|
||||||
|
const router = createRouter({ |
||||||
|
history: createWebHistory(import.meta.env.BASE_URL), |
||||||
|
routes: [ |
||||||
|
{ |
||||||
|
path: '/', |
||||||
|
name: 'home', |
||||||
|
component: HomeView |
||||||
|
}, |
||||||
|
{ |
||||||
|
path: '/settings', |
||||||
|
name: 'settings', |
||||||
|
component: () => import('../views/SettingsView.vue') |
||||||
|
}, |
||||||
|
{ |
||||||
|
path: '/qlc', |
||||||
|
name: 'qlc', |
||||||
|
component: () => import('../views/QlcIframeView.vue') |
||||||
|
} |
||||||
|
] |
||||||
|
}) |
||||||
|
|
||||||
|
export default router |
@ -0,0 +1,55 @@ |
|||||||
|
import { defineStore } from 'pinia' |
||||||
|
|
||||||
|
export const useControlStore = defineStore({ |
||||||
|
id: 'control', |
||||||
|
state: () => ({ |
||||||
|
buttons: [], |
||||||
|
faders: [], |
||||||
|
constrols: { |
||||||
|
bf00: 255, |
||||||
|
bf01: 255, |
||||||
|
bf02: 255, |
||||||
|
bf03: 255, |
||||||
|
bf04: 0, |
||||||
|
bf05: 0, |
||||||
|
bf06: 0, |
||||||
|
bf07: 0, |
||||||
|
bf08: 0, |
||||||
|
bf09: 0, |
||||||
|
bf10: 0, |
||||||
|
bf11: 0, |
||||||
|
bf12: 0, |
||||||
|
bf13: 0, |
||||||
|
bf14: 0, |
||||||
|
bf15: 0, |
||||||
|
bf16: 0, |
||||||
|
bf17: 0, |
||||||
|
bf18: 0, |
||||||
|
bf19: 0, |
||||||
|
bf20: 0, |
||||||
|
bf21: 0, |
||||||
|
bf22: 0, |
||||||
|
bf23: 0, |
||||||
|
bf24: 0, |
||||||
|
bf25: 0, |
||||||
|
bf26: 0, |
||||||
|
bf27: 0, |
||||||
|
bf28: 0, |
||||||
|
bf29: 0, |
||||||
|
|
||||||
|
fc00: 0, |
||||||
|
fc01: 0, |
||||||
|
fc02: 0, |
||||||
|
fc03: 0, |
||||||
|
|
||||||
|
fb00: 0, |
||||||
|
fb01: 0, |
||||||
|
fb02: 0, |
||||||
|
fb03: 0, |
||||||
|
fb04: 0, |
||||||
|
fb05: 0, |
||||||
|
fb06: 0, |
||||||
|
fb07: 0 |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,12 @@ |
|||||||
|
import { ref, computed } from 'vue' |
||||||
|
import { defineStore } from 'pinia' |
||||||
|
|
||||||
|
export const useCounterStore = defineStore('counter', () => { |
||||||
|
const count = ref(0) |
||||||
|
const doubleCount = computed(() => count.value * 2) |
||||||
|
function increment() { |
||||||
|
count.value++ |
||||||
|
} |
||||||
|
|
||||||
|
return { count, doubleCount, increment } |
||||||
|
}) |
@ -0,0 +1,86 @@ |
|||||||
|
<template> |
||||||
|
<div class="about"> |
||||||
|
<h1>This is an about page</h1> |
||||||
|
<canvas |
||||||
|
id="myCanvas" |
||||||
|
ref="canvasElement" |
||||||
|
width="255" |
||||||
|
height="255" |
||||||
|
style="border: 1px solid grey; background-color: bisque" |
||||||
|
></canvas> |
||||||
|
<!-- <button @click="sendMessage()">send</button> --> |
||||||
|
<button @click="ws.sendMessage({ action: 'subscribe' }), render()">subscribe</button> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<script lang="ts"> |
||||||
|
import { useNotification } from '@kyvg/vue3-notification' |
||||||
|
import { ref, type Ref } from 'vue' |
||||||
|
const { notify } = useNotification() |
||||||
|
|
||||||
|
// const canvasElement: Ref<HTMLCanvasElement | undefined> = ref() |
||||||
|
// const context: Ref<CanvasRenderingContext2D | undefined> = ref() |
||||||
|
export default { |
||||||
|
setup(): { |
||||||
|
canvasElement: Ref<HTMLCanvasElement | undefined> |
||||||
|
context: Ref<CanvasRenderingContext2D | undefined> |
||||||
|
} { |
||||||
|
const canvasElement = ref() |
||||||
|
const context = ref() |
||||||
|
// ... |
||||||
|
console.log('canvasElement', canvasElement, canvasElement.value?.getContext('2d')) |
||||||
|
return { |
||||||
|
canvasElement, |
||||||
|
context |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
console.log('mounted', this, this.canvasElement, this.canvasElement?.getContext('2d')) |
||||||
|
this.context = this.canvasElement?.getContext('2d') || undefined |
||||||
|
console.log('this.context', this.context) |
||||||
|
this.render() |
||||||
|
}, |
||||||
|
data() { |
||||||
|
return { |
||||||
|
ws: this.$ws |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
// toggleWs() { |
||||||
|
// this.$ws.connected ? this.$ws.connect() : this.$ws.disconnect() |
||||||
|
// console.log(this) |
||||||
|
// } |
||||||
|
render() { |
||||||
|
console.log('context', this.context) |
||||||
|
if (!this.context) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// this.context.fillText('jelledev.com', 50, 50) |
||||||
|
this.context.beginPath() |
||||||
|
|
||||||
|
// Define a start point |
||||||
|
this.context.moveTo(128, 0) |
||||||
|
|
||||||
|
// Define an end point |
||||||
|
this.context.lineTo(128, 255) |
||||||
|
|
||||||
|
// Define a start point |
||||||
|
this.context.moveTo(0, 128) |
||||||
|
|
||||||
|
// Define an end point |
||||||
|
this.context.lineTo(255, 128) |
||||||
|
this.context.stroke() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
/* @media (min-width: 1024px) { |
||||||
|
.about { |
||||||
|
min-height: 100vh; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
} */ |
||||||
|
</style> |
@ -0,0 +1,29 @@ |
|||||||
|
<template> |
||||||
|
<iframe id="qlciframe" src="http://localhost:9999/" title="qlc+"></iframe> |
||||||
|
</template> |
||||||
|
<script lang="ts"> |
||||||
|
import { useNotification } from '@kyvg/vue3-notification' |
||||||
|
import { defineComponent } from 'vue' |
||||||
|
import { useControlStore } from '../stores/controlStore' |
||||||
|
const controlStore = useControlStore() |
||||||
|
const { notify } = useNotification() |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
data() { |
||||||
|
return { |
||||||
|
innerWidth: innerWidth, |
||||||
|
innerHeight: innerHeight |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
console.log('mounted', this) |
||||||
|
} |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
#qlciframe { |
||||||
|
width: 100%; |
||||||
|
height: calc(100vh - 30px); |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,116 @@ |
|||||||
|
<template> |
||||||
|
<!-- <div class="about"> |
||||||
|
<h1>This is an about page</h1> |
||||||
|
<canvas |
||||||
|
id="myCanvas" |
||||||
|
ref="canvasElement" |
||||||
|
width="255" |
||||||
|
height="255" |
||||||
|
style="border: 1px solid grey; background-color: bisque" |
||||||
|
></canvas> |
||||||
|
<button @click="sendMessage()">send</button> |
||||||
|
<button @click="ws.sendMessage({ action: 'subscribe' }), render()">subscribe</button> |
||||||
|
</div> --> |
||||||
|
<n-grid :cols="2"> |
||||||
|
<n-grid-item> |
||||||
|
<canvas |
||||||
|
id="myCanvas" |
||||||
|
ref="canvasElement" |
||||||
|
width="255" |
||||||
|
height="255" |
||||||
|
style="border: 1px solid grey; background-color: bisque" |
||||||
|
></canvas> |
||||||
|
</n-grid-item> |
||||||
|
<n-grid-item> |
||||||
|
<button @click="ws.sendMessage({ action: 'subscribe' }), render()">subscribe</button> |
||||||
|
<n-button>naive-ui</n-button> |
||||||
|
<n-grid :cols="6"> |
||||||
|
<n-grid-item> </n-grid-item> |
||||||
|
</n-grid> |
||||||
|
</n-grid-item> |
||||||
|
</n-grid> |
||||||
|
</template> |
||||||
|
<script lang="ts"> |
||||||
|
import { useNotification } from '@kyvg/vue3-notification' |
||||||
|
import { ref, type Ref } from 'vue' |
||||||
|
const { notify } = useNotification() |
||||||
|
|
||||||
|
// const canvasElement: Ref<HTMLCanvasElement | undefined> = ref() |
||||||
|
// const context: Ref<CanvasRenderingContext2D | undefined> = ref() |
||||||
|
import { NButton, NGrid, NGridItem } from 'naive-ui' |
||||||
|
import { storeToRefs } from 'pinia' |
||||||
|
import { defineComponent } from 'vue' |
||||||
|
import { useControlStore } from '../stores/controlStore' |
||||||
|
const controlStore = useControlStore() |
||||||
|
// const { posts, loading, error } = storeToRefs(useControlStore()) |
||||||
|
export default defineComponent({ |
||||||
|
components: { |
||||||
|
NButton, |
||||||
|
NGrid, |
||||||
|
NGridItem |
||||||
|
}, |
||||||
|
setup(): { |
||||||
|
canvasElement: Ref<HTMLCanvasElement | undefined> |
||||||
|
context: Ref<CanvasRenderingContext2D | undefined> |
||||||
|
} { |
||||||
|
const canvasElement = ref() |
||||||
|
const context = ref() |
||||||
|
// ... |
||||||
|
console.log('canvasElement', canvasElement, canvasElement.value?.getContext('2d')) |
||||||
|
return { |
||||||
|
canvasElement, |
||||||
|
context, |
||||||
|
...storeToRefs(useControlStore()) |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
console.log('mounted', this) |
||||||
|
this.context = this.canvasElement?.getContext('2d') || undefined |
||||||
|
console.log('this.context', this.context) |
||||||
|
this.render() |
||||||
|
}, |
||||||
|
data() { |
||||||
|
return { |
||||||
|
ws: this.$ws |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
// toggleWs() { |
||||||
|
// this.$ws.connected ? this.$ws.connect() : this.$ws.disconnect() |
||||||
|
// console.log(this) |
||||||
|
// } |
||||||
|
render() { |
||||||
|
console.log('context', this.context) |
||||||
|
if (!this.context) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// this.context.fillText('jelledev.com', 50, 50) |
||||||
|
this.context.beginPath() |
||||||
|
|
||||||
|
// Define a start point |
||||||
|
this.context.moveTo(128, 0) |
||||||
|
|
||||||
|
// Define an end point |
||||||
|
this.context.lineTo(128, 255) |
||||||
|
|
||||||
|
// Define a start point |
||||||
|
this.context.moveTo(0, 128) |
||||||
|
|
||||||
|
// Define an end point |
||||||
|
this.context.lineTo(255, 128) |
||||||
|
this.context.stroke() |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
/* @media (min-width: 1024px) { |
||||||
|
.about { |
||||||
|
min-height: 100vh; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
} */ |
||||||
|
</style> |
@ -0,0 +1,12 @@ |
|||||||
|
{ |
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json", |
||||||
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"], |
||||||
|
"exclude": ["src/**/__tests__/*"], |
||||||
|
"compilerOptions": { |
||||||
|
"composite": true, |
||||||
|
"baseUrl": ".", |
||||||
|
"paths": { |
||||||
|
"@/*": ["./src/*"] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
{ |
||||||
|
"files": [], |
||||||
|
"references": [ |
||||||
|
{ |
||||||
|
"path": "./tsconfig.node.json" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"path": "./tsconfig.app.json" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"extends": "@tsconfig/node18/tsconfig.json", |
||||||
|
"include": [ |
||||||
|
"vite.config.*", |
||||||
|
"vitest.config.*", |
||||||
|
"cypress.config.*", |
||||||
|
"nightwatch.conf.*", |
||||||
|
"playwright.config.*" |
||||||
|
], |
||||||
|
"compilerOptions": { |
||||||
|
"composite": true, |
||||||
|
"module": "ESNext", |
||||||
|
"moduleResolution": "Bundler", |
||||||
|
"types": ["node"] |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
import { fileURLToPath, URL } from 'node:url' |
||||||
|
|
||||||
|
import vue from '@vitejs/plugin-vue' |
||||||
|
import vueJsx from '@vitejs/plugin-vue-jsx' |
||||||
|
import { defineConfig } from 'vite' |
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({ |
||||||
|
plugins: [vue(), vueJsx()], |
||||||
|
resolve: { |
||||||
|
alias: { |
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url)) |
||||||
|
} |
||||||
|
}, |
||||||
|
build: { |
||||||
|
rollupOptions: { |
||||||
|
output: { |
||||||
|
entryFileNames: `assets/[name].js`, |
||||||
|
chunkFileNames: `assets/[name].js`, |
||||||
|
assetFileNames: `assets/[name].[ext]` |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,229 @@ |
|||||||
|
/* Keep Alive engine for wss server example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.) |
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this |
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||||
|
CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <esp_log.h> |
||||||
|
#include <esp_system.h> |
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/queue.h" |
||||||
|
#include "freertos/task.h" |
||||||
|
#include "keep_alive.h" |
||||||
|
#include "esp_timer.h" |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
NO_CLIENT = 0, |
||||||
|
CLIENT_FD_ADD, |
||||||
|
CLIENT_FD_REMOVE, |
||||||
|
CLIENT_UPDATE, |
||||||
|
CLIENT_ACTIVE, |
||||||
|
STOP_TASK, |
||||||
|
} client_fd_action_type_t; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
client_fd_action_type_t type; |
||||||
|
int fd; |
||||||
|
uint64_t last_seen; |
||||||
|
} client_fd_action_t; |
||||||
|
|
||||||
|
typedef struct wss_keep_alive_storage { |
||||||
|
size_t max_clients; |
||||||
|
wss_check_client_alive_cb_t check_client_alive_cb; |
||||||
|
wss_check_client_alive_cb_t client_not_alive_cb; |
||||||
|
size_t keep_alive_period_ms; |
||||||
|
size_t not_alive_after_ms; |
||||||
|
void * user_ctx; |
||||||
|
QueueHandle_t q; |
||||||
|
client_fd_action_t clients[]; |
||||||
|
} wss_keep_alive_storage_t; |
||||||
|
|
||||||
|
typedef struct wss_keep_alive_storage* wss_keep_alive_t; |
||||||
|
|
||||||
|
static const char *TAG = "wss_keep_alive"; |
||||||
|
|
||||||
|
static uint64_t _tick_get_ms(void) |
||||||
|
{ |
||||||
|
return esp_timer_get_time()/1000; |
||||||
|
} |
||||||
|
|
||||||
|
// Goes over active clients to find out how long we could sleep before checking who's alive
|
||||||
|
static uint64_t get_max_delay(wss_keep_alive_t h) |
||||||
|
{ |
||||||
|
int64_t check_after_ms = 30000; // max delay, no need to check anyone
|
||||||
|
for (int i=0; i<h->max_clients; ++i) { |
||||||
|
if (h->clients[i].type == CLIENT_ACTIVE) { |
||||||
|
uint64_t check_this_client_at = h->clients[i].last_seen + h->keep_alive_period_ms; |
||||||
|
if (check_this_client_at < check_after_ms + _tick_get_ms()) { |
||||||
|
check_after_ms = check_this_client_at - _tick_get_ms(); |
||||||
|
if (check_after_ms < 0) { |
||||||
|
check_after_ms = 1000; // min delay, some client(s) not responding already
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return check_after_ms; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
static bool update_client(wss_keep_alive_t h, int sockfd, uint64_t timestamp) |
||||||
|
{ |
||||||
|
for (int i=0; i<h->max_clients; ++i) { |
||||||
|
if (h->clients[i].type == CLIENT_ACTIVE && h->clients[i].fd == sockfd) { |
||||||
|
h->clients[i].last_seen = timestamp; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
static bool remove_client(wss_keep_alive_t h, int sockfd) |
||||||
|
{ |
||||||
|
for (int i=0; i<h->max_clients; ++i) { |
||||||
|
if (h->clients[i].type == CLIENT_ACTIVE && h->clients[i].fd == sockfd) { |
||||||
|
h->clients[i].type = NO_CLIENT; |
||||||
|
h->clients[i].fd = -1; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
static bool add_new_client(wss_keep_alive_t h,int sockfd) |
||||||
|
{ |
||||||
|
for (int i=0; i<h->max_clients; ++i) { |
||||||
|
if (h->clients[i].type == NO_CLIENT) { |
||||||
|
h->clients[i].type = CLIENT_ACTIVE; |
||||||
|
h->clients[i].fd = sockfd; |
||||||
|
h->clients[i].last_seen = _tick_get_ms(); |
||||||
|
return true; // success
|
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
static void keep_alive_task(void* arg) |
||||||
|
{ |
||||||
|
wss_keep_alive_storage_t *keep_alive_storage = arg; |
||||||
|
bool run_task = true; |
||||||
|
client_fd_action_t client_action; |
||||||
|
while (run_task) { |
||||||
|
if (xQueueReceive(keep_alive_storage->q, (void *) &client_action, |
||||||
|
get_max_delay(keep_alive_storage) / portTICK_PERIOD_MS) == pdTRUE) { |
||||||
|
switch (client_action.type) { |
||||||
|
case CLIENT_FD_ADD: |
||||||
|
if (!add_new_client(keep_alive_storage, client_action.fd)) { |
||||||
|
ESP_LOGE(TAG, "Cannot add new client"); |
||||||
|
} |
||||||
|
break; |
||||||
|
case CLIENT_FD_REMOVE: |
||||||
|
if (!remove_client(keep_alive_storage, client_action.fd)) { |
||||||
|
ESP_LOGE(TAG, "Cannot remove client fd:%d", client_action.fd); |
||||||
|
} |
||||||
|
break; |
||||||
|
case CLIENT_UPDATE: |
||||||
|
if (!update_client(keep_alive_storage, client_action.fd, client_action.last_seen)) { |
||||||
|
ESP_LOGE(TAG, "Cannot find client fd:%d", client_action.fd); |
||||||
|
} |
||||||
|
break; |
||||||
|
case STOP_TASK: |
||||||
|
run_task = false; |
||||||
|
break; |
||||||
|
default: |
||||||
|
ESP_LOGE(TAG, "Unexpected client action"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// timeout: check if PING message needed
|
||||||
|
for (int i=0; i<keep_alive_storage->max_clients; ++i) { |
||||||
|
if (keep_alive_storage->clients[i].type == CLIENT_ACTIVE) { |
||||||
|
if (keep_alive_storage->clients[i].last_seen + keep_alive_storage->keep_alive_period_ms <= _tick_get_ms()) { |
||||||
|
ESP_LOGD(TAG, "Haven't seen the client (fd=%d) for a while", keep_alive_storage->clients[i].fd); |
||||||
|
if (keep_alive_storage->clients[i].last_seen + keep_alive_storage->not_alive_after_ms <= _tick_get_ms()) { |
||||||
|
ESP_LOGE(TAG, "Client (fd=%d) not alive!", keep_alive_storage->clients[i].fd); |
||||||
|
keep_alive_storage->client_not_alive_cb(keep_alive_storage, keep_alive_storage->clients[i].fd); |
||||||
|
} else { |
||||||
|
keep_alive_storage->check_client_alive_cb(keep_alive_storage, keep_alive_storage->clients[i].fd); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
vQueueDelete(keep_alive_storage->q); |
||||||
|
free(keep_alive_storage); |
||||||
|
|
||||||
|
vTaskDelete(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
wss_keep_alive_t wss_keep_alive_start(wss_keep_alive_config_t *config) |
||||||
|
{ |
||||||
|
size_t queue_size = config->max_clients/2; |
||||||
|
size_t client_list_size = config->max_clients + queue_size; |
||||||
|
wss_keep_alive_t keep_alive_storage = calloc(1, |
||||||
|
sizeof(wss_keep_alive_storage_t) + client_list_size* sizeof(client_fd_action_t)); |
||||||
|
if (keep_alive_storage == NULL) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
keep_alive_storage->check_client_alive_cb = config->check_client_alive_cb; |
||||||
|
keep_alive_storage->client_not_alive_cb = config->client_not_alive_cb; |
||||||
|
keep_alive_storage->max_clients = config->max_clients; |
||||||
|
keep_alive_storage->not_alive_after_ms = config->not_alive_after_ms; |
||||||
|
keep_alive_storage->keep_alive_period_ms = config->keep_alive_period_ms; |
||||||
|
keep_alive_storage->user_ctx = config->user_ctx; |
||||||
|
keep_alive_storage->q = xQueueCreate(queue_size, sizeof(client_fd_action_t)); |
||||||
|
if (xTaskCreate(keep_alive_task, "keep_alive_task", config->task_stack_size, |
||||||
|
keep_alive_storage, config->task_prio, NULL) != pdTRUE) { |
||||||
|
wss_keep_alive_stop(keep_alive_storage); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return keep_alive_storage; |
||||||
|
} |
||||||
|
|
||||||
|
void wss_keep_alive_stop(wss_keep_alive_t h) |
||||||
|
{ |
||||||
|
client_fd_action_t stop = { .type = STOP_TASK }; |
||||||
|
xQueueSendToBack(h->q, &stop, 0); |
||||||
|
// internal structs will be de-allocated in the task
|
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t wss_keep_alive_add_client(wss_keep_alive_t h, int fd) |
||||||
|
{ |
||||||
|
client_fd_action_t client_fd_action = { .fd = fd, .type = CLIENT_FD_ADD}; |
||||||
|
if (xQueueSendToBack(h->q, &client_fd_action, 0) == pdTRUE) { |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
return ESP_FAIL; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t wss_keep_alive_remove_client(wss_keep_alive_t h, int fd) |
||||||
|
{ |
||||||
|
client_fd_action_t client_fd_action = { .fd = fd, .type = CLIENT_FD_REMOVE}; |
||||||
|
if (xQueueSendToBack(h->q, &client_fd_action, 0) == pdTRUE) { |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
return ESP_FAIL; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t wss_keep_alive_client_is_active(wss_keep_alive_t h, int fd) |
||||||
|
{ |
||||||
|
client_fd_action_t client_fd_action = { .fd = fd, .type = CLIENT_UPDATE, |
||||||
|
.last_seen = _tick_get_ms()}; |
||||||
|
if (xQueueSendToBack(h->q, &client_fd_action, 0) == pdTRUE) { |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
return ESP_FAIL; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void wss_keep_alive_set_user_ctx(wss_keep_alive_t h, void *ctx) |
||||||
|
{ |
||||||
|
h->user_ctx = ctx; |
||||||
|
} |
||||||
|
|
||||||
|
void* wss_keep_alive_get_user_ctx(wss_keep_alive_t h) |
||||||
|
{ |
||||||
|
return h->user_ctx; |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
/* Keep Alive engine for wss server example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.) |
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this |
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||||
|
CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
|
||||||
|
#define KEEP_ALIVE_CONFIG_DEFAULT() \ |
||||||
|
{ \
|
||||||
|
.max_clients = 10, \
|
||||||
|
.task_stack_size = 2048, \
|
||||||
|
.task_prio = tskIDLE_PRIORITY + 1, \
|
||||||
|
.keep_alive_period_ms = 5000, \
|
||||||
|
.not_alive_after_ms = 10000, \
|
||||||
|
} |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" |
||||||
|
{ |
||||||
|
#endif |
||||||
|
|
||||||
|
struct wss_keep_alive_storage; |
||||||
|
typedef struct wss_keep_alive_storage *wss_keep_alive_t; |
||||||
|
typedef bool (*wss_check_client_alive_cb_t)(wss_keep_alive_t h, int fd); |
||||||
|
typedef bool (*wss_client_not_alive_cb_t)(wss_keep_alive_t h, int fd); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Confiuration struct |
||||||
|
*/ |
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
size_t max_clients; /*!< max number of clients */ |
||||||
|
size_t task_stack_size; /*!< stack size of the created task */ |
||||||
|
size_t task_prio; /*!< priority of the created task */ |
||||||
|
size_t keep_alive_period_ms; /*!< check every client after this time */ |
||||||
|
size_t not_alive_after_ms; /*!< consider client not alive after this time */ |
||||||
|
wss_check_client_alive_cb_t check_client_alive_cb; /*!< callback function to check if client is alive */ |
||||||
|
wss_client_not_alive_cb_t client_not_alive_cb; /*!< callback function to notify that the client is not alive */ |
||||||
|
void *user_ctx; /*!< user context available in the keep-alive handle */ |
||||||
|
} wss_keep_alive_config_t; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a new client to internal set of clients to keep an eye on |
||||||
|
* |
||||||
|
* @param h keep-alive handle |
||||||
|
* @param fd socket file descriptor for this client |
||||||
|
* @return ESP_OK on success |
||||||
|
*/ |
||||||
|
esp_err_t wss_keep_alive_add_client(wss_keep_alive_t h, int fd); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes this client from the set |
||||||
|
* |
||||||
|
* @param h keep-alive handle |
||||||
|
* @param fd socket file descriptor for this client |
||||||
|
* @return ESP_OK on success |
||||||
|
*/ |
||||||
|
esp_err_t wss_keep_alive_remove_client(wss_keep_alive_t h, int fd); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notify that this client is alive |
||||||
|
* |
||||||
|
* @param h keep-alive handle |
||||||
|
* @param fd socket file descriptor for this client |
||||||
|
* @return ESP_OK on success |
||||||
|
*/ |
||||||
|
|
||||||
|
esp_err_t wss_keep_alive_client_is_active(wss_keep_alive_t h, int fd); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Starts keep-alive engine |
||||||
|
* |
||||||
|
* @param config keep-alive configuration |
||||||
|
* @return keep alive handle |
||||||
|
*/ |
||||||
|
wss_keep_alive_t wss_keep_alive_start(wss_keep_alive_config_t *config); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stops keep-alive engine |
||||||
|
* |
||||||
|
* @param h keep-alive handle |
||||||
|
*/ |
||||||
|
void wss_keep_alive_stop(wss_keep_alive_t h); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets user defined context |
||||||
|
* |
||||||
|
* @param h keep-alive handle |
||||||
|
* @param ctx user context |
||||||
|
*/ |
||||||
|
void wss_keep_alive_set_user_ctx(wss_keep_alive_t h, void *ctx); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets user defined context |
||||||
|
* |
||||||
|
* @param h keep-alive handle |
||||||
|
* @return ctx user context |
||||||
|
*/ |
||||||
|
void *wss_keep_alive_get_user_ctx(wss_keep_alive_t h); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,194 @@ |
|||||||
|
/* BSD Socket API Example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.) |
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this |
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||||
|
CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
*/ |
||||||
|
#include <string.h> |
||||||
|
#include <sys/param.h> |
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/task.h" |
||||||
|
#include "esp_system.h" |
||||||
|
#include "esp_wifi.h" |
||||||
|
#include "esp_event.h" |
||||||
|
#include "esp_log.h" |
||||||
|
#include "nvs_flash.h" |
||||||
|
#include "esp_netif.h" |
||||||
|
#include "protocol_examples_common.h" |
||||||
|
|
||||||
|
#include "lwip/err.h" |
||||||
|
#include "lwip/sockets.h" |
||||||
|
#include "lwip/sys.h" |
||||||
|
#include <lwip/netdb.h> |
||||||
|
|
||||||
|
#define PORT CONFIG_EXAMPLE_PORT |
||||||
|
|
||||||
|
static const char *TAG = "udp_server"; |
||||||
|
|
||||||
|
void udp_server_task(void *pvParameters) |
||||||
|
{ |
||||||
|
char rx_buffer[128]; |
||||||
|
char addr_str[128]; |
||||||
|
int addr_family = (int)pvParameters; |
||||||
|
int ip_protocol = 0; |
||||||
|
struct sockaddr_in6 dest_addr; |
||||||
|
|
||||||
|
while (1) |
||||||
|
{ |
||||||
|
|
||||||
|
if (addr_family == AF_INET) |
||||||
|
{ |
||||||
|
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr; |
||||||
|
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); |
||||||
|
dest_addr_ip4->sin_family = AF_INET; |
||||||
|
dest_addr_ip4->sin_port = htons(PORT); |
||||||
|
ip_protocol = IPPROTO_IP; |
||||||
|
} |
||||||
|
else if (addr_family == AF_INET6) |
||||||
|
{ |
||||||
|
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un)); |
||||||
|
dest_addr.sin6_family = AF_INET6; |
||||||
|
dest_addr.sin6_port = htons(PORT); |
||||||
|
ip_protocol = IPPROTO_IPV6; |
||||||
|
} |
||||||
|
|
||||||
|
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); |
||||||
|
if (sock < 0) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); |
||||||
|
break; |
||||||
|
} |
||||||
|
ESP_LOGI(TAG, "Socket created"); |
||||||
|
|
||||||
|
#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6) |
||||||
|
int enable = 1; |
||||||
|
lwip_setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable)); |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6) |
||||||
|
if (addr_family == AF_INET6) |
||||||
|
{ |
||||||
|
// Note that by default IPV6 binds to both protocols, it is must be disabled
|
||||||
|
// if both protocols used at the same time (used in CI)
|
||||||
|
int opt = 1; |
||||||
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
||||||
|
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); |
||||||
|
} |
||||||
|
#endif |
||||||
|
// Set timeout
|
||||||
|
struct timeval timeout; |
||||||
|
timeout.tv_sec = 10; |
||||||
|
timeout.tv_usec = 0; |
||||||
|
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout); |
||||||
|
|
||||||
|
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); |
||||||
|
if (err < 0) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); |
||||||
|
} |
||||||
|
ESP_LOGI(TAG, "Socket bound, port %d", PORT); |
||||||
|
|
||||||
|
struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
|
||||||
|
socklen_t socklen = sizeof(source_addr); |
||||||
|
|
||||||
|
#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6) |
||||||
|
struct iovec iov; |
||||||
|
struct msghdr msg; |
||||||
|
struct cmsghdr *cmsgtmp; |
||||||
|
u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
||||||
|
|
||||||
|
iov.iov_base = rx_buffer; |
||||||
|
iov.iov_len = sizeof(rx_buffer); |
||||||
|
msg.msg_control = cmsg_buf; |
||||||
|
msg.msg_controllen = sizeof(cmsg_buf); |
||||||
|
msg.msg_flags = 0; |
||||||
|
msg.msg_iov = &iov; |
||||||
|
msg.msg_iovlen = 1; |
||||||
|
msg.msg_name = (struct sockaddr *)&source_addr; |
||||||
|
msg.msg_namelen = socklen; |
||||||
|
#endif |
||||||
|
|
||||||
|
while (1) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG, "Waiting for data"); |
||||||
|
#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6) |
||||||
|
int len = recvmsg(sock, &msg, 0); |
||||||
|
#else |
||||||
|
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen); |
||||||
|
#endif |
||||||
|
// Error occurred during receiving
|
||||||
|
if (len < 0) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); |
||||||
|
break; |
||||||
|
} |
||||||
|
// Data received
|
||||||
|
else |
||||||
|
{ |
||||||
|
// Get the sender's ip address as string
|
||||||
|
if (source_addr.ss_family == PF_INET) |
||||||
|
{ |
||||||
|
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1); |
||||||
|
#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6) |
||||||
|
for (cmsgtmp = CMSG_FIRSTHDR(&msg); cmsgtmp != NULL; cmsgtmp = CMSG_NXTHDR(&msg, cmsgtmp)) |
||||||
|
{ |
||||||
|
if (cmsgtmp->cmsg_level == IPPROTO_IP && cmsgtmp->cmsg_type == IP_PKTINFO) |
||||||
|
{ |
||||||
|
struct in_pktinfo *pktinfo; |
||||||
|
pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsgtmp); |
||||||
|
ESP_LOGI(TAG, "dest ip: %s", inet_ntoa(pktinfo->ipi_addr)); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
else if (source_addr.ss_family == PF_INET6) |
||||||
|
{ |
||||||
|
inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1); |
||||||
|
} |
||||||
|
|
||||||
|
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string...
|
||||||
|
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str); |
||||||
|
ESP_LOGI(TAG, "%s", rx_buffer); |
||||||
|
|
||||||
|
int err = sendto(sock, rx_buffer, len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr)); |
||||||
|
if (err < 0) |
||||||
|
{ |
||||||
|
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// dont restart
|
||||||
|
// if (sock != -1)
|
||||||
|
// {
|
||||||
|
// ESP_LOGE(TAG, "Shutting down socket and restarting...");
|
||||||
|
// shutdown(sock, 0);
|
||||||
|
// close(sock);
|
||||||
|
// }
|
||||||
|
} |
||||||
|
vTaskDelete(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
// void app_main(void)
|
||||||
|
// {
|
||||||
|
// ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
// ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
// ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
|
// /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||||
|
// * Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||||
|
// * examples/protocols/README.md for more information about this function.
|
||||||
|
// */
|
||||||
|
// ESP_ERROR_CHECK(example_connect());
|
||||||
|
|
||||||
|
// #ifdef CONFIG_EXAMPLE_IPV4
|
||||||
|
// xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET, 5, NULL);
|
||||||
|
// #endif
|
||||||
|
// #ifdef CONFIG_EXAMPLE_IPV6
|
||||||
|
// xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET6, 5, NULL);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// }
|
@ -0,0 +1,11 @@ |
|||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" |
||||||
|
{ |
||||||
|
#endif |
||||||
|
|
||||||
|
void udp_server_task(void *pvParameters); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,157 @@ |
|||||||
|
/* ESP HTTP Client Example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.) |
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this |
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||||
|
CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include "esp_wifi.h" |
||||||
|
#include "esp_system.h" |
||||||
|
#include "nvs_flash.h" |
||||||
|
#include "esp_event.h" |
||||||
|
#include "protocol_examples_common.h" |
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/task.h" |
||||||
|
#include "freertos/semphr.h" |
||||||
|
#include "freertos/event_groups.h" |
||||||
|
|
||||||
|
#include "esp_log.h" |
||||||
|
#include "esp_websocket_client.h" |
||||||
|
#include "esp_event.h" |
||||||
|
|
||||||
|
#include "websocket_client.h" |
||||||
|
#define NO_DATA_TIMEOUT_SEC 10 |
||||||
|
|
||||||
|
static const char *TAG = "WEBSOCKET"; |
||||||
|
|
||||||
|
static TimerHandle_t shutdown_signal_timer; |
||||||
|
static SemaphoreHandle_t shutdown_sema; |
||||||
|
|
||||||
|
static void shutdown_signaler(TimerHandle_t xTimer) |
||||||
|
{ |
||||||
|
ESP_LOGI(TAG, "No data received for %d seconds, signaling shutdown", NO_DATA_TIMEOUT_SEC); |
||||||
|
xSemaphoreGive(shutdown_sema); |
||||||
|
} |
||||||
|
|
||||||
|
#if CONFIG_WEBSOCKET_URI_FROM_STDIN |
||||||
|
static void get_string(char *line, size_t size) |
||||||
|
{ |
||||||
|
int count = 0; |
||||||
|
while (count < size) |
||||||
|
{ |
||||||
|
int c = fgetc(stdin); |
||||||
|
if (c == '\n') |
||||||
|
{ |
||||||
|
line[count] = '\0'; |
||||||
|
break; |
||||||
|
} |
||||||
|
else if (c > 0 && c < 127) |
||||||
|
{ |
||||||
|
line[count] = c; |
||||||
|
++count; |
||||||
|
} |
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */ |
||||||
|
|
||||||
|
static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) |
||||||
|
{ |
||||||
|
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; |
||||||
|
switch (event_id) |
||||||
|
{ |
||||||
|
case WEBSOCKET_EVENT_CONNECTED: |
||||||
|
ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED"); |
||||||
|
break; |
||||||
|
case WEBSOCKET_EVENT_DISCONNECTED: |
||||||
|
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED"); |
||||||
|
break; |
||||||
|
case WEBSOCKET_EVENT_DATA: |
||||||
|
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA"); |
||||||
|
ESP_LOGI(TAG, "Received opcode=%d", data->op_code); |
||||||
|
ESP_LOGW(TAG, "Received=%.*s", data->data_len, (char *)data->data_ptr); |
||||||
|
ESP_LOGW(TAG, "Total payload length=%d, data_len=%d, current payload offset=%d\r\n", data->payload_len, data->data_len, data->payload_offset); |
||||||
|
|
||||||
|
xTimerReset(shutdown_signal_timer, portMAX_DELAY); |
||||||
|
break; |
||||||
|
case WEBSOCKET_EVENT_ERROR: |
||||||
|
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// TODO write task
|
||||||
|
void websocket_client_start(void) |
||||||
|
{ |
||||||
|
esp_websocket_client_config_t websocket_cfg = {}; |
||||||
|
|
||||||
|
shutdown_signal_timer = xTimerCreate("Websocket shutdown timer", NO_DATA_TIMEOUT_SEC * 1000 / portTICK_PERIOD_MS, |
||||||
|
pdFALSE, NULL, shutdown_signaler); |
||||||
|
shutdown_sema = xSemaphoreCreateBinary(); |
||||||
|
|
||||||
|
#if CONFIG_WEBSOCKET_URI_FROM_STDIN |
||||||
|
char line[128]; |
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Please enter uri of websocket endpoint"); |
||||||
|
get_string(line, sizeof(line)); |
||||||
|
|
||||||
|
websocket_cfg.uri = line; |
||||||
|
ESP_LOGI(TAG, "Endpoint uri: %s\n", line); |
||||||
|
|
||||||
|
#else |
||||||
|
websocket_cfg.uri = CONFIG_WEBSOCKET_URI; |
||||||
|
|
||||||
|
#endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */ |
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Connecting to %s...", websocket_cfg.uri); |
||||||
|
|
||||||
|
esp_websocket_client_handle_t client = esp_websocket_client_init(&websocket_cfg); |
||||||
|
esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client); |
||||||
|
|
||||||
|
esp_websocket_client_start(client); |
||||||
|
xTimerStart(shutdown_signal_timer, portMAX_DELAY); |
||||||
|
char data[32]; |
||||||
|
int i = 0; |
||||||
|
while (i < 10) |
||||||
|
{ |
||||||
|
if (esp_websocket_client_is_connected(client)) |
||||||
|
{ |
||||||
|
int len = sprintf(data, "hello %04d", i++); |
||||||
|
ESP_LOGI(TAG, "Sending %s", data); |
||||||
|
esp_websocket_client_send_text(client, data, len, portMAX_DELAY); |
||||||
|
} |
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS); |
||||||
|
} |
||||||
|
|
||||||
|
xSemaphoreTake(shutdown_sema, portMAX_DELAY); |
||||||
|
esp_websocket_client_stop(client); |
||||||
|
ESP_LOGI(TAG, "Websocket Stopped"); |
||||||
|
esp_websocket_client_destroy(client); |
||||||
|
} |
||||||
|
|
||||||
|
// void app_main(void)
|
||||||
|
// {
|
||||||
|
// ESP_LOGI(TAG, "[APP] Startup..");
|
||||||
|
// ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
|
||||||
|
// ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
|
||||||
|
// esp_log_level_set("*", ESP_LOG_INFO);
|
||||||
|
// esp_log_level_set("WEBSOCKET_CLIENT", ESP_LOG_DEBUG);
|
||||||
|
// esp_log_level_set("TRANS_TCP", ESP_LOG_DEBUG);
|
||||||
|
|
||||||
|
// ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
// ESP_ERROR_CHECK(esp_netif_init());
|
||||||
|
// ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||||
|
|
||||||
|
// /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||||
|
// * Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||||
|
// * examples/protocols/README.md for more information about this function.
|
||||||
|
// */
|
||||||
|
// ESP_ERROR_CHECK(example_connect());
|
||||||
|
|
||||||
|
// websocket_app_start();
|
||||||
|
// }
|
@ -0,0 +1,11 @@ |
|||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" |
||||||
|
{ |
||||||
|
#endif |
||||||
|
|
||||||
|
void websocket_client_start(void); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1 @@ |
|||||||
|
e946617ee6bec30ef0cff505bc537e658a80d7b9222c060e225bf748f2029442 |
@ -0,0 +1,271 @@ |
|||||||
|
# Check ESP-IDF version and error out if it is not in the supported range. |
||||||
|
# |
||||||
|
# Note for arduino-esp32 developers: to bypass the version check locally, |
||||||
|
# set ARDUINO_SKIP_IDF_VERSION_CHECK environment variable to 1. For example: |
||||||
|
# export ARDUINO_SKIP_IDF_VERSION_CHECK=1 |
||||||
|
# idf.py build |
||||||
|
|
||||||
|
set(min_supported_idf_version "5.1.0") |
||||||
|
set(max_supported_idf_version "5.1.99") |
||||||
|
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") |
||||||
|
|
||||||
|
if ("${idf_version}" AND NOT "$ENV{ARDUINO_SKIP_IDF_VERSION_CHECK}") |
||||||
|
if (idf_version VERSION_LESS min_supported_idf_version) |
||||||
|
message(FATAL_ERROR "Arduino-esp32 can be used with ESP-IDF versions " |
||||||
|
"between ${min_supported_idf_version} and ${max_supported_idf_version}, " |
||||||
|
"but an older version is detected: ${idf_version}.") |
||||||
|
endif() |
||||||
|
if (idf_version VERSION_GREATER max_supported_idf_version) |
||||||
|
message(FATAL_ERROR "Arduino-esp32 can be used with ESP-IDF versions " |
||||||
|
"between ${min_supported_idf_version} and ${max_supported_idf_version}, " |
||||||
|
"but a newer version is detected: ${idf_version}.") |
||||||
|
endif() |
||||||
|
endif() |
||||||
|
|
||||||
|
set(CORE_SRCS |
||||||
|
cores/esp32/base64.cpp |
||||||
|
cores/esp32/cbuf.cpp |
||||||
|
cores/esp32/chip-debug-report.cpp |
||||||
|
cores/esp32/esp32-hal-adc.c |
||||||
|
cores/esp32/esp32-hal-bt.c |
||||||
|
cores/esp32/esp32-hal-cpu.c |
||||||
|
cores/esp32/esp32-hal-dac.c |
||||||
|
cores/esp32/esp32-hal-gpio.c |
||||||
|
cores/esp32/esp32-hal-i2c.c |
||||||
|
cores/esp32/esp32-hal-i2c-slave.c |
||||||
|
cores/esp32/esp32-hal-ledc.c |
||||||
|
cores/esp32/esp32-hal-matrix.c |
||||||
|
cores/esp32/esp32-hal-misc.c |
||||||
|
cores/esp32/esp32-hal-periman.c |
||||||
|
cores/esp32/esp32-hal-psram.c |
||||||
|
cores/esp32/esp32-hal-rgb-led.c |
||||||
|
cores/esp32/esp32-hal-sigmadelta.c |
||||||
|
cores/esp32/esp32-hal-spi.c |
||||||
|
cores/esp32/esp32-hal-time.c |
||||||
|
cores/esp32/esp32-hal-timer.c |
||||||
|
cores/esp32/esp32-hal-tinyusb.c |
||||||
|
cores/esp32/esp32-hal-touch.c |
||||||
|
cores/esp32/esp32-hal-uart.c |
||||||
|
cores/esp32/esp32-hal-rmt.c |
||||||
|
cores/esp32/Esp.cpp |
||||||
|
cores/esp32/FunctionalInterrupt.cpp |
||||||
|
cores/esp32/HardwareSerial.cpp |
||||||
|
cores/esp32/IPAddress.cpp |
||||||
|
cores/esp32/IPv6Address.cpp |
||||||
|
cores/esp32/libb64/cdecode.c |
||||||
|
cores/esp32/libb64/cencode.c |
||||||
|
cores/esp32/main.cpp |
||||||
|
cores/esp32/MD5Builder.cpp |
||||||
|
cores/esp32/Print.cpp |
||||||
|
cores/esp32/stdlib_noniso.c |
||||||
|
cores/esp32/Stream.cpp |
||||||
|
cores/esp32/StreamString.cpp |
||||||
|
cores/esp32/Tone.cpp |
||||||
|
cores/esp32/HWCDC.cpp |
||||||
|
cores/esp32/USB.cpp |
||||||
|
cores/esp32/USBCDC.cpp |
||||||
|
cores/esp32/USBMSC.cpp |
||||||
|
cores/esp32/FirmwareMSC.cpp |
||||||
|
cores/esp32/firmware_msc_fat.c |
||||||
|
cores/esp32/wiring_pulse.c |
||||||
|
cores/esp32/wiring_shift.c |
||||||
|
cores/esp32/WMath.cpp |
||||||
|
cores/esp32/WString.cpp |
||||||
|
) |
||||||
|
|
||||||
|
set(LIBRARY_SRCS |
||||||
|
libraries/ArduinoOTA/src/ArduinoOTA.cpp |
||||||
|
libraries/AsyncUDP/src/AsyncUDP.cpp |
||||||
|
libraries/BluetoothSerial/src/BluetoothSerial.cpp |
||||||
|
libraries/BluetoothSerial/src/BTAddress.cpp |
||||||
|
libraries/BluetoothSerial/src/BTAdvertisedDeviceSet.cpp |
||||||
|
libraries/BluetoothSerial/src/BTScanResultsSet.cpp |
||||||
|
libraries/DNSServer/src/DNSServer.cpp |
||||||
|
libraries/EEPROM/src/EEPROM.cpp |
||||||
|
libraries/ESPmDNS/src/ESPmDNS.cpp |
||||||
|
libraries/Ethernet/src/ETH.cpp |
||||||
|
libraries/FFat/src/FFat.cpp |
||||||
|
libraries/FS/src/FS.cpp |
||||||
|
libraries/FS/src/vfs_api.cpp |
||||||
|
libraries/HTTPClient/src/HTTPClient.cpp |
||||||
|
libraries/HTTPUpdate/src/HTTPUpdate.cpp |
||||||
|
libraries/LittleFS/src/LittleFS.cpp |
||||||
|
libraries/Insights/src/Insights.cpp |
||||||
|
libraries/I2S/src/I2S.cpp |
||||||
|
libraries/NetBIOS/src/NetBIOS.cpp |
||||||
|
libraries/Preferences/src/Preferences.cpp |
||||||
|
libraries/RainMaker/src/RMaker.cpp |
||||||
|
libraries/RainMaker/src/RMakerNode.cpp |
||||||
|
libraries/RainMaker/src/RMakerParam.cpp |
||||||
|
libraries/RainMaker/src/RMakerDevice.cpp |
||||||
|
libraries/RainMaker/src/RMakerType.cpp |
||||||
|
libraries/RainMaker/src/RMakerQR.cpp |
||||||
|
libraries/RainMaker/src/RMakerUtils.cpp |
||||||
|
libraries/RainMaker/src/AppInsights.cpp |
||||||
|
libraries/SD_MMC/src/SD_MMC.cpp |
||||||
|
libraries/SD/src/SD.cpp |
||||||
|
libraries/SD/src/sd_diskio.cpp |
||||||
|
libraries/SD/src/sd_diskio_crc.c |
||||||
|
libraries/SimpleBLE/src/SimpleBLE.cpp |
||||||
|
libraries/SPIFFS/src/SPIFFS.cpp |
||||||
|
libraries/SPI/src/SPI.cpp |
||||||
|
libraries/Ticker/src/Ticker.cpp |
||||||
|
libraries/Update/src/Updater.cpp |
||||||
|
libraries/Update/src/HttpsOTAUpdate.cpp |
||||||
|
libraries/USB/src/USBHID.cpp |
||||||
|
libraries/USB/src/USBHIDMouse.cpp |
||||||
|
libraries/USB/src/USBHIDKeyboard.cpp |
||||||
|
libraries/USB/src/USBHIDGamepad.cpp |
||||||
|
libraries/USB/src/USBHIDConsumerControl.cpp |
||||||
|
libraries/USB/src/USBHIDSystemControl.cpp |
||||||
|
libraries/USB/src/USBHIDVendor.cpp |
||||||
|
libraries/USB/src/USBVendor.cpp |
||||||
|
libraries/WebServer/src/WebServer.cpp |
||||||
|
libraries/WebServer/src/Parsing.cpp |
||||||
|
libraries/WebServer/src/detail/mimetable.cpp |
||||||
|
libraries/WiFiClientSecure/src/ssl_client.cpp |
||||||
|
libraries/WiFiClientSecure/src/WiFiClientSecure.cpp |
||||||
|
libraries/WiFi/src/WiFiAP.cpp |
||||||
|
libraries/WiFi/src/WiFiClient.cpp |
||||||
|
libraries/WiFi/src/WiFi.cpp |
||||||
|
libraries/WiFi/src/WiFiGeneric.cpp |
||||||
|
libraries/WiFi/src/WiFiMulti.cpp |
||||||
|
libraries/WiFi/src/WiFiScan.cpp |
||||||
|
libraries/WiFi/src/WiFiServer.cpp |
||||||
|
libraries/WiFi/src/WiFiSTA.cpp |
||||||
|
libraries/WiFi/src/WiFiUdp.cpp |
||||||
|
libraries/WiFiProv/src/WiFiProv.cpp |
||||||
|
libraries/Wire/src/Wire.cpp |
||||||
|
) |
||||||
|
|
||||||
|
set(BLE_SRCS |
||||||
|
libraries/BLE/src/BLE2902.cpp |
||||||
|
libraries/BLE/src/BLE2904.cpp |
||||||
|
libraries/BLE/src/BLEAddress.cpp |
||||||
|
libraries/BLE/src/BLEAdvertisedDevice.cpp |
||||||
|
libraries/BLE/src/BLEAdvertising.cpp |
||||||
|
libraries/BLE/src/BLEBeacon.cpp |
||||||
|
libraries/BLE/src/BLECharacteristic.cpp |
||||||
|
libraries/BLE/src/BLECharacteristicMap.cpp |
||||||
|
libraries/BLE/src/BLEClient.cpp |
||||||
|
libraries/BLE/src/BLEDescriptor.cpp |
||||||
|
libraries/BLE/src/BLEDescriptorMap.cpp |
||||||
|
libraries/BLE/src/BLEDevice.cpp |
||||||
|
libraries/BLE/src/BLEEddystoneTLM.cpp |
||||||
|
libraries/BLE/src/BLEEddystoneURL.cpp |
||||||
|
libraries/BLE/src/BLEExceptions.cpp |
||||||
|
libraries/BLE/src/BLEHIDDevice.cpp |
||||||
|
libraries/BLE/src/BLERemoteCharacteristic.cpp |
||||||
|
libraries/BLE/src/BLERemoteDescriptor.cpp |
||||||
|
libraries/BLE/src/BLERemoteService.cpp |
||||||
|
libraries/BLE/src/BLEScan.cpp |
||||||
|
libraries/BLE/src/BLESecurity.cpp |
||||||
|
libraries/BLE/src/BLEServer.cpp |
||||||
|
libraries/BLE/src/BLEService.cpp |
||||||
|
libraries/BLE/src/BLEServiceMap.cpp |
||||||
|
libraries/BLE/src/BLEUtils.cpp |
||||||
|
libraries/BLE/src/BLEUUID.cpp |
||||||
|
libraries/BLE/src/BLEValue.cpp |
||||||
|
libraries/BLE/src/FreeRTOS.cpp |
||||||
|
libraries/BLE/src/GeneralUtils.cpp |
||||||
|
) |
||||||
|
|
||||||
|
set(includedirs |
||||||
|
variants/${CONFIG_ARDUINO_VARIANT}/ |
||||||
|
cores/esp32/ |
||||||
|
libraries/ArduinoOTA/src |
||||||
|
libraries/AsyncUDP/src |
||||||
|
libraries/BLE/src |
||||||
|
libraries/BluetoothSerial/src |
||||||
|
libraries/DNSServer/src |
||||||
|
libraries/EEPROM/src |
||||||
|
libraries/ESP32/src |
||||||
|
libraries/ESPmDNS/src |
||||||
|
libraries/Ethernet/src |
||||||
|
libraries/FFat/src |
||||||
|
libraries/FS/src |
||||||
|
libraries/HTTPClient/src |
||||||
|
libraries/HTTPUpdate/src |
||||||
|
libraries/LittleFS/src |
||||||
|
libraries/Insights/src |
||||||
|
libraries/I2S/src |
||||||
|
libraries/NetBIOS/src |
||||||
|
libraries/Preferences/src |
||||||
|
libraries/RainMaker/src |
||||||
|
libraries/SD_MMC/src |
||||||
|
libraries/SD/src |
||||||
|
libraries/SimpleBLE/src |
||||||
|
libraries/SPIFFS/src |
||||||
|
libraries/SPI/src |
||||||
|
libraries/Ticker/src |
||||||
|
libraries/Update/src |
||||||
|
libraries/USB/src |
||||||
|
libraries/WebServer/src |
||||||
|
libraries/WiFiClientSecure/src |
||||||
|
libraries/WiFi/src |
||||||
|
libraries/WiFiProv/src |
||||||
|
libraries/Wire/src |
||||||
|
) |
||||||
|
|
||||||
|
set(srcs ${CORE_SRCS} ${LIBRARY_SRCS} ${BLE_SRCS}) |
||||||
|
set(priv_includes cores/esp32/libb64) |
||||||
|
set(requires spi_flash mbedtls wifi_provisioning wpa_supplicant esp_adc esp_eth http_parser) |
||||||
|
set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support bt esp_hid) |
||||||
|
|
||||||
|
idf_component_register(INCLUDE_DIRS ${includedirs} PRIV_INCLUDE_DIRS ${priv_includes} SRCS ${srcs} REQUIRES ${requires} PRIV_REQUIRES ${priv_requires}) |
||||||
|
|
||||||
|
if(NOT CONFIG_FREERTOS_HZ EQUAL 1000 AND NOT "$ENV{ARDUINO_SKIP_TICK_CHECK}") |
||||||
|
# See delay() in cores/esp32/esp32-hal-misc.c. |
||||||
|
message(FATAL_ERROR "esp32-arduino requires CONFIG_FREERTOS_HZ=1000 " |
||||||
|
"(currently ${CONFIG_FREERTOS_HZ})") |
||||||
|
endif() |
||||||
|
|
||||||
|
string(TOUPPER ${CONFIG_ARDUINO_VARIANT} idf_target_caps) |
||||||
|
string(REPLACE "-" "_" idf_target_for_macro "${idf_target_caps}") |
||||||
|
target_compile_options(${COMPONENT_TARGET} PUBLIC |
||||||
|
-DARDUINO=10812 |
||||||
|
-DARDUINO_${idf_target_for_macro}_DEV |
||||||
|
-DARDUINO_ARCH_ESP32 |
||||||
|
-DARDUINO_BOARD="${idf_target_caps}_DEV" |
||||||
|
-DARDUINO_VARIANT="${CONFIG_ARDUINO_VARIANT}" |
||||||
|
-DESP32) |
||||||
|
|
||||||
|
if(CONFIG_AUTOSTART_ARDUINO) |
||||||
|
# in autostart mode, arduino-esp32 contains app_main() function and needs to |
||||||
|
# reference setup() and loop() in the main component. If we add main |
||||||
|
# component to priv_requires then we create a large circular dependency |
||||||
|
# (arduino-esp32 -> main -> arduino-esp32) and can get linker errors, so |
||||||
|
# instead we add setup() and loop() to the undefined symbols list so the |
||||||
|
# linker will always include them. |
||||||
|
# |
||||||
|
# (As they are C++ symbol, we need to add the C++ mangled names.) |
||||||
|
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u _Z5setupv -u _Z4loopv") |
||||||
|
endif() |
||||||
|
|
||||||
|
# This function adds a dependency on the given component if the component is included into the build. |
||||||
|
function(maybe_add_component component_name) |
||||||
|
idf_build_get_property(components BUILD_COMPONENTS) |
||||||
|
if (${component_name} IN_LIST components) |
||||||
|
idf_component_get_property(lib_name ${component_name} COMPONENT_LIB) |
||||||
|
target_link_libraries(${COMPONENT_LIB} PUBLIC ${lib_name}) |
||||||
|
endif() |
||||||
|
endfunction() |
||||||
|
|
||||||
|
maybe_add_component(esp-dsp) |
||||||
|
|
||||||
|
if(CONFIG_ESP_INSIGHTS_ENABLED) |
||||||
|
maybe_add_component(esp_insights) |
||||||
|
endif() |
||||||
|
if(CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK) |
||||||
|
maybe_add_component(esp_rainmaker) |
||||||
|
maybe_add_component(qrcode) |
||||||
|
endif() |
||||||
|
if(IDF_TARGET MATCHES "esp32s2|esp32s3" AND CONFIG_TINYUSB_ENABLED) |
||||||
|
maybe_add_component(arduino_tinyusb) |
||||||
|
endif() |
||||||
|
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_ArduinoOTA) |
||||||
|
maybe_add_component(esp_https_ota) |
||||||
|
endif() |
||||||
|
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_LITTLEFS) |
||||||
|
maybe_add_component(esp_littlefs) |
||||||
|
endif() |
@ -0,0 +1,412 @@ |
|||||||
|
menu "Arduino Configuration" |
||||||
|
|
||||||
|
config ARDUINO_VARIANT |
||||||
|
string "Arduino target variant (board)" |
||||||
|
default IDF_TARGET |
||||||
|
help |
||||||
|
The name of a target variant (e.g., a specific board) in the variants/ |
||||||
|
folder, e.g. "heltec_wifi_lora_32_V2". The name is case sensitive. |
||||||
|
Specifying a variant name different from the target enables additional |
||||||
|
customization, for example the definition of GPIO pins. |
||||||
|
|
||||||
|
config ENABLE_ARDUINO_DEPENDS |
||||||
|
bool |
||||||
|
select LWIP_SO_RCVBUF |
||||||
|
select ETHERNET |
||||||
|
select WIFI_ENABLED |
||||||
|
select ESP32_PHY_CALIBRATION_AND_DATA_STORAGE if IDF_TARGET_ESP32 |
||||||
|
select MEMMAP_SMP |
||||||
|
default "y" |
||||||
|
|
||||||
|
config AUTOSTART_ARDUINO |
||||||
|
bool "Autostart Arduino setup and loop on boot" |
||||||
|
default "n" |
||||||
|
help |
||||||
|
Enabling this option will implement app_main and start Arduino. |
||||||
|
All you need to implement in your main.cpp is setup() and loop() |
||||||
|
and include Arduino.h |
||||||
|
If disabled, you can call initArduino() to run any preparations |
||||||
|
required by the framework |
||||||
|
|
||||||
|
choice ARDUINO_RUNNING_CORE |
||||||
|
bool "Core on which Arduino's setup() and loop() are running" |
||||||
|
default ARDUINO_RUN_CORE0 if FREERTOS_UNICORE |
||||||
|
default ARDUINO_RUN_CORE1 if !FREERTOS_UNICORE |
||||||
|
help |
||||||
|
Select on which core Arduino's setup() and loop() functions run |
||||||
|
|
||||||
|
config ARDUINO_RUN_CORE0 |
||||||
|
bool "CORE 0" |
||||||
|
config ARDUINO_RUN_CORE1 |
||||||
|
bool "CORE 1" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
config ARDUINO_RUN_NO_AFFINITY |
||||||
|
bool "BOTH" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
|
||||||
|
endchoice |
||||||
|
|
||||||
|
config ARDUINO_RUNNING_CORE |
||||||
|
int |
||||||
|
default 0 if ARDUINO_RUN_CORE0 |
||||||
|
default 1 if ARDUINO_RUN_CORE1 |
||||||
|
default -1 if ARDUINO_RUN_NO_AFFINITY |
||||||
|
|
||||||
|
config ARDUINO_LOOP_STACK_SIZE |
||||||
|
int "Loop thread stack size" |
||||||
|
default 8192 |
||||||
|
help |
||||||
|
Amount of stack available for the Arduino task. |
||||||
|
|
||||||
|
choice ARDUINO_EVENT_RUNNING_CORE |
||||||
|
bool "Core on which Arduino's event handler is running" |
||||||
|
default ARDUINO_EVENT_RUN_CORE0 if FREERTOS_UNICORE |
||||||
|
default ARDUINO_EVENT_RUN_CORE1 if !FREERTOS_UNICORE |
||||||
|
help |
||||||
|
Select on which core Arduino's WiFi.onEvent() run |
||||||
|
|
||||||
|
config ARDUINO_EVENT_RUN_CORE0 |
||||||
|
bool "CORE 0" |
||||||
|
config ARDUINO_EVENT_RUN_CORE1 |
||||||
|
bool "CORE 1" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
config ARDUINO_EVENT_RUN_NO_AFFINITY |
||||||
|
bool "BOTH" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
|
||||||
|
endchoice |
||||||
|
|
||||||
|
config ARDUINO_EVENT_RUNNING_CORE |
||||||
|
int |
||||||
|
default 0 if ARDUINO_EVENT_RUN_CORE0 |
||||||
|
default 1 if ARDUINO_EVENT_RUN_CORE1 |
||||||
|
default -1 if ARDUINO_EVENT_RUN_NO_AFFINITY |
||||||
|
|
||||||
|
choice ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE |
||||||
|
bool "Core on which Arduino's Serial Event task is running" |
||||||
|
default ARDUINO_SERIAL_EVENT_RUN_CORE0 if FREERTOS_UNICORE |
||||||
|
default ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY if !FREERTOS_UNICORE |
||||||
|
help |
||||||
|
Select on which core Arduino's Serial Event task run |
||||||
|
|
||||||
|
config ARDUINO_SERIAL_EVENT_RUN_CORE0 |
||||||
|
bool "CORE 0" |
||||||
|
config ARDUINO_SERIAL_EVENT_RUN_CORE1 |
||||||
|
bool "CORE 1" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
config ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY |
||||||
|
bool "BOTH" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
|
||||||
|
endchoice |
||||||
|
|
||||||
|
config ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE |
||||||
|
int |
||||||
|
default 0 if ARDUINO_SERIAL_EVENT_RUN_CORE0 |
||||||
|
default 1 if ARDUINO_SERIAL_EVENT_RUN_CORE1 |
||||||
|
default -1 if ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY |
||||||
|
|
||||||
|
config ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE |
||||||
|
int "Serial Event task stack size" |
||||||
|
default 2048 |
||||||
|
help |
||||||
|
Amount of stack available for the Serial Event task. |
||||||
|
|
||||||
|
config ARDUINO_SERIAL_EVENT_TASK_PRIORITY |
||||||
|
int "Priority of the Serial Event task" |
||||||
|
default 24 |
||||||
|
help |
||||||
|
Select at what priority you want the Serial Event task to run. |
||||||
|
|
||||||
|
choice ARDUINO_UDP_RUNNING_CORE |
||||||
|
bool "Core on which Arduino's UDP is running" |
||||||
|
default ARDUINO_UDP_RUN_CORE0 |
||||||
|
help |
||||||
|
Select on which core Arduino's UDP run |
||||||
|
|
||||||
|
config ARDUINO_UDP_RUN_CORE0 |
||||||
|
bool "CORE 0" |
||||||
|
config ARDUINO_UDP_RUN_CORE1 |
||||||
|
bool "CORE 1" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
config ARDUINO_UDP_RUN_NO_AFFINITY |
||||||
|
bool "BOTH" |
||||||
|
depends on !FREERTOS_UNICORE |
||||||
|
|
||||||
|
endchoice |
||||||
|
|
||||||
|
config ARDUINO_UDP_RUNNING_CORE |
||||||
|
int |
||||||
|
default 0 if ARDUINO_UDP_RUN_CORE0 |
||||||
|
default 1 if ARDUINO_UDP_RUN_CORE1 |
||||||
|
default -1 if ARDUINO_UDP_RUN_NO_AFFINITY |
||||||
|
|
||||||
|
config ARDUINO_UDP_TASK_PRIORITY |
||||||
|
int "Priority of the UDP task" |
||||||
|
default 3 |
||||||
|
help |
||||||
|
Select at what priority you want the UDP task to run. |
||||||
|
|
||||||
|
config ARDUINO_ISR_IRAM |
||||||
|
bool "Run interrupts in IRAM" |
||||||
|
default "n" |
||||||
|
help |
||||||
|
Enabling this option will Attach all interrupts with the IRAm flag. |
||||||
|
It will also make some HAL function, like, digitalRead/Write and more |
||||||
|
be loaded into IRAM for access inside ISRs. |
||||||
|
Beware that this is a very dangerous setting. Enable it only if you |
||||||
|
are fully aware of the consequences. |
||||||
|
|
||||||
|
config DISABLE_HAL_LOCKS |
||||||
|
bool "Disable mutex locks for HAL" |
||||||
|
default "n" |
||||||
|
help |
||||||
|
Enabling this option will run all hardware abstraction without locks. |
||||||
|
While communication with external hardware will be faster, you need to |
||||||
|
make sure that there is no option to use the same bus from another thread |
||||||
|
or interrupt at the same time. Option is best used with Arduino enabled |
||||||
|
and code implemented only in setup/loop and Arduino callbacks |
||||||
|
|
||||||
|
menu "Debug Log Configuration" |
||||||
|
choice ARDUHAL_LOG_DEFAULT_LEVEL |
||||||
|
bool "Default log level" |
||||||
|
default ARDUHAL_LOG_DEFAULT_LEVEL_ERROR |
||||||
|
help |
||||||
|
Specify how much output to see in logs by default. |
||||||
|
|
||||||
|
config ARDUHAL_LOG_DEFAULT_LEVEL_NONE |
||||||
|
bool "No output" |
||||||
|
config ARDUHAL_LOG_DEFAULT_LEVEL_ERROR |
||||||
|
bool "Error" |
||||||
|
config ARDUHAL_LOG_DEFAULT_LEVEL_WARN |
||||||
|
bool "Warning" |
||||||
|
config ARDUHAL_LOG_DEFAULT_LEVEL_INFO |
||||||
|
bool "Info" |
||||||
|
config ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG |
||||||
|
bool "Debug" |
||||||
|
config ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE |
||||||
|
bool "Verbose" |
||||||
|
endchoice |
||||||
|
|
||||||
|
config ARDUHAL_LOG_DEFAULT_LEVEL |
||||||
|
int |
||||||
|
default 0 if ARDUHAL_LOG_DEFAULT_LEVEL_NONE |
||||||
|
default 1 if ARDUHAL_LOG_DEFAULT_LEVEL_ERROR |
||||||
|
default 2 if ARDUHAL_LOG_DEFAULT_LEVEL_WARN |
||||||
|
default 3 if ARDUHAL_LOG_DEFAULT_LEVEL_INFO |
||||||
|
default 4 if ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG |
||||||
|
default 5 if ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE |
||||||
|
|
||||||
|
config ARDUHAL_LOG_COLORS |
||||||
|
bool "Use ANSI terminal colors in log output" |
||||||
|
default "n" |
||||||
|
help |
||||||
|
Enable ANSI terminal color codes in bootloader output. |
||||||
|
In order to view these, your terminal program must support ANSI color codes. |
||||||
|
|
||||||
|
config ARDUHAL_ESP_LOG |
||||||
|
bool "Forward ESP_LOGx to Arduino log output" |
||||||
|
default "n" |
||||||
|
help |
||||||
|
This option will redefine the ESP_LOGx macros to Arduino's log_x macros. |
||||||
|
To enable for your application, add the follwing after your includes: |
||||||
|
#ifdef ARDUINO_ARCH_ESP32 |
||||||
|
#include "esp32-hal-log.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
endmenu |
||||||
|
|
||||||
|
choice ARDUHAL_PARTITION_SCHEME |
||||||
|
bool "Used partition scheme" |
||||||
|
default ARDUHAL_PARTITION_SCHEME_DEFAULT |
||||||
|
help |
||||||
|
Specify which partition scheme to be used. |
||||||
|
|
||||||
|
config ARDUHAL_PARTITION_SCHEME_DEFAULT |
||||||
|
bool "Default" |
||||||
|
config ARDUHAL_PARTITION_SCHEME_MINIMAL |
||||||
|
bool "Minimal (for 2MB FLASH)" |
||||||
|
config ARDUHAL_PARTITION_SCHEME_NO_OTA |
||||||
|
bool "No OTA (for large apps)" |
||||||
|
config ARDUHAL_PARTITION_SCHEME_HUGE_APP |
||||||
|
bool "Huge App (for very large apps)" |
||||||
|
config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS |
||||||
|
bool "Minimal SPIFFS (for large apps with OTA)" |
||||||
|
endchoice |
||||||
|
|
||||||
|
config ARDUHAL_PARTITION_SCHEME |
||||||
|
string |
||||||
|
default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT |
||||||
|
default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL |
||||||
|
default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA |
||||||
|
default "huge_app" if ARDUHAL_PARTITION_SCHEME_HUGE_APP |
||||||
|
default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS |
||||||
|
|
||||||
|
|
||||||
|
config AUTOCONNECT_WIFI |
||||||
|
bool "Autoconnect WiFi on boot" |
||||||
|
default "n" |
||||||
|
depends on AUTOSTART_ARDUINO |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
help |
||||||
|
If enabled, WiFi will connect to the last used SSID (if station was enabled), |
||||||
|
else connection will be started only after calling WiFi.begin(ssid, password) |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_COMPILATION |
||||||
|
bool "Include only specific Arduino libraries" |
||||||
|
default n |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_ArduinoOTA |
||||||
|
bool "Enable ArduinoOTA" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
select ARDUINO_SELECTIVE_ESPmDNS |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_AsyncUDP |
||||||
|
bool "Enable AsyncUDP" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_AzureIoT |
||||||
|
bool "Enable AzureIoT" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_HTTPClient |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_BLE |
||||||
|
bool "Enable BLE" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_BluetoothSerial |
||||||
|
bool "Enable BluetoothSerial" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_DNSServer |
||||||
|
bool "Enable DNSServer" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_EEPROM |
||||||
|
bool "Enable EEPROM" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_ESP32 |
||||||
|
bool "Enable ESP32" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_ESPmDNS |
||||||
|
bool "Enable ESPmDNS" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_FFat |
||||||
|
bool "Enable FFat" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_FS |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_FS |
||||||
|
bool "Enable FS" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_HTTPClient |
||||||
|
bool "Enable HTTPClient" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
select ARDUINO_SELECTIVE_WiFiClientSecure |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_LITTLEFS |
||||||
|
bool "Enable LITTLEFS" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_FS |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_NetBIOS |
||||||
|
bool "Enable NetBIOS" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_Preferences |
||||||
|
bool "Enable Preferences" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SD |
||||||
|
bool "Enable SD" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_FS |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SD_MMC |
||||||
|
bool "Enable SD_MMC" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_FS |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SimpleBLE |
||||||
|
bool "Enable SimpleBLE" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SPI |
||||||
|
bool "Enable SPI" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_SPIFFS |
||||||
|
bool "Enable SPIFFS" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_FS |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_Ticker |
||||||
|
bool "Enable Ticker" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_Update |
||||||
|
bool "Enable Update" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_WebServer |
||||||
|
bool "Enable WebServer" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
select ARDUINO_SELECTIVE_FS |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_WiFi |
||||||
|
bool "Enable WiFi" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_WiFiClientSecure |
||||||
|
bool "Enable WiFiClientSecure" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_WiFiProv |
||||||
|
bool "Enable WiFiProv" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
select ARDUINO_SELECTIVE_WiFi |
||||||
|
default y |
||||||
|
|
||||||
|
config ARDUINO_SELECTIVE_Wire |
||||||
|
bool "Enable Wire" |
||||||
|
depends on ARDUINO_SELECTIVE_COMPILATION |
||||||
|
default y |
||||||
|
|
||||||
|
|
||||||
|
endmenu |
||||||
|
|
@ -0,0 +1,239 @@ |
|||||||
|
/*
|
||||||
|
Arduino.h - Main include file for the Arduino SDK |
||||||
|
Copyright (c) 2005-2013 Arduino Team. All right reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef Arduino_h |
||||||
|
#define Arduino_h |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdarg.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <inttypes.h> |
||||||
|
|
||||||
|
#include "esp_arduino_version.h" |
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/task.h" |
||||||
|
#include "freertos/semphr.h" |
||||||
|
#include "esp32-hal.h" |
||||||
|
#include "esp8266-compat.h" |
||||||
|
#include "soc/gpio_reg.h" |
||||||
|
|
||||||
|
#include "stdlib_noniso.h" |
||||||
|
#include "binary.h" |
||||||
|
|
||||||
|
#define PI 3.1415926535897932384626433832795 |
||||||
|
#define HALF_PI 1.5707963267948966192313216916398 |
||||||
|
#define TWO_PI 6.283185307179586476925286766559 |
||||||
|
#define DEG_TO_RAD 0.017453292519943295769236907684886 |
||||||
|
#define RAD_TO_DEG 57.295779513082320876798154814105 |
||||||
|
#define EULER 2.718281828459045235360287471352 |
||||||
|
|
||||||
|
#define SERIAL 0x0 |
||||||
|
#define DISPLAY 0x1 |
||||||
|
|
||||||
|
#define LSBFIRST 0 |
||||||
|
#define MSBFIRST 1 |
||||||
|
|
||||||
|
//Interrupt Modes
|
||||||
|
#define RISING 0x01 |
||||||
|
#define FALLING 0x02 |
||||||
|
#define CHANGE 0x03 |
||||||
|
#define ONLOW 0x04 |
||||||
|
#define ONHIGH 0x05 |
||||||
|
#define ONLOW_WE 0x0C |
||||||
|
#define ONHIGH_WE 0x0D |
||||||
|
|
||||||
|
#define DEFAULT 1 |
||||||
|
#define EXTERNAL 0 |
||||||
|
|
||||||
|
#ifndef __STRINGIFY |
||||||
|
#define __STRINGIFY(a) #a |
||||||
|
#endif |
||||||
|
|
||||||
|
// can't define max() / min() because of conflicts with C++
|
||||||
|
#define _min(a,b) ((a)<(b)?(a):(b)) |
||||||
|
#define _max(a,b) ((a)>(b)?(a):(b)) |
||||||
|
#define _abs(x) ((x)>0?(x):-(x)) // abs() comes from STL
|
||||||
|
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) |
||||||
|
#define _round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) // round() comes from STL
|
||||||
|
#define radians(deg) ((deg)*DEG_TO_RAD) |
||||||
|
#define degrees(rad) ((rad)*RAD_TO_DEG) |
||||||
|
#define sq(x) ((x)*(x)) |
||||||
|
|
||||||
|
// ESP32xx runs FreeRTOS... disabling interrupts can lead to issues, such as Watchdog Timeout
|
||||||
|
#define sei() portENABLE_INTERRUPTS() |
||||||
|
#define cli() portDISABLE_INTERRUPTS() |
||||||
|
#define interrupts() sei() |
||||||
|
#define noInterrupts() cli() |
||||||
|
|
||||||
|
#define clockCyclesPerMicrosecond() ( (long int)getCpuFrequencyMhz() ) |
||||||
|
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) |
||||||
|
#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) |
||||||
|
|
||||||
|
#define lowByte(w) ((uint8_t) ((w) & 0xff)) |
||||||
|
#define highByte(w) ((uint8_t) ((w) >> 8)) |
||||||
|
|
||||||
|
#define bitRead(value, bit) (((value) >> (bit)) & 0x01) |
||||||
|
#define bitSet(value, bit) ((value) |= (1UL << (bit))) |
||||||
|
#define bitClear(value, bit) ((value) &= ~(1UL << (bit))) |
||||||
|
#define bitToggle(value, bit) ((value) ^= (1UL << (bit))) |
||||||
|
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit)) |
||||||
|
|
||||||
|
// avr-libc defines _NOP() since 1.6.2
|
||||||
|
#ifndef _NOP |
||||||
|
#define _NOP() do { __asm__ volatile ("nop"); } while (0) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define bit(b) (1UL << (b)) |
||||||
|
#define _BV(b) (1UL << (b)) |
||||||
|
|
||||||
|
#define digitalPinToTimer(pin) (0) |
||||||
|
#define analogInPinToBit(P) (P) |
||||||
|
#if SOC_GPIO_PIN_COUNT <= 32 |
||||||
|
#define digitalPinToPort(pin) (0) |
||||||
|
#define digitalPinToBitMask(pin) (1UL << (pin)) |
||||||
|
#define portOutputRegister(port) ((volatile uint32_t*)GPIO_OUT_REG) |
||||||
|
#define portInputRegister(port) ((volatile uint32_t*)GPIO_IN_REG) |
||||||
|
#define portModeRegister(port) ((volatile uint32_t*)GPIO_ENABLE_REG) |
||||||
|
#elif SOC_GPIO_PIN_COUNT <= 64 |
||||||
|
#define digitalPinToPort(pin) (((pin)>31)?1:0) |
||||||
|
#define digitalPinToBitMask(pin) (1UL << (((pin)>31)?((pin)-32):(pin))) |
||||||
|
#define portOutputRegister(port) ((volatile uint32_t*)((port)?GPIO_OUT1_REG:GPIO_OUT_REG)) |
||||||
|
#define portInputRegister(port) ((volatile uint32_t*)((port)?GPIO_IN1_REG:GPIO_IN_REG)) |
||||||
|
#define portModeRegister(port) ((volatile uint32_t*)((port)?GPIO_ENABLE1_REG:GPIO_ENABLE_REG)) |
||||||
|
#else |
||||||
|
#error SOC_GPIO_PIN_COUNT > 64 not implemented |
||||||
|
#endif |
||||||
|
|
||||||
|
#define NOT_A_PIN -1 |
||||||
|
#define NOT_A_PORT -1 |
||||||
|
#define NOT_AN_INTERRUPT -1 |
||||||
|
#define NOT_ON_TIMER 0 |
||||||
|
|
||||||
|
// some defines generic for all SoC moved from variants/board_name/pins_arduino.h
|
||||||
|
#define NUM_DIGITAL_PINS SOC_GPIO_PIN_COUNT // All GPIOs
|
||||||
|
#if SOC_ADC_PERIPH_NUM == 1 |
||||||
|
#define NUM_ANALOG_INPUTS (SOC_ADC_CHANNEL_NUM(0)) // Depends on the SoC (ESP32C6, ESP32H2, ESP32C2, ESP32P4)
|
||||||
|
#elif SOC_ADC_PERIPH_NUM == 2 |
||||||
|
#define NUM_ANALOG_INPUTS (SOC_ADC_CHANNEL_NUM(0)+SOC_ADC_CHANNEL_NUM(1)) // Depends on the SoC (ESP32, ESP32S2, ESP32S3, ESP32C3)
|
||||||
|
#endif |
||||||
|
#define EXTERNAL_NUM_INTERRUPTS NUM_DIGITAL_PINS // All GPIOs
|
||||||
|
#define analogInputToDigitalPin(p) (((p)<NUM_ANALOG_INPUTS)?(analogChannelToDigitalPin(p)):-1) |
||||||
|
#define digitalPinToInterrupt(p) (((p)<NUM_DIGITAL_PINS)?(p):NOT_AN_INTERRUPT) |
||||||
|
#define digitalPinHasPWM(p) ((p)<NUM_DIGITAL_PINS) |
||||||
|
|
||||||
|
typedef bool boolean; |
||||||
|
typedef uint8_t byte; |
||||||
|
typedef unsigned int word; |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
void setup(void); |
||||||
|
void loop(void); |
||||||
|
|
||||||
|
// The default is using Real Hardware random number generator
|
||||||
|
// But when randomSeed() is called, it turns to Psedo random
|
||||||
|
// generator, exactly as done in Arduino mainstream
|
||||||
|
long random(long); |
||||||
|
long random(long, long); |
||||||
|
// Calling randomSeed() will make random()
|
||||||
|
// using pseudo random like in Arduino
|
||||||
|
void randomSeed(unsigned long); |
||||||
|
// Allow the Application to decide if the random generator
|
||||||
|
// will use Real Hardware random generation (true - default)
|
||||||
|
// or Pseudo random generation (false) as in Arduino MainStream
|
||||||
|
void useRealRandomGenerator(bool useRandomHW); |
||||||
|
#endif |
||||||
|
long map(long, long, long, long, long); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
void init(void); |
||||||
|
void initVariant(void); |
||||||
|
void initArduino(void); |
||||||
|
|
||||||
|
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout); |
||||||
|
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout); |
||||||
|
|
||||||
|
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); |
||||||
|
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <cmath> |
||||||
|
|
||||||
|
#include "WCharacter.h" |
||||||
|
#include "WString.h" |
||||||
|
#include "Stream.h" |
||||||
|
#include "Printable.h" |
||||||
|
#include "Print.h" |
||||||
|
#include "IPAddress.h" |
||||||
|
#include "Client.h" |
||||||
|
#include "Server.h" |
||||||
|
#include "Udp.h" |
||||||
|
#include "HardwareSerial.h" |
||||||
|
#include "Esp.h" |
||||||
|
|
||||||
|
// Use float-compatible stl abs() and round(), we don't use Arduino macros to avoid issues with the C++ libraries
|
||||||
|
using std::abs; |
||||||
|
using std::isinf; |
||||||
|
using std::isnan; |
||||||
|
using std::max; |
||||||
|
using std::min; |
||||||
|
using std::round; |
||||||
|
|
||||||
|
uint16_t makeWord(uint16_t w); |
||||||
|
uint16_t makeWord(uint8_t h, uint8_t l); |
||||||
|
|
||||||
|
#define word(...) makeWord(__VA_ARGS__) |
||||||
|
|
||||||
|
size_t getArduinoLoopTaskStackSize(void); |
||||||
|
#define SET_LOOP_TASK_STACK_SIZE(sz) size_t getArduinoLoopTaskStackSize() { return sz;} |
||||||
|
|
||||||
|
bool shouldPrintChipDebugReport(void); |
||||||
|
#define ENABLE_CHIP_DEBUG_REPORT bool shouldPrintChipDebugReport(void){return true;} |
||||||
|
|
||||||
|
// allows user to bypass esp_spiram_test()
|
||||||
|
bool esp_psram_extram_test(void); |
||||||
|
#define BYPASS_SPIRAM_TEST(bypass) bool testSPIRAM(void) { if (bypass) return true; else return esp_psram_extram_test(); } |
||||||
|
|
||||||
|
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); |
||||||
|
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); |
||||||
|
|
||||||
|
extern "C" bool getLocalTime(struct tm * info, uint32_t ms = 5000); |
||||||
|
extern "C" void configTime(long gmtOffset_sec, int daylightOffset_sec, |
||||||
|
const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); |
||||||
|
extern "C" void configTzTime(const char* tz, |
||||||
|
const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); |
||||||
|
|
||||||
|
void setToneChannel(uint8_t channel = 0); |
||||||
|
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); |
||||||
|
void noTone(uint8_t _pin); |
||||||
|
|
||||||
|
#endif /* __cplusplus */ |
||||||
|
|
||||||
|
#include "pins_arduino.h" |
||||||
|
|
||||||
|
#endif /* _ESP32_CORE_ARDUINO_H_ */ |
@ -0,0 +1,48 @@ |
|||||||
|
/*
|
||||||
|
Client.h - Base class that provides Client |
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef client_h |
||||||
|
#define client_h |
||||||
|
#include "Print.h" |
||||||
|
#include "Stream.h" |
||||||
|
#include "IPAddress.h" |
||||||
|
|
||||||
|
class Client: public Stream |
||||||
|
{ |
||||||
|
public: |
||||||
|
virtual int connect(IPAddress ip, uint16_t port) =0; |
||||||
|
virtual int connect(const char *host, uint16_t port) =0; |
||||||
|
virtual size_t write(uint8_t) =0; |
||||||
|
virtual size_t write(const uint8_t *buf, size_t size) =0; |
||||||
|
virtual int available() = 0; |
||||||
|
virtual int read() = 0; |
||||||
|
virtual int read(uint8_t *buf, size_t size) = 0; |
||||||
|
virtual int peek() = 0; |
||||||
|
virtual void flush() = 0; |
||||||
|
virtual void stop() = 0; |
||||||
|
virtual uint8_t connected() = 0; |
||||||
|
virtual operator bool() = 0; |
||||||
|
protected: |
||||||
|
uint8_t* rawIPAddress(IPAddress& addr) |
||||||
|
{ |
||||||
|
return addr.raw_address(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,471 @@ |
|||||||
|
/*
|
||||||
|
Esp.cpp - ESP31B-specific APIs |
||||||
|
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
#include "Esp.h" |
||||||
|
#include "esp_sleep.h" |
||||||
|
#include "spi_flash_mmap.h" |
||||||
|
#include <memory> |
||||||
|
#include <soc/soc.h> |
||||||
|
#include <esp_partition.h> |
||||||
|
extern "C" { |
||||||
|
#include "esp_ota_ops.h" |
||||||
|
#include "esp_image_format.h" |
||||||
|
} |
||||||
|
#include <MD5Builder.h> |
||||||
|
|
||||||
|
#include "soc/spi_reg.h" |
||||||
|
#include "esp_system.h" |
||||||
|
#include "esp_chip_info.h" |
||||||
|
#include "esp_mac.h" |
||||||
|
#include "esp_flash.h" |
||||||
|
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
|
#include "esp32/rom/spi_flash.h" |
||||||
|
#include "soc/efuse_reg.h" |
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing flash size and spi mode
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 |
||||||
|
#include "esp32s2/rom/spi_flash.h" |
||||||
|
#include "soc/efuse_reg.h" |
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x1000 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3 |
||||||
|
#include "esp32s3/rom/spi_flash.h" |
||||||
|
#include "soc/efuse_reg.h" |
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3 |
||||||
|
#include "esp32c3/rom/spi_flash.h" |
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6 |
||||||
|
#include "esp32c6/rom/spi_flash.h" |
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c6 is located at 0x0000
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32H2 |
||||||
|
#include "esp32h2/rom/spi_flash.h" |
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32h2 is located at 0x0000
|
||||||
|
#else |
||||||
|
#error Target CONFIG_IDF_TARGET is not supported |
||||||
|
#endif |
||||||
|
#else // ESP32 Before IDF 4.0
|
||||||
|
#include "rom/spi_flash.h" |
||||||
|
#define ESP_FLASH_IMAGE_BASE 0x1000 |
||||||
|
#endif |
||||||
|
|
||||||
|
// REG_SPI_BASE is not defined for S3/C3 ??
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 |
||||||
|
#ifndef REG_SPI_BASE |
||||||
|
#define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 ))) |
||||||
|
#endif // REG_SPI_BASE
|
||||||
|
#endif // TARGET
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-defined Literals |
||||||
|
* usage: |
||||||
|
* |
||||||
|
* uint32_t = test = 10_MHz; // --> 10000000
|
||||||
|
*/ |
||||||
|
|
||||||
|
unsigned long long operator"" _kHz(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1000; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _MHz(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1000 * 1000; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _GHz(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1000 * 1000 * 1000; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _kBit(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1024; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _MBit(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1024 * 1024; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _GBit(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1024 * 1024 * 1024; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _kB(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1024; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _MB(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1024 * 1024; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned long long operator"" _GB(unsigned long long x) |
||||||
|
{ |
||||||
|
return x * 1024 * 1024 * 1024; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
EspClass ESP; |
||||||
|
|
||||||
|
void EspClass::deepSleep(uint32_t time_us) |
||||||
|
{ |
||||||
|
esp_deep_sleep(time_us); |
||||||
|
} |
||||||
|
|
||||||
|
void EspClass::restart(void) |
||||||
|
{ |
||||||
|
esp_restart(); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getHeapSize(void) |
||||||
|
{ |
||||||
|
return heap_caps_get_total_size(MALLOC_CAP_INTERNAL); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getFreeHeap(void) |
||||||
|
{ |
||||||
|
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getMinFreeHeap(void) |
||||||
|
{ |
||||||
|
return heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getMaxAllocHeap(void) |
||||||
|
{ |
||||||
|
return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getPsramSize(void) |
||||||
|
{ |
||||||
|
if(psramFound()){ |
||||||
|
return heap_caps_get_total_size(MALLOC_CAP_SPIRAM); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getFreePsram(void) |
||||||
|
{ |
||||||
|
if(psramFound()){ |
||||||
|
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getMinFreePsram(void) |
||||||
|
{ |
||||||
|
if(psramFound()){ |
||||||
|
return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getMaxAllocPsram(void) |
||||||
|
{ |
||||||
|
if(psramFound()){ |
||||||
|
return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t sketchSize(sketchSize_t response) { |
||||||
|
esp_image_metadata_t data; |
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition(); |
||||||
|
if (!running) return 0; |
||||||
|
const esp_partition_pos_t running_pos = { |
||||||
|
.offset = running->address, |
||||||
|
.size = running->size, |
||||||
|
}; |
||||||
|
data.start_addr = running_pos.offset; |
||||||
|
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data); |
||||||
|
if (response) { |
||||||
|
return running_pos.size - data.image_len; |
||||||
|
} else { |
||||||
|
return data.image_len; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getSketchSize () { |
||||||
|
return sketchSize(SKETCH_SIZE_TOTAL); |
||||||
|
} |
||||||
|
|
||||||
|
String EspClass::getSketchMD5() |
||||||
|
{ |
||||||
|
static String result; |
||||||
|
if (result.length()) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
uint32_t lengthLeft = getSketchSize(); |
||||||
|
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition(); |
||||||
|
if (!running) { |
||||||
|
log_e("Partition could not be found"); |
||||||
|
|
||||||
|
return String(); |
||||||
|
} |
||||||
|
const size_t bufSize = SPI_FLASH_SEC_SIZE; |
||||||
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]); |
||||||
|
uint32_t offset = 0; |
||||||
|
if(!buf.get()) { |
||||||
|
log_e("Not enough memory to allocate buffer"); |
||||||
|
|
||||||
|
return String(); |
||||||
|
} |
||||||
|
MD5Builder md5; |
||||||
|
md5.begin(); |
||||||
|
while( lengthLeft > 0) { |
||||||
|
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize; |
||||||
|
if (!ESP.flashRead(running->address + offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) { |
||||||
|
log_e("Could not read buffer from flash"); |
||||||
|
|
||||||
|
return String(); |
||||||
|
} |
||||||
|
md5.add(buf.get(), readBytes); |
||||||
|
lengthLeft -= readBytes; |
||||||
|
offset += readBytes; |
||||||
|
} |
||||||
|
md5.calculate(); |
||||||
|
result = md5.toString(); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getFreeSketchSpace () { |
||||||
|
const esp_partition_t* _partition = esp_ota_get_next_update_partition(NULL); |
||||||
|
if(!_partition){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
return _partition->size; |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t EspClass::getChipRevision(void) |
||||||
|
{ |
||||||
|
esp_chip_info_t chip_info; |
||||||
|
esp_chip_info(&chip_info); |
||||||
|
return chip_info.revision; |
||||||
|
} |
||||||
|
|
||||||
|
const char * EspClass::getChipModel(void) |
||||||
|
{ |
||||||
|
#if CONFIG_IDF_TARGET_ESP32 |
||||||
|
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_PACKAGE); |
||||||
|
uint32_t pkg_ver = chip_ver & 0x7; |
||||||
|
switch (pkg_ver) { |
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6 : |
||||||
|
if (getChipRevision() == 3) |
||||||
|
return "ESP32-D0WDQ6-V3";
|
||||||
|
else |
||||||
|
return "ESP32-D0WDQ6"; |
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5 : |
||||||
|
if (getChipRevision() == 3) |
||||||
|
return "ESP32-D0WD-V3";
|
||||||
|
else |
||||||
|
return "ESP32-D0WD"; |
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 : |
||||||
|
return "ESP32-D2WD"; |
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 : |
||||||
|
return "ESP32-PICO-D2"; |
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4 : |
||||||
|
return "ESP32-PICO-D4"; |
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302 : |
||||||
|
return "ESP32-PICO-V3-02"; |
||||||
|
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3 : |
||||||
|
return "ESP32-D0WDR2-V3"; |
||||||
|
default: |
||||||
|
return "Unknown"; |
||||||
|
} |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 |
||||||
|
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION); |
||||||
|
switch (pkg_ver) { |
||||||
|
case 0: |
||||||
|
return "ESP32-S2"; |
||||||
|
case 1: |
||||||
|
return "ESP32-S2FH16"; |
||||||
|
case 2: |
||||||
|
return "ESP32-S2FH32"; |
||||||
|
default: |
||||||
|
return "ESP32-S2 (Unknown)"; |
||||||
|
} |
||||||
|
#else |
||||||
|
esp_chip_info_t chip_info; |
||||||
|
esp_chip_info(&chip_info); |
||||||
|
switch(chip_info.model){ |
||||||
|
case CHIP_ESP32S3: return "ESP32-S3"; |
||||||
|
case CHIP_ESP32C3: return "ESP32-C3"; |
||||||
|
case CHIP_ESP32C2: return "ESP32-C2"; |
||||||
|
case CHIP_ESP32C6: return "ESP32-C6"; |
||||||
|
case CHIP_ESP32H2: return "ESP32-H2"; |
||||||
|
default: return "UNKNOWN"; |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t EspClass::getChipCores(void) |
||||||
|
{ |
||||||
|
esp_chip_info_t chip_info; |
||||||
|
esp_chip_info(&chip_info); |
||||||
|
return chip_info.cores; |
||||||
|
} |
||||||
|
|
||||||
|
const char * EspClass::getSdkVersion(void) |
||||||
|
{ |
||||||
|
return esp_get_idf_version(); |
||||||
|
} |
||||||
|
|
||||||
|
const char * EspClass::getCoreVersion(void) |
||||||
|
{ |
||||||
|
return ESP_ARDUINO_VERSION_STR; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t ESP_getFlashChipId(void) |
||||||
|
{ |
||||||
|
uint32_t id = g_rom_flashchip.device_id; |
||||||
|
id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00); |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getFlashChipSize(void) |
||||||
|
{ |
||||||
|
uint32_t id = (ESP_getFlashChipId() >> 16) & 0xFF; |
||||||
|
return 2 << (id - 1); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::getFlashChipSpeed(void) |
||||||
|
{ |
||||||
|
esp_image_header_t fhdr; |
||||||
|
if(esp_flash_read(esp_flash_default_chip, (void*)&fhdr, ESP_FLASH_IMAGE_BASE, sizeof(esp_image_header_t)) && fhdr.magic != ESP_IMAGE_HEADER_MAGIC) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return magicFlashChipSpeed(fhdr.spi_speed); |
||||||
|
} |
||||||
|
|
||||||
|
FlashMode_t EspClass::getFlashChipMode(void) |
||||||
|
{ |
||||||
|
#if CONFIG_IDF_TARGET_ESP32S2 |
||||||
|
uint32_t spi_ctrl = REG_READ(PERIPHS_SPI_FLASH_CTRL); |
||||||
|
#else |
||||||
|
#if CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C6 |
||||||
|
uint32_t spi_ctrl = REG_READ(DR_REG_SPI0_BASE + 0x8); |
||||||
|
#else |
||||||
|
uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0)); |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
/* Not all of the following constants are already defined in older versions of spi_reg.h, so do it manually for now*/ |
||||||
|
if (spi_ctrl & BIT(24)) { //SPI_FREAD_QIO
|
||||||
|
return (FM_QIO); |
||||||
|
} else if (spi_ctrl & BIT(20)) { //SPI_FREAD_QUAD
|
||||||
|
return (FM_QOUT); |
||||||
|
} else if (spi_ctrl & BIT(23)) { //SPI_FREAD_DIO
|
||||||
|
return (FM_DIO); |
||||||
|
} else if (spi_ctrl & BIT(14)) { // SPI_FREAD_DUAL
|
||||||
|
return (FM_DOUT); |
||||||
|
} else if (spi_ctrl & BIT(13)) { //SPI_FASTRD_MODE
|
||||||
|
return (FM_FAST_READ); |
||||||
|
} else { |
||||||
|
return (FM_SLOW_READ); |
||||||
|
} |
||||||
|
return (FM_DOUT); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::magicFlashChipSize(uint8_t byte) |
||||||
|
{ |
||||||
|
switch(byte & 0x0F) { |
||||||
|
case 0x0: // 8 MBit (1MB)
|
||||||
|
return (1_MB); |
||||||
|
case 0x1: // 16 MBit (2MB)
|
||||||
|
return (2_MB); |
||||||
|
case 0x2: // 32 MBit (4MB)
|
||||||
|
return (4_MB); |
||||||
|
case 0x3: // 64 MBit (8MB)
|
||||||
|
return (8_MB); |
||||||
|
case 0x4: // 128 MBit (16MB)
|
||||||
|
return (16_MB); |
||||||
|
default: // fail?
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) |
||||||
|
{ |
||||||
|
switch(byte & 0x0F) { |
||||||
|
case 0x0: // 40 MHz
|
||||||
|
return (40_MHz); |
||||||
|
case 0x1: // 26 MHz
|
||||||
|
return (26_MHz); |
||||||
|
case 0x2: // 20 MHz
|
||||||
|
return (20_MHz); |
||||||
|
case 0xf: // 80 MHz
|
||||||
|
return (80_MHz); |
||||||
|
default: // fail?
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) |
||||||
|
{ |
||||||
|
FlashMode_t mode = (FlashMode_t) byte; |
||||||
|
if(mode > FM_SLOW_READ) { |
||||||
|
mode = FM_UNKNOWN; |
||||||
|
} |
||||||
|
return mode; |
||||||
|
} |
||||||
|
|
||||||
|
bool EspClass::flashEraseSector(uint32_t sector) |
||||||
|
{ |
||||||
|
return esp_flash_erase_region(esp_flash_default_chip, sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE) == ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
// Warning: These functions do not work with encrypted flash
|
||||||
|
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) |
||||||
|
{ |
||||||
|
return esp_flash_write(esp_flash_default_chip, (const void*) data, offset, size) == ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) |
||||||
|
{ |
||||||
|
return esp_flash_read(esp_flash_default_chip, (void*) data, offset, size) == ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
bool EspClass::partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size)
|
||||||
|
{ |
||||||
|
return esp_partition_erase_range(partition, offset, size) == ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
bool EspClass::partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size)
|
||||||
|
{ |
||||||
|
return esp_partition_write(partition, offset, data, size) == ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
bool EspClass::partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size)
|
||||||
|
{ |
||||||
|
return esp_partition_read(partition, offset, data, size) == ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t EspClass::getEfuseMac(void) |
||||||
|
{ |
||||||
|
uint64_t _chipmacid = 0LL; |
||||||
|
esp_efuse_mac_get_default((uint8_t*) (&_chipmacid)); |
||||||
|
return _chipmacid; |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
/*
|
||||||
|
Esp.h - ESP31B-specific APIs |
||||||
|
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ESP_H |
||||||
|
#define ESP_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <esp_partition.h> |
||||||
|
#include <hal/cpu_hal.h> |
||||||
|
#include "esp_cpu.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* AVR macros for WDT managment |
||||||
|
*/ |
||||||
|
typedef enum { |
||||||
|
WDTO_0MS = 0, //!< WDTO_0MS
|
||||||
|
WDTO_15MS = 15, //!< WDTO_15MS
|
||||||
|
WDTO_30MS = 30, //!< WDTO_30MS
|
||||||
|
WDTO_60MS = 60, //!< WDTO_60MS
|
||||||
|
WDTO_120MS = 120, //!< WDTO_120MS
|
||||||
|
WDTO_250MS = 250, //!< WDTO_250MS
|
||||||
|
WDTO_500MS = 500, //!< WDTO_500MS
|
||||||
|
WDTO_1S = 1000,//!< WDTO_1S
|
||||||
|
WDTO_2S = 2000,//!< WDTO_2S
|
||||||
|
WDTO_4S = 4000,//!< WDTO_4S
|
||||||
|
WDTO_8S = 8000 //!< WDTO_8S
|
||||||
|
} WDTO_t; |
||||||
|
|
||||||
|
|
||||||
|
typedef enum { |
||||||
|
FM_QIO = 0x00, |
||||||
|
FM_QOUT = 0x01, |
||||||
|
FM_DIO = 0x02, |
||||||
|
FM_DOUT = 0x03, |
||||||
|
FM_FAST_READ = 0x04, |
||||||
|
FM_SLOW_READ = 0x05, |
||||||
|
FM_UNKNOWN = 0xff |
||||||
|
} FlashMode_t; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
SKETCH_SIZE_TOTAL = 0, |
||||||
|
SKETCH_SIZE_FREE = 1 |
||||||
|
} sketchSize_t; |
||||||
|
|
||||||
|
class EspClass |
||||||
|
{ |
||||||
|
public: |
||||||
|
EspClass() {} |
||||||
|
~EspClass() {} |
||||||
|
void restart(); |
||||||
|
|
||||||
|
//Internal RAM
|
||||||
|
uint32_t getHeapSize(); //total heap size
|
||||||
|
uint32_t getFreeHeap(); //available heap
|
||||||
|
uint32_t getMinFreeHeap(); //lowest level of free heap since boot
|
||||||
|
uint32_t getMaxAllocHeap(); //largest block of heap that can be allocated at once
|
||||||
|
|
||||||
|
//SPI RAM
|
||||||
|
uint32_t getPsramSize(); |
||||||
|
uint32_t getFreePsram(); |
||||||
|
uint32_t getMinFreePsram(); |
||||||
|
uint32_t getMaxAllocPsram(); |
||||||
|
|
||||||
|
uint16_t getChipRevision(); |
||||||
|
const char * getChipModel(); |
||||||
|
uint8_t getChipCores(); |
||||||
|
uint32_t getCpuFreqMHz(){ return getCpuFrequencyMhz(); } |
||||||
|
inline uint32_t getCycleCount() __attribute__((always_inline)); |
||||||
|
|
||||||
|
const char * getSdkVersion(); //version of ESP-IDF
|
||||||
|
const char * getCoreVersion();//version of this core
|
||||||
|
|
||||||
|
void deepSleep(uint32_t time_us); |
||||||
|
|
||||||
|
uint32_t getFlashChipSize(); |
||||||
|
uint32_t getFlashChipSpeed(); |
||||||
|
FlashMode_t getFlashChipMode(); |
||||||
|
|
||||||
|
uint32_t magicFlashChipSize(uint8_t byte); |
||||||
|
uint32_t magicFlashChipSpeed(uint8_t byte); |
||||||
|
FlashMode_t magicFlashChipMode(uint8_t byte); |
||||||
|
|
||||||
|
uint32_t getSketchSize(); |
||||||
|
String getSketchMD5(); |
||||||
|
uint32_t getFreeSketchSpace(); |
||||||
|
|
||||||
|
bool flashEraseSector(uint32_t sector); |
||||||
|
bool flashWrite(uint32_t offset, uint32_t *data, size_t size); |
||||||
|
bool flashRead(uint32_t offset, uint32_t *data, size_t size); |
||||||
|
|
||||||
|
bool partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size); |
||||||
|
bool partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size); |
||||||
|
bool partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size); |
||||||
|
|
||||||
|
uint64_t getEfuseMac(); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
uint32_t ARDUINO_ISR_ATTR EspClass::getCycleCount() |
||||||
|
{ |
||||||
|
return (uint32_t)esp_cpu_get_cycle_count(); |
||||||
|
} |
||||||
|
|
||||||
|
extern EspClass ESP; |
||||||
|
|
||||||
|
#endif //ESP_H
|
@ -0,0 +1,426 @@ |
|||||||
|
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "FirmwareMSC.h" |
||||||
|
|
||||||
|
#if CONFIG_TINYUSB_MSC_ENABLED |
||||||
|
|
||||||
|
#include <cstring> |
||||||
|
#include "esp_partition.h" |
||||||
|
#include "esp_ota_ops.h" |
||||||
|
#include "esp_image_format.h" |
||||||
|
#include "esp32-hal.h" |
||||||
|
#include "pins_arduino.h" |
||||||
|
#include "firmware_msc_fat.h" |
||||||
|
#include "spi_flash_mmap.h" |
||||||
|
|
||||||
|
#ifndef USB_FW_MSC_VENDOR_ID |
||||||
|
#define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars
|
||||||
|
#endif |
||||||
|
#ifndef USB_FW_MSC_PRODUCT_ID |
||||||
|
#define USB_FW_MSC_PRODUCT_ID "Firmware MSC"//max 16 chars
|
||||||
|
#endif |
||||||
|
#ifndef USB_FW_MSC_PRODUCT_REVISION |
||||||
|
#define USB_FW_MSC_PRODUCT_REVISION "1.0" //max 4 chars
|
||||||
|
#endif |
||||||
|
#ifndef USB_FW_MSC_VOLUME_NAME |
||||||
|
#define USB_FW_MSC_VOLUME_NAME "ESP32-FWMSC" //max 11 chars
|
||||||
|
#endif |
||||||
|
#ifndef USB_FW_MSC_SERIAL_NUMBER |
||||||
|
#define USB_FW_MSC_SERIAL_NUMBER 0x00000000 |
||||||
|
#endif |
||||||
|
|
||||||
|
ESP_EVENT_DEFINE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS); |
||||||
|
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); |
||||||
|
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); |
||||||
|
|
||||||
|
//General Variables
|
||||||
|
static uint8_t * msc_ram_disk = NULL; |
||||||
|
static fat_boot_sector_t * msc_boot = NULL; |
||||||
|
static uint8_t * msc_table = NULL; |
||||||
|
static uint16_t msc_table_sectors = 0; |
||||||
|
static uint16_t msc_total_sectors = 0; |
||||||
|
static bool mcs_is_fat16 = false; |
||||||
|
|
||||||
|
//Firmware Read
|
||||||
|
static const esp_partition_t* msc_run_partition = NULL; |
||||||
|
static uint16_t fw_start_sector = 0; |
||||||
|
static uint16_t fw_end_sector = 0; |
||||||
|
static size_t fw_size = 0; |
||||||
|
static fat_dir_entry_t * fw_entry = NULL; |
||||||
|
|
||||||
|
//Firmware Write
|
||||||
|
typedef enum { |
||||||
|
MSC_UPDATE_IDLE, |
||||||
|
MSC_UPDATE_STARTING, |
||||||
|
MSC_UPDATE_RUNNING, |
||||||
|
MSC_UPDATE_END |
||||||
|
} msc_update_state_t; |
||||||
|
|
||||||
|
static const esp_partition_t* msc_ota_partition = NULL; |
||||||
|
static msc_update_state_t msc_update_state = MSC_UPDATE_IDLE; |
||||||
|
static uint16_t msc_update_start_sector = 0; |
||||||
|
static uint32_t msc_update_bytes_written = 0; |
||||||
|
static fat_dir_entry_t * msc_update_entry = NULL; |
||||||
|
|
||||||
|
static uint32_t get_firmware_size(const esp_partition_t* partition){ |
||||||
|
esp_image_metadata_t data; |
||||||
|
const esp_partition_pos_t running_pos = { |
||||||
|
.offset = partition->address, |
||||||
|
.size = partition->size, |
||||||
|
}; |
||||||
|
data.start_addr = running_pos.offset; |
||||||
|
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data); |
||||||
|
return data.image_len; |
||||||
|
} |
||||||
|
|
||||||
|
//Get number of sectors required based on the size of the firmware and OTA partition
|
||||||
|
static size_t msc_update_get_required_disk_sectors(){ |
||||||
|
size_t data_sectors = 16; |
||||||
|
size_t total_sectors = 0; |
||||||
|
msc_run_partition = esp_ota_get_running_partition(); |
||||||
|
msc_ota_partition = esp_ota_get_next_update_partition(NULL); |
||||||
|
if(msc_run_partition){ |
||||||
|
fw_size = get_firmware_size(msc_run_partition); |
||||||
|
data_sectors += FAT_SIZE_TO_SECTORS(fw_size); |
||||||
|
log_d("APP size: %u (%u sectors)", fw_size, FAT_SIZE_TO_SECTORS(fw_size)); |
||||||
|
} else { |
||||||
|
log_w("APP partition not found. Reading disabled"); |
||||||
|
} |
||||||
|
if(msc_ota_partition){ |
||||||
|
data_sectors += FAT_SIZE_TO_SECTORS(msc_ota_partition->size); |
||||||
|
log_d("OTA size: %u (%u sectors)", msc_ota_partition->size, FAT_SIZE_TO_SECTORS(msc_ota_partition->size)); |
||||||
|
} else { |
||||||
|
log_w("OTA partition not found. Writing disabled"); |
||||||
|
} |
||||||
|
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, false); |
||||||
|
total_sectors = data_sectors + msc_table_sectors + 2; |
||||||
|
if(total_sectors > 0xFF4){ |
||||||
|
log_d("USING FAT16"); |
||||||
|
mcs_is_fat16 = true; |
||||||
|
total_sectors -= msc_table_sectors; |
||||||
|
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, true); |
||||||
|
total_sectors += msc_table_sectors; |
||||||
|
} else { |
||||||
|
log_d("USING FAT12"); |
||||||
|
mcs_is_fat16 = false; |
||||||
|
} |
||||||
|
log_d("FAT sector size: %u", DISK_SECTOR_SIZE); |
||||||
|
log_d("FAT data sectors: %u", data_sectors); |
||||||
|
log_d("FAT table sectors: %u", msc_table_sectors); |
||||||
|
log_d("FAT total sectors: %u (%uKB)", total_sectors, (total_sectors * DISK_SECTOR_SIZE) / 1024); |
||||||
|
return total_sectors; |
||||||
|
} |
||||||
|
|
||||||
|
//setup the ramdisk and add the firmware download file
|
||||||
|
static bool msc_update_setup_disk(const char * volume_label, uint32_t serial_number){ |
||||||
|
msc_total_sectors = msc_update_get_required_disk_sectors(); |
||||||
|
uint8_t ram_sectors = msc_table_sectors + 2; |
||||||
|
msc_ram_disk = (uint8_t*)calloc(ram_sectors, DISK_SECTOR_SIZE); |
||||||
|
if(!msc_ram_disk){ |
||||||
|
log_e("Failed to allocate RAM Disk: %u bytes", ram_sectors * DISK_SECTOR_SIZE); |
||||||
|
return false; |
||||||
|
} |
||||||
|
fw_start_sector = ram_sectors; |
||||||
|
fw_end_sector = fw_start_sector; |
||||||
|
msc_boot = fat_add_boot_sector(msc_ram_disk, msc_total_sectors, msc_table_sectors, fat_file_system_type(mcs_is_fat16), volume_label, serial_number); |
||||||
|
msc_table = fat_add_table(msc_ram_disk, msc_boot, mcs_is_fat16); |
||||||
|
//fat_dir_entry_t * label = fat_add_label(msc_ram_disk, volume_label);
|
||||||
|
if(msc_run_partition){ |
||||||
|
fw_entry = fat_add_root_file(msc_ram_disk, 0, "FIRMWARE", "BIN", fw_size, 2, mcs_is_fat16); |
||||||
|
fw_end_sector = FAT_SIZE_TO_SECTORS(fw_size) + fw_start_sector; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
static void msc_update_delete_disk(){ |
||||||
|
fw_entry = NULL; |
||||||
|
fw_size = 0; |
||||||
|
fw_end_sector = 0; |
||||||
|
fw_start_sector = 0; |
||||||
|
msc_table = NULL; |
||||||
|
msc_boot = NULL; |
||||||
|
msc_table_sectors = 0; |
||||||
|
msc_total_sectors = 0; |
||||||
|
msc_run_partition = NULL; |
||||||
|
msc_ota_partition = NULL; |
||||||
|
msc_update_state = MSC_UPDATE_IDLE; |
||||||
|
msc_update_start_sector = 0; |
||||||
|
msc_update_bytes_written = 0; |
||||||
|
msc_update_entry = NULL; |
||||||
|
free(msc_ram_disk); |
||||||
|
msc_ram_disk = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
//filter out entries to only include BINs in the root folder
|
||||||
|
static fat_dir_entry_t * msc_update_get_root_bin_entry(uint8_t index){ |
||||||
|
fat_dir_entry_t * entry = (fat_dir_entry_t *)(msc_ram_disk + ((msc_boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t))); |
||||||
|
fat_lfn_entry_t * lfn = (fat_lfn_entry_t*)entry; |
||||||
|
|
||||||
|
//empty entry
|
||||||
|
if(entry->file_magic == 0){ |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
//long file name
|
||||||
|
if(lfn->attr == 0x0F && lfn->type == 0x00 && lfn->first_cluster == 0x0000){ |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
//only files marked as archives
|
||||||
|
if(entry->file_attr != FAT_FILE_ATTR_ARCHIVE){ |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
//deleted
|
||||||
|
if(entry->file_magic == 0xE5 || entry->file_magic == 0x05){ |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
//not bins
|
||||||
|
if(memcmp("BIN", entry->file_extension, 3)){ |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
return entry; |
||||||
|
} |
||||||
|
|
||||||
|
//get an empty bin (the host will add an entry for file about to be written with size of zero)
|
||||||
|
static fat_dir_entry_t * msc_update_find_new_bin(){ |
||||||
|
for(uint8_t i=16; i;){ |
||||||
|
i--; |
||||||
|
fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i); |
||||||
|
if(entry && entry->file_size == 0){ |
||||||
|
return entry; |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
//get a bin starting from particular sector
|
||||||
|
static fat_dir_entry_t * msc_update_find_bin(uint16_t sector){ |
||||||
|
for(uint8_t i=16; i; ){ |
||||||
|
i--; |
||||||
|
fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i); |
||||||
|
if(entry && entry->data_start_sector == (sector - msc_boot->sectors_per_alloc_table)){ |
||||||
|
return entry; |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
//write the new data and erase the flash blocks when necessary
|
||||||
|
static esp_err_t msc_update_write(const esp_partition_t *partition, uint32_t offset, void *data, size_t size){ |
||||||
|
esp_err_t err = ESP_OK; |
||||||
|
if((offset & (SPI_FLASH_SEC_SIZE-1)) == 0){ |
||||||
|
err = esp_partition_erase_range(partition, offset, SPI_FLASH_SEC_SIZE); |
||||||
|
log_v("ERASE[0x%08X]: %s", offset, (err != ESP_OK)?"FAIL":"OK"); |
||||||
|
if(err != ESP_OK){ |
||||||
|
return err; |
||||||
|
} |
||||||
|
} |
||||||
|
return esp_partition_write(partition, offset, data, size); |
||||||
|
} |
||||||
|
|
||||||
|
//called when error was encountered while updating
|
||||||
|
static void msc_update_error(){ |
||||||
|
log_e("UPDATE_ERROR: %u", msc_update_bytes_written); |
||||||
|
arduino_firmware_msc_event_data_t p; |
||||||
|
p.error.size = msc_update_bytes_written; |
||||||
|
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_ERROR_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); |
||||||
|
msc_update_state = MSC_UPDATE_IDLE; |
||||||
|
msc_update_entry = NULL; |
||||||
|
msc_update_bytes_written = 0; |
||||||
|
msc_update_start_sector = 0; |
||||||
|
} |
||||||
|
|
||||||
|
//called when all firmware bytes have been received
|
||||||
|
static void msc_update_end(){ |
||||||
|
log_d("UPDATE_END: %u", msc_update_entry->file_size); |
||||||
|
msc_update_state = MSC_UPDATE_END; |
||||||
|
size_t ota_size = get_firmware_size(msc_ota_partition); |
||||||
|
if(ota_size != msc_update_entry->file_size){ |
||||||
|
log_e("OTA SIZE MISMATCH %u != %u", ota_size, msc_update_entry->file_size); |
||||||
|
msc_update_error(); |
||||||
|
return; |
||||||
|
} |
||||||
|
if(!ota_size || esp_ota_set_boot_partition(msc_ota_partition) != ESP_OK){ |
||||||
|
log_e("ENABLING OTA PARTITION FAILED"); |
||||||
|
msc_update_error(); |
||||||
|
return; |
||||||
|
} |
||||||
|
arduino_firmware_msc_event_data_t p; |
||||||
|
p.end.size = msc_update_entry->file_size; |
||||||
|
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_END_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); |
||||||
|
} |
||||||
|
|
||||||
|
static int32_t msc_write(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){ |
||||||
|
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
|
||||||
|
if(lba < fw_start_sector){ |
||||||
|
//write to sectors that are in RAM
|
||||||
|
memcpy(msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize); |
||||||
|
if(msc_ota_partition && lba == (fw_start_sector - 1)){ |
||||||
|
//monitor the root folder table
|
||||||
|
if(msc_update_state <= MSC_UPDATE_RUNNING){ |
||||||
|
fat_dir_entry_t * update_entry = msc_update_find_new_bin(); |
||||||
|
if(update_entry) { |
||||||
|
if(msc_update_entry) { |
||||||
|
log_v("REPLACING ENTRY"); |
||||||
|
} else { |
||||||
|
log_v("ASSIGNING ENTRY"); |
||||||
|
} |
||||||
|
if(msc_update_state <= MSC_UPDATE_STARTING){ |
||||||
|
msc_update_state = MSC_UPDATE_STARTING; |
||||||
|
msc_update_bytes_written = 0; |
||||||
|
msc_update_start_sector = 0; |
||||||
|
} |
||||||
|
msc_update_entry = update_entry; |
||||||
|
} else if(msc_update_state == MSC_UPDATE_RUNNING){ |
||||||
|
if(!msc_update_entry && msc_update_start_sector){ |
||||||
|
msc_update_entry = msc_update_find_bin(msc_update_start_sector); |
||||||
|
} |
||||||
|
if(msc_update_entry && msc_update_bytes_written >= msc_update_entry->file_size){ |
||||||
|
msc_update_end(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} else if(msc_ota_partition && lba >= msc_update_start_sector){ |
||||||
|
//handle writes to the region where the new firmware will be uploaded
|
||||||
|
arduino_firmware_msc_event_data_t p; |
||||||
|
if(msc_update_state <= MSC_UPDATE_STARTING && buffer[0] == 0xE9){ |
||||||
|
msc_update_state = MSC_UPDATE_RUNNING; |
||||||
|
msc_update_start_sector = lba; |
||||||
|
msc_update_bytes_written = 0; |
||||||
|
log_d("UPDATE_START: %u (0x%02X)", lba, lba - msc_boot->sectors_per_alloc_table); |
||||||
|
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_START_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); |
||||||
|
if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){ |
||||||
|
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize); |
||||||
|
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize; |
||||||
|
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset; |
||||||
|
p.write.size = bufsize; |
||||||
|
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); |
||||||
|
} else { |
||||||
|
msc_update_error(); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} else if(msc_update_state == MSC_UPDATE_RUNNING){ |
||||||
|
if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written < msc_update_entry->file_size && (msc_update_bytes_written + bufsize) >= msc_update_entry->file_size){ |
||||||
|
bufsize = msc_update_entry->file_size - msc_update_bytes_written; |
||||||
|
} |
||||||
|
if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){ |
||||||
|
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize); |
||||||
|
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize; |
||||||
|
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset; |
||||||
|
p.write.size = bufsize; |
||||||
|
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); |
||||||
|
if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written >= msc_update_entry->file_size){ |
||||||
|
msc_update_end(); |
||||||
|
} |
||||||
|
} else { |
||||||
|
msc_update_error(); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return bufsize; |
||||||
|
} |
||||||
|
|
||||||
|
static int32_t msc_read(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){ |
||||||
|
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
|
||||||
|
if(lba < fw_start_sector){ |
||||||
|
memcpy(buffer, msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize); |
||||||
|
} else if(msc_run_partition && lba < fw_end_sector){ |
||||||
|
//read the currently running firmware
|
||||||
|
if(esp_partition_read(msc_run_partition, ((lba - fw_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) != ESP_OK){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} else { |
||||||
|
memset(buffer, 0, bufsize); |
||||||
|
} |
||||||
|
return bufsize; |
||||||
|
} |
||||||
|
|
||||||
|
static bool msc_start_stop(uint8_t power_condition, bool start, bool load_eject){ |
||||||
|
//log_d("power: %u, start: %u, eject: %u", power_condition, start, load_eject);
|
||||||
|
arduino_firmware_msc_event_data_t p; |
||||||
|
p.power.power_condition = power_condition; |
||||||
|
p.power.start = start; |
||||||
|
p.power.load_eject = load_eject; |
||||||
|
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_POWER_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
static volatile TaskHandle_t msc_task_handle = NULL; |
||||||
|
static void msc_task(void *pvParameters){ |
||||||
|
for (;;) { |
||||||
|
if(msc_update_state == MSC_UPDATE_END){ |
||||||
|
delay(100); |
||||||
|
esp_restart(); |
||||||
|
} |
||||||
|
delay(100); |
||||||
|
} |
||||||
|
msc_task_handle = NULL; |
||||||
|
vTaskDelete(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
FirmwareMSC::FirmwareMSC():msc(){} |
||||||
|
|
||||||
|
FirmwareMSC::~FirmwareMSC(){ |
||||||
|
end(); |
||||||
|
} |
||||||
|
|
||||||
|
bool FirmwareMSC::begin(){ |
||||||
|
if(msc_ram_disk){ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if(!msc_update_setup_disk(USB_FW_MSC_VOLUME_NAME, USB_FW_MSC_SERIAL_NUMBER)){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if(!msc_task_handle){ |
||||||
|
xTaskCreateUniversal(msc_task, "msc_disk", 1024, NULL, 2, (TaskHandle_t*)&msc_task_handle, 0); |
||||||
|
if(!msc_task_handle){ |
||||||
|
msc_update_delete_disk(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
msc.vendorID(USB_FW_MSC_VENDOR_ID); |
||||||
|
msc.productID(USB_FW_MSC_PRODUCT_ID); |
||||||
|
msc.productRevision(USB_FW_MSC_PRODUCT_REVISION); |
||||||
|
msc.onStartStop(msc_start_stop); |
||||||
|
msc.onRead(msc_read); |
||||||
|
msc.onWrite(msc_write); |
||||||
|
msc.mediaPresent(true); |
||||||
|
msc.begin(msc_boot->fat12_sector_num, DISK_SECTOR_SIZE); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void FirmwareMSC::end(){ |
||||||
|
msc.end(); |
||||||
|
if(msc_task_handle){ |
||||||
|
vTaskDelete(msc_task_handle); |
||||||
|
msc_task_handle = NULL; |
||||||
|
} |
||||||
|
msc_update_delete_disk(); |
||||||
|
} |
||||||
|
|
||||||
|
void FirmwareMSC::onEvent(esp_event_handler_t callback){ |
||||||
|
onEvent(ARDUINO_FIRMWARE_MSC_ANY_EVENT, callback); |
||||||
|
} |
||||||
|
void FirmwareMSC::onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback){ |
||||||
|
arduino_usb_event_handler_register_with(ARDUINO_FIRMWARE_MSC_EVENTS, event, callback, this); |
||||||
|
} |
||||||
|
|
||||||
|
#if ARDUINO_USB_MSC_ON_BOOT |
||||||
|
FirmwareMSC MSC_Update; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* CONFIG_USB_MSC_ENABLED */ |
@ -0,0 +1,70 @@ |
|||||||
|
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
#include <stdbool.h> |
||||||
|
#include "USBMSC.h" |
||||||
|
|
||||||
|
#if CONFIG_TINYUSB_MSC_ENABLED |
||||||
|
|
||||||
|
#include "esp_event.h" |
||||||
|
|
||||||
|
ESP_EVENT_DECLARE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS); |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
ARDUINO_FIRMWARE_MSC_ANY_EVENT = ESP_EVENT_ANY_ID, |
||||||
|
ARDUINO_FIRMWARE_MSC_START_EVENT = 0, |
||||||
|
ARDUINO_FIRMWARE_MSC_WRITE_EVENT, |
||||||
|
ARDUINO_FIRMWARE_MSC_END_EVENT, |
||||||
|
ARDUINO_FIRMWARE_MSC_ERROR_EVENT, |
||||||
|
ARDUINO_FIRMWARE_MSC_POWER_EVENT, |
||||||
|
ARDUINO_FIRMWARE_MSC_MAX_EVENT, |
||||||
|
} arduino_firmware_msc_event_t; |
||||||
|
|
||||||
|
typedef union { |
||||||
|
struct { |
||||||
|
size_t offset; |
||||||
|
size_t size; |
||||||
|
} write; |
||||||
|
struct { |
||||||
|
uint8_t power_condition; |
||||||
|
bool start; |
||||||
|
bool load_eject; |
||||||
|
} power; |
||||||
|
struct { |
||||||
|
size_t size; |
||||||
|
} end; |
||||||
|
struct { |
||||||
|
size_t size; |
||||||
|
} error; |
||||||
|
} arduino_firmware_msc_event_data_t; |
||||||
|
|
||||||
|
class FirmwareMSC { |
||||||
|
private: |
||||||
|
USBMSC msc; |
||||||
|
|
||||||
|
public: |
||||||
|
FirmwareMSC(); |
||||||
|
~FirmwareMSC(); |
||||||
|
bool begin(); |
||||||
|
void end(); |
||||||
|
void onEvent(esp_event_handler_t callback); |
||||||
|
void onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback); |
||||||
|
}; |
||||||
|
|
||||||
|
#if ARDUINO_USB_MSC_ON_BOOT |
||||||
|
extern FirmwareMSC MSC_Update; |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* CONFIG_TINYUSB_MSC_ENABLED */ |
@ -0,0 +1,44 @@ |
|||||||
|
/*
|
||||||
|
* FunctionalInterrupt.cpp |
||||||
|
* |
||||||
|
* Created on: 8 jul. 2018 |
||||||
|
* Author: Herman |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "FunctionalInterrupt.h" |
||||||
|
#include "Arduino.h" |
||||||
|
|
||||||
|
typedef void (*voidFuncPtr)(void); |
||||||
|
typedef void (*voidFuncPtrArg)(void*); |
||||||
|
|
||||||
|
extern "C" |
||||||
|
{ |
||||||
|
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional); |
||||||
|
} |
||||||
|
|
||||||
|
void ARDUINO_ISR_ATTR interruptFunctional(void* arg) |
||||||
|
{ |
||||||
|
InterruptArgStructure* localArg = (InterruptArgStructure*)arg; |
||||||
|
if (localArg->interruptFunction) |
||||||
|
{ |
||||||
|
localArg->interruptFunction(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode) |
||||||
|
{ |
||||||
|
// use the local interrupt routine which takes the ArgStructure as argument
|
||||||
|
__attachInterruptFunctionalArg (pin, (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true); |
||||||
|
} |
||||||
|
|
||||||
|
extern "C" |
||||||
|
{ |
||||||
|
void cleanupFunctional(void* arg) |
||||||
|
{ |
||||||
|
delete (InterruptArgStructure*)arg; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,21 @@ |
|||||||
|
/*
|
||||||
|
* FunctionalInterrupt.h |
||||||
|
* |
||||||
|
* Created on: 8 jul. 2018 |
||||||
|
* Author: Herman |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_ |
||||||
|
#define CORE_CORE_FUNCTIONALINTERRUPT_H_ |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
struct InterruptArgStructure { |
||||||
|
std::function<void(void)> interruptFunction; |
||||||
|
}; |
||||||
|
|
||||||
|
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode); |
||||||
|
|
||||||
|
|
||||||
|
#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */ |
@ -0,0 +1,447 @@ |
|||||||
|
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "USB.h" |
||||||
|
#if SOC_USB_SERIAL_JTAG_SUPPORTED |
||||||
|
|
||||||
|
#include "esp32-hal.h" |
||||||
|
#include "esp32-hal-periman.h" |
||||||
|
#include "HWCDC.h" |
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/semphr.h" |
||||||
|
#include "freertos/queue.h" |
||||||
|
#include "freertos/ringbuf.h" |
||||||
|
#include "esp_intr_alloc.h" |
||||||
|
#include "soc/periph_defs.h" |
||||||
|
#include "soc/io_mux_reg.h" |
||||||
|
#pragma GCC diagnostic ignored "-Wvolatile" |
||||||
|
#include "hal/usb_serial_jtag_ll.h" |
||||||
|
#pragma GCC diagnostic warning "-Wvolatile" |
||||||
|
#include "rom/ets_sys.h" |
||||||
|
|
||||||
|
ESP_EVENT_DEFINE_BASE(ARDUINO_HW_CDC_EVENTS); |
||||||
|
|
||||||
|
static RingbufHandle_t tx_ring_buf = NULL; |
||||||
|
static QueueHandle_t rx_queue = NULL; |
||||||
|
static uint8_t rx_data_buf[64] = {0}; |
||||||
|
static intr_handle_t intr_handle = NULL; |
||||||
|
static volatile bool initial_empty = false; |
||||||
|
static SemaphoreHandle_t tx_lock = NULL; |
||||||
|
|
||||||
|
// workaround for when USB CDC is not connected
|
||||||
|
static uint32_t tx_timeout_ms = 0; |
||||||
|
static bool tx_timeout_change_request = false; |
||||||
|
|
||||||
|
static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL; |
||||||
|
|
||||||
|
static esp_err_t arduino_hw_cdc_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, BaseType_t *task_unblocked){ |
||||||
|
if(arduino_hw_cdc_event_loop_handle == NULL){ |
||||||
|
return ESP_FAIL; |
||||||
|
} |
||||||
|
return esp_event_isr_post_to(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_data, event_data_size, task_unblocked); |
||||||
|
} |
||||||
|
|
||||||
|
static esp_err_t arduino_hw_cdc_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg){ |
||||||
|
if (!arduino_hw_cdc_event_loop_handle) { |
||||||
|
esp_event_loop_args_t event_task_args = { |
||||||
|
.queue_size = 5, |
||||||
|
.task_name = "arduino_hw_cdc_events", |
||||||
|
.task_priority = 5, |
||||||
|
.task_stack_size = 2048, |
||||||
|
.task_core_id = tskNO_AFFINITY |
||||||
|
}; |
||||||
|
if (esp_event_loop_create(&event_task_args, &arduino_hw_cdc_event_loop_handle) != ESP_OK) { |
||||||
|
log_e("esp_event_loop_create failed"); |
||||||
|
} |
||||||
|
} |
||||||
|
if(arduino_hw_cdc_event_loop_handle == NULL){ |
||||||
|
return ESP_FAIL; |
||||||
|
} |
||||||
|
return esp_event_handler_register_with(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_handler, event_handler_arg); |
||||||
|
} |
||||||
|
|
||||||
|
static void hw_cdc_isr_handler(void *arg) { |
||||||
|
portBASE_TYPE xTaskWoken = 0; |
||||||
|
uint32_t usbjtag_intr_status = 0; |
||||||
|
arduino_hw_cdc_event_data_t event = {0}; |
||||||
|
usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask(); |
||||||
|
|
||||||
|
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) { |
||||||
|
// Interrupt tells us the host picked up the data we sent.
|
||||||
|
if (usb_serial_jtag_ll_txfifo_writable() == 1) { |
||||||
|
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
|
||||||
|
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
if(!initial_empty){ |
||||||
|
initial_empty = true; |
||||||
|
// First time USB is plugged and the application has not explicitly set TX Timeout, set it to default 100ms.
|
||||||
|
// Otherwise, USB is still unplugged and the timeout will be kept as Zero in order to avoid any delay in the
|
||||||
|
// application whenever it uses write() and the TX Queue gets full.
|
||||||
|
if (!tx_timeout_change_request) { |
||||||
|
tx_timeout_ms = 100; |
||||||
|
} |
||||||
|
//send event?
|
||||||
|
//ets_printf("CONNECTED\n");
|
||||||
|
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_CONNECTED_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); |
||||||
|
} |
||||||
|
size_t queued_size; |
||||||
|
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64); |
||||||
|
// If the hardware fifo is avaliable, write in it. Otherwise, do nothing.
|
||||||
|
if (queued_buff != NULL) { //Although tx_queued_bytes may be larger than 0. We may have interrupt before xRingbufferSend() was called.
|
||||||
|
//Copy the queued buffer into the TX FIFO
|
||||||
|
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size); |
||||||
|
usb_serial_jtag_ll_txfifo_flush(); |
||||||
|
vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken); |
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
//send event?
|
||||||
|
//ets_printf("TX:%u\n", queued_size);
|
||||||
|
event.tx.len = queued_size; |
||||||
|
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_TX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); |
||||||
|
} |
||||||
|
} else { |
||||||
|
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT) { |
||||||
|
// read rx buffer(max length is 64), and send avaliable data to ringbuffer.
|
||||||
|
// Ensure the rx buffer size is larger than RX_MAX_SIZE.
|
||||||
|
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT); |
||||||
|
uint32_t rx_fifo_len = usb_serial_jtag_ll_read_rxfifo(rx_data_buf, 64); |
||||||
|
uint32_t i=0; |
||||||
|
for(i=0; i<rx_fifo_len; i++){ |
||||||
|
if(rx_queue == NULL || !xQueueSendFromISR(rx_queue, rx_data_buf+i, &xTaskWoken)){ |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
//send event?
|
||||||
|
//ets_printf("RX:%u/%u\n", i, rx_fifo_len);
|
||||||
|
event.rx.len = i; |
||||||
|
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); |
||||||
|
} |
||||||
|
|
||||||
|
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) { |
||||||
|
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET); |
||||||
|
initial_empty = false; |
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
//ets_printf("BUS_RESET\n");
|
||||||
|
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); |
||||||
|
} |
||||||
|
|
||||||
|
if (xTaskWoken == pdTRUE) { |
||||||
|
portYIELD_FROM_ISR(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) { |
||||||
|
if(xPortInIsrContext()){ |
||||||
|
xRingbufferSendFromISR(tx_ring_buf, (void*) (&c), 1, NULL); |
||||||
|
} else { |
||||||
|
xRingbufferSend(tx_ring_buf, (void*) (&c), 1, tx_timeout_ms / portTICK_PERIOD_MS); |
||||||
|
} |
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
} |
||||||
|
|
||||||
|
HWCDC::HWCDC() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
HWCDC::~HWCDC(){ |
||||||
|
end(); |
||||||
|
} |
||||||
|
|
||||||
|
HWCDC::operator bool() const |
||||||
|
{ |
||||||
|
return initial_empty; |
||||||
|
} |
||||||
|
|
||||||
|
void HWCDC::onEvent(esp_event_handler_t callback){ |
||||||
|
onEvent(ARDUINO_HW_CDC_ANY_EVENT, callback); |
||||||
|
} |
||||||
|
|
||||||
|
void HWCDC::onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback){ |
||||||
|
arduino_hw_cdc_event_handler_register_with(ARDUINO_HW_CDC_EVENTS, event, callback, this); |
||||||
|
} |
||||||
|
|
||||||
|
bool HWCDC::deinit(void * busptr)
|
||||||
|
{ |
||||||
|
// avoid any recursion issue with Peripheral Manager perimanSetPinBus() call
|
||||||
|
static bool running = false; |
||||||
|
if (running) return true; |
||||||
|
running = true;
|
||||||
|
// Setting USB D+ D- pins
|
||||||
|
bool retCode = true; |
||||||
|
retCode &= perimanSetPinBus(USB_DM_GPIO_NUM, ESP32_BUS_TYPE_INIT, NULL); |
||||||
|
retCode &= perimanSetPinBus(USB_DP_GPIO_NUM, ESP32_BUS_TYPE_INIT, NULL); |
||||||
|
if (retCode) { |
||||||
|
// Force the host to re-enumerate (BUS_RESET)
|
||||||
|
pinMode(USB_DM_GPIO_NUM, OUTPUT_OPEN_DRAIN); |
||||||
|
pinMode(USB_DP_GPIO_NUM, OUTPUT_OPEN_DRAIN); |
||||||
|
digitalWrite(USB_DM_GPIO_NUM, LOW); |
||||||
|
digitalWrite(USB_DP_GPIO_NUM, LOW); |
||||||
|
} |
||||||
|
// release the flag
|
||||||
|
running = false; |
||||||
|
return retCode; |
||||||
|
} |
||||||
|
|
||||||
|
void HWCDC::begin(unsigned long baud) |
||||||
|
{ |
||||||
|
if(tx_lock == NULL) { |
||||||
|
tx_lock = xSemaphoreCreateMutex(); |
||||||
|
} |
||||||
|
//RX Buffer default has 256 bytes if not preset
|
||||||
|
if(rx_queue == NULL) { |
||||||
|
if (!setRxBufferSize(256)) { |
||||||
|
log_e("HW CDC RX Buffer error"); |
||||||
|
} |
||||||
|
} |
||||||
|
//TX Buffer default has 256 bytes if not preset
|
||||||
|
if (tx_ring_buf == NULL) { |
||||||
|
if (!setTxBufferSize(256)) { |
||||||
|
log_e("HW CDC TX Buffer error"); |
||||||
|
}
|
||||||
|
} |
||||||
|
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK); |
||||||
|
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK); |
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET); |
||||||
|
if(!intr_handle && esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_isr_handler, NULL, &intr_handle) != ESP_OK){ |
||||||
|
isr_log_e("HW USB CDC failed to init interrupts"); |
||||||
|
end(); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (perimanSetBusDeinit(ESP32_BUS_TYPE_USB, HWCDC::deinit)) { |
||||||
|
// Setting USB D+ D- pins
|
||||||
|
perimanSetPinBus(USB_DM_GPIO_NUM, ESP32_BUS_TYPE_USB, (void *) this); |
||||||
|
perimanSetPinBus(USB_DP_GPIO_NUM, ESP32_BUS_TYPE_USB, (void *) this); |
||||||
|
} else { |
||||||
|
log_e("Serial JTAG Pins can't be set into Peripheral Manager."); |
||||||
|
} |
||||||
|
|
||||||
|
usb_serial_jtag_ll_txfifo_flush(); |
||||||
|
} |
||||||
|
|
||||||
|
void HWCDC::end() |
||||||
|
{ |
||||||
|
//Disable tx/rx interrupt.
|
||||||
|
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK); |
||||||
|
esp_intr_free(intr_handle); |
||||||
|
intr_handle = NULL; |
||||||
|
if(tx_lock != NULL) { |
||||||
|
vSemaphoreDelete(tx_lock); |
||||||
|
tx_lock = NULL; |
||||||
|
} |
||||||
|
setRxBufferSize(0); |
||||||
|
setTxBufferSize(0); |
||||||
|
if (arduino_hw_cdc_event_loop_handle) { |
||||||
|
esp_event_loop_delete(arduino_hw_cdc_event_loop_handle); |
||||||
|
arduino_hw_cdc_event_loop_handle = NULL; |
||||||
|
} |
||||||
|
HWCDC::deinit(this); |
||||||
|
} |
||||||
|
|
||||||
|
void HWCDC::setTxTimeoutMs(uint32_t timeout){ |
||||||
|
tx_timeout_ms = timeout; |
||||||
|
// it registers that the user has explicitly requested to use a value as TX timeout
|
||||||
|
// used for the workaround with unplugged USB and TX Queue Full that causes a delay on every write()
|
||||||
|
tx_timeout_change_request = true; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* WRITING |
||||||
|
*/ |
||||||
|
|
||||||
|
size_t HWCDC::setTxBufferSize(size_t tx_queue_len){ |
||||||
|
if(tx_ring_buf){ |
||||||
|
vRingbufferDelete(tx_ring_buf); |
||||||
|
tx_ring_buf = NULL; |
||||||
|
} |
||||||
|
if(!tx_queue_len){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
tx_ring_buf = xRingbufferCreate(tx_queue_len, RINGBUF_TYPE_BYTEBUF); |
||||||
|
if(!tx_ring_buf){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return tx_queue_len; |
||||||
|
} |
||||||
|
|
||||||
|
int HWCDC::availableForWrite(void) |
||||||
|
{ |
||||||
|
if(tx_ring_buf == NULL || tx_lock == NULL){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
size_t a = xRingbufferGetCurFreeSize(tx_ring_buf); |
||||||
|
xSemaphoreGive(tx_lock); |
||||||
|
return a; |
||||||
|
} |
||||||
|
|
||||||
|
size_t HWCDC::write(const uint8_t *buffer, size_t size) |
||||||
|
{ |
||||||
|
if(buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
size_t max_size = xRingbufferGetMaxItemSize(tx_ring_buf); |
||||||
|
size_t space = xRingbufferGetCurFreeSize(tx_ring_buf); |
||||||
|
size_t to_send = size, so_far = 0; |
||||||
|
|
||||||
|
if(space > size){ |
||||||
|
space = size; |
||||||
|
} |
||||||
|
// Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR.
|
||||||
|
if(xRingbufferSend(tx_ring_buf, (void*) (buffer), space, 0) != pdTRUE){ |
||||||
|
size = 0; |
||||||
|
} else { |
||||||
|
to_send -= space; |
||||||
|
so_far += space; |
||||||
|
// Now trigger the ISR to read data from the ring buffer.
|
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
|
||||||
|
while(to_send){ |
||||||
|
if(max_size > to_send){ |
||||||
|
max_size = to_send; |
||||||
|
} |
||||||
|
// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
|
||||||
|
if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), max_size, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){ |
||||||
|
size = so_far; |
||||||
|
break; |
||||||
|
} |
||||||
|
so_far += max_size; |
||||||
|
to_send -= max_size; |
||||||
|
// Now trigger the ISR to read data from the ring buffer.
|
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
} |
||||||
|
} |
||||||
|
xSemaphoreGive(tx_lock); |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
size_t HWCDC::write(uint8_t c) |
||||||
|
{ |
||||||
|
return write(&c, 1); |
||||||
|
} |
||||||
|
|
||||||
|
void HWCDC::flush(void) |
||||||
|
{ |
||||||
|
if(tx_ring_buf == NULL || tx_lock == NULL){ |
||||||
|
return; |
||||||
|
} |
||||||
|
if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ |
||||||
|
return; |
||||||
|
} |
||||||
|
UBaseType_t uxItemsWaiting = 0; |
||||||
|
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); |
||||||
|
if(uxItemsWaiting){ |
||||||
|
// Now trigger the ISR to read data from the ring buffer.
|
||||||
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); |
||||||
|
} |
||||||
|
while(uxItemsWaiting){ |
||||||
|
delay(5); |
||||||
|
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); |
||||||
|
} |
||||||
|
xSemaphoreGive(tx_lock); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* READING |
||||||
|
*/ |
||||||
|
|
||||||
|
size_t HWCDC::setRxBufferSize(size_t rx_queue_len){ |
||||||
|
if(rx_queue){ |
||||||
|
vQueueDelete(rx_queue); |
||||||
|
rx_queue = NULL; |
||||||
|
} |
||||||
|
if(!rx_queue_len){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t)); |
||||||
|
if(!rx_queue){ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return rx_queue_len; |
||||||
|
} |
||||||
|
|
||||||
|
int HWCDC::available(void) |
||||||
|
{ |
||||||
|
if(rx_queue == NULL){ |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return uxQueueMessagesWaiting(rx_queue); |
||||||
|
} |
||||||
|
|
||||||
|
int HWCDC::peek(void) |
||||||
|
{ |
||||||
|
if(rx_queue == NULL){ |
||||||
|
return -1; |
||||||
|
} |
||||||
|
uint8_t c; |
||||||
|
if(xQueuePeek(rx_queue, &c, 0)) { |
||||||
|
return c; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
int HWCDC::read(void) |
||||||
|
{ |
||||||
|
if(rx_queue == NULL){ |
||||||
|
return -1; |
||||||
|
} |
||||||
|
uint8_t c = 0; |
||||||
|
if(xQueueReceive(rx_queue, &c, 0)) { |
||||||
|
return c; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
size_t HWCDC::read(uint8_t *buffer, size_t size) |
||||||
|
{ |
||||||
|
if(rx_queue == NULL){ |
||||||
|
return -1; |
||||||
|
} |
||||||
|
uint8_t c = 0; |
||||||
|
size_t count = 0; |
||||||
|
while(count < size && xQueueReceive(rx_queue, &c, 0)){ |
||||||
|
buffer[count++] = c; |
||||||
|
} |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* DEBUG |
||||||
|
*/ |
||||||
|
|
||||||
|
void HWCDC::setDebugOutput(bool en) |
||||||
|
{ |
||||||
|
if(en) { |
||||||
|
uartSetDebug(NULL); |
||||||
|
ets_install_putc1((void (*)(char)) &cdc0_write_char); |
||||||
|
} else { |
||||||
|
ets_install_putc1(NULL); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#if ARDUINO_USB_MODE |
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT//Serial used for USB CDC
|
||||||
|
HWCDC Serial; |
||||||
|
#else |
||||||
|
HWCDC USBSerial; |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* SOC_USB_SERIAL_JTAG_SUPPORTED */ |
@ -0,0 +1,114 @@ |
|||||||
|
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "sdkconfig.h" |
||||||
|
#include "soc/soc_caps.h" |
||||||
|
|
||||||
|
#if SOC_USB_SERIAL_JTAG_SUPPORTED |
||||||
|
|
||||||
|
#include <inttypes.h> |
||||||
|
#include "esp_event.h" |
||||||
|
#include "Stream.h" |
||||||
|
|
||||||
|
ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS); |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
ARDUINO_HW_CDC_ANY_EVENT = ESP_EVENT_ANY_ID, |
||||||
|
ARDUINO_HW_CDC_CONNECTED_EVENT = 0, |
||||||
|
ARDUINO_HW_CDC_BUS_RESET_EVENT, |
||||||
|
ARDUINO_HW_CDC_RX_EVENT, |
||||||
|
ARDUINO_HW_CDC_TX_EVENT, |
||||||
|
ARDUINO_HW_CDC_MAX_EVENT, |
||||||
|
} arduino_hw_cdc_event_t; |
||||||
|
|
||||||
|
typedef union { |
||||||
|
struct { |
||||||
|
size_t len; |
||||||
|
} rx; |
||||||
|
struct { |
||||||
|
size_t len; |
||||||
|
} tx; |
||||||
|
} arduino_hw_cdc_event_data_t; |
||||||
|
|
||||||
|
class HWCDC: public Stream |
||||||
|
{ |
||||||
|
private: |
||||||
|
static bool deinit(void * busptr); |
||||||
|
|
||||||
|
public: |
||||||
|
HWCDC(); |
||||||
|
~HWCDC(); |
||||||
|
|
||||||
|
void onEvent(esp_event_handler_t callback); |
||||||
|
void onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback); |
||||||
|
|
||||||
|
size_t setRxBufferSize(size_t); |
||||||
|
size_t setTxBufferSize(size_t); |
||||||
|
void setTxTimeoutMs(uint32_t timeout); |
||||||
|
void begin(unsigned long baud=0); |
||||||
|
void end(); |
||||||
|
|
||||||
|
int available(void); |
||||||
|
int availableForWrite(void); |
||||||
|
int peek(void); |
||||||
|
int read(void); |
||||||
|
size_t read(uint8_t *buffer, size_t size); |
||||||
|
size_t write(uint8_t); |
||||||
|
size_t write(const uint8_t *buffer, size_t size); |
||||||
|
void flush(void); |
||||||
|
|
||||||
|
inline size_t read(char * buffer, size_t size) |
||||||
|
{ |
||||||
|
return read((uint8_t*) buffer, size); |
||||||
|
} |
||||||
|
inline size_t write(const char * buffer, size_t size) |
||||||
|
{ |
||||||
|
return write((uint8_t*) buffer, size); |
||||||
|
} |
||||||
|
inline size_t write(const char * s) |
||||||
|
{ |
||||||
|
return write((uint8_t*) s, strlen(s)); |
||||||
|
} |
||||||
|
inline size_t write(unsigned long n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
inline size_t write(long n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
inline size_t write(unsigned int n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
inline size_t write(int n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
operator bool() const; |
||||||
|
void setDebugOutput(bool); |
||||||
|
uint32_t baudRate(){return 115200;} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#if ARDUINO_USB_MODE |
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT//Serial used for USB CDC
|
||||||
|
extern HWCDC Serial; |
||||||
|
#else |
||||||
|
extern HWCDC USBSerial; |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* CONFIG_IDF_TARGET_ESP32C3 */ |
@ -0,0 +1,627 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include <inttypes.h> |
||||||
|
#include <ctime> |
||||||
|
|
||||||
|
#include "pins_arduino.h" |
||||||
|
#include "HardwareSerial.h" |
||||||
|
#include "soc/soc_caps.h" |
||||||
|
#include "driver/uart.h" |
||||||
|
#include "freertos/queue.h" |
||||||
|
|
||||||
|
#ifndef ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE |
||||||
|
#define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE 2048 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef ARDUINO_SERIAL_EVENT_TASK_PRIORITY |
||||||
|
#define ARDUINO_SERIAL_EVENT_TASK_PRIORITY (configMAX_PRIORITIES-1) |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE |
||||||
|
#define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE -1 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef SOC_RX0 |
||||||
|
#if CONFIG_IDF_TARGET_ESP32 |
||||||
|
#define SOC_RX0 3 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 |
||||||
|
#define SOC_RX0 44 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3 |
||||||
|
#define SOC_RX0 20 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6 |
||||||
|
#define SOC_RX0 17 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32H2 |
||||||
|
#define SOC_RX0 23 |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef SOC_TX0 |
||||||
|
#if CONFIG_IDF_TARGET_ESP32 |
||||||
|
#define SOC_TX0 1 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 |
||||||
|
#define SOC_TX0 43 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3 |
||||||
|
#define SOC_TX0 21 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6 |
||||||
|
#define SOC_TX0 16 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32H2 |
||||||
|
#define SOC_TX0 24 |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
void serialEvent(void) __attribute__((weak)); |
||||||
|
void serialEvent(void) {} |
||||||
|
|
||||||
|
#if SOC_UART_NUM > 1 |
||||||
|
|
||||||
|
#ifndef RX1 |
||||||
|
#if CONFIG_IDF_TARGET_ESP32 |
||||||
|
#define RX1 9 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 |
||||||
|
#define RX1 18 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3 |
||||||
|
#define RX1 18 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3 |
||||||
|
#define RX1 15 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6 |
||||||
|
#define RX1 4 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32H2 |
||||||
|
#define RX1 0 |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef TX1 |
||||||
|
#if CONFIG_IDF_TARGET_ESP32 |
||||||
|
#define TX1 10 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 |
||||||
|
#define TX1 17 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3 |
||||||
|
#define TX1 19 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3 |
||||||
|
#define TX1 16 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C6 |
||||||
|
#define TX1 5 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32H2 |
||||||
|
#define TX1 1 |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
void serialEvent1(void) __attribute__((weak)); |
||||||
|
void serialEvent1(void) {} |
||||||
|
#endif /* SOC_UART_NUM > 1 */ |
||||||
|
|
||||||
|
#if SOC_UART_NUM > 2 |
||||||
|
#ifndef RX2 |
||||||
|
#if CONFIG_IDF_TARGET_ESP32 |
||||||
|
#define RX2 16 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3 |
||||||
|
#define RX2 19 |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef TX2 |
||||||
|
#if CONFIG_IDF_TARGET_ESP32 |
||||||
|
#define TX2 17 |
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S3 |
||||||
|
#define TX2 20 |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
void serialEvent2(void) __attribute__((weak)); |
||||||
|
void serialEvent2(void) {} |
||||||
|
#endif /* SOC_UART_NUM > 2 */ |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) |
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||||
|
HardwareSerial Serial0(0); |
||||||
|
#else |
||||||
|
HardwareSerial Serial(0); |
||||||
|
#endif |
||||||
|
#if SOC_UART_NUM > 1 |
||||||
|
HardwareSerial Serial1(1); |
||||||
|
#endif |
||||||
|
#if SOC_UART_NUM > 2 |
||||||
|
HardwareSerial Serial2(2); |
||||||
|
#endif |
||||||
|
|
||||||
|
void serialEventRun(void) |
||||||
|
{ |
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||||
|
if(Serial0.available()) serialEvent(); |
||||||
|
#else |
||||||
|
if(Serial.available()) serialEvent(); |
||||||
|
#endif |
||||||
|
#if SOC_UART_NUM > 1 |
||||||
|
if(Serial1.available()) serialEvent1(); |
||||||
|
#endif |
||||||
|
#if SOC_UART_NUM > 2 |
||||||
|
if(Serial2.available()) serialEvent2(); |
||||||
|
#endif |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS |
||||||
|
#define HSERIAL_MUTEX_LOCK() do {} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS) |
||||||
|
#define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock) |
||||||
|
#else |
||||||
|
#define HSERIAL_MUTEX_LOCK() |
||||||
|
#define HSERIAL_MUTEX_UNLOCK() |
||||||
|
#endif |
||||||
|
|
||||||
|
HardwareSerial::HardwareSerial(uint8_t uart_nr) : |
||||||
|
_uart_nr(uart_nr), |
||||||
|
_uart(NULL), |
||||||
|
_rxBufferSize(256), |
||||||
|
_txBufferSize(0), |
||||||
|
_onReceiveCB(NULL), |
||||||
|
_onReceiveErrorCB(NULL), |
||||||
|
_onReceiveTimeout(false), |
||||||
|
_rxTimeout(2), |
||||||
|
_rxFIFOFull(0), |
||||||
|
_eventTask(NULL) |
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS |
||||||
|
,_lock(NULL) |
||||||
|
#endif |
||||||
|
{ |
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS |
||||||
|
if(_lock == NULL){ |
||||||
|
_lock = xSemaphoreCreateMutex(); |
||||||
|
if(_lock == NULL){ |
||||||
|
log_e("xSemaphoreCreateMutex failed"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
// sets UART0 (default console) RX/TX pins as already configured in boot
|
||||||
|
if (uart_nr == 0) { |
||||||
|
setPins(SOC_RX0, SOC_TX0); |
||||||
|
} |
||||||
|
// set deinit function in the Peripheral Manager
|
||||||
|
uart_init_PeriMan(); |
||||||
|
} |
||||||
|
|
||||||
|
HardwareSerial::~HardwareSerial() |
||||||
|
{ |
||||||
|
end(); |
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS |
||||||
|
if(_lock != NULL){ |
||||||
|
vSemaphoreDelete(_lock); |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void HardwareSerial::_createEventTask(void *args) |
||||||
|
{ |
||||||
|
// Creating UART event Task
|
||||||
|
xTaskCreateUniversal(_uartEventTask, "uart_event_task", ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, this, ARDUINO_SERIAL_EVENT_TASK_PRIORITY, &_eventTask, ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE); |
||||||
|
if (_eventTask == NULL) { |
||||||
|
log_e(" -- UART%d Event Task not Created!", _uart_nr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::_destroyEventTask(void) |
||||||
|
{ |
||||||
|
if (_eventTask != NULL) { |
||||||
|
vTaskDelete(_eventTask); |
||||||
|
_eventTask = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::onReceiveError(OnReceiveErrorCb function) |
||||||
|
{ |
||||||
|
HSERIAL_MUTEX_LOCK(); |
||||||
|
// function may be NULL to cancel onReceive() from its respective task
|
||||||
|
_onReceiveErrorCB = function; |
||||||
|
// this can be called after Serial.begin(), therefore it shall create the event task
|
||||||
|
if (function != NULL && _uart != NULL && _eventTask == NULL) { |
||||||
|
_createEventTask(this); |
||||||
|
} |
||||||
|
HSERIAL_MUTEX_UNLOCK(); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout) |
||||||
|
{ |
||||||
|
HSERIAL_MUTEX_LOCK(); |
||||||
|
// function may be NULL to cancel onReceive() from its respective task
|
||||||
|
_onReceiveCB = function; |
||||||
|
|
||||||
|
// setting the callback to NULL will just disable it
|
||||||
|
if (_onReceiveCB != NULL) { |
||||||
|
// When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes
|
||||||
|
_onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false; |
||||||
|
|
||||||
|
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
|
||||||
|
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
|
||||||
|
if (_onReceiveTimeout) { |
||||||
|
uartSetRxFIFOFull(_uart, 120); |
||||||
|
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes."); |
||||||
|
} |
||||||
|
|
||||||
|
// this method can be called after Serial.begin(), therefore it shall create the event task
|
||||||
|
if (_uart != NULL && _eventTask == NULL) { |
||||||
|
_createEventTask(this); // Create event task
|
||||||
|
} |
||||||
|
} |
||||||
|
HSERIAL_MUTEX_UNLOCK(); |
||||||
|
} |
||||||
|
|
||||||
|
// This function allow the user to define how many bytes will trigger an Interrupt that will copy RX FIFO to the internal RX Ringbuffer
|
||||||
|
// ISR will also move data from FIFO to RX Ringbuffer after a RX Timeout defined in HardwareSerial::setRxTimeout(uint8_t symbols_timeout)
|
||||||
|
// A low value of FIFO Full bytes will consume more CPU time within the ISR
|
||||||
|
// A high value of FIFO Full bytes will make the application wait longer to have byte available for the Stkech in a streaming scenario
|
||||||
|
// Both RX FIFO Full and RX Timeout may affect when onReceive() will be called
|
||||||
|
bool HardwareSerial::setRxFIFOFull(uint8_t fifoBytes) |
||||||
|
{ |
||||||
|
HSERIAL_MUTEX_LOCK(); |
||||||
|
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
|
||||||
|
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
|
||||||
|
if (_onReceiveCB != NULL && _onReceiveTimeout) { |
||||||
|
fifoBytes = 120; |
||||||
|
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes."); |
||||||
|
} |
||||||
|
bool retCode = uartSetRxFIFOFull(_uart, fifoBytes); // Set new timeout
|
||||||
|
if (fifoBytes > 0 && fifoBytes < SOC_UART_FIFO_LEN - 1) _rxFIFOFull = fifoBytes; |
||||||
|
HSERIAL_MUTEX_UNLOCK(); |
||||||
|
return retCode; |
||||||
|
} |
||||||
|
|
||||||
|
// timout is calculates in time to receive UART symbols at the UART baudrate.
|
||||||
|
// the estimation is about 11 bits per symbol (SERIAL_8N1)
|
||||||
|
bool HardwareSerial::setRxTimeout(uint8_t symbols_timeout) |
||||||
|
{ |
||||||
|
HSERIAL_MUTEX_LOCK(); |
||||||
|
|
||||||
|
// Zero disables timeout, thus, onReceive callback will only be called when RX FIFO reaches 120 bytes
|
||||||
|
// Any non-zero value will activate onReceive callback based on UART baudrate with about 11 bits per symbol
|
||||||
|
_rxTimeout = symbols_timeout; |
||||||
|
if (!symbols_timeout) _onReceiveTimeout = false; // only when RX timeout is disabled, we also must disable this flag
|
||||||
|
|
||||||
|
bool retCode = uartSetRxTimeout(_uart, _rxTimeout); // Set new timeout
|
||||||
|
|
||||||
|
HSERIAL_MUTEX_UNLOCK(); |
||||||
|
return retCode; |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::eventQueueReset() |
||||||
|
{ |
||||||
|
QueueHandle_t uartEventQueue = NULL; |
||||||
|
if (_uart == NULL) { |
||||||
|
return; |
||||||
|
} |
||||||
|
uartGetEventQueue(_uart, &uartEventQueue); |
||||||
|
if (uartEventQueue != NULL) { |
||||||
|
xQueueReset(uartEventQueue); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::_uartEventTask(void *args) |
||||||
|
{ |
||||||
|
HardwareSerial *uart = (HardwareSerial *)args; |
||||||
|
uart_event_t event; |
||||||
|
QueueHandle_t uartEventQueue = NULL; |
||||||
|
uartGetEventQueue(uart->_uart, &uartEventQueue); |
||||||
|
if (uartEventQueue != NULL) { |
||||||
|
for(;;) { |
||||||
|
//Waiting for UART event.
|
||||||
|
if(xQueueReceive(uartEventQueue, (void * )&event, (TickType_t)portMAX_DELAY)) { |
||||||
|
hardwareSerial_error_t currentErr = UART_NO_ERROR; |
||||||
|
switch(event.type) { |
||||||
|
case UART_DATA: |
||||||
|
if(uart->_onReceiveCB && uart->available() > 0 && |
||||||
|
((uart->_onReceiveTimeout && event.timeout_flag) || !uart->_onReceiveTimeout) ) |
||||||
|
uart->_onReceiveCB(); |
||||||
|
break; |
||||||
|
case UART_FIFO_OVF: |
||||||
|
log_w("UART%d FIFO Overflow. Consider adding Hardware Flow Control to your Application.", uart->_uart_nr); |
||||||
|
currentErr = UART_FIFO_OVF_ERROR; |
||||||
|
break; |
||||||
|
case UART_BUFFER_FULL: |
||||||
|
log_w("UART%d Buffer Full. Consider increasing your buffer size of your Application.", uart->_uart_nr); |
||||||
|
currentErr = UART_BUFFER_FULL_ERROR; |
||||||
|
break; |
||||||
|
case UART_BREAK: |
||||||
|
log_w("UART%d RX break.", uart->_uart_nr); |
||||||
|
currentErr = UART_BREAK_ERROR; |
||||||
|
break; |
||||||
|
case UART_PARITY_ERR: |
||||||
|
log_w("UART%d parity error.", uart->_uart_nr); |
||||||
|
currentErr = UART_PARITY_ERROR; |
||||||
|
break; |
||||||
|
case UART_FRAME_ERR: |
||||||
|
log_w("UART%d frame error.", uart->_uart_nr); |
||||||
|
currentErr = UART_FRAME_ERROR; |
||||||
|
break; |
||||||
|
default: |
||||||
|
log_w("UART%d unknown event type %d.", uart->_uart_nr, event.type); |
||||||
|
break; |
||||||
|
} |
||||||
|
if (currentErr != UART_NO_ERROR) { |
||||||
|
if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(currentErr); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
vTaskDelete(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd) |
||||||
|
{ |
||||||
|
if(_uart_nr >= SOC_UART_NUM) { |
||||||
|
log_e("Serial number is invalid, please use a number from 0 to %u", SOC_UART_NUM - 1); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS |
||||||
|
if(_lock == NULL){ |
||||||
|
log_e("MUTEX Lock failed. Can't begin."); |
||||||
|
return; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
HSERIAL_MUTEX_LOCK(); |
||||||
|
// First Time or after end() --> set default Pins
|
||||||
|
if (!uartIsDriverInstalled(_uart)) { |
||||||
|
// get previously used RX/TX pins, if any.
|
||||||
|
int8_t _rxPin = uart_get_RxPin(_uart_nr); |
||||||
|
int8_t _txPin = uart_get_TxPin(_uart_nr); |
||||||
|
switch (_uart_nr) { |
||||||
|
case UART_NUM_0: |
||||||
|
if (rxPin < 0 && txPin < 0) { |
||||||
|
// do not change RX0/TX0 if it has already been set before
|
||||||
|
rxPin = _rxPin < 0 ? SOC_RX0 : _rxPin; |
||||||
|
txPin = _txPin < 0 ? SOC_TX0 : _txPin; |
||||||
|
} |
||||||
|
break; |
||||||
|
#if SOC_UART_NUM > 1 // may save some flash bytes...
|
||||||
|
case UART_NUM_1: |
||||||
|
if (rxPin < 0 && txPin < 0) { |
||||||
|
// do not change RX1/TX1 if it has already been set before
|
||||||
|
rxPin = _rxPin < 0 ? RX1 : _rxPin; |
||||||
|
txPin = _txPin < 0 ? TX1 : _txPin; |
||||||
|
} |
||||||
|
break; |
||||||
|
#endif |
||||||
|
#if SOC_UART_NUM > 2 // may save some flash bytes...
|
||||||
|
case UART_NUM_2: |
||||||
|
if (rxPin < 0 && txPin < 0) { |
||||||
|
// do not change RX2/TX2 if it has already been set before
|
||||||
|
rxPin = _rxPin < 0 ? RX2 : _rxPin; |
||||||
|
txPin = _txPin < 0 ? TX2 : _txPin; |
||||||
|
} |
||||||
|
break; |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if(_uart) { |
||||||
|
// in this case it is a begin() over a previous begin() - maybe to change baud rate
|
||||||
|
// thus do not disable debug output
|
||||||
|
end(false); |
||||||
|
} |
||||||
|
|
||||||
|
// IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified.
|
||||||
|
// it will detach previous UART attached pins
|
||||||
|
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); |
||||||
|
if (!baud) { |
||||||
|
// using baud rate as zero, forces it to try to detect the current baud rate in place
|
||||||
|
uartStartDetectBaudrate(_uart); |
||||||
|
time_t startMillis = millis(); |
||||||
|
unsigned long detectedBaudRate = 0; |
||||||
|
while(millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) { |
||||||
|
yield(); |
||||||
|
} |
||||||
|
|
||||||
|
end(false); |
||||||
|
|
||||||
|
if(detectedBaudRate) { |
||||||
|
delay(100); // Give some time...
|
||||||
|
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); |
||||||
|
} else { |
||||||
|
log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible"); |
||||||
|
_uart = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
// create a task to deal with Serial Events when, for example, calling begin() twice to change the baudrate,
|
||||||
|
// or when setting the callback before calling begin()
|
||||||
|
if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) { |
||||||
|
_createEventTask(this); |
||||||
|
} |
||||||
|
|
||||||
|
// Set UART RX timeout
|
||||||
|
uartSetRxTimeout(_uart, _rxTimeout); |
||||||
|
|
||||||
|
// Set UART FIFO Full depending on the baud rate.
|
||||||
|
// Lower baud rates will force to emulate byte-by-byte reading
|
||||||
|
// Higher baud rates will keep IDF default of 120 bytes for FIFO FULL Interrupt
|
||||||
|
// It can also be changed by the application at any time
|
||||||
|
if (!_rxFIFOFull) { // it has not being changed before calling begin()
|
||||||
|
// set a default FIFO Full value for the IDF driver
|
||||||
|
uint8_t fifoFull = 1; |
||||||
|
if (baud > 57600 || (_onReceiveCB != NULL && _onReceiveTimeout)) { |
||||||
|
fifoFull = 120; |
||||||
|
} |
||||||
|
uartSetRxFIFOFull(_uart, fifoFull); |
||||||
|
_rxFIFOFull = fifoFull; |
||||||
|
} |
||||||
|
|
||||||
|
HSERIAL_MUTEX_UNLOCK(); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::updateBaudRate(unsigned long baud) |
||||||
|
{ |
||||||
|
uartSetBaudRate(_uart, baud); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::end(bool fullyTerminate) |
||||||
|
{ |
||||||
|
// default Serial.end() will completely disable HardwareSerial,
|
||||||
|
// including any tasks or debug message channel (log_x()) - but not for IDF log messages!
|
||||||
|
if(fullyTerminate) { |
||||||
|
_onReceiveCB = NULL; |
||||||
|
_onReceiveErrorCB = NULL; |
||||||
|
if (uartGetDebug() == _uart_nr) { |
||||||
|
uartSetDebug(0); |
||||||
|
} |
||||||
|
_rxFIFOFull = 0; |
||||||
|
uartEnd(_uart_nr); // fully detach all pins and delete the UART driver
|
||||||
|
} else { |
||||||
|
// do not invalidate callbacks, detach pins, invalidate DBG output
|
||||||
|
uart_driver_delete(_uart_nr); |
||||||
|
} |
||||||
|
|
||||||
|
uartEnd(_uart_nr); |
||||||
|
_uart = 0; |
||||||
|
_destroyEventTask(); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::setDebugOutput(bool en) |
||||||
|
{ |
||||||
|
if(_uart == 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if(en) { |
||||||
|
uartSetDebug(_uart); |
||||||
|
} else { |
||||||
|
if(uartGetDebug() == _uart_nr) { |
||||||
|
uartSetDebug(NULL); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int HardwareSerial::available(void) |
||||||
|
{ |
||||||
|
return uartAvailable(_uart); |
||||||
|
} |
||||||
|
int HardwareSerial::availableForWrite(void) |
||||||
|
{ |
||||||
|
return uartAvailableForWrite(_uart); |
||||||
|
} |
||||||
|
|
||||||
|
int HardwareSerial::peek(void) |
||||||
|
{ |
||||||
|
if (available()) { |
||||||
|
return uartPeek(_uart); |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
int HardwareSerial::read(void) |
||||||
|
{ |
||||||
|
uint8_t c = 0; |
||||||
|
if (uartReadBytes(_uart, &c, 1, 0) == 1) { |
||||||
|
return c; |
||||||
|
} else { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// read characters into buffer
|
||||||
|
// terminates if size characters have been read, or no further are pending
|
||||||
|
// returns the number of characters placed in the buffer
|
||||||
|
// the buffer is NOT null terminated.
|
||||||
|
size_t HardwareSerial::read(uint8_t *buffer, size_t size) |
||||||
|
{ |
||||||
|
return uartReadBytes(_uart, buffer, size, 0); |
||||||
|
} |
||||||
|
|
||||||
|
// Overrides Stream::readBytes() to be faster using IDF
|
||||||
|
size_t HardwareSerial::readBytes(uint8_t *buffer, size_t length) |
||||||
|
{ |
||||||
|
return uartReadBytes(_uart, buffer, length, (uint32_t)getTimeout()); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::flush(void) |
||||||
|
{ |
||||||
|
uartFlush(_uart); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::flush(bool txOnly) |
||||||
|
{ |
||||||
|
uartFlushTxOnly(_uart, txOnly); |
||||||
|
} |
||||||
|
|
||||||
|
size_t HardwareSerial::write(uint8_t c) |
||||||
|
{ |
||||||
|
uartWrite(_uart, c); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
size_t HardwareSerial::write(const uint8_t *buffer, size_t size) |
||||||
|
{ |
||||||
|
uartWriteBuf(_uart, buffer, size); |
||||||
|
return size; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t HardwareSerial::baudRate() |
||||||
|
{ |
||||||
|
return uartGetBaudRate(_uart); |
||||||
|
} |
||||||
|
HardwareSerial::operator bool() const |
||||||
|
{ |
||||||
|
return uartIsDriverInstalled(_uart); |
||||||
|
} |
||||||
|
|
||||||
|
void HardwareSerial::setRxInvert(bool invert) |
||||||
|
{ |
||||||
|
uartSetRxInvert(_uart, invert); |
||||||
|
} |
||||||
|
|
||||||
|
// negative Pin value will keep it unmodified
|
||||||
|
// can be called after or before begin()
|
||||||
|
bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) |
||||||
|
{ |
||||||
|
// uartSetPins() checks if pins are valid and, if necessary, detaches the previous ones
|
||||||
|
return uartSetPins(_uart_nr, rxPin, txPin, ctsPin, rtsPin); |
||||||
|
} |
||||||
|
|
||||||
|
// Enables or disables Hardware Flow Control using RTS and/or CTS pins
|
||||||
|
// must use setAllPins() in order to set RTS/CTS pins
|
||||||
|
// SerialHwFlowCtrl = UART_HW_FLOWCTRL_DISABLE, UART_HW_FLOWCTRL_RTS,
|
||||||
|
// UART_HW_FLOWCTRL_CTS, UART_HW_FLOWCTRL_CTS_RTS
|
||||||
|
bool HardwareSerial::setHwFlowCtrlMode(SerialHwFlowCtrl mode, uint8_t threshold) |
||||||
|
{ |
||||||
|
return uartSetHwFlowCtrlMode(_uart, mode, threshold); |
||||||
|
} |
||||||
|
|
||||||
|
// Sets the uart mode in the esp32 uart for use with RS485 modes
|
||||||
|
// HwFlowCtrl must be disabled and RTS pin set
|
||||||
|
// SerialMode = UART_MODE_UART, UART_MODE_RS485_HALF_DUPLEX, UART_MODE_IRDA,
|
||||||
|
// or testing mode: UART_MODE_RS485_COLLISION_DETECT, UART_MODE_RS485_APP_CTRL
|
||||||
|
bool HardwareSerial::setMode(SerialMode mode) |
||||||
|
{ |
||||||
|
return uartSetMode(_uart, mode); |
||||||
|
} |
||||||
|
|
||||||
|
size_t HardwareSerial::setRxBufferSize(size_t new_size) { |
||||||
|
|
||||||
|
if (_uart) { |
||||||
|
log_e("RX Buffer can't be resized when Serial is already running.\n"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (new_size <= SOC_UART_FIFO_LEN) { |
||||||
|
log_e("RX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
_rxBufferSize = new_size; |
||||||
|
return _rxBufferSize; |
||||||
|
} |
||||||
|
|
||||||
|
size_t HardwareSerial::setTxBufferSize(size_t new_size) { |
||||||
|
|
||||||
|
if (_uart) { |
||||||
|
log_e("TX Buffer can't be resized when Serial is already running.\n"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (new_size <= SOC_UART_FIFO_LEN) { |
||||||
|
log_e("TX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
_txBufferSize = new_size; |
||||||
|
return _txBufferSize; |
||||||
|
} |
@ -0,0 +1,260 @@ |
|||||||
|
/*
|
||||||
|
HardwareSerial.h - Hardware serial library for Wiring |
||||||
|
Copyright (c) 2006 Nicholas Zambetti. All right reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
|
||||||
|
Modified 28 September 2010 by Mark Sproul |
||||||
|
Modified 14 August 2012 by Alarus |
||||||
|
Modified 3 December 2013 by Matthijs Kooijman |
||||||
|
Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support) |
||||||
|
Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266) |
||||||
|
Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266) |
||||||
|
Modified 13 October 2018 by Jeroen Döll (add baudrate detection) |
||||||
|
Baudrate detection example usage (detection on Serial1): |
||||||
|
void setup() { |
||||||
|
Serial.begin(115200); |
||||||
|
delay(100); |
||||||
|
Serial.println(); |
||||||
|
|
||||||
|
Serial1.begin(0, SERIAL_8N1, -1, -1, true, 11000UL); // Passing 0 for baudrate to detect it, the last parameter is a timeout in ms
|
||||||
|
|
||||||
|
unsigned long detectedBaudRate = Serial1.baudRate(); |
||||||
|
if(detectedBaudRate) { |
||||||
|
Serial.printf("Detected baudrate is %lu\n", detectedBaudRate); |
||||||
|
} else { |
||||||
|
Serial.println("No baudrate detected, Serial1 will not work!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Pay attention: the baudrate returned by baudRate() may be rounded, eg 115200 returns 115201 |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef HardwareSerial_h |
||||||
|
#define HardwareSerial_h |
||||||
|
|
||||||
|
#include <inttypes.h> |
||||||
|
#include <functional> |
||||||
|
#include "Stream.h" |
||||||
|
#include "esp32-hal.h" |
||||||
|
#include "soc/soc_caps.h" |
||||||
|
#include "HWCDC.h" |
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/task.h" |
||||||
|
#include "freertos/semphr.h" |
||||||
|
|
||||||
|
enum SerialConfig { |
||||||
|
SERIAL_5N1 = 0x8000010, |
||||||
|
SERIAL_6N1 = 0x8000014, |
||||||
|
SERIAL_7N1 = 0x8000018, |
||||||
|
SERIAL_8N1 = 0x800001c, |
||||||
|
SERIAL_5N2 = 0x8000030, |
||||||
|
SERIAL_6N2 = 0x8000034, |
||||||
|
SERIAL_7N2 = 0x8000038, |
||||||
|
SERIAL_8N2 = 0x800003c, |
||||||
|
SERIAL_5E1 = 0x8000012, |
||||||
|
SERIAL_6E1 = 0x8000016, |
||||||
|
SERIAL_7E1 = 0x800001a, |
||||||
|
SERIAL_8E1 = 0x800001e, |
||||||
|
SERIAL_5E2 = 0x8000032, |
||||||
|
SERIAL_6E2 = 0x8000036, |
||||||
|
SERIAL_7E2 = 0x800003a, |
||||||
|
SERIAL_8E2 = 0x800003e, |
||||||
|
SERIAL_5O1 = 0x8000013, |
||||||
|
SERIAL_6O1 = 0x8000017, |
||||||
|
SERIAL_7O1 = 0x800001b, |
||||||
|
SERIAL_8O1 = 0x800001f, |
||||||
|
SERIAL_5O2 = 0x8000033, |
||||||
|
SERIAL_6O2 = 0x8000037, |
||||||
|
SERIAL_7O2 = 0x800003b, |
||||||
|
SERIAL_8O2 = 0x800003f |
||||||
|
}; |
||||||
|
|
||||||
|
typedef uart_mode_t SerialMode; |
||||||
|
typedef uart_hw_flowcontrol_t SerialHwFlowCtrl; |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
UART_NO_ERROR, |
||||||
|
UART_BREAK_ERROR, |
||||||
|
UART_BUFFER_FULL_ERROR, |
||||||
|
UART_FIFO_OVF_ERROR, |
||||||
|
UART_FRAME_ERROR, |
||||||
|
UART_PARITY_ERROR |
||||||
|
} hardwareSerial_error_t; |
||||||
|
|
||||||
|
typedef std::function<void(void)> OnReceiveCb; |
||||||
|
typedef std::function<void(hardwareSerial_error_t)> OnReceiveErrorCb; |
||||||
|
|
||||||
|
class HardwareSerial: public Stream |
||||||
|
{ |
||||||
|
public: |
||||||
|
HardwareSerial(uint8_t uart_nr); |
||||||
|
~HardwareSerial(); |
||||||
|
|
||||||
|
// setRxTimeout sets the timeout after which onReceive callback will be called (after receiving data, it waits for this time of UART rx inactivity to call the callback fnc)
|
||||||
|
// param symbols_timeout defines a timeout threshold in uart symbol periods. Setting 0 symbol timeout disables the callback call by timeout.
|
||||||
|
// Maximum timeout setting is calculacted automatically by IDF. If set above the maximum, it is ignored and an error is printed on Serial0 (check console).
|
||||||
|
// Examples: Maximum for 11 bits symbol is 92 (SERIAL_8N2, SERIAL_8E1, SERIAL_8O1, etc), Maximum for 10 bits symbol is 101 (SERIAL_8N1).
|
||||||
|
// For example symbols_timeout=1 defines a timeout equal to transmission time of one symbol (~11 bit) on current baudrate.
|
||||||
|
// For a baudrate of 9600, SERIAL_8N1 (10 bit symbol) and symbols_timeout = 3, the timeout would be 3 / (9600 / 10) = 3.125 ms
|
||||||
|
bool setRxTimeout(uint8_t symbols_timeout); |
||||||
|
|
||||||
|
// setRxFIFOFull(uint8_t fifoBytes) will set the number of bytes that will trigger UART_INTR_RXFIFO_FULL interrupt and fill up RxRingBuffer
|
||||||
|
// This affects some functions such as Serial::available() and Serial.read() because, in a UART flow of receiving data, Serial internal
|
||||||
|
// RxRingBuffer will be filled only after these number of bytes arrive or a RX Timeout happens.
|
||||||
|
// This parameter can be set to 1 in order to receive byte by byte, but it will also consume more CPU time as the ISR will be activates often.
|
||||||
|
bool setRxFIFOFull(uint8_t fifoBytes); |
||||||
|
|
||||||
|
// onReceive will setup a callback that will be called whenever an UART interruption occurs (UART_INTR_RXFIFO_FULL or UART_INTR_RXFIFO_TOUT)
|
||||||
|
// UART_INTR_RXFIFO_FULL interrupt triggers at UART_FULL_THRESH_DEFAULT bytes received (defined as 120 bytes by default in IDF)
|
||||||
|
// UART_INTR_RXFIFO_TOUT interrupt triggers at UART_TOUT_THRESH_DEFAULT symbols passed without any reception (defined as 10 symbos by default in IDF)
|
||||||
|
// onlyOnTimeout parameter will define how onReceive will behave:
|
||||||
|
// Default: true -- The callback will only be called when RX Timeout happens.
|
||||||
|
// Whole stream of bytes will be ready for being read on the callback function at once.
|
||||||
|
// This option may lead to Rx Overflow depending on the Rx Buffer Size and number of bytes received in the streaming
|
||||||
|
// false -- The callback will be called when FIFO reaches 120 bytes and also on RX Timeout.
|
||||||
|
// The stream of incommig bytes will be "split" into blocks of 120 bytes on each callback.
|
||||||
|
// This option avoid any sort of Rx Overflow, but leaves the UART packet reassembling work to the Application.
|
||||||
|
void onReceive(OnReceiveCb function, bool onlyOnTimeout = false); |
||||||
|
|
||||||
|
// onReceive will be called on error events (see hardwareSerial_error_t)
|
||||||
|
void onReceiveError(OnReceiveErrorCb function); |
||||||
|
|
||||||
|
// eventQueueReset clears all events in the queue (the events that trigger onReceive and onReceiveError) - maybe usefull in some use cases
|
||||||
|
void eventQueueReset(); |
||||||
|
|
||||||
|
// When pins are changed, it will detach the previous ones
|
||||||
|
// if pin is negative, it won't be set/changed and will be kept as is
|
||||||
|
// timeout_ms is used in baudrate detection (ESP32, ESP32S2 only)
|
||||||
|
// invert will invert RX/TX polarity
|
||||||
|
// rxfifo_full_thrhd if the UART Flow Control Threshold in the UART FIFO (max 127)
|
||||||
|
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112); |
||||||
|
void end(bool fullyTerminate = true); |
||||||
|
void updateBaudRate(unsigned long baud); |
||||||
|
int available(void); |
||||||
|
int availableForWrite(void); |
||||||
|
int peek(void); |
||||||
|
int read(void); |
||||||
|
size_t read(uint8_t *buffer, size_t size); |
||||||
|
inline size_t read(char * buffer, size_t size) |
||||||
|
{ |
||||||
|
return read((uint8_t*) buffer, size); |
||||||
|
} |
||||||
|
// Overrides Stream::readBytes() to be faster using IDF
|
||||||
|
size_t readBytes(uint8_t *buffer, size_t length); |
||||||
|
size_t readBytes(char *buffer, size_t length) |
||||||
|
{ |
||||||
|
return readBytes((uint8_t *) buffer, length); |
||||||
|
}
|
||||||
|
void flush(void); |
||||||
|
void flush( bool txOnly); |
||||||
|
size_t write(uint8_t); |
||||||
|
size_t write(const uint8_t *buffer, size_t size); |
||||||
|
inline size_t write(const char * buffer, size_t size) |
||||||
|
{ |
||||||
|
return write((uint8_t*) buffer, size); |
||||||
|
} |
||||||
|
inline size_t write(const char * s) |
||||||
|
{ |
||||||
|
return write((uint8_t*) s, strlen(s)); |
||||||
|
} |
||||||
|
inline size_t write(unsigned long n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
inline size_t write(long n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
inline size_t write(unsigned int n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
inline size_t write(int n) |
||||||
|
{ |
||||||
|
return write((uint8_t) n); |
||||||
|
} |
||||||
|
uint32_t baudRate(); |
||||||
|
operator bool() const; |
||||||
|
|
||||||
|
void setDebugOutput(bool); |
||||||
|
|
||||||
|
void setRxInvert(bool); |
||||||
|
|
||||||
|
// Negative Pin Number will keep it unmodified, thus this function can set individual pins
|
||||||
|
// setPins() can be called after or before begin()
|
||||||
|
// When pins are changed, it will detach the previous ones
|
||||||
|
bool setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin = -1, int8_t rtsPin = -1); |
||||||
|
// Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before)
|
||||||
|
// UART_HW_FLOWCTRL_DISABLE = 0x0 disable hardware flow control
|
||||||
|
// UART_HW_FLOWCTRL_RTS = 0x1 enable RX hardware flow control (rts)
|
||||||
|
// UART_HW_FLOWCTRL_CTS = 0x2 enable TX hardware flow control (cts)
|
||||||
|
// UART_HW_FLOWCTRL_CTS_RTS = 0x3 enable hardware flow control
|
||||||
|
bool setHwFlowCtrlMode(SerialHwFlowCtrl mode = UART_HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); // 64 is half FIFO Length
|
||||||
|
// Used to set RS485 modes such as UART_MODE_RS485_HALF_DUPLEX for Auto RTS function on ESP32
|
||||||
|
// UART_MODE_UART = 0x00 mode: regular UART mode
|
||||||
|
// UART_MODE_RS485_HALF_DUPLEX = 0x01 mode: half duplex RS485 UART mode control by RTS pin
|
||||||
|
// UART_MODE_IRDA = 0x02 mode: IRDA UART mode
|
||||||
|
// UART_MODE_RS485_COLLISION_DETECT = 0x03 mode: RS485 collision detection UART mode (used for test purposes)
|
||||||
|
// UART_MODE_RS485_APP_CTRL = 0x04 mode: application control RS485 UART mode (used for test purposes)
|
||||||
|
bool setMode(SerialMode mode); |
||||||
|
size_t setRxBufferSize(size_t new_size); |
||||||
|
size_t setTxBufferSize(size_t new_size); |
||||||
|
|
||||||
|
protected: |
||||||
|
uint8_t _uart_nr; |
||||||
|
uart_t* _uart; |
||||||
|
size_t _rxBufferSize; |
||||||
|
size_t _txBufferSize; |
||||||
|
OnReceiveCb _onReceiveCB; |
||||||
|
OnReceiveErrorCb _onReceiveErrorCB; |
||||||
|
// _onReceive and _rxTimeout have be consistent when timeout is disabled
|
||||||
|
bool _onReceiveTimeout; |
||||||
|
uint8_t _rxTimeout, _rxFIFOFull; |
||||||
|
TaskHandle_t _eventTask; |
||||||
|
#if !CONFIG_DISABLE_HAL_LOCKS |
||||||
|
SemaphoreHandle_t _lock; |
||||||
|
#endif |
||||||
|
|
||||||
|
void _createEventTask(void *args); |
||||||
|
void _destroyEventTask(void); |
||||||
|
static void _uartEventTask(void *args); |
||||||
|
}; |
||||||
|
|
||||||
|
extern void serialEventRun(void) __attribute__((weak)); |
||||||
|
|
||||||
|
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) |
||||||
|
#ifndef ARDUINO_USB_CDC_ON_BOOT |
||||||
|
#define ARDUINO_USB_CDC_ON_BOOT 0 |
||||||
|
#endif |
||||||
|
#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
|
||||||
|
#if !ARDUINO_USB_MODE |
||||||
|
#include "USB.h" |
||||||
|
#include "USBCDC.h" |
||||||
|
#endif |
||||||
|
extern HardwareSerial Serial0; |
||||||
|
#else |
||||||
|
extern HardwareSerial Serial; |
||||||
|
#endif |
||||||
|
#if SOC_UART_NUM > 1 |
||||||
|
extern HardwareSerial Serial1; |
||||||
|
#endif |
||||||
|
#if SOC_UART_NUM > 2 |
||||||
|
extern HardwareSerial Serial2; |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif // HardwareSerial_h
|
@ -0,0 +1,125 @@ |
|||||||
|
/*
|
||||||
|
IPAddress.cpp - Base class that provides IPAddress |
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <IPAddress.h> |
||||||
|
#include <Print.h> |
||||||
|
|
||||||
|
IPAddress::IPAddress() |
||||||
|
{ |
||||||
|
_address.dword = 0; |
||||||
|
} |
||||||
|
|
||||||
|
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) |
||||||
|
{ |
||||||
|
_address.bytes[0] = first_octet; |
||||||
|
_address.bytes[1] = second_octet; |
||||||
|
_address.bytes[2] = third_octet; |
||||||
|
_address.bytes[3] = fourth_octet; |
||||||
|
} |
||||||
|
|
||||||
|
IPAddress::IPAddress(uint32_t address) |
||||||
|
{ |
||||||
|
_address.dword = address; |
||||||
|
} |
||||||
|
|
||||||
|
IPAddress::IPAddress(const uint8_t *address) |
||||||
|
{ |
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes)); |
||||||
|
} |
||||||
|
|
||||||
|
IPAddress& IPAddress::operator=(const uint8_t *address) |
||||||
|
{ |
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes)); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
IPAddress& IPAddress::operator=(uint32_t address) |
||||||
|
{ |
||||||
|
_address.dword = address; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
bool IPAddress::operator==(const uint8_t* addr) const |
||||||
|
{ |
||||||
|
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
size_t IPAddress::printTo(Print& p) const |
||||||
|
{ |
||||||
|
size_t n = 0; |
||||||
|
for(int i = 0; i < 3; i++) { |
||||||
|
n += p.print(_address.bytes[i], DEC); |
||||||
|
n += p.print('.'); |
||||||
|
} |
||||||
|
n += p.print(_address.bytes[3], DEC); |
||||||
|
return n; |
||||||
|
} |
||||||
|
|
||||||
|
String IPAddress::toString() const |
||||||
|
{ |
||||||
|
char szRet[16]; |
||||||
|
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]); |
||||||
|
return String(szRet); |
||||||
|
} |
||||||
|
|
||||||
|
bool IPAddress::fromString(const char *address) |
||||||
|
{ |
||||||
|
// TODO: add support for "a", "a.b", "a.b.c" formats
|
||||||
|
|
||||||
|
uint16_t acc = 0; // Accumulator
|
||||||
|
uint8_t dots = 0; |
||||||
|
|
||||||
|
while (*address) |
||||||
|
{ |
||||||
|
char c = *address++; |
||||||
|
if (c >= '0' && c <= '9') |
||||||
|
{ |
||||||
|
acc = acc * 10 + (c - '0'); |
||||||
|
if (acc > 255) { |
||||||
|
// Value out of [0..255] range
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
else if (c == '.') |
||||||
|
{ |
||||||
|
if (dots == 3) { |
||||||
|
// Too much dots (there must be 3 dots)
|
||||||
|
return false; |
||||||
|
} |
||||||
|
_address.bytes[dots++] = acc; |
||||||
|
acc = 0; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// Invalid char
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (dots != 3) { |
||||||
|
// Too few dots (there must be 3 dots)
|
||||||
|
return false; |
||||||
|
} |
||||||
|
_address.bytes[3] = acc; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// declared one time - as external in IPAddress.h
|
||||||
|
IPAddress INADDR_NONE(0, 0, 0, 0); |
@ -0,0 +1,96 @@ |
|||||||
|
/*
|
||||||
|
IPAddress.h - Base class that provides IPAddress |
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef IPAddress_h |
||||||
|
#define IPAddress_h |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <WString.h> |
||||||
|
#include <Printable.h> |
||||||
|
|
||||||
|
// A class to make it easier to handle and pass around IP addresses
|
||||||
|
|
||||||
|
class IPAddress: public Printable |
||||||
|
{ |
||||||
|
private: |
||||||
|
union { |
||||||
|
uint8_t bytes[4]; // IPv4 address
|
||||||
|
uint32_t dword; |
||||||
|
} _address; |
||||||
|
|
||||||
|
// Access the raw byte array containing the address. Because this returns a pointer
|
||||||
|
// to the internal structure rather than a copy of the address this function should only
|
||||||
|
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||||
|
// stored.
|
||||||
|
uint8_t* raw_address() |
||||||
|
{ |
||||||
|
return _address.bytes; |
||||||
|
} |
||||||
|
|
||||||
|
public: |
||||||
|
// Constructors
|
||||||
|
IPAddress(); |
||||||
|
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); |
||||||
|
IPAddress(uint32_t address); |
||||||
|
IPAddress(const uint8_t *address); |
||||||
|
virtual ~IPAddress() {} |
||||||
|
|
||||||
|
bool fromString(const char *address); |
||||||
|
bool fromString(const String &address) { return fromString(address.c_str()); } |
||||||
|
|
||||||
|
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
|
||||||
|
// to a four-byte uint8_t array is expected
|
||||||
|
operator uint32_t() const |
||||||
|
{ |
||||||
|
return _address.dword; |
||||||
|
} |
||||||
|
bool operator==(const IPAddress& addr) const |
||||||
|
{ |
||||||
|
return _address.dword == addr._address.dword; |
||||||
|
} |
||||||
|
bool operator==(const uint8_t* addr) const; |
||||||
|
|
||||||
|
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||||
|
uint8_t operator[](int index) const |
||||||
|
{ |
||||||
|
return _address.bytes[index]; |
||||||
|
} |
||||||
|
uint8_t& operator[](int index) |
||||||
|
{ |
||||||
|
return _address.bytes[index]; |
||||||
|
} |
||||||
|
|
||||||
|
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
|
||||||
|
IPAddress& operator=(const uint8_t *address); |
||||||
|
IPAddress& operator=(uint32_t address); |
||||||
|
|
||||||
|
virtual size_t printTo(Print& p) const; |
||||||
|
String toString() const; |
||||||
|
|
||||||
|
friend class EthernetClass; |
||||||
|
friend class UDP; |
||||||
|
friend class Client; |
||||||
|
friend class Server; |
||||||
|
friend class DhcpClass; |
||||||
|
friend class DNSClient; |
||||||
|
}; |
||||||
|
|
||||||
|
// changed to extern because const declaration creates copies in BSS of INADDR_NONE for each CPP unit that includes it
|
||||||
|
extern IPAddress INADDR_NONE; |
||||||
|
#endif |
@ -0,0 +1,90 @@ |
|||||||
|
/*
|
||||||
|
IPv6Address.cpp - Base class that provides IPv6Address |
||||||
|
Copyright (c) 2011 Adrian McEwen. All right reserved. |
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or |
||||||
|
modify it under the terms of the GNU Lesser General Public |
||||||
|
License as published by the Free Software Foundation; either |
||||||
|
version 2.1 of the License, or (at your option) any later version. |
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||||
|
Lesser General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public |
||||||
|
License along with this library; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <IPv6Address.h> |
||||||
|
#include <Print.h> |
||||||
|
|
||||||
|
IPv6Address::IPv6Address() |
||||||
|
{ |
||||||
|
memset(_address.bytes, 0, sizeof(_address.bytes)); |
||||||
|
} |
||||||
|
|
||||||
|
IPv6Address::IPv6Address(const uint8_t *address) |
||||||
|
{ |
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes)); |
||||||
|
} |
||||||
|
|
||||||
|
IPv6Address::IPv6Address(const uint32_t *address) |
||||||
|
{ |
||||||
|
memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes)); |
||||||
|
} |
||||||
|
|
||||||
|
IPv6Address& IPv6Address::operator=(const uint8_t *address) |
||||||
|
{ |
||||||
|
memcpy(_address.bytes, address, sizeof(_address.bytes)); |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
bool IPv6Address::operator==(const uint8_t* addr) const |
||||||
|
{ |
||||||
|
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; |
||||||
|
} |
||||||
|
|
||||||
|
size_t IPv6Address::printTo(Print& p) const |
||||||
|
{ |
||||||
|
size_t n = 0; |
||||||
|
for(int i = 0; i < 16; i+=2) { |
||||||
|
if(i){ |
||||||
|
n += p.print(':'); |
||||||
|
} |
||||||
|
n += p.printf("%02x", _address.bytes[i]); |
||||||
|
n += p.printf("%02x", _address.bytes[i+1]); |
||||||
|
|
||||||
|
} |
||||||
|
return n; |
||||||
|
} |
||||||
|
|
||||||
|
String IPv6Address::toString() const |
||||||
|
{ |
||||||
|
char szRet[40]; |
||||||
|
sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", |
||||||
|
_address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3], |
||||||
|
_address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7], |
||||||
|
_address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11], |
||||||
|
_address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]); |
||||||
|
return String(szRet); |
||||||
|
} |
||||||
|
|
||||||
|
bool IPv6Address::fromString(const char *address) |
||||||
|
{ |
||||||
|
//format 0011:2233:4455:6677:8899:aabb:ccdd:eeff
|
||||||
|
if(strlen(address) != 39){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
char * pos = (char *)address; |
||||||
|
size_t i = 0; |
||||||
|
for(i = 0; i < 16; i+=2) { |
||||||
|
if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
pos += 5; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue