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