Compare commits

..

113 Commits

Author SHA1 Message Date
77810471ce Move Example to proper folder 2018-12-16 19:24:02 +01:00
be081ac026 Add FreeRTOS multi-thread example (#2067)
* Add FreeRTOS exsample

* Update Code

* Change stack size

* Fix LED_BUILTIN not defined

Fix LED_BUILTIN not defined
2018-12-16 18:09:57 +01:00
4d3f6caa0d Add quotes around macro value (Issue #2193) (#2197)
* Replaced ARDUINO_VARIANT with const char

* Fixed missing return value

* Added quotes around defined value in macro (Issue #2193)
2018-12-16 17:10:56 +01:00
2db811f7f3 Update build tools (#2200) 2018-12-16 17:09:44 +01:00
39836f12df correct bounds checking in Print::printf to avoid corner case of len=64 (#2204) 2018-12-16 17:09:26 +01:00
25fd2d0f3b OTA success reporting fix (#2202) 2018-12-16 13:00:15 +01:00
278fa0d87a Fix read(), peek() and available() in WiFiClientSecure
closes: https://github.com/espressif/arduino-esp32/pull/2151
2018-12-15 18:14:38 +01:00
b37f4069e4 Increase _network_event_task priority (#2184)
Fixes https://github.com/espressif/arduino-esp32/issues/1595
2018-12-15 17:39:51 +01:00
e602145c2b fix #2105 (#2192) 2018-12-15 17:39:20 +01:00
6f6ee98188 Update ESP-IDF da2116f + esp32-camera f8f26ab + BLE b232e7f (#2194)
* ESP-IDF da2116f + esp32-camera f8f26ab +BLE b232e7f

* Fix fail compilation due to missing cpp guard
2018-12-15 17:38:34 +01:00
1289f4be4b Add MD5 computation to Esp and add request header with MD5 hash of the running sketch to HTTPUpdate (#2176) 2018-12-11 02:06:58 +01:00
70f000da71 Fix a compilation error if Bluetooth not enabled (#2172) 2018-12-10 10:11:37 +01:00
884e417a49 Fix HTTPUpdate flash size check and add SPIFFS size check (#2161)
* Fix error in PR #2048: if ::available() is called before ::connect() _rxBuffer is not initialised

* Fixed flash size check and added SPIFFS size check

* Rewriting ESP.getFreeSketchSpace(), moving code from HTTPUpdate.cpp
2018-12-06 20:39:52 +01:00
bb7dea1566 Fix missing SS define for D32 Pro
Fixes: https://github.com/espressif/arduino-esp32/issues/2153
2018-12-06 19:13:30 +01:00
bff9f0b6b1 Fix error in PR #2048: if ::available() is called before ::connect() _rxBuffer is not initialised (#2155) 2018-12-06 19:01:06 +01:00
72803703fd Updated table of tested locations of Eduroam network (#2157) 2018-12-06 18:56:48 +01:00
0596a2ac86 Abort update if http.begin() returns false. Fix a typo in httpUpdate.ino (#2156) 2018-12-06 18:50:53 +01:00
fe1fdd2790 #2147 available() shouldn't return 0 after disconnect if there is still data in the buffer. Otherwise, how would we know it was there? (#2148) 2018-12-04 18:23:34 +01:00
af7e489f01 WiFiClientSecure: add support for PSK (pre-shared key) ciphers (#2133)
* WiFiClientSecure: add support for PSK (pre-shared key) ciphers

* add example for WiFiClientSecure PSK

* WiFiClientSecure: added README
2018-12-03 16:17:55 +01:00
Luc
5cfff190e9 removing log (#2140)
* Use right function for BSSID

* removing log
2018-12-03 16:17:16 +01:00
7a332864ab I2C ReSTART returns Success (#2141)
* Don't Return I2C_ERROR_CONTINUE on ReSTART

ReSTART operations on the ESP32 have to be handled differently than on AVR chips, so ReSTART operations(`Wire.endTransmission(false), Wire.requestFrom(id,size,false);` are queued until a STOP is send (`Wire.endTransmission(TRUE), Wire.endTransmission(), Wire.requestFrom(id,size), Wire.requestFrom(id,size,TRUE)). To indicate the queuing I had used `I2C_ERROR_CONTINUE`, this caused compatibility issues with the existing Arduino I2C Code base. So, back to Lying to the public(for their own good of course) about success!  This update just returns `I2C_ERROR_OK` on ReSTART commands.

* add comments

add comments

* Change Return error for ReSTART operation to I2C_ERROR_OK

This change restores compatibility with pre-existing Arduino Libraries.  The ReSTART queuing operations are hidden behind the scenes.  Wire.endTransmission(id,len,FALSE); will know return I2C_ERROR_OK instead of I2C_ERROR_CONTINUE, Wire.lastError() will return the true condition of I2C_ERROR_CONTINUE.
2018-12-03 16:16:43 +01:00
8aa6e2e143 bugfix lopy/pins_arduino.h (#2135)
Correction of Pin A14 that was double defined.
2018-12-02 18:15:25 +01:00
b5f317010c Fixed Arduino SPI/Ethernet compile issue as described in issue #1623 (#2136)
* #1623, implementing suggested change

Splitted suggested fix issue #1623 in a header and source part. Dit not completely dive into the code.
Giving data twice as parameter feels wrong, but it compiles, and i can succesfully use the W5500 with SPI with this fix.
Doesn't compile without.

* #1623, implementing suggested change SPI.h/cpp

Splitted suggested fix issue #1623 in a header and source part. Dit not completely dive into the code.
Giving data twice as parameter feels wrong, but it compiles, and i can succesfully use the W5500 with SPI with this fix.
Doesn't compile without.
2018-12-02 18:15:01 +01:00
Luc
f644d9d157 Use right function for BSSID (#2132) 2018-12-01 10:20:13 +01:00
a15b7e9088 Update IDF to afe4c76 and BLE lib (#2130)
* Update BLE

* Update IDF to afe4c76

* Update CMakeLists.txt

* Update BLE to fix compilation issues

* Update BLE
2018-11-30 17:21:06 +01:00
Luc
ce340faf94 Clean warnings when all warning enabled (#2112)
* Clean warnings when all warning enabled

Not used variables / functions due to debug log

Dual define with different values :
cores\esp32/binary.h
#define B110 6
#define B1000000 64

tools/sdk/include/newlib/sys/termios.h
#define B110        3
#define B1000000   23

Local variable returned in WiFiclient Secure

* change due to deprecated function

* Update with proper variable and label

* Update esp32-hal-i2c.c

* Apply changes requested

* Fix warnings due to #define conflict thanks @atanisoft
2018-11-29 11:34:55 +01:00
b69b04cb2b Update build-release.sh
Reenable git log in release builds
2018-11-28 13:18:54 +01:00
0cbba8a71d * remove git clone --depth parameter (#2124) 2018-11-28 13:15:49 +01:00
9e1f8cc457 Update pins_arduino.h (#2120)
Bugfix: pin A14 was double defined; pin A15 was missing.
2018-11-28 11:47:11 +01:00
cfe7e01158 Remove F() macro's (#2121) 2018-11-28 11:45:25 +01:00
fcd734a13c Some fixes found by gcc 8 2018-11-28 00:35:43 +01:00
aa030e044c Temporary stop git log to build prerelease 2018-11-28 00:05:36 +01:00
95d417cd93 Update build-release.sh
force correct dir
2018-11-27 23:59:12 +01:00
df4eeb3005 Update build-release.sh
fix debug
2018-11-27 23:53:29 +01:00
a360064768 Update build-release.sh
Add more debug
2018-11-27 23:50:30 +01:00
bfde8daf75 Update build-release.sh
print out the command
2018-11-27 23:43:34 +01:00
e583a0e879 Travis: Set proper dir before checking git 2018-11-27 23:35:23 +01:00
7e9afe8c5e Add response headers with sketch and flash sizes, and a SHA256 (#2116) 2018-11-27 21:27:38 +01:00
dcb007a485 do not skip main.cpp when checking CMakeLists 2018-11-27 18:50:09 +01:00
bec6f87235 Update CMakeLists.txt 2018-11-26 23:54:13 +01:00
4ae64c541e Fixed missing return value (#2090) 2018-11-26 23:26:08 +01:00
46257c03b3 handshake in ssl_client.cpp (#2044)
* issue #2041

* handshake timeout

* seconds to milliseconds
2018-11-26 23:25:08 +01:00
0640964879 Solve issue #2092 by initializing * _client to nullptr (#2097) 2018-11-26 23:23:19 +01:00
e609c78f20 Initialize detectedBaudRate to prevent compilation errors (#2101) 2018-11-26 23:22:37 +01:00
04963009ee Update IDF to a0468b2 (#2108)
* Update IDF to a0468b2

* add missing ld file

* Fix PIO builds and change coex policy
2018-11-26 23:22:11 +01:00
Luc
c3ec91f968 Allow to add custom callback in BT Serial (#2081)
This allow to catch the events when connected /disconnected,, etc...
This also allow to get parameters of events like the remote address of connected devices, etc...
Small change but lot of flexibility
2018-11-19 19:30:28 +01:00
a30005949a adding D-duino-32 board/pins (#2030)
* adding d-duino-32 board/pins, fixes #930

* remove OLED_RST
2018-11-19 19:29:57 +01:00
Luc
acefd4bf2e Fix Partition Calculation for min SPIFFS (#2072)
I have kept the original APPS size on purpose as 40KB SPIFFS is useless IMHO
2018-11-19 17:10:10 +01:00
c700e5694f Some fixes to nghttp2 to provide basic functionality. Added the missing main header. Removed the asio headers which require Boost libraries. Moved http_parser into the expected location. (#2068) 2018-11-19 17:09:38 +01:00
0d564d7b1d Limit the number of simultaneously connected devices to BluetoothSerial to only 1 (#2061) 2018-11-19 17:08:15 +01:00
44ca2ee976 Fix uart TX flushing (#2029)
wait for FSM to return idle
2018-11-19 17:04:05 +01:00
af79e18ecb Added ESP:: functions for sketch size (#2028)
* Added ESP:: functions for sketch size

* Fixed free space name to match ESP8266
2018-11-19 17:03:36 +01:00
273196d7e6 HTTPClientEnterprise example (#2023) 2018-11-19 17:02:47 +01:00
5d2460c74a Working example for HTTPS over Eduroam network - WifiClient secure library (#2022)
* Working example for HTTPS over Eduroam network

* Update WiFiClientSecureEnterprise.ino
2018-11-19 17:02:24 +01:00
259ff80d60 use libbase64 macro to calculate base64 length (#2007) 2018-11-19 17:01:38 +01:00
Bob
3902aa4019 Adding path arguments to WebServer (#1994) 2018-11-19 17:00:52 +01:00
f9d1b24c01 Update (#1992)
Added Olimex board ESP32-PoE. pins_arduino header file based on the ESP32-EVB with changed SS pin and removed BOARD_HAS_1BIT_SDMMC macro.
2018-11-19 17:00:20 +01:00
c7fa251d78 updated WifiClientEnterprise example (#1988)
* updated WifiClientEnterprise example

* Update WiFiClientEnterprise.ino
2018-11-19 16:59:56 +01:00
7b811f9b3a leave possible endless loop (#1986) 2018-11-19 16:59:14 +01:00
1fdc660641 [Feature] Boards added: Pycom LoPy & LoPy4 (#1984)
* Create pins_arduino.h

* Update boards.txt

Pycom LoPy + LoPy4 boards added

* Create pins_arduino.h

* Update pins_arduino.h

bugfix antenna switch GPIO port 16 -> 21

* Update pins_arduino.h

* Update pins_arduino.h

* Update pins_arduino.h

* Update pins_arduino.h

* Update pins_arduino.h

* Update boards.txt
2018-11-19 16:58:45 +01:00
a53c9de09d Updated table of tested locations - Enterprise wifi networks - WifiClientEnterprise (#1980)
* Updated table of tested locations

* updated
2018-11-19 16:58:07 +01:00
b58a3509b8 Feature/http update (#1979)
* Added HTTPUpdate class for downloading sketches from a server

* Added HTTPUpdate class for downloading sketches from a server

* Added HTTPUpdate to CMakeLists.txt

* Change ESP8266 class references to ESP32 for httpUpdate.ino example

* Change ESP8266 class references to ESP32 for httpUpdate.ino example. setLedPin() commented out because not all boards support LED_BUITLIN

* Added check to handle mixup of old and present api properly

* Correct HTTPClient::setTimeout() to convert milliseconds to seconds. Correct WiFiClient::setTimeout() to call Stream::setTimeout() with seconds converted back to milliseconds. Remove inproper checks for _insecure.

* Added small comment because it looked like the Travis build did not finish
2018-11-19 16:57:38 +01:00
01d22c8807 Feature/http client (#1973)
* Pass client parameter into two new begin() functions. Set other begin() functions deprecated. Updated library version to 1.2

* Added working HTTPS example on a public url with a certificate

* Remove two unnecessary tests in ::disconnect()

* Add a scoping block to BasicHttpsClient.ino to assure HTTPClient is destroyed before WiFiClientSecure

* Added check to handle mixup of old and present api properly

* Correct HTTPClient::setTimeout() to convert milliseconds to seconds. Correct WiFiClient::setTimeout() to call Stream::setTimeout() with seconds converted back to milliseconds. Remove inproper checks for _insecure.

* Added small comment because it looked like the Travis build did not finish
2018-11-19 16:57:23 +01:00
b70737d276 Fix partition tables to reflect that M5Stack Fire has 16MB flash and 4MB PSRAM (#1969) 2018-11-19 16:53:13 +01:00
233d31bed2 Added baudrate detection to esp32-hal-uart and HardwareSerial (#1961)
* Added baudrate detection to esp32-hal-uart and HardwareSerial

* Solved compiler warning for uartResizeRxBuffer()

* Add unit to header variable name (timeout_ms)

* Reverting accidentally changed files to master

* Add small delay after baudrate detection
2018-11-19 16:51:55 +01:00
65c861ad4c Added loadCert methods to WiFiClientSecure (#1959) 2018-11-19 16:50:08 +01:00
f6a71da378 Update pins_arduino.h (#1949)
Pin definitions T8/T9 & DAC1/DAC2 Back To Front (Issue #1831)
2018-11-19 16:47:07 +01:00
b8c9819e7f Update pins_arduino.h (#1948)
Pin definitions T8/T9 & DAC1/DAC2 Back To Front
2018-11-19 16:46:53 +01:00
1bc1e8c602 Update WString.cpp (#1936) 2018-11-19 16:45:09 +01:00
2132d9f809 Update WString.h (#1935) 2018-11-19 16:44:53 +01:00
14ff311479 make WiFi.softAP() more robust (#1925)
* make WiFi.softAP() more robust

* WiFi.softAP() revert fallback to WIFI_AUTH_OPEN
2018-11-19 16:43:59 +01:00
a3ed511884 WiFi: improve WiFiEvent list in WiFiClientEvents example (#1917)
fixes #1875
2018-11-19 16:42:33 +01:00
9e2888392e Added a message for all event types. (#1916) 2018-11-19 16:41:56 +01:00
a43682596f Deep-sleep example-sketches reported wrong wakeup-reason (#1911)
Incorrect values used, use the proper defines from header-files instead.
2018-11-19 16:41:07 +01:00
825b6ea79e Fixed previous error (#1908) 2018-11-19 16:40:44 +01:00
deaf339bde Wire endTransmission() fix for issue #1725 (#1888)
* removed uint8_t Wire.endTransmission(uint8_t sendStop)

Having both endTransmission(bool) and endTransmission(uint8_t) creates problems.
There is no need for endTransmission(uint8_t)
endTransmission(1) and endTransmission(0) works with endTransmission(bool).
Removing endTransmission(uint8_t) allows the ESP32 code to be compatible with
all the other Arduino cores by allowing endTransmission(1) and endTranmission(0)
to work as it does on all the other cores.

* Wire library version bump for endTransmission() update
2018-11-19 16:40:14 +01:00
f12df4c719 allow component projects to compile with CONFIG_DISABLE_HAL_LOCKS (#1880) 2018-11-19 16:39:42 +01:00
85032b226c Do not break UDP if pbuf is null 2018-09-26 23:29:51 +02:00
e5ea089a7f Reduce resource requirements, Share Interrupt (#1877)
#1869 exposed a resource exhaustion issue. The current HAL layer for I2C support is designed to use a shared interrupt, But, during debugging to solve the interrupt overloading condition identified in #1588, and the generation of pr #1717, the interrupt allocation parameters were changed.  This change was unnecessary, the code will work successfully with shared interrupts.  So, there is no need to assign a private interrupt for each I2C peripheral.
2018-09-21 08:40:01 +02:00
96822d783f Update IDF to 3.2-3276a13 and esptool.py to 2.5.0 (#1878)
* TX Flow Control and Code cleanup

* Use semaphore instead of delay

TX functionality is done.

* Use single buffer and empty queue on exit

* Fix compile issues because of LwIP code relocation

* Add temporary header to fix Azure not compiling

* Fix AsyncUDP early init

* AsyncUDP Multicast fixes

* Add source mac address and rework multicast

* Allow redefinition of default pins for Serials 1 and 2

* Update IDF to 3276a13

* Update esptool.py to 2.5.0

* Fix sketches

* Fix log level in BluetoothSetial
2018-09-21 08:39:36 +02:00
4e96bffe0e Initial version of rmt driver (#1525)
* rmt driver initial version

* supporting conti mode plus interrupts

* using conitnous mode for sending more data

* working continous mode

* rmt driver cleanup after conti mode

* initial version of rmt driver

* adding a simple example

* adding channel and block locks

* modified of rmt interface for simpler/easier usage

* adding header sentinels, split interface to common and additional settings

* Fixes per code review + support for rx callback mode

* renamed internal structures and enums, fixed formatting

* cmake support for rmt

* refactored tx-conti interrupts to function to make it more readable

* added Tx and Rx examples

* added license headers

* minor updates per review

* used struct access, renamed defines, corrected diagram
2018-09-17 23:19:27 +02:00
ea61563c69 Functional interrupt (#1728)
* Initial

* Implementation

* Add to CMakelist.txt

* Add example

* Add IRAM_ATTR
2018-09-17 23:13:58 +02:00
5be3078b76 InterruptArg should take voidFuncPtrArg as argument (#1776) 2018-09-17 23:10:13 +02:00
7206b2f397 FAT on SPI Flash Library (#1809)
* First commit of FFat library

* Fixed reboot loops if no fat present. Added CMakeLists

* Functionalize the partition checks

* Cleanup, especially in format

* Dont format if mounted.  More wording cleanup

* 16M ffat should only be on 16M board

* Fix some casting issues that trip up the compiler when building as ESP-IDF component
2018-09-17 23:06:04 +02:00
3028ec42c7 Add BananaPi-BIT Development Board Support (#1810)
* Add BPI-BIT boards connfig

* Add BPI-BIT v1.3 boards
2018-09-17 22:32:14 +02:00
145904fb9c Add wESP32 support (#1821)
* Add wESP32 support

* Add default Ethernet config
2018-09-17 22:28:24 +02:00
cb8d72fdc7 Fix WifiClientEnterprise - STA mode set (#1782)
Few testers let me know, they were unable with connection to Eduroam network under PEAP+MsCHAPv2 methods.
They were trying many modifications. Solution is very absurd. 
Change board to STA mode manually. 
One tester from Italy was able to connect to Eduroam network under TTLS + MsCHAPv2 with that sketch too!

Sketch was tested and worked almost for all testers with that problem.
2018-09-17 21:43:16 +02:00
1e4bf14a3e cores: replace max, min, round macros with imports from std (#1783)
fixes #1734
2018-09-17 21:33:01 +02:00
f9f995b283 Flush serial in DeepSleep example to allow print before sleep (#1791)
* Add delay into example to allow print before sleep

* Changed to Serial.flush()
2018-09-17 21:31:24 +02:00
c8fe873965 add WiFiAccessPoint example (#1833) 2018-09-17 21:24:23 +02:00
18d260ec6e Fix for TFT_eSPI and Adafruit libs (#1837)
see #1800
2018-09-17 21:21:51 +02:00
3a8ac27a86 layout fix (#1845)
(removed quotes from last line, and fixed indentation, no text changes)
2018-09-17 21:20:57 +02:00
a6a9a518a7 _uart_isr use wr_addr != rd_addr as test for internal queue not empty (#1849) 2018-09-17 21:19:51 +02:00
02ee799f35 Add T-Beam Board Support (#1852) 2018-09-17 21:19:02 +02:00
ce61074802 Add functionality allowing rxBuffer of HardwareSerial to be changed in size via HardwareSerial::setRxBufferSize. (#1855) 2018-09-17 21:16:18 +02:00
339618f8ef Updating Boart.txt ESP32Thing in PartitionScheme and DebugLevel (#1860) 2018-09-17 21:12:15 +02:00
6e4e4c96ee Updated ISSUE_TEMPLATE to try to get better postings (#1730)
* Updated ISSUE_TEMPLATE to try to get better postings

* Added line for PSRAM enabled

* More complete info request as per @stickbreaker
2018-09-17 21:11:41 +02:00
a0f0bd930c Fix BTserial memory leaks (#1801)
- Delete queue at end
- Close BT connection before end
- DeInit SPP
2018-08-27 12:06:23 +02:00
80c110ece7 Add more methods to access memory properties 2018-08-18 17:10:35 +02:00
65511b23d3 Add separate method to get free PSRAM and report only internal in getFreeHeap 2018-08-18 16:34:41 +02:00
14d6f6e7e6 Correct pins for actual hardware (#1768)
The original pins_arduino.h did not correspond to the actual hardware.
2018-08-18 08:51:22 +02:00
9db207afbe Improve bus recovery (#1767)
If the esp32 is reset during a i2c read cycle the slave device may be in control of the SDA line.  

If the SDA line is held low, the esp32 cannot issue a START or STOP to recover the bus. 

The previous code did not correctly configure the SCL output pin, and it cycled SCL 9 times with SDA Low.  Since the slave device was in a READ cycle, it just continued outputting the bits of the current byte.  When the ACK/NAK bit space occurred, The low output value of SDA was interpreted as ACK so the slave device continued with the next byte.  It never terminated the READ cycle. 

This new code will correctly recover from an interrupted READ
2018-08-18 08:50:59 +02:00
a989853d4a Update mac.md (#1760)
add a troubleshooting hint
2018-08-18 08:50:14 +02:00
172802b18e Remove duplicate ota_data flashing under IDF
Fixes: https://github.com/espressif/arduino-esp32/issues/1724
2018-08-16 13:34:47 +02:00
fff1783046 Switch to isolated build flags per framework (#1748) 2018-08-14 12:01:07 +02:00
cb53ec4891 Informations about WifiClientEnterprise.ino sketch (#1737)
* informations about sketch

* Update README.md
2018-08-14 12:00:31 +02:00
7d3a67ada0 Update Arduino/hardware path (#1727) 2018-08-14 11:53:34 +02:00
d057e544e0 Added a freeEntries method to Preferences library (#1722) 2018-08-14 11:52:01 +02:00
b05430cfd9 Wire ReSTART fix, with others (#1717)
* ReSTART fix, Sequencing fix

pr #1665 introduce a problem with ReSTART, when solving this problem I found an interaction between the TxFifo refill, RxFifo empty and CMD[] fill.  during certain sequences a dataqueue command would be skipped, this skipping resulted in a mismatch between the contents of the TxFifo and the i2c command sequence.  The problem manifested as an ACK error. 
In addition to this required bug fix I propose:
* `Wire.begin()` be changed from a `void` to a `bool` this will allow the reset functionality of `Wire.begin()` to be reported.  Currently `Wire.begin()` attempts to reset the i2c Peripheral, but cannot report success/failure.
* `Wire.busy()` be added. this `bool` function returns the hardware status of the bus. This status can be use in multi-master environments for application level interleaving of commands, also in single master environment, it can be used to detect a 'hung' bus.  With the functional change to `Wire.begin()` this allows app level recover of a hung bus.
* `Wire.lastError()` value updated for all errors, previously when interleaving `Wire.endTransmission(false)` and `Wire.readTransmission(false)`, the 128 byte `Wire.write()` buffer was exhausted without generating and error(very exotic). I discovered this error when I created a sequence of directed reads to a EEPROM. Each directed read used 2 bytes of the 128 byte `write()` buffer, so after 64 consecutive ReSTART writes with ReSTART reads, `Wire()`  had no room to record the directed address bytes.  It generated just a NAK check without setting the EEPROMs internal register address.  The succeeding ReSTART read succeeded at incorrect address.
* Changes to the HAL layer:
** added `i2cGetStatus()` which returns the i2c peripheral status word, used to detect bus_busy currently
** added `i2cDebug()` programmatic control of debug buffer output
** changed `i2cAddQueue()` to allow data_only queue element this will allow a i2c transaction to use multiple data pointers.
** removed direct access to DumpInts(), DumpI2c() from app, use i2cDebug() to set trigger points 
 
*

* Update esp32-hal-i2c.c

* Update Wire.cpp

* ReSTART, Sequencing

pr #1665 introduce a problem with ReSTART, when solving this problem I found an interaction between the TxFifo refill, RxFifo empty and CMD[] fill.  during certain sequences a dataqueue command would be skipped, this skipping resulted in a mismatch between the contents of the TxFifo and the i2c command sequence.  The problem manifested as an ACK error. 
In addition to this required bug fix I propose:
* `Wire.begin()` be changed from a `void` to a `bool` this will allow the reset functionality of `Wire.begin()` to be reported.  Currently `Wire.begin()` attempts to reset the i2c Peripheral, but cannot report success/failure.
* `Wire.busy()` be added. this `bool` function returns the hardware status of the bus. This status can be use in multi-master environments for application level interleaving of commands, also in single master environment, it can be used to detect a 'hung' bus.  With the functional change to `Wire.begin()` this allows app level recover of a hung bus.
* `Wire.lastError()` value updated for all errors, previously when interleaving `Wire.endTransmission(false)` and `Wire.readTransmission(false)`, the 128 byte `Wire.write()` buffer was exhausted without generating and error(very exotic). I discovered this error when I created a sequence of directed reads to a EEPROM. Each directed read used 2 bytes of the 128 byte `write()` buffer, so after 64 consecutive ReSTART writes with ReSTART reads, `Wire()`  had no room to record the directed address bytes.  It generated just a NAK check without setting the EEPROMs internal register address.  The succeeding ReSTART read succeeded at incorrect address.
* Changes to the HAL layer:
** added `i2cGetStatus()` which returns the i2c peripheral status word, used to detect bus_busy currently
** added `i2cDebug()` programmatic control of debug buffer output
** changed `i2cAddQueue()` to allow data_only queue element this will allow a i2c transaction to use multiple data pointers.
** removed direct access to DumpInts(), DumpI2c() from app, use i2cDebug() to set trigger points 
 
*

* Forgot DebugFlags Return

@andriyadi found this, total brain fade on my part.
2018-08-14 11:51:15 +02:00
e346f20aa9 Fix WiFiMulti Logs 2018-07-30 09:42:44 -03:00
bdc45e39ef [OTA] Fix "Error response from device" if OK response comes to early (#1695)
Because TCP is stream-based, an earlier read can 'take away' the "OK" response
from the device, so that a later read doesn't get the message.
2018-07-30 10:22:01 +03:00
a7ddf39521 Cleanup on README and boards_manager (#1693)
* Added instructions for installation with boards manager (stolen directly from esp8266)

* changed to production link instead of dev

* Added to main README. Made mods to images as requested.

* Added links for development package

* Moved version images to README.md

* Just a little change for cleaner look

* Cleaned up README.md and boards_manager.md to make installation easier.
2018-07-30 10:21:03 +03:00
abb8ea99d5 Fix WiFiMulti Logs (#1690) 2018-07-30 10:18:48 +03:00
1350 changed files with 150277 additions and 62398 deletions

View File

@ -7,6 +7,9 @@ python:
os:
- linux
git:
depth: false
env:
global:
- secure: "l/4Dt+KQ/mACtGAHDUsPr66fUte840PZoQ4xpPikqWZI0uARu4l+Ym7+sHinnT6fBqrj8AJeBYGz4nFa8NK4LutZn9mSD40w+sxl0wSV4oHV8rzKe3Cd8+sMG3+o33yWoikMNjSvqa73Q0rm+SgrlInNdZbuAyixL+a2alaWSnGPm4F2xwUGj+S33TOy5P/Xp77CYtCV5S8vzyk/eEdNhoF0GYePJVdfuzCOUjXMyT5OWxORkzzQ7Hnn/Ka/RDfV8Si4HgujLQBrK5q6iPnNBFqBSqilYBepSMn4opnOBpIm0SCgePz7XQEFC83buA7GUcnCnfg38bf+dCwHaODf1d1PmqVRYt2QmfinexXtM4afAtL0iBUDtvrfnXHzwW9w82VeZhpbJSVh9DUQvB0IlsZeCz9J9PUBAi3N+SMX+9l+BomYwRUlPuKY+Ef2JKk9q6mxtUkky5R0daAlVxEhpVdQks1rT+T+NMoDMemxQ3SKEiqAHh6EgHecruszffmZ71uLX9MpERpew0qN+UFiafws+jkTjx+3yF9yut0Hf9sMbeAYzzkGzRqJTUEBJ6B29Cql8M0yRXCNN/8wuuTHhG8esstozga4ZQoIVrq7mEAgup376PTcNfr1+imbbWVQ7lJdYIuDe6OS5V3OX6np11vgK/DbhfyzvQv9Z1zAGnM="

View File

@ -16,12 +16,15 @@ set(CORE_SRCS
cores/esp32/esp32-hal-timer.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
@ -40,9 +43,11 @@ set(LIBRARY_SRCS
libraries/DNSServer/src/DNSServer.cpp
libraries/EEPROM/EEPROM.cpp
libraries/ESPmDNS/src/ESPmDNS.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/NetBIOS/src/NetBIOS.cpp
libraries/Preferences/src/Preferences.cpp
libraries/SD_MMC/src/SD_MMC.cpp
@ -145,6 +150,8 @@ set(BLE_SRCS
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
@ -175,8 +182,10 @@ set(COMPONENT_ADD_INCLUDEDIRS
libraries/DNSServer/src
libraries/ESP32/src
libraries/ESPmDNS/src
libraries/FFat/src
libraries/FS/src
libraries/HTTPClient/src
libraries/HTTPUpdate/src
libraries/NetBIOS/src
libraries/Preferences/src
libraries/SD_MMC/src
@ -195,6 +204,6 @@ set(COMPONENT_ADD_INCLUDEDIRS
set(COMPONENT_PRIV_INCLUDEDIRS cores/esp32/libb64)
set(COMPONENT_REQUIRES spi_flash mbedtls mdns ethernet)
set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl)
set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl bt)
register_component()

View File

@ -1,17 +1,7 @@
BOOT_APP_BIN_OFFSET := 0xe000
BOOT_APP_BIN_ROOT := $(call dequote,$(COMPONENT_PATH))
BOOT_APP_BIN_PATH := $(call dequote,$(abspath $(BOOT_APP_BIN_ROOT)/$(subst $(quote),,tools/partitions/boot_app0.bin)))
ifndef CONFIG_PARTITION_TABLE_CUSTOM
PARTITION_TABLE_CSV_PATH = $(call dequote,$(abspath $(BOOT_APP_BIN_ROOT)/$(subst $(quote),,tools/partitions/$(CONFIG_ARDUHAL_PARTITION_SCHEME).csv)))
endif
BOOT_APP_BIN_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(BOOT_APP_BIN_OFFSET) $(BOOT_APP_BIN_PATH)
ESPTOOL_ALL_FLASH_ARGS += $(BOOT_APP_BIN_OFFSET) $(BOOT_APP_BIN_PATH)
CPPFLAGS += -DARDUINO=10800 -DESP32=1 -DARDUINO_ARCH_ESP32=1 -DBOARD_HAS_PSRAM
boot-app0:
@echo "Rebooting to APP0"
$(BOOT_APP_BIN_FLASH_CMD)

View File

@ -1,6 +1,4 @@
# Arduino core for ESP32 WiFi chip
[![Build Status](https://travis-ci.org/espressif/arduino-esp32.svg?branch=master)](https://travis-ci.org/espressif/arduino-esp32)
# Arduino core for ESP32 WiFi chip [![Build Status](https://travis-ci.org/espressif/arduino-esp32.svg?branch=master)](https://travis-ci.org/espressif/arduino-esp32)
### Need help or have a question? Join the chat at [![https://gitter.im/espressif/arduino-esp32](https://badges.gitter.im/espressif/arduino-esp32.svg)](https://gitter.im/espressif/arduino-esp32?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@ -12,15 +10,16 @@
- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap)
## Development Status
[Latest stable release ![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic) ![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
[Latest development release ![Development Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic) ![Development Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
Most of the framework is implemented. Most noticable is the missing analogWrite. While analogWrite is on it's way, there are a few other options that you can use:
- 16 channels [LEDC](cores/esp32/esp32-hal-ledc.h) which is PWM
- 8 channels [SigmaDelta](cores/esp32/esp32-hal-sigmadelta.h) which uses SigmaDelta modulation
- 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output
## Installation Instructions
#### [Latest release ![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic) ![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
- Using Arduino IDE Boards Manager (preferred)
+ [Instructions for Boards Manager](docs/arduino-ide/boards_manager.md)
- Using Arduino IDE with the development repository

File diff suppressed because it is too large Load Diff

View File

@ -75,7 +75,6 @@
#define abs(x) ((x)>0?(x):-(x))
#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))
#define radians(deg) ((deg)*DEG_TO_RAD)
#define degrees(rad) ((rad)*RAD_TO_DEG)
#define sq(x) ((x)*(x))
@ -146,6 +145,9 @@ 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"
@ -158,6 +160,12 @@ void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
#include "HardwareSerial.h"
#include "Esp.h"
using std::isinf;
using std::isnan;
using std::max;
using std::min;
using ::round;
uint16_t makeWord(uint16_t w);
uint16_t makeWord(byte h, byte l);
@ -176,12 +184,6 @@ extern "C" void configTzTime(const char* tz,
long random(long);
#endif /* __cplusplus */
#ifndef _GLIBCXX_VECTOR
// arduino is not compatible with std::vector
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#define _min(a,b) ((a)<(b)?(a):(b))
#define _max(a,b) ((a)>(b)?(a):(b))

View File

@ -25,20 +25,12 @@
#include <memory>
#include <soc/soc.h>
#include <soc/efuse_reg.h>
/* Main header of binary image */
typedef struct {
uint8_t magic;
uint8_t segment_count;
uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */
uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */
uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */
uint32_t entry_addr;
uint8_t encrypt_flag; /* encrypt flag */
uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */
} esp_image_header_t;
#define ESP_IMAGE_HEADER_MAGIC 0xE9
#include <esp_partition.h>
extern "C" {
#include "esp_ota_ops.h"
#include "esp_image_format.h"
}
#include <MD5Builder.h>
/**
* User-defined Literals
@ -112,9 +104,118 @@ void EspClass::restart(void)
esp_restart();
}
uint32_t EspClass::getHeapSize(void)
{
multi_heap_info_t info;
heap_caps_get_info(&info, MALLOC_CAP_INTERNAL);
return info.total_free_bytes + info.total_allocated_bytes;
}
uint32_t EspClass::getFreeHeap(void)
{
return esp_get_free_heap_size();
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)
{
multi_heap_info_t info;
heap_caps_get_info(&info, MALLOC_CAP_SPIRAM);
return info.total_free_bytes + info.total_allocated_bytes;
}
uint32_t EspClass::getFreePsram(void)
{
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
}
uint32_t EspClass::getMinFreePsram(void)
{
return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
}
uint32_t EspClass::getMaxAllocPsram(void)
{
return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
}
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;
}
uint8_t EspClass::getChipRevision(void)

View File

@ -50,13 +50,30 @@ typedef enum {
FM_UNKNOWN = 0xff
} FlashMode_t;
typedef enum {
SKETCH_SIZE_TOTAL = 0,
SKETCH_SIZE_FREE = 1
} sketchSize_t;
class EspClass
{
public:
EspClass() {}
~EspClass() {}
void restart();
uint32_t getFreeHeap();
//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();
uint8_t getChipRevision();
uint8_t getCpuFreqMHz(){ return CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; }
uint32_t getCycleCount();
@ -72,6 +89,10 @@ public:
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);

View File

@ -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 IRAM_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;
}
}

View File

@ -0,0 +1,20 @@
/*
* FunctionalInterrupt.h
*
* Created on: 8 jul. 2018
* Author: Herman
*/
#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_
#define CORE_CORE_FUNCTIONALINTERRUPT_H_
#include <functional>
struct InterruptArgStructure {
std::function<void(void)> interruptFunction;
};
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode);
#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */

View File

@ -3,8 +3,25 @@
#include <string.h>
#include <inttypes.h>
#include "pins_arduino.h"
#include "HardwareSerial.h"
#ifndef RX1
#define RX1 9
#endif
#ifndef TX1
#define TX1 10
#endif
#ifndef RX2
#define RX2 16
#endif
#ifndef TX2
#define TX2 17
#endif
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
HardwareSerial Serial(0);
HardwareSerial Serial1(1);
@ -13,7 +30,7 @@ HardwareSerial Serial2(2);
HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert)
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms)
{
if(0 > _uart_nr || _uart_nr > 2) {
log_e("Serial number is invalid, please use 0, 1 or 2");
@ -27,14 +44,33 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
txPin = 1;
}
if(_uart_nr == 1 && rxPin < 0 && txPin < 0) {
rxPin = 9;
txPin = 10;
rxPin = RX1;
txPin = TX1;
}
if(_uart_nr == 2 && rxPin < 0 && txPin < 0) {
rxPin = 16;
txPin = 17;
rxPin = RX2;
txPin = TX2;
}
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert);
if(!baud) {
time_t startMillis = millis();
unsigned long detectedBaudRate = 0;
while(millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) {
yield();
}
end();
if(detectedBaudRate) {
delay(100); // Give some time...
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, 256, invert);
} 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;
}
}
_uart = uartBegin(_uart_nr, baud, config, rxPin, txPin, 256, invert);
}
void HardwareSerial::end()
@ -46,6 +82,10 @@ void HardwareSerial::end()
_uart = 0;
}
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
return uartResizeRxBuffer(_uart, new_size);
}
void HardwareSerial::setDebugOutput(bool en)
{
if(_uart == 0) {

View File

@ -22,6 +22,24 @@
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
@ -37,7 +55,7 @@ class HardwareSerial: public Stream
public:
HardwareSerial(int uart_nr);
void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false);
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);
void end();
int available(void);
int availableForWrite(void);
@ -70,6 +88,7 @@ public:
uint32_t baudRate();
operator bool() const;
size_t setRxBufferSize(size_t);
void setDebugOutput(bool);
protected:

View File

@ -63,7 +63,7 @@ size_t Print::printf(const char *format, ...)
len = vsnprintf(temp, len+1, format, arg);
write((uint8_t*)temp, len);
va_end(arg);
if(len > 64){
if(len >= sizeof(loc_buf)){
delete[] temp;
}
return len;

View File

@ -882,6 +882,12 @@ float String::toFloat(void) const
return 0;
}
double String::toDouble(void) const {
if (buffer) {
return atof(buffer);
}
return 0;
}
unsigned char String::equalsConstantTime(const String &s2) const {
// To avoid possible time-based attacks present function

View File

@ -260,6 +260,7 @@ public:
// parsing/conversion
long toInt(void) const;
float toFloat(void) const;
double toDouble(void) const;
protected:
char *buffer; // the actual char array

View File

@ -0,0 +1 @@
#include "lwip/apps/sntp.h"

View File

@ -37,8 +37,7 @@ extern "C" {
*/
String base64::encode(uint8_t * data, size_t length)
{
// base64 needs more size then the source data
size_t size = ((length * 1.6f) + 1);
size_t size = base64_encode_expected_len(length) + 1;
char * buffer = (char *) malloc(size);
if(buffer) {
base64_encodestate _state;

View File

@ -74,6 +74,7 @@ typedef void (*voidFuncPtrArg)(void*);
typedef struct {
voidFuncPtr fn;
void* arg;
bool functional;
} InterruptHandle_t;
static InterruptHandle_t __pinInterruptHandlers[GPIO_PIN_COUNT] = {0,};
@ -238,16 +239,26 @@ static void IRAM_ATTR __onPinInterrupt()
}
}
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type)
extern void cleanupFunctional(void* arg);
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional)
{
static bool interrupt_initialized = false;
if(!interrupt_initialized) {
interrupt_initialized = true;
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, __onPinInterrupt, NULL, &gpio_intr_handle);
}
// if new attach without detach remove old info
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg)
{
cleanupFunctional(__pinInterruptHandlers[pin].arg);
}
__pinInterruptHandlers[pin].fn = (voidFuncPtr)userFunc;
__pinInterruptHandlers[pin].arg = arg;
__pinInterruptHandlers[pin].functional = functional;
esp_intr_disable(gpio_intr_handle);
if(esp_intr_get_cpu(gpio_intr_handle)) { //APP_CPU
GPIO.pin[pin].int_ena = 1;
@ -258,15 +269,26 @@ extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * ar
esp_intr_enable(gpio_intr_handle);
}
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type)
{
__attachInterruptFunctionalArg(pin, userFunc, arg, intr_type, false);
}
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type) {
__attachInterruptArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type);
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type, false);
}
extern void __detachInterrupt(uint8_t pin)
{
esp_intr_disable(gpio_intr_handle);
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg)
{
cleanupFunctional(__pinInterruptHandlers[pin].arg);
}
__pinInterruptHandlers[pin].fn = NULL;
__pinInterruptHandlers[pin].arg = NULL;
__pinInterruptHandlers[pin].arg = false;
GPIO.pin[pin].int_ena = 0;
GPIO.pin[pin].int_type = 0;
esp_intr_enable(gpio_intr_handle);
@ -277,6 +299,6 @@ extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pi
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite")));
extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead")));
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt")));
extern void attachInterruptArg(uint8_t pin, voidFuncPtr handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg")));
extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt")));

View File

@ -79,7 +79,7 @@ void digitalWrite(uint8_t pin, uint8_t val);
int digitalRead(uint8_t pin);
void attachInterrupt(uint8_t pin, void (*)(void), int mode);
void attachInterruptArg(uint8_t pin, void (*)(void), void * arg, int mode);
void attachInterruptArg(uint8_t pin, void (*)(void*), void * arg, int mode);
void detachInterrupt(uint8_t pin);
#ifdef __cplusplus

View File

@ -36,17 +36,21 @@
/* Stickbreaker ISR mode debug support
ENABLE_I2C_DEBUG_BUFFER
Enable debug interrupt history buffer, setting this define will result in 1544 bytes of RAM
being used whenever CORE_DEBUG_LEVEL is higher than WARNING. Unless you are debugging
a problem in the I2C subsystem I would recommend you leave it commented out.
Enable debug interrupt history buffer, fifoTx history buffer.
Setting this define will result in 2570 bytes of RAM being used whenever CORE_DEBUG_LEVEL
is higher than WARNING. Unless you are debugging a problem in the I2C subsystem,
I would recommend you leave it commented out.
*/
//#define ENABLE_I2C_DEBUG_BUFFER
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
#define INTBUFFMAX 64
#define FIFOMAX 512
static uint32_t intBuff[INTBUFFMAX][3][2];
static uint32_t intPos[2]= {0,0};
static uint16_t fifoBuffer[FIFOMAX];
static uint16_t fifoPos = 0;
#endif
// start from tools/sdk/include/soc/soc/i2c_struct.h
@ -154,7 +158,7 @@ typedef union {
// individual dq element
typedef struct {
uint8_t *data; // datapointer for read/write buffer
uint8_t *data; // data pointer for read/write buffer
uint16_t length; // size of data buffer
uint16_t position; // current position for next char in buffer (<length)
uint16_t cmdBytesNeeded; // used to control number of I2C_COMMAND_t blocks added to queue
@ -179,9 +183,10 @@ struct i2c_struct_t {
I2C_DATA_QUEUE_t * dq;
uint16_t queueCount; // number of dq entries in queue.
uint16_t queuePos; // current queue that still has or needs data (out/in)
uint16_t errorByteCnt; // count of bytes moved (both in and out)
int16_t errorByteCnt; // byte pos where error happened, -1 devId, 0..(length-1) data
uint16_t errorQueue; // errorByteCnt is in this queue,(for error locus)
uint32_t exitCode;
uint32_t debugFlags;
};
enum {
@ -197,16 +202,16 @@ enum {
#define I2C_MUTEX_UNLOCK()
static i2c_t _i2c_bus_array[2] = {
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0},
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0}
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), 0, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0},
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}
};
#else
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS)
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock)
static i2c_t _i2c_bus_array[2] = {
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0},
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0}
{(volatile i2c_dev_t *)(DR_REG_I2C_EXT_BASE_FIXED), NULL, 0, -1, -1, I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0},
{(volatile i2c_dev_t *)(DR_REG_I2C1_EXT_BASE_FIXED), NULL, 1, -1, -1,I2C_NONE,I2C_NONE,I2C_ERROR_OK,NULL,NULL,NULL,0,0,0,0,0,0}
};
#endif
@ -218,6 +223,228 @@ static i2c_t _i2c_bus_array[2] = {
* ack_exp - This bit is to set an expected ACK value for the transmitter.
* ack_check - This bit is to decide whether the transmitter checks ACK bit. 1 means yes and 0 means no.
* */
/* Stickbreaker ISR mode debug support
*/
static void IRAM_ATTR i2cDumpCmdQueue(i2c_t *i2c)
{
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR)&&(defined ENABLE_I2C_DEBUG_BUFFER)
static const char * const cmdName[] ={"RSTART","WRITE","READ","STOP","END"};
uint8_t i=0;
while(i<16) {
I2C_COMMAND_t c;
c.val=i2c->dev->command[i].val;
log_e("[%2d]\t%c\t%s\tval[%d]\texp[%d]\ten[%d]\tbytes[%d]",i,(c.done?'Y':'N'),
cmdName[c.op_code],
c.ack_val,
c.ack_exp,
c.ack_en,
c.byte_num);
i++;
}
#endif
}
/* Stickbreaker ISR mode debug support
*/
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
static void i2cDumpDqData(i2c_t * i2c)
{
#if defined (ENABLE_I2C_DEBUG_BUFFER)
uint16_t a=0;
char buff[140];
I2C_DATA_QUEUE_t *tdq;
int digits=0,lenDigits=0;
a = i2c->queueCount;
while(a>0) {
digits++;
a /= 10;
}
while(a<i2c->queueCount) { // find maximum number of len decimal digits for formatting
if (i2c->dq[a].length > lenDigits ) lenDigits = i2c->dq[a].length;
a++;
}
a=0;
while(lenDigits>0){
a++;
lenDigits /= 10;
}
lenDigits = a;
a = 0;
while(a<i2c->queueCount) {
tdq=&i2c->dq[a];
char buf1[10],buf2[10];
sprintf(buf1,"%0*d",lenDigits,tdq->length);
sprintf(buf2,"%0*d",lenDigits,tdq->position);
log_i("[%0*d] %sbit %x %c %s buf@=%p, len=%s, pos=%s, ctrl=%d%d%d%d%d",digits,a,
(tdq->ctrl.addr>0x100)?"10":"7",
(tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1),
(tdq->ctrl.mode)?'R':'W',
(tdq->ctrl.stop)?"STOP":"",
tdq->data,
buf1,buf2,
tdq->ctrl.startCmdSent,tdq->ctrl.addrCmdSent,tdq->ctrl.dataCmdSent,(tdq->ctrl.stop)?tdq->ctrl.stopCmdSent:0,tdq->ctrl.addrSent
);
uint16_t offset = 0;
while(offset<tdq->length) {
memset(buff,' ',140);
buff[139]='\0';
uint16_t i = 0,j;
j=sprintf(buff,"0x%04x: ",offset);
while((i<32)&&(offset < tdq->length)) {
char ch = tdq->data[offset];
sprintf((char*)&buff[(i*3)+41],"%02x ",ch);
if((ch<32)||(ch>126)) {
ch='.';
}
j+=sprintf((char*)&buff[j],"%c",ch);
buff[j]=' ';
i++;
offset++;
}
log_i("%s",buff);
}
a++;
}
#else
log_i("Debug Buffer not Enabled");
#endif
}
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
static void i2cDumpI2c(i2c_t * i2c)
{
log_e("i2c=%p",i2c);
log_i("dev=%p date=%p",i2c->dev,i2c->dev->date);
#if !CONFIG_DISABLE_HAL_LOCKS
log_i("lock=%p",i2c->lock);
#endif
log_i("num=%d",i2c->num);
log_i("mode=%d",i2c->mode);
log_i("stage=%d",i2c->stage);
log_i("error=%d",i2c->error);
log_i("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0);
log_i("intr_handle=%p",i2c->intr_handle);
log_i("dq=%p",i2c->dq);
log_i("queueCount=%d",i2c->queueCount);
log_i("queuePos=%d",i2c->queuePos);
log_i("errorByteCnt=%d",i2c->errorByteCnt);
log_i("errorQueue=%d",i2c->errorQueue);
log_i("debugFlags=0x%08X",i2c->debugFlags);
if(i2c->dq) {
i2cDumpDqData(i2c);
}
}
#endif
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
static void i2cDumpInts(uint8_t num)
{
#if defined (ENABLE_I2C_DEBUG_BUFFER)
uint32_t b;
log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num);
for(uint32_t a=1; a<=INTBUFFMAX; a++) {
b=(a+intPos[num])%INTBUFFMAX;
if(intBuff[b][0][num]!=0) {
log_i("[%02d]\t0x%04x\t0x%04x\t0x%04x\t0x%04x\t0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]);
}
}
#else
log_i("Debug Buffer not Enabled");
#endif
}
#endif
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
static void IRAM_ATTR i2cDumpStatus(i2c_t * i2c){
typedef union {
struct {
uint32_t ack_rec: 1; /*This register stores the value of ACK bit.*/
uint32_t slave_rw: 1; /*when in slave mode 1master read slave 0: master write slave.*/
uint32_t time_out: 1; /*when I2C takes more than time_out_reg clocks to receive a data then this register changes to high level.*/
uint32_t arb_lost: 1; /*when I2C lost control of SDA line this register changes to high level.*/
uint32_t bus_busy: 1; /*1:I2C bus is busy transferring data. 0:I2C bus is in idle state.*/
uint32_t slave_addressed: 1; /*when configured as i2c slave and the address send by master is equal to slave's address then this bit will be high level.*/
uint32_t byte_trans: 1; /*This register changes to high level when one byte is transferred.*/
uint32_t reserved7: 1;
uint32_t rx_fifo_cnt: 6; /*This register represent the amount of data need to send.*/
uint32_t reserved14: 4;
uint32_t tx_fifo_cnt: 6; /*This register stores the amount of received data in ram.*/
uint32_t scl_main_state_last: 3; /*This register stores the value of state machine for i2c module. 3'h0: SCL_MAIN_IDLE 3'h1: SCL_ADDRESS_SHIFT 3'h2: SCL_ACK_ADDRESS 3'h3: SCL_RX_DATA 3'h4 SCL_TX_DATA 3'h5:SCL_SEND_ACK 3'h6:SCL_WAIT_ACK*/
uint32_t reserved27: 1;
uint32_t scl_state_last: 3; /*This register stores the value of state machine to produce SCL. 3'h0: SCL_IDLE 3'h1:SCL_START 3'h2:SCL_LOW_EDGE 3'h3: SCL_LOW 3'h4:SCL_HIGH_EDGE 3'h5:SCL_HIGH 3'h6:SCL_STOP*/
uint32_t reserved31: 1;
};
uint32_t val;
} status_reg;
status_reg sr;
sr.val= i2c->dev->status_reg.val;
log_i("ack(%d) sl_rw(%d) to(%d) arb(%d) busy(%d) sl(%d) trans(%d) rx(%d) tx(%d) sclMain(%d) scl(%d)",sr.ack_rec,sr.slave_rw,sr.time_out,sr.arb_lost,sr.bus_busy,sr.slave_addressed,sr.byte_trans, sr.rx_fifo_cnt, sr.tx_fifo_cnt,sr.scl_main_state_last, sr.scl_state_last);
}
#endif
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
static void i2cDumpFifo(i2c_t * i2c){
char buf[64];
uint16_t k = 0;
uint16_t i = fifoPos+1;
i %=FIFOMAX;
while((fifoBuffer[i]==0)&&(i!=fifoPos)){
i++;
i %=FIFOMAX;
}
if(i != fifoPos){// actual data
do{
if(fifoBuffer[i] & 0x8000){ // address byte
if(fifoBuffer[i] & 0x100) { // read
if(fifoBuffer[i] & 0x1) { // valid read dev id
k+= sprintf(&buf[k],"READ 0x%02X",(fifoBuffer[i]&0xff)>>1);
} else { // invalid read dev id
k+= sprintf(&buf[k],"Bad READ 0x%02X",(fifoBuffer[i]&0xff));
}
} else { // write
if(fifoBuffer[i] & 0x1) { // bad write dev id
k+= sprintf(&buf[k],"bad WRITE 0x%02X",(fifoBuffer[i]&0xff));
} else { // good Write
k+= sprintf(&buf[k],"WRITE 0x%02X",(fifoBuffer[i]&0xff)>>1);
}
}
} else k += sprintf(&buf[k],"% 4X ",fifoBuffer[i]);
i++;
i %= FIFOMAX;
bool outBuffer=false;
if( fifoBuffer[i] & 0x8000){
outBuffer=true;
k=0;
}
if((outBuffer)||(k>50)||(i==fifoPos)) log_i("%s",buf);
outBuffer = false;
if(k>50) {
k=sprintf(buf,"-> ");
}
}while( i!= fifoPos);
}
}
#endif
static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char locus[]){
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
if( trigger ){
log_i("%s",locus);
if(trigger & 1) i2cDumpI2c(i2c);
if(trigger & 2) i2cDumpInts(i2c->num);
if(trigger & 4) i2cDumpCmdQueue(i2c);
if(trigger & 8) i2cDumpStatus(i2c);
if(trigger & 16) i2cDumpFifo(i2c);
}
#endif
}
// end of debug support routines
static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check)
{
I2C_COMMAND_t cmd;
@ -229,43 +456,24 @@ static void IRAM_ATTR i2cSetCmd(i2c_t * i2c, uint8_t index, uint8_t op_code, uin
cmd.op_code = op_code;
i2c->dev->command[index].val = cmd.val;
}
/* Stickbreaker ISR mode debug support
*/
void IRAM_ATTR dumpCmdQueue(i2c_t *i2c)
{
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
uint8_t i=0;
while(i<16) {
I2C_COMMAND_t c;
c.val=i2c->dev->command[i].val;
log_e("[%2d] %c op[%d] val[%d] exp[%d] en[%d] bytes[%d]",i,(c.done?'Y':'N'),
c.op_code,
c.ack_val,
c.ack_exp,
c.ack_en,
c.byte_num);
i++;
}
#endif
}
/* Stickbreaker ISR mode support
*/
static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
{
/* this function is called on initial i2cProcQueue() or when a I2C_END_DETECT_INT occurs
*/
uint16_t cmdIdx = 0;
uint16_t qp = i2c->queuePos;
uint16_t qp = i2c->queuePos; // all queues before queuePos have been completely processed,
// so look start by checking the 'current queue' so see if it needs commands added to
// hardware peripheral command list. walk through each queue entry until all queues have been
// checked
bool done;
bool needMoreCmds = false;
bool ena_rx=false; // if we add a read op, better enable Rx_Fifo IRQ
bool ena_tx=false; // if we add a Write op, better enable TX_Fifo IRQ
while(!needMoreCmds&&(qp < i2c->queueCount)) { // check if more possible cmds
if(i2c->dq[qp].ctrl.stopCmdSent) {
if(i2c->dq[qp].ctrl.stopCmdSent) {// marks that all required cmds[] have been added to peripheral
qp++;
} else {
needMoreCmds=true;
@ -282,7 +490,6 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
// (cmdIdx<14) because a START op cannot directly precede an END op, else a time out cascade occurs
i2cSetCmd(i2c, cmdIdx++, I2C_CMD_RSTART, 0, false, false, false);
tdq->ctrl.startCmdSent=1;
done = (cmdIdx>14);
}
//CMD WRITE ADDRESS
@ -341,11 +548,11 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
tdq->ctrl.dataCmdSent=(tdq->cmdBytesNeeded==0); // enough command[] to send all data
if((!done)&&(tdq->ctrl.dataCmdSent)) { // possibly add stop
if(tdq->ctrl.stop) { //send a stop
i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false);
done = cmdIdx > 14;
tdq->ctrl.stopCmdSent = 1;
} else { // dummy a stop because this is a restart
if(!tdq->ctrl.stopCmdSent){
if(tdq->ctrl.stop) { //send a stop
i2cSetCmd(i2c, (cmdIdx)++,I2C_CMD_STOP,0,false,false,false);
done = cmdIdx > 14;
}
tdq->ctrl.stopCmdSent = 1;
}
}
@ -370,7 +577,7 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
i--;
} else { // unable to stretch, fatal
log_e("invalid CMD[] layout Stretch Failed");
dumpCmdQueue(i2c);
i2cDumpCmdQueue(i2c);
done = true;
}
}
@ -409,8 +616,6 @@ static void IRAM_ATTR fillCmdQueue(i2c_t * i2c, bool INTS)
}
}
/* Stickbreaker ISR mode support
*/
static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
{
/*
@ -421,6 +626,7 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
uint16_t a=i2c->queuePos; // currently executing dq,
bool full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
uint8_t cnt;
bool rxQueueEncountered = false;
while((a < i2c->queueCount) && !full) {
I2C_DATA_QUEUE_t *tdq = &i2c->dq[a];
cnt=0;
@ -449,24 +655,38 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
i2c->dev->fifo_data.val = tdq->ctrl.addr&0xFF;
cnt++;
tdq->ctrl.addrSent=1; // 7bit lowbyte sent
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
uint16_t a = 0x8000 | tdq->ctrl.addr | (tdq->ctrl.mode<<8);
fifoBuffer[fifoPos++] = a;
fifoPos %= FIFOMAX;
#endif
}
}
}
}
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
// add write data to fifo
//21NOV2017 might want to look into using local capacity counter instead of reading status_reg
// a double while loop, like emptyRxFifo()
if(tdq->ctrl.mode==0) { // write
if(tdq->ctrl.addrSent == tdq->ctrl.addrReq) { //address has been sent, is Write Mode!
while((!full)&&(tdq->position < tdq->length)) {
uint32_t moveCnt = 32 - i2c->dev->status_reg.tx_fifo_cnt; // how much room in txFifo?
while(( moveCnt>0)&&(tdq->position < tdq->length)) {
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
fifoBuffer[fifoPos++] = tdq->data[tdq->position];
fifoPos %= FIFOMAX;
#endif
i2c->dev->fifo_data.val = tdq->data[tdq->position++];
cnt++;
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
moveCnt--;
}
}
} else { // read Queue Encountered, can't update QueuePos past this point, emptyRxfifo will do it
if( ! rxQueueEncountered ) {
rxQueueEncountered = true;
if(a > i2c->queuePos){
i2c->queuePos = a;
}
}
}
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
// update debug buffer tx counts
@ -474,65 +694,78 @@ static void IRAM_ATTR fillTxFifo(i2c_t * i2c)
intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF)|(cnt<<16);
#endif
full=!(i2c->dev->status_reg.tx_fifo_cnt<31);
if(!full) {
a++; // check next buffer for tx
a++; // check next buffer for address tx, or write data
}
}
if(a >= i2c->queueCount ) { // disable IRQ, the next dq will re-enable it
// (a >= i2c->queueCount) means no more data is available
if(a >= i2c->queueCount ) { // disable TX IRQ, all tx Data has been queued
i2c->dev->int_ena.tx_fifo_empty= 0;
}
i2c->dev->int_clr.tx_fifo_empty=1;
}
/* Stickbreaker ISR mode support
*/
static void IRAM_ATTR emptyRxFifo(i2c_t * i2c)
{
uint32_t d, cnt=0, moveCnt;
moveCnt = i2c->dev->status_reg.rx_fifo_cnt;//no need to check the reg until this many are read
while((moveCnt > 0)&&(i2c->queuePos < i2c->queueCount)){ // data to move
I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut
if(tdq->ctrl.mode == 1){ // read command
if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq
// part of these reads go into the next dq
moveCnt = (tdq->length - tdq->position);
}
} else {// error
log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos);
return;
}
while(moveCnt > 0) {
d = i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
tdq->data[tdq->position++] = (d&0xFF);
}
if(tdq->position >= tdq->length ){ // inc queuePos until next READ command or end of queue
i2c->queuePos++;
while((i2c->queuePos < i2c->queueCount)&&(i2c->dq[i2c->queuePos].ctrl.mode !=1)){
i2c->queuePos++;
}
if(i2c->queuePos < i2c->queueCount){ // found new place to store rx data
tdq = &i2c->dq[i2c->queuePos]; // update shortcut
// see if any more chars showed up while empting Fifo.
moveCnt = i2c->dev->status_reg.rx_fifo_cnt;
I2C_DATA_QUEUE_t *tdq =&i2c->dq[i2c->queuePos]; //short cut
while((tdq->position >= tdq->length)&&( i2c->queuePos < i2c->queueCount)){ // find were to store
i2c->queuePos++;
tdq = &i2c->dq[i2c->queuePos];
}
if(i2c->queuePos >= i2c->queueCount){ // bad stuff, rx data but no place to put it!
log_e("no Storage location for %d",moveCnt);
// discard
while(moveCnt>0){
d = i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
}
break;
}
if(tdq->ctrl.mode == 1){ // read command
if(moveCnt > (tdq->length - tdq->position)) { //make sure they go in this dq
// part of these reads go into the next dq
// part of these reads go into the next dq
moveCnt = (tdq->length - tdq->position);
}
} else {// error
log_e("RxEmpty(%d) call on TxBuffer? dq=%d",moveCnt,i2c->queuePos);
// discard
while(moveCnt>0){
d = i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
}
break;
}
}
while(moveCnt > 0) { // store data
d = i2c->dev->fifo_data.val;
moveCnt--;
cnt++;
tdq->data[tdq->position++] = (d&0xFF);
}
moveCnt = i2c->dev->status_reg.rx_fifo_cnt; //any more out there?
}
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&& (defined ENABLE_I2C_DEBUG_BUFFER)
// update Debug rxCount
cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&&0xffFF;
cnt += (intBuff[intPos[i2c->num]][1][i2c->num])&0xffFF;
intBuff[intPos[i2c->num]][1][i2c->num] = (intBuff[intPos[i2c->num]][1][i2c->num]&0xFFFF0000)|cnt;
#endif
}
}
static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal)
{
@ -578,16 +811,17 @@ static void IRAM_ATTR i2cIsrExit(i2c_t * i2c,const uint32_t eventCode,bool Fatal
}
}
static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c)
{
/* i2c_update_error_byte_cnt 07/18/2018
Only called after an error has occurred, so, most of the time this function is never used.
This function obliterates the need to interrupt monitor each byte transferred, at high bitrates
the byte interrupts were overwhelming the OS. Spurious Interrupts were being generated.
it update errorByteCnt, errorQueue.
it updates errorByteCnt, errorQueue.
*/
static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c)
{
uint16_t a=0; // start at top of DQ, count how many bytes added to tx fifo, and received from rx_fifo.
uint16_t bc = 0;
int16_t bc = 0;
I2C_DATA_QUEUE_t *tdq;
i2c->errorByteCnt = 0;
while( a < i2c->queueCount){ // add up all bytes loaded into fifo's
@ -596,40 +830,34 @@ static void IRAM_ATTR i2c_update_error_byte_cnt(i2c_t * i2c)
i2c->errorByteCnt += tdq->position;
a++;
}
// log_v("errorByteCnt=%d",i2c->errorByteCnt);
// now errorByteCnt contains total bytes moved into and out of FIFO's
// but, there may still be bytes waiting in Fifo's
i2c->errorByteCnt -= i2c->dev->status_reg.tx_fifo_cnt; // waiting to go out;
i2c->errorByteCnt += i2c->dev->status_reg.rx_fifo_cnt; // already received
// now walk thru DQ again, find which byte is 'current'
bool done = false;
bc = i2c->errorByteCnt;
i2c->errorQueue = 0;
while(( i2c->errorQueue < i2c->queueCount)&&( !done )){
while( i2c->errorQueue < i2c->queueCount ){
tdq = &i2c->dq[i2c->errorQueue];
if(bc>0){ // not found yet
if( tdq->ctrl.addrSent >= bc){ // in address
done = true;
continue;
bc = -1; // in address
break;
} else {
bc -= tdq->ctrl.addrSent;
if( tdq->position >= bc) { // data nak
done = true;
continue;
if( tdq->length > bc) { // data nak
break;
} else { // count down
bc -= tdq->position;
bc -= tdq->length;
}
}
} else {
done= true;
continue;
}
} else break;
i2c->errorQueue++;
}
// log_v("errorByteCnt=%d errorQueue=%d",i2c->errorByteCnt,i2c->errorQueue);
i2c->errorByteCnt = bc;
}
}
static void IRAM_ATTR i2c_isr_handler_default(void* arg)
{
@ -637,17 +865,14 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
uint32_t activeInt = p_i2c->dev->int_status.val&0x7FF;
if(p_i2c->stage==I2C_DONE) { //get Out, can't service, not configured
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
uint32_t raw = p_i2c->dev->int_raw.val;
#endif
p_i2c->dev->int_ena.val = 0;
p_i2c->dev->int_clr.val = 0x1FFF;
log_v("eject raw=%p, int=%p",raw,activeInt);
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
uint32_t raw = p_i2c->dev->int_raw.val;
log_w("eject raw=%p, int=%p",raw,activeInt);
#endif
return;
}else if(!activeInt){ //spurious interrupt, possibly bus relate 20180711
log_v("r=0x%x s=0x%x %d",p_i2c->dev->int_raw.val,p_i2c->dev->int_status.val,p_i2c->stage);
}
while (activeInt != 0) { // Ordering of 'if(activeInt)' statements is important, don't change
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
@ -690,8 +915,7 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
if (activeInt & I2C_ACK_ERR_INT_ST_M) {//fatal error, abort i2c service
if (p_i2c->mode == I2C_MASTER) {
i2c_update_error_byte_cnt(p_i2c); // calc which byte caused ack Error, check if address or data
log_v("AcK Err errorByteCnt=%d, errorQueue=%d queuepos=%d",p_i2c->errorByteCnt,p_i2c->errorQueue, p_i2c->queuePos);
if(p_i2c->errorByteCnt <= p_i2c->dq[p_i2c->errorQueue].ctrl.addrReq) { // address
if(p_i2c->errorByteCnt < 0 ) { // address
i2cIsrExit(p_i2c,EVENT_ERROR_NAK,true);
} else {
i2cIsrExit(p_i2c,EVENT_ERROR_DATA_NAK,true); //data
@ -750,14 +974,16 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
p_i2c->dev->int_ena.val = p_i2c->dev->int_ena.val & (~activeInt);
}
activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened
// activeInt = p_i2c->dev->int_status.val; // start all over if another interrupt happened
// 01AUG2018 if another interrupt happened, the OS will schedule another interrupt
// this is the source of spurious interrupts
}
}
/* Stickbreaker added for ISR 11/2017
functional with Silicon date=0x16042000
*/
static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, EventGroupHandle_t event)
static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop, bool dataOnly, EventGroupHandle_t event)
{
// need to grab a MUTEX for exclusive Queue,
// what about if ISR is running?
@ -772,10 +998,21 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u
dqx.position = 0;
dqx.cmdBytesNeeded = dataLen;
dqx.ctrl.val = 0;
if( dataOnly) {
/* special case to add a queue data only element.
START and devAddr will not be sent, this dq element can have a STOP.
allows multiple buffers to be used for one transaction.
sequence: normal transaction(sendStop==false), [dataonly(sendStop==false)],dataOnly(sendStop==true)
*** Currently only works with WRITE, final byte NAK an READ will cause a fail between dq buffer elements. (in progress 30JUL2018)
*/
dqx.ctrl.startCmdSent = 1; // mark as already sent
dqx.ctrl.addrCmdSent = 1;
} else {
dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address
}
dqx.ctrl.addr = i2cDeviceAddr;
dqx.ctrl.mode = mode;
dqx.ctrl.stop= sendStop;
dqx.ctrl.addrReq = ((i2cDeviceAddr&0xFC00)==0x7800)?2:1; // 10bit or 7bit address
dqx.queueEvent = event;
if(event) { // an eventGroup exist, so, initialize it
@ -808,7 +1045,7 @@ static i2c_err_t i2cAddQueue(i2c_t * i2c,uint8_t mode, uint16_t i2cDeviceAddr, u
i2c_err_t i2cAddQueueWrite(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event)
{
return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,event);
return i2cAddQueue(i2c,0,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event);
}
i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen,bool sendStop,EventGroupHandle_t event)
@ -822,16 +1059,15 @@ i2c_err_t i2cAddQueueRead(i2c_t * i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr,
// this is the Industry Standard specification.
if((i2cDeviceAddr &0xFC00)==0x7800) { // ten bit read
i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,event);
i2c_err_t err = i2cAddQueue(i2c,0,i2cDeviceAddr,NULL,0,false,false,event);
if(err==I2C_ERROR_OK) {
return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,event);
return i2cAddQueue(i2c,1,(i2cDeviceAddr>>8),dataPtr,dataLen,sendStop,false,event);
} else {
return err;
}
}
return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,event);
return i2cAddQueue(i2c,1,i2cDeviceAddr,dataPtr,dataLen,sendStop,false,event);
}
// Stickbreaker
i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
{
@ -847,6 +1083,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
if(i2c == NULL) {
return I2C_ERROR_DEV;
}
if(i2c->debugFlags & 0xff000000) i2cTriggerDumps(i2c,(i2c->debugFlags>>24),"before ProcQueue");
if (i2c->dev->status_reg.bus_busy) { // return error, let TwoWire() handle resetting the hardware.
/* if multi master then this if should be changed to this 03/12/2018
if(multiMaster){// try to let the bus clear by its self
@ -876,6 +1113,8 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
intBuff[i][2][i2c->num] = 0;
}
intPos[i2c->num] = 0;
fifoPos = 0;
memset(fifoBuffer,0,FIFOMAX);
#endif
// EventGroup is used to signal transmission completion from ISR
// not always reliable. Sometimes, the FreeRTOS scheduler is maxed out and refuses request
@ -909,7 +1148,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
if(tdq->ctrl.addrReq ==2) { // 10bit address
taddr =((tdq->ctrl.addr >> 7) & 0xFE)
|tdq->ctrl.mode;
taddr = (taddr <<8) || (tdq->ctrl.addr&0xFF);
taddr = (taddr <<8) | (tdq->ctrl.addr&0xFF);
} else { // 7bit address
taddr = ((tdq->ctrl.addr<<1)&0xFE)
|tdq->ctrl.mode;
@ -950,31 +1189,31 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
// receives the TRANS_START
i2c->dev->int_ena.val =
uint32_t interruptsEnabled =
I2C_ACK_ERR_INT_ENA | // (BIT(10)) Causes Fatal Error Exit
I2C_TRANS_START_INT_ENA | // (BIT(9)) Triggered by trans_start=1, initial,END
I2C_TIME_OUT_INT_ENA | //(BIT(8)) Trigger by SLAVE SCL stretching, NOT an ERROR
I2C_TRANS_COMPLETE_INT_ENA | // (BIT(7)) triggered by STOP, successful exit
// I2C_MASTER_TRAN_COMP_INT_ENA | // (BIT(6)) counts each byte xfer'd, inc's queuePos
I2C_ARBITRATION_LOST_INT_ENA | // (BIT(5)) cause fatal error exit
I2C_SLAVE_TRAN_COMP_INT_ENA | // (BIT(4)) unhandled
I2C_END_DETECT_INT_ENA | // (BIT(3)) refills cmd[] list
I2C_RXFIFO_OVF_INT_ENA | //(BIT(2)) unhandled
I2C_TXFIFO_EMPTY_INT_ENA | // (BIT(1)) triggers fillTxFifo()
I2C_RXFIFO_FULL_INT_ENA; // (BIT(0)) trigger emptyRxFifo()
i2c->dev->int_ena.val = interruptsEnabled;
if(!i2c->intr_handle) { // create ISR for either peripheral
// log_i("create ISR %d",i2c->num);
uint32_t ret = 0;
uint32_t flags = ESP_INTR_FLAG_EDGE | //< Edge-triggered interrupt
ESP_INTR_FLAG_IRAM | //< ISR can be called if cache is disabled
ESP_INTR_FLAG_LOWMED; //< Low and medium prio interrupts. These can be handled in C.
uint32_t flags = ESP_INTR_FLAG_IRAM | //< ISR can be called if cache is disabled
ESP_INTR_FLAG_LOWMED | //< Low and medium prio interrupts. These can be handled in C.
ESP_INTR_FLAG_SHARED; //< Reduce resource requirements, Share interrupts
if(i2c->num) {
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x7FF, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT1_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
} else {
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, 0x7FF, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, (uint32_t)&i2c->dev->int_status.val, interruptsEnabled, &i2c_isr_handler_default,i2c, &i2c->intr_handle);
}
if(ret!=ESP_OK) {
@ -983,7 +1222,6 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
return I2C_ERROR_MEMORY;
}
}
//hang until it completes.
// how many ticks should it take to transfer totalBytes through the I2C hardware,
@ -1052,7 +1290,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
i2c->dev->int_ena.val =0;
i2c->dev->int_clr.val = 0x1FFF;
i2c_update_error_byte_cnt(i2c);
if(i2c->errorByteCnt == 0) { // Bus Busy no bytes Moved
if((i2c->errorByteCnt == 0)&&(i2c->errorQueue==0)) { // Bus Busy no bytes Moved
reason = I2C_ERROR_BUSY;
eBits = eBits | EVENT_ERROR_BUS_BUSY|EVENT_ERROR|EVENT_DONE;
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
@ -1103,6 +1341,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
}
b++;
}
if(i2c->debugFlags & 0x00ff0000) i2cTriggerDumps(i2c,(i2c->debugFlags>>16),"after ProcQueue");
I2C_MUTEX_UNLOCK();
return reason;
@ -1117,33 +1356,32 @@ static void i2cReleaseISR(i2c_t * i2c)
}
static bool i2cCheckLineState(int8_t sda, int8_t scl){
if(sda < 0 || scl < 0){
return true;//return true since there is nothing to do
if(sda < 0 || scl < 0){
return false;//return false since there is nothing to do
}
// if the bus is not 'clear' try the recommended recovery sequence, START, 9 Clocks, STOP
// if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles
digitalWrite(sda, HIGH);
digitalWrite(scl, HIGH);
pinMode(sda, PULLUP|OPEN_DRAIN|OUTPUT|INPUT);
pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT|INPUT);
pinMode(sda, PULLUP|OPEN_DRAIN|INPUT);
pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT);
if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state
log_w("invalid state sda=%d, scl=%d\n", digitalRead(sda), digitalRead(scl));
digitalWrite(sda, HIGH);
log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, digitalRead(sda), scl, digitalRead(scl));
digitalWrite(scl, HIGH);
delayMicroseconds(5);
digitalWrite(sda, LOW);
for(uint8_t a=0; a<9; a++) {
delayMicroseconds(5);
digitalWrite(scl, LOW);
delayMicroseconds(5);
digitalWrite(scl, HIGH);
if(digitalRead(sda)){ // bus recovered
log_d("Recovered after %d Cycles",a+1);
break;
}
}
delayMicroseconds(5);
digitalWrite(sda, HIGH);
}
if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state
log_e("Bus Invalid State, TwoWire() Can't init");
log_e("Bus Invalid State, TwoWire() Can't init sda=%d, scl=%d",digitalRead(sda),digitalRead(scl));
return false; // bus is busy
}
return true;
@ -1311,6 +1549,8 @@ i2c_err_t i2cFlush(i2c_t * i2c)
if(i2c==NULL) {
return I2C_ERROR_DEV;
}
i2cTriggerDumps(i2c,i2c->debugFlags & 0xff, "FLUSH");
// need to grab a MUTEX for exclusive Queue,
// what out if ISR is running?
i2c_err_t rc=I2C_ERROR_OK;
@ -1425,91 +1665,27 @@ uint32_t i2cGetFrequency(i2c_t * i2c)
}
/* Stickbreaker ISR mode debug support
*/
void i2cDumpDqData(i2c_t * i2c)
{
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
uint16_t a=0;
char buff[140];
I2C_DATA_QUEUE_t *tdq;
while(a<i2c->queueCount) {
tdq=&i2c->dq[a];
log_e("[%d] %sbit %x %c %s buf@=%p, len=%d, pos=%d, eventH=%p bits=%x",a,
(tdq->ctrl.addr>0x100)?"10":"7",
(tdq->ctrl.addr>0x100)?(((tdq->ctrl.addr&0x600)>>1)|(tdq->ctrl.addr&0xff)):(tdq->ctrl.addr>>1),
(tdq->ctrl.mode)?'R':'W',
(tdq->ctrl.stop)?"STOP":"",
tdq->data,tdq->length,tdq->position,tdq->queueEvent,(tdq->queueEvent)?xEventGroupGetBits(tdq->queueEvent):0);
uint16_t offset = 0;
while(offset<tdq->length) {
memset(buff,' ',140);
buff[139]='\0';
uint16_t i = 0,j;
j=sprintf(buff,"0x%04x: ",offset);
while((i<32)&&(offset < tdq->length)) {
char ch = tdq->data[offset];
sprintf((char*)&buff[(i*3)+41],"%02x ",ch);
if((ch<32)||(ch>126)) {
ch='.';
}
j+=sprintf((char*)&buff[j],"%c",ch);
buff[j]=' ';
i++;
offset++;
}
log_e("%s",buff);
}
a++;
uint32_t i2cDebug(i2c_t * i2c, uint32_t setBits, uint32_t resetBits){
if(i2c != NULL) {
i2c->debugFlags = ((i2c->debugFlags | setBits) & ~resetBits);
return i2c->debugFlags;
}
#endif
}
void i2cDumpI2c(i2c_t * i2c)
{
log_e("i2c=%p",i2c);
log_e("dev=%p date=%p",i2c->dev,i2c->dev->date);
#if !CONFIG_DISABLE_HAL_LOCKS
log_e("lock=%p",i2c->lock);
#endif
log_e("num=%d",i2c->num);
log_e("mode=%d",i2c->mode);
log_e("stage=%d",i2c->stage);
log_e("error=%d",i2c->error);
log_e("event=%p bits=%x",i2c->i2c_event,(i2c->i2c_event)?xEventGroupGetBits(i2c->i2c_event):0);
log_e("intr_handle=%p",i2c->intr_handle);
log_e("dq=%p",i2c->dq);
log_e("queueCount=%d",i2c->queueCount);
log_e("queuePos=%d",i2c->queuePos);
log_e("errorByteCnt=%d",i2c->errorByteCnt);
log_e("errorQueue=%d",i2c->errorQueue);
if(i2c->dq) {
i2cDumpDqData(i2c);
return 0;
}
uint32_t i2cGetStatus(i2c_t * i2c){
if(i2c != NULL){
return i2c->dev->status_reg.val;
}
else return 0;
}
void i2cDumpInts(uint8_t num)
{
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER)
uint32_t b;
log_e("%u row count INTR TX RX",num);
for(uint32_t a=1; a<=INTBUFFMAX; a++) {
b=(a+intPos[num])%INTBUFFMAX;
if(intBuff[b][0][num]!=0) {
log_e("[%02d] 0x%04x 0x%04x 0x%04x 0x%04x 0x%08x",b,((intBuff[b][0][num]>>16)&0xFFFF),(intBuff[b][0][num]&0xFFFF),((intBuff[b][1][num]>>16)&0xFFFF),(intBuff[b][1][num]&0xFFFF),intBuff[b][2][num]);
}
}
#else
log_i("Debug Buffer not Enabled");
#endif
}
/* todo
22JUL18
need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads
transactions are present in the same queue, and an error occurs, abort all succeeding unserviced transactions
with the same dq.queueEvent value. Succeeding unserviced transactions with different dq.queueEvent values
can be re-queued and processed independently.
30JUL18 complete data only queue elements, this will allow transfers to use multiple data blocks,
*/

View File

@ -48,6 +48,7 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, b
i2c_err_t i2cFlush(i2c_t *i2c);
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed);
uint32_t i2cGetFrequency(i2c_t * i2c);
uint32_t i2cGetStatus(i2c_t * i2c); // Status register of peripheral
//Functions below should be used only if well understood
//Might be deprecated and removed in future
@ -62,8 +63,17 @@ i2c_err_t i2cAddQueueWrite(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr,
i2c_err_t i2cAddQueueRead(i2c_t *i2c, uint16_t i2cDeviceAddr, uint8_t *dataPtr, uint16_t dataLen, bool SendStop, EventGroupHandle_t event);
//stickbreaker debug support
void i2cDumpInts(uint8_t num);
void i2cDumpI2c(i2c_t *i2c);
uint32_t i2cDebug(i2c_t *, uint32_t setBits, uint32_t resetBits);
// Debug actions have 3 currently defined locus
// 0xXX------ : at entry of ProcQueue
// 0x--XX---- : at exit of ProcQueue
// 0x------XX : at entry of Flush
//
// bit 0 causes DumpI2c to execute
// bit 1 causes DumpInts to execute
// bit 2 causes DumpCmdqueue to execute
// bit 3 causes DumpStatus to execute
// bit 4 causes DumpFifo to execute
#ifdef __cplusplus
}

View File

@ -79,38 +79,50 @@ int log_printf(const char *fmt, ...);
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
#else
#define log_v(format, ...)
#define isr_log_v(format, ...)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
#else
#define log_d(format, ...)
#define isr_log_d(format, ...)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
#else
#define log_i(format, ...)
#define isr_log_i(format, ...)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN
#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
#else
#define log_w(format, ...)
#define isr_log_w(format, ...)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#else
#define log_e(format, ...)
#define isr_log_e(format, ...)
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE
#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
#else
#define log_n(format, ...)
#define isr_log_n(format, ...)
#endif
#include "esp_log.h"
@ -121,12 +133,22 @@ int log_printf(const char *fmt, ...);
#undef ESP_LOGI
#undef ESP_LOGD
#undef ESP_LOGV
#undef ESP_EARLY_LOGE
#undef ESP_EARLY_LOGW
#undef ESP_EARLY_LOGI
#undef ESP_EARLY_LOGD
#undef ESP_EARLY_LOGV
#define ESP_LOGE(tag, ...) log_e(__VA_ARGS__)
#define ESP_LOGW(tag, ...) log_w(__VA_ARGS__)
#define ESP_LOGI(tag, ...) log_i(__VA_ARGS__)
#define ESP_LOGD(tag, ...) log_d(__VA_ARGS__)
#define ESP_LOGV(tag, ...) log_v(__VA_ARGS__)
#define ESP_EARLY_LOGE(tag, ...) isr_log_e(__VA_ARGS__)
#define ESP_EARLY_LOGW(tag, ...) isr_log_w(__VA_ARGS__)
#define ESP_EARLY_LOGI(tag, ...) isr_log_i(__VA_ARGS__)
#define ESP_EARLY_LOGD(tag, ...) isr_log_d(__VA_ARGS__)
#define ESP_EARLY_LOGV(tag, ...) isr_log_v(__VA_ARGS__)
#endif
#ifdef __cplusplus

View File

@ -21,7 +21,9 @@
#include "esp_partition.h"
#include "esp_log.h"
#include "esp_timer.h"
#ifdef CONFIG_BT_ENABLED
#include "esp_bt.h"
#endif //CONFIG_BT_ENABLED
#include <sys/time.h>
#include "esp32-hal.h"

821
cores/esp32/esp32-hal-rmt.c Normal file
View File

@ -0,0 +1,821 @@
// Copyright 2018 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 "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "esp32-hal.h"
#include "esp8266-compat.h"
#include "soc/gpio_reg.h"
#include "soc/gpio_reg.h"
#include "esp32-hal-rmt.h"
#include "driver/periph_ctrl.h"
#include "soc/rmt_struct.h"
#include "esp_intr_alloc.h"
/**
* Internal macros
*/
#define MAX_CHANNELS 8
#define MAX_DATA_PER_CHANNEL 64
#define MAX_DATA_PER_ITTERATION 62
#define _ABS(a) (a>0?a:-a)
#define _LIMIT(a,b) (a>b?b:a)
#define __INT_TX_END (1)
#define __INT_RX_END (2)
#define __INT_ERROR (4)
#define __INT_THR_EVNT (1<<24)
#define _INT_TX_END(channel) (__INT_TX_END<<(channel*3))
#define _INT_RX_END(channel) (__INT_RX_END<<(channel*3))
#define _INT_ERROR(channel) (__INT_ERROR<<(channel*3))
#define _INT_THR_EVNT(channel) ((__INT_THR_EVNT)<<(channel))
#if CONFIG_DISABLE_HAL_LOCKS
# define RMT_MUTEX_LOCK(channel)
# define RMT_MUTEX_UNLOCK(channel)
#else
# define RMT_MUTEX_LOCK(channel) do {} while (xSemaphoreTake(g_rmt_objlocks[channel], portMAX_DELAY) != pdPASS)
# define RMT_MUTEX_UNLOCK(channel) xSemaphoreGive(g_rmt_objlocks[channel])
#endif /* CONFIG_DISABLE_HAL_LOCKS */
#define _RMT_INTERNAL_DEBUG
#ifdef _RMT_INTERNAL_DEBUG
# define DEBUG_INTERRUPT_START(pin) digitalWrite(pin, 1);
# define DEBUG_INTERRUPT_END(pin) digitalWrite(pin, 0);
#else
# define DEBUG_INTERRUPT_START(pin)
# define DEBUG_INTERRUPT_END(pin)
#endif /* _RMT_INTERNAL_DEBUG */
/**
* Typedefs for internal stuctures, enums
*/
typedef enum {
E_NO_INTR = 0,
E_TX_INTR = 1,
E_TXTHR_INTR = 2,
E_RX_INTR = 4,
} intr_mode_t;
typedef enum {
E_INACTIVE = 0,
E_FIRST_HALF = 1,
E_LAST_DATA = 2,
E_END_TRANS = 4,
E_SET_CONTI = 8,
} transaction_state_t;
struct rmt_obj_s
{
bool allocated;
EventGroupHandle_t events;
int pin;
int channel;
bool tx_not_rx;
int buffers;
int data_size;
uint32_t* data_ptr;
intr_mode_t intr_mode;
transaction_state_t tx_state;
rmt_rx_data_cb_t cb;
bool data_alloc;
};
/**
* Internal variables for channel descriptors
*/
static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = {
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
{ false, NULL, 0, 0, 0, 0, 0, NULL, E_NO_INTR, E_INACTIVE, NULL, false},
};
/**
* Internal variables for driver data
*/
static intr_handle_t intr_handle;
static bool periph_enabled = false;
static xSemaphoreHandle g_rmt_block_lock = NULL;
/**
* Internal method (private) declarations
*/
static void _initPin(int pin, int channel, bool tx_not_rx);
static bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
static void IRAM_ATTR _rmt_isr(void* arg);
static rmt_obj_t* _rmtAllocate(int pin, int from, int size);
static void _initPin(int pin, int channel, bool tx_not_rx);
static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel);
static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch);
static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch);
/**
* Public method definitions
*/
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high)
{
if (!rmt || low > 0xFFFF || high > 0xFFFF) {
return false;
}
size_t channel = rmt->channel;
RMT_MUTEX_LOCK(channel);
RMT.carrier_duty_ch[channel].low = low;
RMT.carrier_duty_ch[channel].low = high;
RMT.conf_ch[channel].conf0.carrier_en = carrier_en;
RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level;
RMT_MUTEX_UNLOCK(channel);
return true;
}
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level)
{
if (!rmt || filter_level > 0xFF) {
return false;
}
size_t channel = rmt->channel;
RMT_MUTEX_LOCK(channel);
RMT.conf_ch[channel].conf1.rx_filter_thres = filter_level;
RMT.conf_ch[channel].conf1.rx_filter_en = filter_en;
RMT_MUTEX_UNLOCK(channel);
return true;
}
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value)
{
if (!rmt || value > 0xFFFF) {
return false;
}
size_t channel = rmt->channel;
RMT_MUTEX_LOCK(channel);
RMT.conf_ch[channel].conf0.idle_thres = value;
RMT_MUTEX_UNLOCK(channel);
return true;
}
bool rmtDeinit(rmt_obj_t *rmt)
{
if (!rmt) {
return false;
}
// sanity check
if (rmt != &(g_rmt_objects[rmt->channel])) {
return false;
}
size_t from = rmt->channel;
size_t to = rmt->buffers + rmt->channel;
size_t i;
#if !CONFIG_DISABLE_HAL_LOCKS
if(g_rmt_objlocks[from] != NULL) {
vSemaphoreDelete(g_rmt_objlocks[from]);
}
#endif
if (g_rmt_objects[from].data_alloc) {
free(g_rmt_objects[from].data_ptr);
}
for (i = from; i < to; i++) {
g_rmt_objects[i].allocated = false;
}
g_rmt_objects[from].channel = 0;
g_rmt_objects[from].buffers = 0;
return true;
}
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
{
if (!rmt) {
return false;
}
int channel = rmt->channel;
int allocated_size = MAX_DATA_PER_CHANNEL * rmt->buffers;
if (size > allocated_size) {
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
RMT_MUTEX_LOCK(channel);
// setup interrupt handler if not yet installed for half and full tx
if (!intr_handle) {
esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle);
}
rmt->data_size = size - MAX_DATA_PER_ITTERATION;
rmt->data_ptr = ((uint32_t*)data) + MAX_DATA_PER_ITTERATION;
rmt->intr_mode = E_TX_INTR | E_TXTHR_INTR;
rmt->tx_state = E_SET_CONTI | E_FIRST_HALF;
// init the tx limit for interruption
RMT.tx_lim_ch[channel].limit = half_tx_nr+2;
// reset memory pointer
RMT.conf_ch[channel].conf1.apb_mem_rst = 1;
RMT.conf_ch[channel].conf1.apb_mem_rst = 0;
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
RMT.conf_ch[channel].conf1.mem_rd_rst = 0;
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
RMT.conf_ch[channel].conf1.mem_wr_rst = 0;
// set the tx end mark
RMTMEM.chan[channel].data32[MAX_DATA_PER_ITTERATION].val = 0;
// clear and enable both Tx completed and half tx event
RMT.int_clr.val = _INT_TX_END(channel);
RMT.int_clr.val = _INT_THR_EVNT(channel);
RMT.int_clr.val = _INT_ERROR(channel);
RMT.int_ena.val |= _INT_TX_END(channel);
RMT.int_ena.val |= _INT_THR_EVNT(channel);
RMT.int_ena.val |= _INT_ERROR(channel);
RMT_MUTEX_UNLOCK(channel);
// start the transation
return _rmtSendOnce(rmt, data, MAX_DATA_PER_ITTERATION);
} else {
// use one-go mode if data fits one buffer
return _rmtSendOnce(rmt, data, size);
}
}
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size)
{
if (!rmt) {
return false;
}
int channel = rmt->channel;
if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) {
return false;
}
size_t i;
volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val);
for (i=0; i<size; i++) {
data[i] = *rmt_mem_ptr++;
}
return true;
}
bool rmtBeginReceive(rmt_obj_t* rmt)
{
if (!rmt) {
return false;
}
int channel = rmt->channel;
RMT.int_clr.val = _INT_ERROR(channel);
RMT.int_ena.val |= _INT_ERROR(channel);
RMT.conf_ch[channel].conf1.mem_owner = 1;
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
RMT.conf_ch[channel].conf1.rx_en = 1;
return true;
}
bool rmtReceiveCompleted(rmt_obj_t* rmt)
{
if (!rmt) {
return false;
}
int channel = rmt->channel;
if (RMT.int_raw.val&_INT_RX_END(channel)) {
// RX end flag
RMT.int_clr.val = _INT_RX_END(channel);
return true;
} else {
return false;
}
}
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb)
{
if (!rmt && !cb) {
return false;
}
int channel = rmt->channel;
RMT_MUTEX_LOCK(channel);
rmt->intr_mode = E_RX_INTR;
rmt->tx_state = E_FIRST_HALF;
rmt->cb = cb;
// allocate internally two buffers which would alternate
if (!rmt->data_alloc) {
rmt->data_ptr = (uint32_t*)malloc(2*MAX_DATA_PER_CHANNEL*(rmt->buffers)*sizeof(uint32_t));
rmt->data_size = MAX_DATA_PER_CHANNEL*rmt->buffers;
rmt->data_alloc = true;
}
RMT.conf_ch[channel].conf1.mem_owner = 1;
RMT.int_clr.val = _INT_RX_END(channel);
RMT.int_clr.val = _INT_ERROR(channel);
RMT.int_ena.val |= _INT_RX_END(channel);
RMT.int_ena.val |= _INT_ERROR(channel);
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
RMT.conf_ch[channel].conf1.rx_en = 1;
RMT_MUTEX_UNLOCK(channel);
return true;
}
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout)
{
if (!rmt) {
return false;
}
int channel = rmt->channel;
if (g_rmt_objects[channel].buffers < size/MAX_DATA_PER_CHANNEL) {
return false;
}
if (eventFlag) {
xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL);
rmt->events = eventFlag;
}
if (data && size>0) {
rmt->data_ptr = (uint32_t*)data;
rmt->data_size = size;
}
RMT_MUTEX_LOCK(channel);
rmt->intr_mode = E_RX_INTR;
RMT.conf_ch[channel].conf1.mem_owner = 1;
RMT.int_clr.val = _INT_RX_END(channel);
RMT.int_clr.val = _INT_ERROR(channel);
RMT.int_ena.val |= _INT_RX_END(channel);
RMT.int_ena.val |= _INT_ERROR(channel);
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
RMT.conf_ch[channel].conf1.rx_en = 1;
RMT_MUTEX_UNLOCK(channel);
// wait for data if requested so
if (waitForData && eventFlag) {
uint32_t flags = xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL,
pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout);
if (flags & RMT_FLAG_ERROR) {
return false;
}
}
return true;
}
float rmtSetTick(rmt_obj_t* rmt, float tick)
{
if (!rmt) {
return false;
}
/*
divider field span from 1 (smallest), 2, 3, ... , 0xFF, 0x00 (highest)
* rmt tick from 1/80M -> 12.5ns (1x) div_cnt = 0x01
3.2 us (256x) div_cnt = 0x00
* rmt tick for 1 MHz -> 1us (1x) div_cnt = 0x01
256us (256x) div_cnt = 0x00
*/
int apb_div = _LIMIT(tick/12.5, 256);
int ref_div = _LIMIT(tick/1000, 256);
float apb_tick = 12.5 * apb_div;
float ref_tick = 1000.0 * ref_div;
size_t channel = rmt->channel;
if (_ABS(apb_tick - tick) < _ABS(ref_tick - tick)) {
RMT.conf_ch[channel].conf0.div_cnt = apb_div & 0xFF;
RMT.conf_ch[channel].conf1.ref_always_on = 1;
return apb_tick;
} else {
RMT.conf_ch[channel].conf0.div_cnt = ref_div & 0xFF;
RMT.conf_ch[channel].conf1.ref_always_on = 0;
return ref_tick;
}
}
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize)
{
int buffers = memsize;
rmt_obj_t* rmt;
size_t i;
size_t j;
// create common block mutex for protecting allocs from multiple threads
if (!g_rmt_block_lock) {
g_rmt_block_lock = xSemaphoreCreateMutex();
}
// lock
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
for (i=0; i<MAX_CHANNELS; i++) {
for (j=0; j<buffers && i+j < MAX_CHANNELS; j++) {
// if the space is ocupied break and continue on other channel
if (g_rmt_objects[i+j].allocated) {
i += j; // continue searching from latter channel
break;
}
}
if (j == buffers) {
// found a space in channel descriptors
break;
}
}
if (i == MAX_CHANNELS || i+j >= MAX_CHANNELS || j != buffers) {
xSemaphoreGive(g_rmt_block_lock);
return NULL;
}
rmt = _rmtAllocate(pin, i, buffers);
xSemaphoreGive(g_rmt_block_lock);
size_t channel = i;
#if !CONFIG_DISABLE_HAL_LOCKS
if(g_rmt_objlocks[channel] == NULL) {
g_rmt_objlocks[channel] = xSemaphoreCreateMutex();
if(g_rmt_objlocks[channel] == NULL) {
return NULL;
}
}
#endif
RMT_MUTEX_LOCK(channel);
rmt->pin = pin;
rmt->tx_not_rx = tx_not_rx;
rmt->buffers =buffers;
rmt->channel = channel;
_initPin(pin, channel, tx_not_rx);
// Initialize the registers in default mode:
// - no carrier, filter
// - timebase tick of 1us
// - idle threshold set to 0x8000 (max pulse width + 1)
RMT.conf_ch[channel].conf0.div_cnt = 1;
RMT.conf_ch[channel].conf0.mem_size = buffers;
RMT.conf_ch[channel].conf0.carrier_en = 0;
RMT.conf_ch[channel].conf0.carrier_out_lv = 0;
RMT.conf_ch[channel].conf0.mem_pd = 0;
RMT.conf_ch[channel].conf0.idle_thres = 0x80;
RMT.conf_ch[channel].conf1.rx_en = 0;
RMT.conf_ch[channel].conf1.tx_conti_mode = 0;
RMT.conf_ch[channel].conf1.ref_cnt_rst = 0;
RMT.conf_ch[channel].conf1.rx_filter_en = 0;
RMT.conf_ch[channel].conf1.rx_filter_thres = 0;
RMT.conf_ch[channel].conf1.idle_out_lv = 0; // signal level for idle
RMT.conf_ch[channel].conf1.idle_out_en = 1; // enable idle
RMT.conf_ch[channel].conf1.ref_always_on = 0; // base clock
RMT.apb_conf.fifo_mask = 1;
if (tx_not_rx) {
// RMT.conf_ch[channel].conf1.rx_en = 0;
RMT.conf_ch[channel].conf1.mem_owner = 0;
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
} else {
// RMT.conf_ch[channel].conf1.rx_en = 1;
RMT.conf_ch[channel].conf1.mem_owner = 1;
RMT.conf_ch[channel].conf1.mem_wr_rst = 1;
}
// install interrupt if at least one channel is active
if (!intr_handle) {
esp_intr_alloc(ETS_RMT_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, _rmt_isr, NULL, &intr_handle);
}
RMT_MUTEX_UNLOCK(channel);
return rmt;
}
/**
* Private methods definitions
*/
bool _rmtSendOnce(rmt_obj_t* rmt, rmt_data_t* data, size_t size)
{
if (!rmt) {
return false;
}
int channel = rmt->channel;
RMT.apb_conf.fifo_mask = 1;
if (data && size>0) {
size_t i;
volatile uint32_t* rmt_mem_ptr = &(RMTMEM.chan[channel].data32[0].val);
for (i = 0; i < size; i++) {
*rmt_mem_ptr++ = data[i].val;
}
// tx end mark
RMTMEM.chan[channel].data32[size].val = 0;
}
RMT_MUTEX_LOCK(channel);
RMT.conf_ch[channel].conf1.mem_rd_rst = 1;
RMT.conf_ch[channel].conf1.tx_start = 1;
RMT_MUTEX_UNLOCK(channel);
return true;
}
static rmt_obj_t* _rmtAllocate(int pin, int from, int size)
{
size_t i;
// setup how many buffers shall we use
g_rmt_objects[from].buffers = size;
for (i=0; i<size; i++) {
// mark the block of channels as used
g_rmt_objects[i+from].allocated = true;
}
return &(g_rmt_objects[from]);
}
static void _initPin(int pin, int channel, bool tx_not_rx)
{
if (!periph_enabled) {
periph_enabled = true;
periph_module_enable( PERIPH_RMT_MODULE );
}
if (tx_not_rx) {
pinMode(pin, OUTPUT);
pinMatrixOutAttach(pin, RMT_SIG_OUT0_IDX + channel, 0, 0);
} else {
pinMode(pin, INPUT);
pinMatrixInAttach(pin, RMT_SIG_IN0_IDX + channel, 0);
}
}
static void IRAM_ATTR _rmt_isr(void* arg)
{
int intr_val = RMT.int_st.val;
size_t ch;
for (ch = 0; ch < MAX_CHANNELS; ch++) {
if (intr_val & _INT_RX_END(ch)) {
// clear the flag
RMT.int_clr.val = _INT_RX_END(ch);
RMT.int_ena.val &= ~_INT_RX_END(ch);
if ((g_rmt_objects[ch].intr_mode) & E_RX_INTR) {
if (g_rmt_objects[ch].events) {
xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_RX_DONE);
}
if (g_rmt_objects[ch].data_ptr && g_rmt_objects[ch].data_size > 0) {
size_t i;
uint32_t * data = g_rmt_objects[ch].data_ptr;
// in case of callback, provide switching between memories
if (g_rmt_objects[ch].cb) {
if (g_rmt_objects[ch].tx_state & E_FIRST_HALF) {
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
} else {
g_rmt_objects[ch].tx_state |= E_FIRST_HALF;
data += MAX_DATA_PER_CHANNEL*(g_rmt_objects[ch].buffers);
}
}
for (i = 0; i < g_rmt_objects[ch].data_size; i++ ) {
*data++ = RMTMEM.chan[ch].data32[i].val;
}
if (g_rmt_objects[ch].cb) {
// actually received data ptr
uint32_t * data = g_rmt_objects[ch].data_ptr;
(g_rmt_objects[ch].cb)(data, _rmt_get_mem_len(ch));
// restart the reception
RMT.conf_ch[ch].conf1.mem_owner = 1;
RMT.conf_ch[ch].conf1.mem_wr_rst = 1;
RMT.conf_ch[ch].conf1.rx_en = 1;
RMT.int_ena.val |= _INT_RX_END(ch);
} else {
// if not callback provide, expect only one Rx
g_rmt_objects[ch].intr_mode &= ~E_RX_INTR;
}
}
} else {
// Report error and disable Rx interrupt
log_e("Unexpected Rx interrupt!\n"); // TODO: eplace messages with log_X
RMT.int_ena.val &= ~_INT_RX_END(ch);
}
}
if (intr_val & _INT_ERROR(ch)) {
digitalWrite(2, 1);
// clear the flag
RMT.int_clr.val = _INT_ERROR(ch);
RMT.int_ena.val &= ~_INT_ERROR(ch);
// report error
log_e("RMT Error %d!\n", ch);
if (g_rmt_objects[ch].events) {
xEventGroupSetBits(g_rmt_objects[ch].events, RMT_FLAG_ERROR);
}
// reset memory
RMT.conf_ch[ch].conf1.mem_rd_rst = 1;
RMT.conf_ch[ch].conf1.mem_rd_rst = 0;
RMT.conf_ch[ch].conf1.mem_wr_rst = 1;
RMT.conf_ch[ch].conf1.mem_wr_rst = 0;
}
if (intr_val & _INT_TX_END(ch)) {
RMT.int_clr.val = _INT_TX_END(ch);
_rmt_tx_mem_second(ch);
}
if (intr_val & _INT_THR_EVNT(ch)) {
// clear the flag
RMT.int_clr.val = _INT_THR_EVNT(ch);
// initial setup of continuous mode
if (g_rmt_objects[ch].tx_state & E_SET_CONTI) {
RMT.conf_ch[ch].conf1.tx_conti_mode = 1;
g_rmt_objects[ch].intr_mode &= ~E_SET_CONTI;
}
_rmt_tx_mem_first(ch);
}
}
}
static void IRAM_ATTR _rmt_tx_mem_second(uint8_t ch)
{
DEBUG_INTERRUPT_START(4)
uint32_t* data = g_rmt_objects[ch].data_ptr;
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
int i;
RMT.tx_lim_ch[ch].limit = half_tx_nr+2;
RMT.int_clr.val = _INT_THR_EVNT(ch);
RMT.int_ena.val |= _INT_THR_EVNT(ch);
g_rmt_objects[ch].tx_state |= E_FIRST_HALF;
if (data) {
int remaining_size = g_rmt_objects[ch].data_size;
// will the remaining data occupy the entire halfbuffer
if (remaining_size > half_tx_nr) {
for (i = 0; i < half_tx_nr; i++) {
RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i];
}
g_rmt_objects[ch].data_size -= half_tx_nr;
g_rmt_objects[ch].data_ptr += half_tx_nr;
} else {
for (i = 0; i < half_tx_nr; i++) {
if (i < remaining_size) {
RMTMEM.chan[ch].data32[half_tx_nr+i].val = data[i];
} else {
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F;
}
}
g_rmt_objects[ch].data_ptr = NULL;
}
} else if ((!(g_rmt_objects[ch].tx_state & E_LAST_DATA)) &&
(!(g_rmt_objects[ch].tx_state & E_END_TRANS))) {
for (i = 0; i < half_tx_nr; i++) {
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0x000F000F;
}
RMTMEM.chan[ch].data32[half_tx_nr+i].val = 0;
g_rmt_objects[ch].tx_state |= E_LAST_DATA;
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
} else {
log_d("RMT Tx finished %d!\n", ch);
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
RMT.int_ena.val &= ~_INT_TX_END(ch);
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
g_rmt_objects[ch].intr_mode = E_NO_INTR;
g_rmt_objects[ch].tx_state = E_INACTIVE;
}
DEBUG_INTERRUPT_END(4);
}
static void IRAM_ATTR _rmt_tx_mem_first(uint8_t ch)
{
DEBUG_INTERRUPT_START(2);
uint32_t* data = g_rmt_objects[ch].data_ptr;
int half_tx_nr = MAX_DATA_PER_ITTERATION/2;
int i;
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
RMT.tx_lim_ch[ch].limit = 0;
if (data) {
int remaining_size = g_rmt_objects[ch].data_size;
// will the remaining data occupy the entire halfbuffer
if (remaining_size > half_tx_nr) {
RMTMEM.chan[ch].data32[0].val = data[0] - 1;
for (i = 1; i < half_tx_nr; i++) {
RMTMEM.chan[ch].data32[i].val = data[i];
}
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
// turn off the treshold interrupt
RMT.int_ena.val &= ~_INT_THR_EVNT(ch);
RMT.tx_lim_ch[ch].limit = 0;
g_rmt_objects[ch].data_size -= half_tx_nr;
g_rmt_objects[ch].data_ptr += half_tx_nr;
} else {
RMTMEM.chan[ch].data32[0].val = data[0] - 1;
for (i = 1; i < half_tx_nr; i++) {
if (i < remaining_size) {
RMTMEM.chan[ch].data32[i].val = data[i];
} else {
RMTMEM.chan[ch].data32[i].val = 0x000F000F;
}
}
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
g_rmt_objects[ch].data_ptr = NULL;
}
} else {
for (i = 0; i < half_tx_nr; i++) {
RMTMEM.chan[ch].data32[i].val = 0x000F000F;
}
RMTMEM.chan[ch].data32[i].val = 0;
g_rmt_objects[ch].tx_state &= ~E_FIRST_HALF;
RMT.tx_lim_ch[ch].limit = 0;
g_rmt_objects[ch].tx_state |= E_LAST_DATA;
RMT.conf_ch[ch].conf1.tx_conti_mode = 0;
}
DEBUG_INTERRUPT_END(2);
}
static int IRAM_ATTR _rmt_get_mem_len(uint8_t channel)
{
int block_num = RMT.conf_ch[channel].conf0.mem_size;
int item_block_len = block_num * 64;
volatile rmt_item32_t* data = RMTMEM.chan[channel].data32;
int idx;
for(idx = 0; idx < item_block_len; idx++) {
if(data[idx].duration0 == 0) {
return idx;
} else if(data[idx].duration1 == 0) {
return idx + 1;
}
}
return idx;
}

137
cores/esp32/esp32-hal-rmt.h Normal file
View File

@ -0,0 +1,137 @@
// Copyright 2018 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.
#ifndef MAIN_ESP32_HAL_RMT_H_
#define MAIN_ESP32_HAL_RMT_H_
#ifdef __cplusplus
extern "C" {
#endif
// notification flags
#define RMT_FLAG_TX_DONE (1)
#define RMT_FLAG_RX_DONE (2)
#define RMT_FLAG_ERROR (4)
#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR)
struct rmt_obj_s;
typedef enum {
RMT_MEM_64 = 1,
RMT_MEM_128 = 2,
RMT_MEM_192 = 3,
RMT_MEM_256 = 4,
RMT_MEM_320 = 5,
RMT_MEM_384 = 6,
RMT_MEM_448 = 7,
RMT_MEM_512 = 8,
} rmt_reserve_memsize_t;
typedef struct rmt_obj_s rmt_obj_t;
typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len);
typedef struct {
union {
struct {
uint32_t duration0 :15;
uint32_t level0 :1;
uint32_t duration1 :15;
uint32_t level1 :1;
};
uint32_t val;
};
} rmt_data_t;
/**
* Initialize the object
*
*/
rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize);
/**
* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds
* return the real actual tick value in ns
*/
float rmtSetTick(rmt_obj_t* rmt, float tick);
/**
* Sending data in one-go mode or continual mode
* (more data being send while updating buffers in interrupts)
*
*/
bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size);
/**
* Initiates async receive, event flag indicates data received
*
*/
bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout);
/**
* Initiates async receive with automatic buffering
* and callback with data from ISR
*
*/
bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb);
/* Additional interface */
/**
* Start reception
*
*/
bool rmtBeginReceive(rmt_obj_t* rmt);
/**
* Checks if reception completes
*
*/
bool rmtReceiveCompleted(rmt_obj_t* rmt);
/**
* Reads the data for particular channel
*
*/
bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size);
/**
* Setting threshold for Rx completed
*/
bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value);
/**
* Setting carrier
*/
bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high);
/**
* Setting input filter
*/
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level);
// TODO:
// * uninstall interrupt when all channels are deinit
// * send only-conti mode with circular-buffer
// * put sanity checks to some macro or inlines
// * doxy comments
// * error reporting
#ifdef __cplusplus
}
#endif
#endif /* MAIN_ESP32_HAL_RMT_H_ */

View File

@ -13,13 +13,13 @@
// limitations under the License.
#include "esp32-hal.h"
#include "apps/sntp/sntp.h"
#include "lwip/apps/sntp.h"
static void setTimeZone(long offset, int daylight)
{
char cst[16] = {0};
char cdt[16] = "DST";
char tz[32] = {0};
char cst[17] = {0};
char cdt[17] = "DST";
char tz[33] = {0};
if(offset % 3600){
sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));

View File

@ -80,7 +80,7 @@ static void IRAM_ATTR _uart_isr(void *arg)
uart->dev->int_clr.rxfifo_full = 1;
uart->dev->int_clr.frm_err = 1;
uart->dev->int_clr.rxfifo_tout = 1;
while(uart->dev->status.rxfifo_cnt) {
while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
c = uart->dev->fifo.rw_byte;
if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
@ -240,6 +240,26 @@ void uartEnd(uart_t* uart)
uartDetachTx(uart);
}
size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
if(uart == NULL) {
return 0;
}
UART_MUTEX_LOCK();
if(uart->queue != NULL) {
uint8_t c;
while(xQueueReceive(uart->queue, &c, 0));
vQueueDelete(uart->queue);
uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
if(uart->queue == NULL) {
return NULL;
}
}
UART_MUTEX_UNLOCK();
return new_size;
}
uint32_t uartAvailable(uart_t* uart)
{
if(uart == NULL || uart->queue == NULL) {
@ -313,7 +333,7 @@ void uartFlush(uart_t* uart)
}
UART_MUTEX_LOCK();
while(uart->dev->status.txfifo_cnt);
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
//Due to hardware issue, we can not use fifo_rst to reset uart fifo.
//See description about UART_TXFIFO_RST and UART_RXFIFO_RST in <<esp32_technical_reference_manual>> v2.6 or later.
@ -435,3 +455,65 @@ int log_printf(const char *format, ...)
}
return len;
}
/*
* if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two.
* This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses.
*/
unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
{
while(uart->dev->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
if(flg) return 0;
ets_delay_us(1000);
}
UART_MUTEX_LOCK();
unsigned long ret = ((uart->dev->lowpulse.min_cnt + uart->dev->highpulse.min_cnt) >> 1) + 12;
UART_MUTEX_UNLOCK();
return ret;
}
/*
* To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is
* detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is
* rounded to the closed real baudrate.
*/
unsigned long
uartDetectBaudrate(uart_t *uart)
{
static bool uartStateDetectingBaudrate = false;
if(!uartStateDetectingBaudrate) {
uart->dev->auto_baud.glitch_filt = 0x08;
uart->dev->auto_baud.en = 0;
uart->dev->auto_baud.en = 1;
uartStateDetectingBaudrate = true;
}
unsigned long divisor = uartBaudrateDetect(uart, true);
if (!divisor) {
return 0;
}
uart->dev->auto_baud.en = 0;
uartStateDetectingBaudrate = false; // Initialize for the next round
unsigned long baudrate = UART_CLK_FREQ / divisor;
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
size_t i;
for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate
{
if (baudrate <= default_rates[i])
{
if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) {
i--;
}
break;
}
}
return default_rates[i];
}

View File

@ -67,9 +67,13 @@ void uartFlush(uart_t* uart);
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate);
uint32_t uartGetBaudRate(uart_t* uart);
size_t uartResizeRxBuffer(uart_t* uart, size_t new_size);
void uartSetDebug(uart_t* uart);
int uartGetDebug();
unsigned long uartDetectBaudrate(uart_t *uart);
#ifdef __cplusplus
}
#endif

View File

@ -57,6 +57,7 @@ void yield(void);
#include "esp32-hal-spi.h"
#include "esp32-hal-i2c.h"
#include "esp32-hal-ledc.h"
#include "esp32-hal-rmt.h"
#include "esp32-hal-sigmadelta.h"
#include "esp32-hal-timer.h"
#include "esp32-hal-bt.h"

View File

@ -7,13 +7,10 @@ For details, see http://sourceforge.net/projects/libb64
#include "cencode.h"
const int CHARS_PER_LINE = 72;
void base64_init_encodestate(base64_encodestate* state_in)
{
state_in->step = step_A;
state_in->result = 0;
state_in->stepcount = 0;
}
char base64_encode_value(char value_in)
@ -68,12 +65,6 @@ int base64_encode_block(const char* plaintext_in, int length_in, char* code_out,
*codechar++ = base64_encode_value(result);
result = (fragment & 0x03f) >> 0;
*codechar++ = base64_encode_value(result);
++(state_in->stepcount);
if (state_in->stepcount == CHARS_PER_LINE/4) {
*codechar++ = '\n';
state_in->stepcount = 0;
}
}
}
/* control should not reach here */

View File

@ -1,24 +1,32 @@
Please fill the info fields, it helps to get you faster support ;)
Make your question, not a Statement, inclusive. Include all pertinent information:
If you have a Guru Meditation Error, please decode it:
What you are trying to do.
Describe your system( Hardware, computer, O/S, core version, environment)
Describe what is failing
Show the shortest possible code that will duplicate the error
Show the EXACT error message(it doesn't work is not enough)
Then if someone is interested and knowledgeable you might get a answer. All of this work on your part shows us that you have worked to solve YOUR problem. The more complete your issue posting is, the more likely someone will volunteer their time to help you.
If you have a Guru Meditation Error or Backtrace, ***please decode it***:
https://github.com/me-no-dev/EspExceptionDecoder
----------------------------- Remove above -----------------------------
### Hardware:
Board: ?ESP32 Dev Module?
Board: ?ESP32 Dev Module? ?node32? ?ttgo_lora?
Core Installation/update date: ?11/jul/2017?
IDE name: ?Arduino IDE? ?Platform.io? ?IDF component?
Flash Frequency: ?40Mhz?
PSRAM enabled: ?no?
Upload Speed: ?115200?
Computer OS: ?Windows 10? ?Mac OSX? ?Ubuntu?
### Description:
Describe your problem here
### Sketch:
### Sketch: (leave the backquotes for [code formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks/))
```cpp
//Change the code below by your sketch

View File

@ -8,8 +8,6 @@ Starting with 1.6.4, Arduino allows installation of third-party platform package
- Enter ```https://dl.espressif.com/dl/package_esp32_index.json``` into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas.
- Open Boards Manager from Tools > Board menu and install *esp32* platform (and don't forget to select your ESP32 board from Tools > Board menu after installation).
#### [Latest stable release ![Release Version](https://img.shields.io/github/release/espressif/arduino-esp32.svg?style=plastic) ![Release Date](https://img.shields.io/github/release-date/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
Stable release link: `https://dl.espressif.com/dl/package_esp32_index.json`
#### [Latest development release ![Development Version](https://img.shields.io/github/release/espressif/arduino-esp32/all.svg?style=plastic) ![Development Date](https://img.shields.io/github/release-date-pre/espressif/arduino-esp32.svg?style=plastic)](https://github.com/espressif/arduino-esp32/releases/latest/)
Development release link: `https://dl.espressif.com/dl/package_esp32_dev_index.json`

View File

@ -22,14 +22,15 @@ Installation instructions for Debian / Ubuntu OS
- If you have Arduino.app installed to /Applications/, modify the installation as follows, beginning at `mkdir -p ~/Arduino...`:
- If you have Arduino installed to ~/, modify the installation as follows, beginning at `mkdir -p ~/Arduino/hardware`:
```bash
cd /Applications/Arduino_*/Contents/java/hardware/
```bash
cd ~/Arduino/hardware
mkdir -p espressif && \
cd espressif && \
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
cd esp32 && \
git submodule update --init --recursive && \
cd tools && \
python2 get.py```
python2 get.py
```

View File

@ -11,7 +11,7 @@ Installation instructions for Mac OS
cd esp32 && \
git submodule update --init --recursive && \
cd tools && \
python get.py
python get.py
```
Where `~/Documents/Arduino` represents your sketch book location as per "Arduino" > "Preferences" > "Sketchbook location" (in the IDE once started). Adjust the command above accordingly if necessary!
 
@ -20,6 +20,8 @@ Installation instructions for Mac OS
```xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun```
```xcode-select --install```
- Try `python3` instead of `python` if you get the error: `IOError: [Errno socket error] [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)` when running `python get.py`
- Restart Arduino IDE

View File

@ -8,6 +8,7 @@ extern "C" {
#include "lwip/igmp.h"
#include "lwip/ip_addr.h"
#include "lwip/mld6.h"
#include "lwip/prot/ethernet.h"
#include <esp_err.h>
#include <esp_wifi.h>
}
@ -15,7 +16,7 @@ extern "C" {
#include "lwip/priv/tcpip_priv.h"
typedef struct {
struct tcpip_api_call call;
struct tcpip_api_call_data call;
udp_pcb * pcb;
const ip_addr_t *addr;
uint16_t port;
@ -24,7 +25,7 @@ typedef struct {
err_t err;
} udp_api_call_t;
static err_t _udp_connect_api(struct tcpip_api_call *api_call_msg){
static err_t _udp_connect_api(struct tcpip_api_call_data *api_call_msg){
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
msg->err = udp_connect(msg->pcb, msg->addr, msg->port);
return msg->err;
@ -35,11 +36,11 @@ static err_t _udp_connect(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port
msg.pcb = pcb;
msg.addr = addr;
msg.port = port;
tcpip_api_call(_udp_connect_api, (struct tcpip_api_call*)&msg);
tcpip_api_call(_udp_connect_api, (struct tcpip_api_call_data*)&msg);
return msg.err;
}
static err_t _udp_disconnect_api(struct tcpip_api_call *api_call_msg){
static err_t _udp_disconnect_api(struct tcpip_api_call_data *api_call_msg){
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
msg->err = 0;
udp_disconnect(msg->pcb);
@ -49,10 +50,10 @@ static err_t _udp_disconnect_api(struct tcpip_api_call *api_call_msg){
static void _udp_disconnect(struct udp_pcb *pcb){
udp_api_call_t msg;
msg.pcb = pcb;
tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call*)&msg);
tcpip_api_call(_udp_disconnect_api, (struct tcpip_api_call_data*)&msg);
}
static err_t _udp_remove_api(struct tcpip_api_call *api_call_msg){
static err_t _udp_remove_api(struct tcpip_api_call_data *api_call_msg){
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
msg->err = 0;
udp_remove(msg->pcb);
@ -62,10 +63,10 @@ static err_t _udp_remove_api(struct tcpip_api_call *api_call_msg){
static void _udp_remove(struct udp_pcb *pcb){
udp_api_call_t msg;
msg.pcb = pcb;
tcpip_api_call(_udp_remove_api, (struct tcpip_api_call*)&msg);
tcpip_api_call(_udp_remove_api, (struct tcpip_api_call_data*)&msg);
}
static err_t _udp_bind_api(struct tcpip_api_call *api_call_msg){
static err_t _udp_bind_api(struct tcpip_api_call_data *api_call_msg){
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
msg->err = udp_bind(msg->pcb, msg->addr, msg->port);
return msg->err;
@ -76,11 +77,11 @@ static err_t _udp_bind(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port){
msg.pcb = pcb;
msg.addr = addr;
msg.port = port;
tcpip_api_call(_udp_bind_api, (struct tcpip_api_call*)&msg);
tcpip_api_call(_udp_bind_api, (struct tcpip_api_call_data*)&msg);
return msg.err;
}
static err_t _udp_sendto_api(struct tcpip_api_call *api_call_msg){
static err_t _udp_sendto_api(struct tcpip_api_call_data *api_call_msg){
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
msg->err = udp_sendto(msg->pcb, msg->pb, msg->addr, msg->port);
return msg->err;
@ -92,11 +93,11 @@ static err_t _udp_sendto(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_t *
msg.addr = addr;
msg.port = port;
msg.pb = pb;
tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call*)&msg);
tcpip_api_call(_udp_sendto_api, (struct tcpip_api_call_data*)&msg);
return msg.err;
}
static err_t _udp_sendto_if_api(struct tcpip_api_call *api_call_msg){
static err_t _udp_sendto_if_api(struct tcpip_api_call_data *api_call_msg){
udp_api_call_t * msg = (udp_api_call_t *)api_call_msg;
msg->err = udp_sendto_if(msg->pcb, msg->pb, msg->addr, msg->port, msg->netif);
return msg->err;
@ -109,7 +110,7 @@ static err_t _udp_sendto_if(struct udp_pcb *pcb, struct pbuf *pb, const ip_addr_
msg.port = port;
msg.pb = pb;
msg.netif = netif;
tcpip_api_call(_udp_sendto_if_api, (struct tcpip_api_call*)&msg);
tcpip_api_call(_udp_sendto_if_api, (struct tcpip_api_call_data*)&msg);
return msg.err;
}
@ -131,7 +132,7 @@ static void _udp_task(void *pvParameters){
if(xQueueReceive(_udp_queue, &e, portMAX_DELAY) == pdTRUE){
if(!e->pb){
free((void*)(e));
break;
continue;
}
AsyncUDP::_s_recv(e->arg, e->pcb, e->pb, e->addr, e->port, e->netif);
free((void*)(e));
@ -292,19 +293,23 @@ AsyncUDPPacket::AsyncUDPPacket(AsyncUDP *udp, pbuf *pb, const ip_addr_t *raddr,
_remoteIp.type = raddr->type;
_localIp.type = _remoteIp.type;
eth_hdr* eth = NULL;
udp_hdr* udphdr = reinterpret_cast<udp_hdr*>(_data - UDP_HLEN);
_localPort = ntohs(udphdr->dest);
_remotePort = ntohs(udphdr->src);
if (_remoteIp.type == IPADDR_TYPE_V4) {
eth = (eth_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP_HLEN - SIZEOF_ETH_HDR);
struct ip_hdr * iphdr = (struct ip_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP_HLEN);
_localIp.u_addr.ip4.addr = iphdr->dest.addr;
_remoteIp.u_addr.ip4.addr = iphdr->src.addr;
} else {
eth = (eth_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP6_HLEN - SIZEOF_ETH_HDR);
struct ip6_hdr * ip6hdr = (struct ip6_hdr *)(((uint8_t *)(pb->payload)) - UDP_HLEN - IP6_HLEN);
memcpy(&_localIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->dest.addr, 16);
memcpy(&_remoteIp.u_addr.ip6.addr, (uint8_t *)ip6hdr->src.addr, 16);
}
memcpy(_remoteMac, eth->src.addr, 6);
struct netif * netif = NULL;
void * nif = NULL;
@ -415,6 +420,11 @@ uint16_t AsyncUDPPacket::remotePort()
return _remotePort;
}
void AsyncUDPPacket::remoteMac(uint8_t * mac)
{
memcpy(mac, _remoteMac, 6);
}
bool AsyncUDPPacket::isIPv6()
{
return _localIp.type == IPADDR_TYPE_V6;
@ -452,16 +462,24 @@ size_t AsyncUDPPacket::send(AsyncUDPMessage &message)
return write(message.data(), message.length());
}
AsyncUDP::AsyncUDP()
{
bool AsyncUDP::_init(){
if(_pcb){
return true;
}
_pcb = udp_new();
_connected = false;
_handler = NULL;
if(!_pcb){
return;
return false;
}
//_lock = xSemaphoreCreateMutex();
udp_recv(_pcb, &_udp_recv, (void *) this);
return true;
}
AsyncUDP::AsyncUDP()
{
_pcb = NULL;
_connected = false;
_handler = NULL;
}
AsyncUDP::~AsyncUDP()
@ -483,8 +501,7 @@ void AsyncUDP::close()
_udp_disconnect(_pcb);
}
_connected = false;
_pcb->multicast_ip.type = IPADDR_TYPE_V4;
_pcb->multicast_ip.u_addr.ip4.addr = 0;
//todo: unjoin multicast group
}
UDP_MUTEX_UNLOCK();
}
@ -495,7 +512,7 @@ bool AsyncUDP::connect(const ip_addr_t *addr, uint16_t port)
log_e("failed to start task");
return false;
}
if(_pcb == NULL) {
if(!_init()) {
return false;
}
close();
@ -516,7 +533,7 @@ bool AsyncUDP::listen(const ip_addr_t *addr, uint16_t port)
log_e("failed to start task");
return false;
}
if(_pcb == NULL) {
if(!_init()) {
return false;
}
close();
@ -534,57 +551,53 @@ bool AsyncUDP::listen(const ip_addr_t *addr, uint16_t port)
return true;
}
static esp_err_t joinMulticastGroup(const ip_addr_t *addr, bool join, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX)
{
struct netif * netif = NULL;
if(tcpip_if < TCPIP_ADAPTER_IF_MAX){
void * nif = NULL;
esp_err_t err = tcpip_adapter_get_netif(tcpip_if, &nif);
if (err) {
return ESP_ERR_INVALID_ARG;
}
netif = (struct netif *)nif;
}
if (addr->type == IPADDR_TYPE_V4) {
if(join){
if (igmp_joingroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (igmp_leavegroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
return ESP_ERR_INVALID_STATE;
}
}
} else {
if(join){
if (mld6_joingroup_netif(netif, &(addr->u_addr.ip6))) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (mld6_leavegroup_netif(netif, &(addr->u_addr.ip6))) {
return ESP_ERR_INVALID_STATE;
}
}
}
return ESP_OK;
}
bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if)
{
if(!ip_addr_ismulticast(addr)) {
return false;
}
ip_addr_t multicast_if_addr;
uint8_t mode;
if(esp_wifi_get_mode((wifi_mode_t*)&mode)){
mode = WIFI_MODE_NULL;
}
if(addr->type == IPADDR_TYPE_V6){
multicast_if_addr.type = IPADDR_TYPE_V6;
if((tcpip_if == TCPIP_ADAPTER_IF_STA && (mode & WIFI_MODE_STA))
|| (tcpip_if == TCPIP_ADAPTER_IF_AP && (mode & WIFI_MODE_AP))
|| (tcpip_if == TCPIP_ADAPTER_IF_ETH)) {
if(tcpip_adapter_get_ip6_linklocal(tcpip_if, &multicast_if_addr.u_addr.ip6)){
return false;
}
} else {
return false;
}
if (mld6_joingroup(&(multicast_if_addr.u_addr.ip6), &(addr->u_addr.ip6))) {
return false;
}
} else if(addr->type == IPADDR_TYPE_V4){
tcpip_adapter_ip_info_t ifIpInfo;
if((tcpip_if == TCPIP_ADAPTER_IF_STA && (mode & WIFI_MODE_STA))
|| (tcpip_if == TCPIP_ADAPTER_IF_AP && (mode & WIFI_MODE_AP))
|| (tcpip_if == TCPIP_ADAPTER_IF_ETH)) {
if(tcpip_adapter_get_ip_info(tcpip_if, &ifIpInfo)){
return false;
}
} else {
return false;
}
multicast_if_addr.type = IPADDR_TYPE_V4;
multicast_if_addr.u_addr.ip4.addr = ifIpInfo.ip.addr;
if (igmp_joingroup((const ip4_addr *)&multicast_if_addr.u_addr.ip4, (const ip4_addr *)&addr->u_addr.ip4)!= ERR_OK) {
return false;
}
} else {
if (joinMulticastGroup(addr, true, tcpip_if)!= ERR_OK) {
return false;
}
if(!listen(&multicast_if_addr, port)) {
if(!listen(NULL, port)) {
return false;
}
@ -592,9 +605,7 @@ bool AsyncUDP::listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl
_pcb->mcast_ttl = ttl;
_pcb->remote_port = port;
ip_addr_copy(_pcb->remote_ip, *addr);
if(addr->type == IPADDR_TYPE_V4){
ip_addr_copy(_pcb->multicast_ip, multicast_if_addr);
}
//ip_addr_copy(_pcb->remote_ip, ip_addr_any_type);
UDP_MUTEX_UNLOCK();
return true;
@ -604,7 +615,7 @@ size_t AsyncUDP::writeTo(const uint8_t * data, size_t len, const ip_addr_t * add
{
if(!_pcb) {
UDP_MUTEX_LOCK();
_pcb = udp_new_ip_type(addr->type);
_pcb = udp_new();
UDP_MUTEX_UNLOCK();
if(_pcb == NULL) {
return 0;
@ -619,7 +630,7 @@ size_t AsyncUDP::writeTo(const uint8_t * data, size_t len, const ip_addr_t * add
uint8_t* dst = reinterpret_cast<uint8_t*>(pbt->payload);
memcpy(dst, data, len);
UDP_MUTEX_LOCK();
if(tcpip_if != TCPIP_ADAPTER_IF_MAX){
if(tcpip_if < TCPIP_ADAPTER_IF_MAX){
void * nif = NULL;
tcpip_adapter_get_netif((tcpip_adapter_if_t)tcpip_if, &nif);
if(!nif){

View File

@ -53,6 +53,7 @@ protected:
uint16_t _localPort;
ip_addr_t _remoteIp;
uint16_t _remotePort;
uint8_t _remoteMac[6];
uint8_t *_data;
size_t _len;
size_t _index;
@ -74,6 +75,7 @@ public:
IPAddress remoteIP();
IPv6Address remoteIPv6();
uint16_t remotePort();
void remoteMac(uint8_t * mac);
size_t send(AsyncUDPMessage &message);
@ -95,6 +97,7 @@ protected:
bool _connected;
AuPacketHandlerFunction _handler;
bool _init();
void _recv(udp_pcb *upcb, pbuf *pb, const ip_addr_t *addr, uint16_t port, struct netif * netif);
public:
@ -109,9 +112,9 @@ public:
bool listen(const IPv6Address addr, uint16_t port);
bool listen(uint16_t port);
bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_STA);
bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX);
bool connect(const ip_addr_t *addr, uint16_t port);
bool connect(const IPAddress addr, uint16_t port);

View File

@ -40,11 +40,117 @@
#include "esp32-hal-log.h"
#endif
const char * _spp_server_name = "ESP32_SPP_SERVER";
const char * _spp_server_name = "ESP32SPP";
#define QUEUE_SIZE 256
#define RX_QUEUE_SIZE 512
#define TX_QUEUE_SIZE 32
static uint32_t _spp_client = 0;
static xQueueHandle _spp_queue = NULL;
static xQueueHandle _spp_rx_queue = NULL;
static xQueueHandle _spp_tx_queue = NULL;
static SemaphoreHandle_t _spp_tx_done = NULL;
static TaskHandle_t _spp_task_handle = NULL;
static EventGroupHandle_t _spp_event_group = NULL;
static boolean secondConnectionAttempt;
static esp_spp_cb_t * custom_spp_callback = NULL;
#define SPP_RUNNING 0x01
#define SPP_CONNECTED 0x02
#define SPP_CONGESTED 0x04
typedef struct {
size_t len;
uint8_t data[];
} spp_packet_t;
static esp_err_t _spp_queue_packet(uint8_t *data, size_t len){
if(!data || !len){
log_w("No data provided");
return ESP_OK;
}
spp_packet_t * packet = (spp_packet_t*)malloc(sizeof(spp_packet_t) + len);
if(!packet){
log_e("SPP TX Packet Malloc Failed!");
return ESP_FAIL;
}
packet->len = len;
memcpy(packet->data, data, len);
if (xQueueSend(_spp_tx_queue, &packet, portMAX_DELAY) != pdPASS) {
log_e("SPP TX Queue Send Failed!");
free(packet);
return ESP_FAIL;
}
return ESP_OK;
}
const uint16_t SPP_TX_MAX = 330;
static uint8_t _spp_tx_buffer[SPP_TX_MAX];
static uint16_t _spp_tx_buffer_len = 0;
static bool _spp_send_buffer(){
if((xEventGroupWaitBits(_spp_event_group, SPP_CONGESTED, pdFALSE, pdTRUE, portMAX_DELAY) & SPP_CONGESTED)){
esp_err_t err = esp_spp_write(_spp_client, _spp_tx_buffer_len, _spp_tx_buffer);
if(err != ESP_OK){
log_e("SPP Write Failed! [0x%X]", err);
return false;
}
_spp_tx_buffer_len = 0;
if(xSemaphoreTake(_spp_tx_done, portMAX_DELAY) != pdTRUE){
log_e("SPP Ack Failed!");
return false;
}
return true;
}
return false;
}
static void _spp_tx_task(void * arg){
spp_packet_t *packet = NULL;
size_t len = 0, to_send = 0;
uint8_t * data = NULL;
for (;;) {
if(_spp_tx_queue && xQueueReceive(_spp_tx_queue, &packet, portMAX_DELAY) == pdTRUE && packet){
if(packet->len <= (SPP_TX_MAX - _spp_tx_buffer_len)){
memcpy(_spp_tx_buffer+_spp_tx_buffer_len, packet->data, packet->len);
_spp_tx_buffer_len+=packet->len;
free(packet);
packet = NULL;
if(SPP_TX_MAX == _spp_tx_buffer_len || uxQueueMessagesWaiting(_spp_tx_queue) == 0){
_spp_send_buffer();
}
} else {
len = packet->len;
data = packet->data;
to_send = SPP_TX_MAX - _spp_tx_buffer_len;
memcpy(_spp_tx_buffer+_spp_tx_buffer_len, data, to_send);
_spp_tx_buffer_len = SPP_TX_MAX;
data += to_send;
len -= to_send;
_spp_send_buffer();
while(len >= SPP_TX_MAX){
memcpy(_spp_tx_buffer, data, SPP_TX_MAX);
_spp_tx_buffer_len = SPP_TX_MAX;
data += SPP_TX_MAX;
len -= SPP_TX_MAX;
_spp_send_buffer();
}
if(len){
memcpy(_spp_tx_buffer, data, len);
_spp_tx_buffer_len += len;
free(packet);
packet = NULL;
if(uxQueueMessagesWaiting(_spp_tx_queue) == 0){
_spp_send_buffer();
}
}
}
} else {
log_e("Something went horribly wrong");
}
}
vTaskDelete(NULL);
_spp_task_handle = NULL;
}
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
@ -54,86 +160,153 @@ static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
log_i("ESP_SPP_INIT_EVT");
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 0, _spp_server_name);
xEventGroupSetBits(_spp_event_group, SPP_RUNNING);
break;
case ESP_SPP_SRV_OPEN_EVT://Server connection open
if (!_spp_client){
_spp_client = param->open.handle;
} else {
secondConnectionAttempt = true;
esp_spp_disconnect(param->open.handle);
}
xEventGroupSetBits(_spp_event_group, SPP_CONNECTED);
log_i("ESP_SPP_SRV_OPEN_EVT");
break;
case ESP_SPP_CLOSE_EVT://Client connection closed
if(secondConnectionAttempt) {
secondConnectionAttempt = false;
} else {
_spp_client = 0;
}
xEventGroupClearBits(_spp_event_group, SPP_CONNECTED);
log_i("ESP_SPP_CLOSE_EVT");
break;
case ESP_SPP_CONG_EVT://connection congestion status changed
if(param->cong.cong){
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
} else {
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
}
log_v("ESP_SPP_CONG_EVT: %s", param->cong.cong?"CONGESTED":"FREE");
break;
case ESP_SPP_WRITE_EVT://write operation completed
if(param->write.cong){
xEventGroupClearBits(_spp_event_group, SPP_CONGESTED);
}
xSemaphoreGive(_spp_tx_done);//we can try to send another packet
log_v("ESP_SPP_WRITE_EVT: %u %s", param->write.len, param->write.cong?"CONGESTED":"FREE");
break;
case ESP_SPP_DATA_IND_EVT://connection received data
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
//ets_printf("r:%u\n", param->data_ind.len);
if (_spp_rx_queue != NULL){
for (int i = 0; i < param->data_ind.len; i++){
if(xQueueSend(_spp_rx_queue, param->data_ind.data + i, (TickType_t)0) != pdTRUE){
log_e("RX Full! Discarding %u bytes", param->data_ind.len - i);
break;
}
}
}
break;
//should maybe delete those.
case ESP_SPP_DISCOVERY_COMP_EVT://discovery complete
log_i("ESP_SPP_DISCOVERY_COMP_EVT");
break;
case ESP_SPP_OPEN_EVT://Client connection open
log_i("ESP_SPP_OPEN_EVT");
break;
case ESP_SPP_CLOSE_EVT://Client connection closed
_spp_client = 0;
log_i("ESP_SPP_CLOSE_EVT");
break;
case ESP_SPP_START_EVT://server started
log_i("ESP_SPP_START_EVT");
break;
case ESP_SPP_CL_INIT_EVT://client initiated a connection
log_i("ESP_SPP_CL_INIT_EVT");
break;
case ESP_SPP_DATA_IND_EVT://connection received data
log_v("ESP_SPP_DATA_IND_EVT len=%d handle=%d", param->data_ind.len, param->data_ind.handle);
//esp_log_buffer_hex("",param->data_ind.data,param->data_ind.len); //for low level debug
if (_spp_queue != NULL){
for (int i = 0; i < param->data_ind.len; i++)
xQueueSend(_spp_queue, param->data_ind.data + i, (TickType_t)0);
} else {
log_e("SerialQueueBT ERROR");
}
break;
case ESP_SPP_CONG_EVT://connection congestion status changed
log_i("ESP_SPP_CONG_EVT");
break;
case ESP_SPP_WRITE_EVT://write operation completed
log_v("ESP_SPP_WRITE_EVT");
break;
case ESP_SPP_SRV_OPEN_EVT://Server connection open
_spp_client = param->open.handle;
log_i("ESP_SPP_SRV_OPEN_EVT");
break;
default:
break;
}
if(custom_spp_callback)(*custom_spp_callback)(event, param);
}
static bool _init_bt(const char *deviceName)
{
if(!_spp_event_group){
_spp_event_group = xEventGroupCreate();
if(!_spp_event_group){
log_e("SPP Event Group Create Failed!");
return false;
}
xEventGroupClearBits(_spp_event_group, 0xFFFFFF);
xEventGroupSetBits(_spp_event_group, SPP_CONGESTED);
}
if (_spp_rx_queue == NULL){
_spp_rx_queue = xQueueCreate(RX_QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue
if (_spp_rx_queue == NULL){
log_e("RX Queue Create Failed");
return false;
}
}
if (_spp_tx_queue == NULL){
_spp_tx_queue = xQueueCreate(TX_QUEUE_SIZE, sizeof(spp_packet_t*)); //initialize the queue
if (_spp_tx_queue == NULL){
log_e("TX Queue Create Failed");
return false;
}
}
if(_spp_tx_done == NULL){
_spp_tx_done = xSemaphoreCreateBinary();
if (_spp_tx_done == NULL){
log_e("TX Semaphore Create Failed");
return false;
}
xSemaphoreTake(_spp_tx_done, 0);
}
if(!_spp_task_handle){
xTaskCreate(_spp_tx_task, "spp_tx", 4096, NULL, 2, &_spp_task_handle);
if(!_spp_task_handle){
log_e("Network Event Task Start Failed!");
return false;
}
}
if (!btStarted() && !btStart()){
log_e("%s initialize controller failed\n", __func__);
log_e("initialize controller failed");
return false;
}
esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){
if (esp_bluedroid_init()) {
log_e("%s initialize bluedroid failed\n", __func__);
log_e("initialize bluedroid failed");
return false;
}
}
if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){
if (esp_bluedroid_enable()) {
log_e("%s enable bluedroid failed\n", __func__);
log_e("enable bluedroid failed");
return false;
}
}
if (esp_spp_register_callback(esp_spp_cb) != ESP_OK){
log_e("%s spp register failed\n", __func__);
log_e("spp register failed");
return false;
}
if (esp_spp_init(ESP_SPP_MODE_CB) != ESP_OK){
log_e("%s spp init failed\n", __func__);
log_e("spp init failed");
return false;
}
_spp_queue = xQueueCreate(QUEUE_SIZE, sizeof(uint8_t)); //initialize the queue
if (_spp_queue == NULL){
log_e("%s Queue creation error\n", __func__);
return false;
}
esp_bt_dev_set_device_name(deviceName);
// the default BTA_DM_COD_LOUDSPEAKER does not work with the macOS BT stack
@ -142,7 +315,7 @@ static bool _init_bt(const char *deviceName)
cod.minor = 0b000100;
cod.service = 0b00000010110;
if (esp_bt_gap_set_cod(cod, ESP_BT_INIT_COD) != ESP_OK) {
log_e("%s set cod failed\n", __func__);
log_e("set cod failed");
return false;
}
@ -152,10 +325,39 @@ static bool _init_bt(const char *deviceName)
static bool _stop_bt()
{
if (btStarted()){
if(_spp_client)
esp_spp_disconnect(_spp_client);
esp_spp_deinit();
esp_bluedroid_disable();
esp_bluedroid_deinit();
btStop();
}
_spp_client = 0;
if(_spp_task_handle){
vTaskDelete(_spp_task_handle);
_spp_task_handle = NULL;
}
if(_spp_event_group){
vEventGroupDelete(_spp_event_group);
_spp_event_group = NULL;
}
if(_spp_rx_queue){
vQueueDelete(_spp_rx_queue);
//ToDo: clear RX queue when in packet mode
_spp_rx_queue = NULL;
}
if(_spp_tx_queue){
spp_packet_t *packet = NULL;
while(xQueueReceive(_spp_tx_queue, &packet, 0) == pdTRUE){
free(packet);
}
vQueueDelete(_spp_tx_queue);
_spp_tx_queue = NULL;
}
if (_spp_tx_done) {
vSemaphoreDelete(_spp_tx_done);
_spp_tx_done = NULL;
}
return true;
}
@ -184,60 +386,39 @@ bool BluetoothSerial::begin(String localName)
int BluetoothSerial::available(void)
{
if (!_spp_client || _spp_queue == NULL){
if (_spp_rx_queue == NULL){
return 0;
}
return uxQueueMessagesWaiting(_spp_queue);
return uxQueueMessagesWaiting(_spp_rx_queue);
}
int BluetoothSerial::peek(void)
{
if (available()){
if (!_spp_client || _spp_queue == NULL){
return 0;
}
uint8_t c;
if (xQueuePeek(_spp_queue, &c, 0)){
return c;
}
uint8_t c;
if (_spp_rx_queue && xQueuePeek(_spp_rx_queue, &c, 0)){
return c;
}
return -1;
}
bool BluetoothSerial::hasClient(void)
{
if (_spp_client)
return true;
return false;
return _spp_client > 0;
}
int BluetoothSerial::read(void)
{
if (available()){
if (!_spp_client || _spp_queue == NULL){
return 0;
}
uint8_t c;
if (xQueueReceive(_spp_queue, &c, 0)){
return c;
}
uint8_t c = 0;
if (_spp_rx_queue && xQueueReceive(_spp_rx_queue, &c, 0)){
return c;
}
return 0;
return -1;
}
size_t BluetoothSerial::write(uint8_t c)
{
if (!_spp_client){
return 0;
}
uint8_t buffer[1];
buffer[0] = c;
esp_err_t err = esp_spp_write(_spp_client, 1, buffer);
return (err == ESP_OK) ? 1 : 0;
return write(&c, 1);
}
size_t BluetoothSerial::write(const uint8_t *buffer, size_t size)
@ -245,18 +426,12 @@ size_t BluetoothSerial::write(const uint8_t *buffer, size_t size)
if (!_spp_client){
return 0;
}
esp_err_t err = esp_spp_write(_spp_client, size, (uint8_t *)buffer);
return (err == ESP_OK) ? size : 0;
return (_spp_queue_packet((uint8_t *)buffer, size) == ESP_OK) ? size : 0;
}
void BluetoothSerial::flush()
{
if (_spp_client){
int qsize = available();
uint8_t buffer[qsize];
esp_spp_write(_spp_client, qsize, buffer);
}
while(read() >= 0){}
}
void BluetoothSerial::end()
@ -264,4 +439,10 @@ void BluetoothSerial::end()
_stop_bt();
}
esp_err_t BluetoothSerial::register_callback(esp_spp_cb_t * callback)
{
custom_spp_callback = callback;
return ESP_OK;
}
#endif

View File

@ -21,6 +21,7 @@
#include "Arduino.h"
#include "Stream.h"
#include <esp_spp_api.h>
class BluetoothSerial: public Stream
{
@ -38,6 +39,7 @@ class BluetoothSerial: public Stream
size_t write(const uint8_t *buffer, size_t size);
void flush();
void end(void);
esp_err_t register_callback(esp_spp_cb_t * callback);
private:
String local_name;

View File

@ -36,12 +36,12 @@ void print_wakeup_reason(){
switch(wakeup_reason)
{
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case 3 : Serial.println("Wakeup caused by timer"); break;
case 4 : Serial.println("Wakeup caused by touchpad"); break;
case 5 : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.println("Wakeup was not caused by deep sleep"); break;
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}

View File

@ -35,12 +35,12 @@ void print_wakeup_reason(){
switch(wakeup_reason)
{
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case 3 : Serial.println("Wakeup caused by timer"); break;
case 4 : Serial.println("Wakeup caused by touchpad"); break;
case 5 : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.println("Wakeup was not caused by deep sleep"); break;
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
@ -84,6 +84,7 @@ void setup(){
reset occurs.
*/
Serial.println("Going to sleep now");
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
}

View File

@ -26,12 +26,12 @@ void print_wakeup_reason(){
switch(wakeup_reason)
{
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case 3 : Serial.println("Wakeup caused by timer"); break;
case 4 : Serial.println("Wakeup caused by touchpad"); break;
case 5 : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.println("Wakeup was not caused by deep sleep"); break;
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}

View File

@ -0,0 +1,97 @@
#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif
// define two tasks for Blink & AnalogRead
void TaskBlink( void *pvParameters );
void TaskAnalogReadA3( void *pvParameters );
// the setup function runs once when you press reset or power the board
void setup() {
// initialize serial communication at 115200 bits per second:
Serial.begin(115200);
// Now set up two tasks to run independently.
xTaskCreatePinnedToCore(
TaskBlink
, "TaskBlink" // A name just for humans
, 1024 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL
, ARDUINO_RUNNING_CORE);
xTaskCreatePinnedToCore(
TaskAnalogReadA3
, "AnalogReadA3"
, 1024 // Stack size
, NULL
, 1 // Priority
, NULL
, ARDUINO_RUNNING_CORE);
// Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
}
void loop()
{
// Empty. Things are done in Tasks.
}
/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/
void TaskBlink(void *pvParameters) // This is a task.
{
(void) pvParameters;
/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.
If you want to know what pin the on-board LED is connected to on your ESP32 model, check
the Technical Specs of your board.
*/
// initialize digital LED_BUILTIN on pin 13 as an output.
pinMode(LED_BUILTIN, OUTPUT);
for (;;) // A Task shall never return or exit.
{
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
vTaskDelay(100); // one tick delay (15ms) in between reads for stability
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
vTaskDelay(100); // one tick delay (15ms) in between reads for stability
}
}
void TaskAnalogReadA3(void *pvParameters) // This is a task.
{
(void) pvParameters;
/*
AnalogReadSerial
Reads an analog input on pin A3, prints the result to the serial monitor.
Graphical representation is available using serial plotter (Tools > Serial Plotter menu)
Attach the center pin of a potentiometer to pin A3, and the outside pins to +5V and ground.
This example code is in the public domain.
*/
for (;;)
{
// read the input on analog pin A3:
int sensorValueA3 = analogRead(A3);
// print out the value you read:
Serial.println(sensorValueA3);
vTaskDelay(10); // one tick delay (15ms) in between reads for stability
}
}

View File

@ -0,0 +1,47 @@
#include <Arduino.h>
#include <FunctionalInterrupt.h>
#define BUTTON1 16
#define BUTTON2 17
class Button
{
public:
Button(uint8_t reqPin) : PIN(reqPin){
pinMode(PIN, INPUT_PULLUP);
attachInterrupt(PIN, std::bind(&Button::isr,this), FALLING);
};
~Button() {
detachInterrupt(PIN);
}
void IRAM_ATTR isr() {
numberKeyPresses += 1;
pressed = true;
}
void checkPressed() {
if (pressed) {
Serial.printf("Button on pin %u has been pressed %u times\n", PIN, numberKeyPresses);
pressed = false;
}
}
private:
const uint8_t PIN;
volatile uint32_t numberKeyPresses;
volatile bool pressed;
};
Button button1(BUTTON1);
Button button2(BUTTON2);
void setup() {
Serial.begin(115200);
}
void loop() {
button1.checkPressed();
button2.checkPressed();
}

View File

@ -0,0 +1,61 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "Arduino.h"
#include "esp32-hal.h"
rmt_data_t my_data[256];
rmt_data_t data[256];
rmt_obj_t* rmt_send = NULL;
rmt_obj_t* rmt_recv = NULL;
static EventGroupHandle_t events;
void setup()
{
Serial.begin(115200);
if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL)
{
Serial.println("init sender failed\n");
}
if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL)
{
Serial.println("init receiver failed\n");
}
float realTick = rmtSetTick(rmt_send, 100);
printf("real tick set to: %fns\n", realTick);
}
void loop()
{
// Init data
int i;
for (i=0; i<255; i++) {
data[i].val = 0x80010001 + ((i%13)<<16) + 13-(i%13);
}
data[255].val = 0;
// Start receiving
rmtReadAsync(rmt_recv, my_data, 100, events, false, 0);
// Send in continous mode
rmtWrite(rmt_send, data, 100);
// Wait for data
xEventGroupWaitBits(events, RMT_FLAG_RX_DONE, 1, 1, portMAX_DELAY);
// Printout the received data plus the original values
for (i=0; i<60; i++)
{
Serial.printf("%08x=%08x ", my_data[i], data[i] );
if (!((i+1)%4)) Serial.println("\n");
}
Serial.println("\n");
delay(2000);
}

View File

@ -0,0 +1,204 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "Arduino.h"
#include "esp32-hal.h"
//
// Note: This example uses a FrSKY device communication
// using XJT D12 protocol
//
// ; 0 bit = 6us low/10us high
// ; 1 bit = 14us low/10us high
// ;
// ; --------+ +----------+ +----------+
// ; | | | | |
// ; | 0 | | 1 | |
// ; | | | | |
// ; | | | | |
// ; +-------+ +-----------------+ +---------
// ;
// ; | 6us 10us | 14us 10us |
// ; |-------|----------|-----------------|----------|--------
// ; | 16us | 24us |
// Typedef of received frame
//
// ; 0x00 - Sync, 0x7E (sync header ID)
// ; 0x01 - Rx ID, 0x?? (receiver ID number, 0x00-0x??)
// ; 0x02 - Flags 1, 0x?? (used for failsafe and binding)
// ; 0x03 - Flags 2, 0x00 (reserved)
// ; 0x04-0x06, Channels 1/9 and 2/10
// ; 0x07-0x09, Channels 3/11 and 4/12
// ; 0x0A-0x0C, Channels 5/13 and 6/14
// ; 0x0D-0x0F, Channels 7/15 and 8/16
// ; 0x10 - 0x00, always zero
// ; 0x11 - CRC-16 High
// ; 0x12 - CRC-16 Low
// ; 0x13 - Tail, 0x7E (tail ID)
typedef union {
struct {
uint8_t head;//0x7E
uint8_t rxid;//Receiver Number
uint8_t flags;//Range:0x20, Bind:0x01
uint8_t reserved0;//0x00
union {
struct {
uint8_t ch0_l;
uint8_t ch0_h:4;
uint8_t ch1_l:4;
uint8_t ch1_h;
};
uint8_t bytes[3];
} channels[4];
uint8_t reserved1;//0x00
uint8_t crc_h;
uint8_t crc_l;
uint8_t tail;//0x7E
};
uint8_t buffer[20];
} xjt_packet_t;
#define XJT_VALID(i) (i->level0 && !i->level1 && i->duration0 >= 8 && i->duration0 <= 11)
rmt_obj_t* rmt_recv = NULL;
static uint32_t *s_channels;
static uint32_t channels[16];
static uint8_t xjt_flags = 0x0;
static uint8_t xjt_rxid = 0x0;
static bool xjtReceiveBit(size_t index, bool bit){
static xjt_packet_t xjt;
static uint8_t xjt_bit_index = 8;
static uint8_t xht_byte_index = 0;
static uint8_t xht_ones = 0;
if(!index){
xjt_bit_index = 8;
xht_byte_index = 0;
xht_ones = 0;
}
if(xht_byte_index > 19){
//fail!
return false;
}
if(bit){
xht_ones++;
if(xht_ones > 5 && xht_byte_index && xht_byte_index < 19){
//fail!
return false;
}
//add bit
xjt.buffer[xht_byte_index] |= (1 << --xjt_bit_index);
} else if(xht_ones == 5 && xht_byte_index && xht_byte_index < 19){
xht_ones = 0;
//skip bit
return true;
} else {
xht_ones = 0;
//add bit
xjt.buffer[xht_byte_index] &= ~(1 << --xjt_bit_index);
}
if ((!xjt_bit_index) || (xjt_bit_index==1 && xht_byte_index==19) ) {
xjt_bit_index = 8;
if(!xht_byte_index && xjt.buffer[0] != 0x7E){
//fail!
return false;
}
xht_byte_index++;
if(xht_byte_index == 20){
//done
if(xjt.buffer[19] != 0x7E){
//fail!
return false;
}
//check crc?
xjt_flags = xjt.flags;
xjt_rxid = xjt.rxid;
for(int i=0; i<4; i++){
uint16_t ch0 = xjt.channels[i].ch0_l | ((uint16_t)(xjt.channels[i].ch0_h & 0x7) << 8);
ch0 = ((ch0 * 2) + 2452) / 3;
uint16_t ch1 = xjt.channels[i].ch1_l | ((uint16_t)(xjt.channels[i].ch1_h & 0x7F) << 4);
ch1 = ((ch1 * 2) + 2452) / 3;
uint8_t c0n = i*2;
if(xjt.channels[i].ch0_h & 0x8){
c0n += 8;
}
uint8_t c1n = i*2+1;
if(xjt.channels[i].ch1_h & 0x80){
c1n += 8;
}
s_channels[c0n] = ch0;
s_channels[c1n] = ch1;
}
}
}
return true;
}
void parseRmt(rmt_data_t* items, size_t len, uint32_t* channels){
size_t chan = 0;
bool valid = true;
rmt_data_t* it = NULL;
if (!channels) {
log_e("Please provide data block for storing channel info");
return;
}
s_channels = channels;
it = &items[0];
for(size_t i = 0; i<len; i++){
if(!valid){
break;
}
it = &items[i];
if(XJT_VALID(it)){
if(it->duration1 >= 5 && it->duration1 <= 8){
valid = xjtReceiveBit(i, false);
} else if(it->duration1 >= 13 && it->duration1 <= 16){
valid = xjtReceiveBit(i, true);
} else {
valid = false;
}
} else if(!it->duration1 && !it->level1 && it->duration0 >= 5 && it->duration0 <= 8) {
valid = xjtReceiveBit(i, false);
}
}
}
extern "C" void receive_data(uint32_t *data, size_t len)
{
parseRmt((rmt_data_t*) data, len, channels);
}
void setup()
{
Serial.begin(115200);
// Initialize the channel to capture up to 192 items
if ((rmt_recv = rmtInit(21, false, RMT_MEM_192)) == NULL)
{
Serial.println("init receiver failed\n");
}
// Setup 1us tick
float realTick = rmtSetTick(rmt_recv, 1000);
Serial.printf("real tick set to: %fns\n", realTick);
// Ask to start reading
rmtRead(rmt_recv, receive_data);
}
void loop()
{
// printout some of the channels
Serial.printf("%04x %04x %04x %04x\n", channels[0], channels[1], channels[2], channels[3]);
delay(500);
}

View File

@ -0,0 +1,89 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "Arduino.h"
#include "esp32-hal.h"
#define NR_OF_LEDS 8*4
#define NR_OF_ALL_BITS 24*NR_OF_LEDS
//
// Note: This example uses Neopixel LED board, 32 LEDs chained one
// after another, each RGB LED has its 24 bit value
// for color configuration (8b for each color)
//
// Bits encoded as pulses as follows:
//
// "0":
// +-------+ +--
// | | |
// | | |
// | | |
// ---| |--------------|
// + + +
// | 0.4us | 0.85 0us |
//
// "1":
// +-------------+ +--
// | | |
// | | |
// | | |
// | | |
// ---+ +-------+
// | 0.8us | 0.4us |
rmt_data_t led_data[NR_OF_ALL_BITS];
rmt_obj_t* rmt_send = NULL;
void setup()
{
Serial.begin(115200);
if ((rmt_send = rmtInit(18, true, RMT_MEM_64)) == NULL)
{
Serial.println("init sender failed\n");
}
float realTick = rmtSetTick(rmt_send, 100);
Serial.printf("real tick set to: %fns\n", realTick);
}
int color[] = { 0x55, 0x11, 0x77 }; // RGB value
int led_index = 0;
void loop()
{
// Init data with only one led ON
int led, col, bit;
int i=0;
for (led=0; led<NR_OF_LEDS; led++) {
for (col=0; col<3; col++ ) {
for (bit=0; bit<8; bit++){
if ( (color[col] & (1<<(8-bit))) && (led == led_index) ) {
led_data[i].level0 = 1;
led_data[i].duration0 = 8;
led_data[i].level1 = 0;
led_data[i].duration1 = 4;
} else {
led_data[i].level0 = 1;
led_data[i].duration0 = 4;
led_data[i].level1 = 0;
led_data[i].duration1 = 8;
}
i++;
}
}
}
// make the led travel in the pannel
if ((++led_index)>=NR_OF_LEDS) {
led_index = 0;
}
// Send the data
rmtWrite(rmt_send, led_data, NR_OF_ALL_BITS);
delay(100);
}

View File

@ -6,7 +6,7 @@ hw_timer_t *timer = NULL;
void IRAM_ATTR resetModule() {
ets_printf("reboot\n");
esp_restart_noos();
esp_restart();
}
void setup() {

View File

@ -43,6 +43,14 @@ License (MIT license):
#include <functional>
#include "esp_wifi.h"
// Add quotes around defined value
#ifdef __IN_ECLIPSE__
#define STR_EXPAND(tok) #tok
#define STR(tok) STR_EXPAND(tok)
#else
#define STR(tok) tok
#endif
static void _on_sys_event(system_event_t *event){
mdns_handle_system_event(NULL, event);
}
@ -82,7 +90,7 @@ void MDNSResponder::setInstanceName(String name) {
void MDNSResponder::enableArduino(uint16_t port, bool auth){
mdns_txt_item_t arduTxtData[4] = {
{(char*)"board" ,(char*)ARDUINO_VARIANT},
{(char*)"board" ,(char*)STR(ARDUINO_VARIANT)},
{(char*)"tcp_check" ,(char*)"no"},
{(char*)"ssh_upload" ,(char*)"no"},
{(char*)"auth_upload" ,(char*)"no"}

View File

@ -0,0 +1,181 @@
#include "FS.h"
#include "FFat.h"
// You only need to format FFat the first time you run a test
#define FORMAT_FFAT true
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
Serial.printf("Listing directory: %s\r\n", dirname);
File root = fs.open(dirname);
if(!root){
Serial.println("- failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println(" - not a directory");
return;
}
File file = root.openNextFile();
while(file){
if(file.isDirectory()){
Serial.print(" DIR : ");
Serial.println(file.name());
if(levels){
listDir(fs, file.name(), levels -1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print("\tSIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return;
}
Serial.println("- read from file:");
while(file.available()){
Serial.write(file.read());
}
}
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("- failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("- file written");
} else {
Serial.println("- frite failed");
}
}
void appendFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Appending to file: %s\r\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("- failed to open file for appending");
return;
}
if(file.print(message)){
Serial.println("- message appended");
} else {
Serial.println("- append failed");
}
}
void renameFile(fs::FS &fs, const char * path1, const char * path2){
Serial.printf("Renaming file %s to %s\r\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("- file renamed");
} else {
Serial.println("- rename failed");
}
}
void deleteFile(fs::FS &fs, const char * path){
Serial.printf("Deleting file: %s\r\n", path);
if(fs.remove(path)){
Serial.println("- file deleted");
} else {
Serial.println("- delete failed");
}
}
void testFileIO(fs::FS &fs, const char * path){
Serial.printf("Testing file I/O with %s\r\n", path);
static uint8_t buf[512];
size_t len = 0;
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("- failed to open file for writing");
return;
}
size_t i;
Serial.print("- writing" );
uint32_t start = millis();
for(i=0; i<2048; i++){
if ((i & 0x001F) == 0x001F){
Serial.print(".");
}
file.write(buf, 512);
}
Serial.println("");
uint32_t end = millis() - start;
Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end);
file.close();
file = fs.open(path);
start = millis();
end = start;
i = 0;
if(file && !file.isDirectory()){
len = file.size();
size_t flen = len;
start = millis();
Serial.print("- reading" );
while(len){
size_t toRead = len;
if(toRead > 512){
toRead = 512;
}
file.read(buf, toRead);
if ((i++ & 0x001F) == 0x001F){
Serial.print(".");
}
len -= toRead;
}
Serial.println("");
end = millis() - start;
Serial.printf("- %u bytes read in %u ms\r\n", flen, end);
file.close();
} else {
Serial.println("- failed to open file for reading");
}
}
void setup(){
Serial.begin(115200);
Serial.setDebugOutput(true);
if (FORMAT_FFAT) FFat.format();
if(!FFat.begin()){
Serial.println("FFat Mount Failed");
return;
}
Serial.printf("Total space: %10lu\n", FFat.totalBytes());
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
listDir(FFat, "/", 0);
writeFile(FFat, "/hello.txt", "Hello ");
appendFile(FFat, "/hello.txt", "World!\r\n");
readFile(FFat, "/hello.txt");
renameFile(FFat, "/hello.txt", "/foo.txt");
readFile(FFat, "/foo.txt");
deleteFile(FFat, "/foo.txt");
testFileIO(FFat, "/test.txt");
Serial.printf("Free space: %10lu\n", FFat.freeBytes());
deleteFile(FFat, "/test.txt");
Serial.println( "Test complete" );
}
void loop(){
}

View File

@ -0,0 +1,9 @@
name=FFat
version=1.0
author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone
maintainer=Hristo Gochkov <hristo@espressif.com>
sentence=ESP32 FAT on Flash File System
paragraph=
category=Data Storage
url=
architectures=esp32

144
libraries/FFat/src/FFat.cpp Normal file
View File

@ -0,0 +1,144 @@
// Copyright 2015-2016 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 "vfs_api.h"
extern "C" {
#include "esp_vfs_fat.h"
#include "diskio.h"
#include "diskio_wl.h"
#include "vfs_fat_internal.h"
}
#include "FFat.h"
using namespace fs;
F_Fat::F_Fat(FSImplPtr impl)
: FS(impl)
{}
const esp_partition_t *check_ffat_partition(const char* label)
{
const esp_partition_t* ck_part = esp_partition_find_first(
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, label);
if (!ck_part) {
log_e("No FAT partition found with label %s", label);
return NULL;
}
return ck_part;
}
bool F_Fat::begin(bool formatOnFail, const char * basePath, uint8_t maxOpenFiles, const char * partitionLabel)
{
if(_wl_handle){
log_w("Already Mounted!");
return true;
}
if (!check_ffat_partition(partitionLabel)) return false;
esp_vfs_fat_mount_config_t conf = {
.format_if_mount_failed = formatOnFail,
.max_files = maxOpenFiles
};
esp_err_t err = esp_vfs_fat_spiflash_mount(basePath, partitionLabel, &conf, &_wl_handle);
if(err){
log_e("Mounting FFat partition failed! Error: %d", err);
return false;
}
_impl->mountpoint(basePath);
return true;
}
void F_Fat::end()
{
if(_wl_handle){
esp_err_t err = esp_vfs_fat_spiflash_unmount(_impl->mountpoint(), _wl_handle);
if(err){
log_e("Unmounting FFat partition failed! Error: %d", err);
return;
}
_wl_handle = NULL;
_impl->mountpoint(NULL);
}
}
bool F_Fat::format(bool full_wipe, char* partitionLabel)
{
esp_err_t result;
if(_wl_handle){
log_w("Already Mounted!");
return false;
}
wl_handle_t temp_handle;
// Attempt to mount to see if there is already data
const esp_partition_t *ffat_partition = check_ffat_partition(partitionLabel);
if (!ffat_partition) return false;
result = wl_mount(ffat_partition, &temp_handle);
if (result == ESP_OK) {
// Wipe disk- quick just wipes the FAT. Full zeroes the whole disk
uint32_t wipe_size = full_wipe ? wl_size(temp_handle) : 16384;
wl_erase_range(temp_handle, 0, wipe_size);
wl_unmount(temp_handle);
}
// Now do a mount with format_if_fail (which it will)
esp_vfs_fat_mount_config_t conf = {
.format_if_mount_failed = true,
.max_files = 1
};
result = esp_vfs_fat_spiflash_mount("/format_ffat", partitionLabel, &conf, &temp_handle);
esp_vfs_fat_spiflash_unmount("/format_ffat", temp_handle);
return result;
}
size_t F_Fat::totalBytes()
{
FATFS *fs;
DWORD free_clust, tot_sect, sect_size;
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
char drv[3] = {(char)(48+pdrv), ':', 0};
FRESULT res = f_getfree(drv, &free_clust, &fs);
tot_sect = (fs->n_fatent - 2) * fs->csize;
sect_size = CONFIG_WL_SECTOR_SIZE;
return tot_sect * sect_size;
}
size_t F_Fat::freeBytes()
{
FATFS *fs;
DWORD free_clust, free_sect, sect_size;
BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle);
char drv[3] = {(char)(48+pdrv), ':', 0};
FRESULT res = f_getfree(drv, &free_clust, &fs);
free_sect = free_clust * fs->csize;
sect_size = CONFIG_WL_SECTOR_SIZE;
return free_sect * sect_size;
}
bool F_Fat::exists(const char* path)
{
File f = open(path, "r");
return (f == true) && !f.isDirectory();
}
bool F_Fat::exists(const String& path)
{
return exists(path.c_str());
}
F_Fat FFat = F_Fat(FSImplPtr(new VFSImpl()));

47
libraries/FFat/src/FFat.h Normal file
View File

@ -0,0 +1,47 @@
// Copyright 2015-2016 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.
#ifndef _FFAT_H_
#define _FFAT_H_
#include "FS.h"
#include "wear_levelling.h"
#define FFAT_WIPE_QUICK 0
#define FFAT_WIPE_FULL 1
#define FFAT_PARTITION_LABEL "ffat"
namespace fs
{
class F_Fat : public FS
{
public:
F_Fat(FSImplPtr impl);
bool begin(bool formatOnFail=false, const char * basePath="/ffat", uint8_t maxOpenFiles=10, const char * partitionLabel = (char*)FFAT_PARTITION_LABEL);
bool format(bool full_wipe = FFAT_WIPE_QUICK, char* partitionLabel = (char*)FFAT_PARTITION_LABEL);
size_t totalBytes();
size_t freeBytes();
void end();
bool exists(const char* path);
bool exists(const String& path);
private:
wl_handle_t _wl_handle;
};
}
extern fs::F_Fat FFat;
#endif /* _FFAT_H_ */

View File

@ -0,0 +1,147 @@
/**
BasicHTTPSClient.ino
Created on: 14.10.2018
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
// This is GandiStandardSSLCA2.pem, the root Certificate Authority that signed
// the server certifcate for the demo server https://jigsaw.w3.org in this
// example. This certificate is valid until Sep 11 23:59:59 2024 GMT
const char* rootCACertificate = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIF6TCCA9GgAwIBAgIQBeTcO5Q4qzuFl8umoZhQ4zANBgkqhkiG9w0BAQwFADCB\n" \
"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" \
"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" \
"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQw\n" \
"OTEyMDAwMDAwWhcNMjQwOTExMjM1OTU5WjBfMQswCQYDVQQGEwJGUjEOMAwGA1UE\n" \
"CBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMQ4wDAYDVQQKEwVHYW5kaTEgMB4GA1UE\n" \
"AxMXR2FuZGkgU3RhbmRhcmQgU1NMIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" \
"DwAwggEKAoIBAQCUBC2meZV0/9UAPPWu2JSxKXzAjwsLibmCg5duNyj1ohrP0pIL\n" \
"m6jTh5RzhBCf3DXLwi2SrCG5yzv8QMHBgyHwv/j2nPqcghDA0I5O5Q1MsJFckLSk\n" \
"QFEW2uSEEi0FXKEfFxkkUap66uEHG4aNAXLy59SDIzme4OFMH2sio7QQZrDtgpbX\n" \
"bmq08j+1QvzdirWrui0dOnWbMdw+naxb00ENbLAb9Tr1eeohovj0M1JLJC0epJmx\n" \
"bUi8uBL+cnB89/sCdfSN3tbawKAyGlLfOGsuRTg/PwSWAP2h9KK71RfWJ3wbWFmV\n" \
"XooS/ZyrgT5SKEhRhWvzkbKGPym1bgNi7tYFAgMBAAGjggF1MIIBcTAfBgNVHSME\n" \
"GDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQUs5Cn2MmvTs1hPJ98\n" \
"rV1/Qf1pMOowDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD\n" \
"VR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCIGA1UdIAQbMBkwDQYLKwYBBAGy\n" \
"MQECAhowCAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNl\n" \
"cnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNy\n" \
"bDB2BggrBgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRy\n" \
"dXN0LmNvbS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZ\n" \
"aHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAWGf9\n" \
"crJq13xhlhl+2UNG0SZ9yFP6ZrBrLafTqlb3OojQO3LJUP33WbKqaPWMcwO7lWUX\n" \
"zi8c3ZgTopHJ7qFAbjyY1lzzsiI8Le4bpOHeICQW8owRc5E69vrOJAKHypPstLbI\n" \
"FhfFcvwnQPYT/pOmnVHvPCvYd1ebjGU6NSU2t7WKY28HJ5OxYI2A25bUeo8tqxyI\n" \
"yW5+1mUfr13KFj8oRtygNeX56eXVlogMT8a3d2dIhCe2H7Bo26y/d7CQuKLJHDJd\n" \
"ArolQ4FCR7vY4Y8MDEZf7kYzawMUgtN+zY+vkNaOJH1AQrRqahfGlZfh8jjNp+20\n" \
"J0CT33KpuMZmYzc4ZCIwojvxuch7yPspOqsactIGEk72gtQjbz7Dk+XYtsDe3CMW\n" \
"1hMwt6CaDixVBgBwAc/qOR2A24j3pSC4W/0xJmmPLQphgzpHphNULB7j7UTKvGof\n" \
"KA5R2d4On3XNDgOVyvnFqSot/kGkoUeuDcL5OWYzSlvhhChZbH2UF3bkRYKtcCD9\n" \
"0m9jqNf6oDP6N8v3smWe2lBvP+Sn845dWDKXcCMu5/3EFZucJ48y7RetWIExKREa\n" \
"m9T8bJUox04FB6b9HbwZ4ui3uRGKLXASUoWNjDNKD/yZkuBjcNqllEdjB+dYxzFf\n" \
"BT02Vf6Dsuimrdfp5gJ0iHRc2jTbkNJtUQoj1iM=\n" \
"-----END CERTIFICATE-----\n";
// Not sure if WiFiClientSecure checks the validity date of the certificate.
// Setting clock just to be sure...
void setClock() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.print(F("Waiting for NTP time sync: "));
time_t nowSecs = time(nullptr);
while (nowSecs < 8 * 3600 * 2) {
delay(500);
Serial.print(F("."));
yield();
nowSecs = time(nullptr);
}
Serial.println();
struct tm timeinfo;
gmtime_r(&nowSecs, &timeinfo);
Serial.print(F("Current time: "));
Serial.print(asctime(&timeinfo));
}
WiFiMulti WiFiMulti;
void setup() {
Serial.begin(115200);
// Serial.setDebugOutput(true);
Serial.println();
Serial.println();
Serial.println();
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD");
// wait for WiFi connection
Serial.print("Waiting for WiFi to connect...");
while ((WiFiMulti.run() != WL_CONNECTED)) {
Serial.print(".");
}
Serial.println(" connected");
setClock();
}
void loop() {
WiFiClientSecure *client = new WiFiClientSecure;
if(client) {
client -> setCACert(rootCACertificate);
{
// Add a scoping block for HTTPClient https to make sure it is destroyed before WiFiClientSecure *client is
HTTPClient https;
Serial.print("[HTTPS] begin...\n");
if (https.begin(*client, "https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS
Serial.print("[HTTPS] GET...\n");
// start connection and send HTTP header
int httpCode = https.GET();
// httpCode will be negative on error
if (httpCode > 0) {
// HTTP header has been send and Server response header has been handled
Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
// file found at server
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = https.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
}
https.end();
} else {
Serial.printf("[HTTPS] Unable to connect\n");
}
// End extra scoping block
}
delete client;
} else {
Serial.println("Unable to create client");
}
Serial.println();
Serial.println("Waiting 10s before the next round...");
delay(10000);
}

View File

@ -0,0 +1,98 @@
/*|----------------------------------------------------------|*/
/*|WORKING EXAMPLE FOR HTTP/HTTPS CONNECTION |*/
/*|TESTED BOARDS: Devkit v1 DOIT, Devkitc v4 |*/
/*|CORE: June 2018 |*/
/*|----------------------------------------------------------|*/
#include <WiFi.h>
#include <HTTPClient.h>
#include "esp_wpa2.h"
#include <Wire.h>
#define EAP_IDENTITY "identity" //if connecting from another corporation, use identity@organisation.domain in Eduroam
#define EAP_PASSWORD "password" //your Eduroam password
const char* ssid = "eduroam"; // Eduroam SSID
int counter = 0;
const char* test_root_ca= \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \
"-----END CERTIFICATE-----\n";
void setup() {
Serial.begin(115200);
delay(10);
Serial.println();
Serial.print("Connecting to network: ");
Serial.println(ssid);
WiFi.disconnect(true); //disconnect form wifi to set new wifi connection
WiFi.mode(WIFI_STA); //init wifi mode
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide identity
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username --> identity and username is same
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD)); //provide password
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
WiFi.begin(ssid); //connect to wifi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
counter++;
if(counter>=60){ //after 30 seconds timeout - reset board
ESP.restart();
}
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address set: ");
Serial.println(WiFi.localIP()); //print LAN IP
}
void loop() {
if (WiFi.status() == WL_CONNECTED) { //if we are connected to Eduroam network
counter = 0; //reset counter
Serial.println("Wifi is still connected with IP: ");
Serial.println(WiFi.localIP()); //inform user about his IP address
}else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
WiFi.begin(ssid);
}
while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
delay(500);
Serial.print(".");
counter++;
if(counter>=60){ //30 seconds timeout - reset board
ESP.restart();
}
}
Serial.print("Connecting to website: ");
HTTPClient http;
http.begin("https://arduino.php5.sk/rele/rele1.txt", test_root_ca); //HTTPS example connection
//http.begin("http://www.arduino.php5.sk/rele/rele1.txt"); //HTTP example connection
//if uncomment HTTP example, you can comment root CA certificate too!
int httpCode = http.GET();
if(httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
//file found at server --> on unsucessful connection code will be -1
if(httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
}else{
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
delay(2000);
}

View File

@ -1,5 +1,5 @@
name=HTTPClient
version=1.1
version=1.2
author=Markus Sattler
maintainer=Markus Sattler
sentence=http Client for ESP32

View File

@ -22,17 +22,23 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Adapted in October 2018
*/
#include <Arduino.h>
#include <esp32-hal-log.h>
#ifdef HTTPCLIENT_1_1_COMPATIBLE
#include <WiFi.h>
#include <WiFiClientSecure.h>
#endif
#include <StreamString.h>
#include <base64.h>
#include "HTTPClient.h"
#ifdef HTTPCLIENT_1_1_COMPATIBLE
class TransportTraits
{
public:
@ -78,6 +84,7 @@ protected:
const char* _clicert;
const char* _clikey;
};
#endif // HTTPCLIENT_1_1_COMPATIBLE
/**
* constructor
@ -91,8 +98,8 @@ HTTPClient::HTTPClient()
*/
HTTPClient::~HTTPClient()
{
if(_tcp) {
_tcp->stop();
if(_client) {
_client->stop();
}
if(_currentHeaders) {
delete[] _currentHeaders;
@ -107,9 +114,81 @@ void HTTPClient::clear()
}
/**
* parsing the url for all needed parameters
* @param client Client&
* @param url String
* @param https bool
* @return success bool
*/
bool HTTPClient::begin(WiFiClient &client, String url) {
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if(_tcpDeprecated) {
log_d("mix up of new and deprecated api");
_canReuse = false;
end();
}
#endif
_client = &client;
// check for : (http: or https:)
int index = url.indexOf(':');
if(index < 0) {
log_d("failed to parse protocol");
return false;
}
String protocol = url.substring(0, index);
if(protocol != "http" && protocol != "https") {
log_d("unknown protocol '%s'", protocol.c_str());
return false;
}
_port = (protocol == "https" ? 443 : 80);
return beginInternal(url, protocol.c_str());
}
/**
* directly supply all needed parameters
* @param client Client&
* @param host String
* @param port uint16_t
* @param uri String
* @param https bool
* @return success bool
*/
bool HTTPClient::begin(WiFiClient &client, String host, uint16_t port, String uri, bool https)
{
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if(_tcpDeprecated) {
log_d("mix up of new and deprecated api");
_canReuse = false;
end();
}
#endif
_client = &client;
clear();
_host = host;
_port = port;
_uri = uri;
_protocol = (https ? "https" : "http");
return true;
}
#ifdef HTTPCLIENT_1_1_COMPATIBLE
bool HTTPClient::begin(String url, const char* CAcert)
{
_transportTraits.reset(nullptr);
if(_client && !_tcpDeprecated) {
log_d("mix up of new and deprecated api");
_canReuse = false;
end();
}
_port = 443;
if (!beginInternal(url, "https")) {
return false;
@ -125,8 +204,12 @@ bool HTTPClient::begin(String url, const char* CAcert)
*/
bool HTTPClient::begin(String url)
{
if(_client && !_tcpDeprecated) {
log_d("mix up of new and deprecated api");
_canReuse = false;
end();
}
_transportTraits.reset(nullptr);
_port = 80;
if (!beginInternal(url, "http")) {
return begin(url, (const char*)NULL);
@ -134,6 +217,7 @@ bool HTTPClient::begin(String url)
_transportTraits = TransportTraitsPtr(new TransportTraits());
return true;
}
#endif // HTTPCLIENT_1_1_COMPATIBLE
bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
{
@ -182,8 +266,15 @@ bool HTTPClient::beginInternal(String url, const char* expectedProtocol)
return true;
}
#ifdef HTTPCLIENT_1_1_COMPATIBLE
bool HTTPClient::begin(String host, uint16_t port, String uri)
{
if(_client && !_tcpDeprecated) {
log_d("mix up of new and deprecated api");
_canReuse = false;
end();
}
clear();
_host = host;
_port = port;
@ -195,6 +286,12 @@ bool HTTPClient::begin(String host, uint16_t port, String uri)
bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcert)
{
if(_client && !_tcpDeprecated) {
log_d("mix up of new and deprecated api");
_canReuse = false;
end();
}
clear();
_host = host;
_port = port;
@ -210,6 +307,12 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcer
bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcert, const char* cli_cert, const char* cli_key)
{
if(_client && !_tcpDeprecated) {
log_d("mix up of new and deprecated api");
_canReuse = false;
end();
}
clear();
_host = host;
_port = port;
@ -222,37 +325,60 @@ bool HTTPClient::begin(String host, uint16_t port, String uri, const char* CAcer
_transportTraits = TransportTraitsPtr(new TLSTraits(CAcert, cli_cert, cli_key));
return true;
}
#endif // HTTPCLIENT_1_1_COMPATIBLE
/**
* end
* called after the payload is handled
*/
void HTTPClient::end(void)
{
disconnect();
}
/**
* disconnect
* close the TCP socket
*/
void HTTPClient::disconnect()
{
if(connected()) {
if(_tcp->available() > 0) {
log_d("still data in buffer (%d), clean up.", _tcp->available());
_tcp->flush();
if(_client->available() > 0) {
log_d("still data in buffer (%d), clean up.\n", _client->available());
while(_client->available() > 0) {
_client->read();
}
}
if(_reuse && _canReuse) {
log_d("tcp keep open for reuse");
log_d("tcp keep open for reuse\n");
} else {
log_d("tcp stop");
_tcp->stop();
log_d("tcp stop\n");
_client->stop();
_client = nullptr;
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if(_tcpDeprecated) {
_transportTraits.reset(nullptr);
_tcpDeprecated.reset(nullptr);
}
#endif
}
} else {
log_v("tcp is closed");
log_d("tcp is closed\n");
}
}
/**
* connected
* @return connected status
*/
bool HTTPClient::connected()
{
if(_tcp) {
return ((_tcp->available() > 0) || _tcp->connected());
if(_client) {
return ((_client->available() > 0) || _client->connected());
}
return false;
}
@ -309,8 +435,8 @@ void HTTPClient::setAuthorization(const char * auth)
void HTTPClient::setTimeout(uint16_t timeout)
{
_tcpTimeout = timeout;
if(connected() && !_secure) {
_tcp->setTimeout(timeout);
if(connected()) {
_client->setTimeout((timeout + 500) / 1000);
}
}
@ -398,7 +524,7 @@ int HTTPClient::sendRequest(const char * type, uint8_t * payload, size_t size)
// send Payload if needed
if(payload && size > 0) {
if(_tcp->write(&payload[0], size) != size) {
if(_client->write(&payload[0], size) != size) {
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
}
}
@ -477,7 +603,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
int bytesRead = stream->readBytes(buff, readBytes);
// write it to Stream
int bytesWrite = _tcp->write((const uint8_t *) buff, bytesRead);
int bytesWrite = _client->write((const uint8_t *) buff, bytesRead);
bytesWritten += bytesWrite;
// are all Bytes a writen to stream ?
@ -485,11 +611,11 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
log_d("short write, asked for %d but got %d retry...", bytesRead, bytesWrite);
// check for write error
if(_tcp->getWriteError()) {
log_d("stream write error %d", _tcp->getWriteError());
if(_client->getWriteError()) {
log_d("stream write error %d", _client->getWriteError());
//reset write error for retry
_tcp->clearWriteError();
_client->clearWriteError();
}
// some time for the stream
@ -498,7 +624,7 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
int leftBytes = (readBytes - bytesWrite);
// retry to send the missed bytes
bytesWrite = _tcp->write((const uint8_t *) (buff + bytesWrite), leftBytes);
bytesWrite = _client->write((const uint8_t *) (buff + bytesWrite), leftBytes);
bytesWritten += bytesWrite;
if(bytesWrite != leftBytes) {
@ -510,8 +636,8 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size)
}
// check for write error
if(_tcp->getWriteError()) {
log_d("stream write error %d", _tcp->getWriteError());
if(_client->getWriteError()) {
log_d("stream write error %d", _client->getWriteError());
free(buff);
return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED);
}
@ -561,8 +687,8 @@ int HTTPClient::getSize(void)
*/
WiFiClient& HTTPClient::getStream(void)
{
if (connected() && !_secure) {
return *_tcp;
if (connected()) {
return *_client;
}
log_w("getStream: not connected");
@ -577,7 +703,7 @@ WiFiClient& HTTPClient::getStream(void)
WiFiClient* HTTPClient::getStreamPtr(void)
{
if(connected()) {
return _tcp.get();
return _client;
}
log_w("getStreamPtr: not connected");
@ -617,7 +743,7 @@ int HTTPClient::writeToStream(Stream * stream)
if(!connected()) {
return returnError(HTTPC_ERROR_CONNECTION_LOST);
}
String chunkHeader = _tcp->readStringUntil('\n');
String chunkHeader = _client->readStringUntil('\n');
if(chunkHeader.length() <= 0) {
return returnError(HTTPC_ERROR_READ_TIMEOUT);
@ -654,7 +780,7 @@ int HTTPClient::writeToStream(Stream * stream)
// read trailing \r\n at the end of the chunk
char buf[2];
auto trailing_seq_len = _tcp->readBytes((uint8_t*)buf, 2);
auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2);
if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') {
return returnError(HTTPC_ERROR_READ_TIMEOUT);
}
@ -822,38 +948,46 @@ bool HTTPClient::connect(void)
if(connected()) {
log_d("already connected, try reuse!");
while(_tcp->available() > 0) {
_tcp->read();
while(_client->available() > 0) {
_client->read();
}
return true;
}
if (!_transportTraits) {
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if(!_client) {
_tcpDeprecated = _transportTraits->create();
_client = _tcpDeprecated.get();
}
#endif
if (!_client) {
log_d("HTTPClient::begin was not called or returned error");
return false;
}
_tcp = _transportTraits->create();
// set Timeout for WiFiClient and for Stream::readBytesUntil() and Stream::readStringUntil()
_client->setTimeout((_tcpTimeout + 500) / 1000);
if (!_transportTraits->verify(*_tcp, _host.c_str())) {
log_d("transport level verify failed");
_tcp->stop();
return false;
}
if(!_tcp->connect(_host.c_str(), _port)) {
if(!_client->connect(_host.c_str(), _port)) {
log_d("failed connect to %s:%u", _host.c_str(), _port);
return false;
}
log_d(" connected to %s:%u", _host.c_str(), _port);
// set Timeout for readBytesUntil and readStringUntil
setTimeout(_tcpTimeout);
#ifdef HTTPCLIENT_1_1_COMPATIBLE
if (_tcpDeprecated && !_transportTraits->verify(*_client, _host.c_str())) {
log_d("transport level verify failed");
_client->stop();
return false;
}
#endif
/*
#ifdef ESP8266
_tcp->setNoDelay(true);
_client->setNoDelay(true);
#endif
*/
return connected();
@ -907,7 +1041,7 @@ bool HTTPClient::sendHeader(const char * type)
header += _headers + "\r\n";
return (_tcp->write((const uint8_t *) header.c_str(), header.length()) == header.length());
return (_client->write((const uint8_t *) header.c_str(), header.length()) == header.length());
}
/**
@ -928,9 +1062,9 @@ int HTTPClient::handleHeaderResponse()
unsigned long lastDataTime = millis();
while(connected()) {
size_t len = _tcp->available();
size_t len = _client->available();
if(len > 0) {
String headerLine = _tcp->readStringUntil('\n');
String headerLine = _client->readStringUntil('\n');
headerLine.trim(); // remove \r
lastDataTime = millis();
@ -1026,7 +1160,7 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
while(connected() && (len > 0 || len == -1)) {
// get available data size
size_t sizeAvailable = _tcp->available();
size_t sizeAvailable = _client->available();
if(sizeAvailable) {
@ -1041,9 +1175,13 @@ int HTTPClient::writeToStreamDataBlock(Stream * stream, int size)
if(readBytes > buff_size) {
readBytes = buff_size;
}
// stop if no more reading
if (readBytes == 0)
break;
// read data
int bytesRead = _tcp->readBytes(buff, readBytes);
int bytesRead = _client->readBytes(buff, readBytes);
// write it to Stream
int bytesWrite = stream->write(buff, bytesRead);
@ -1124,7 +1262,7 @@ int HTTPClient::returnError(int error)
log_w("error(%d): %s", error, errorToString(error).c_str());
if(connected()) {
log_d("tcp stop");
_tcp->stop();
_client->stop();
}
}
return error;

View File

@ -27,6 +27,8 @@
#ifndef HTTPClient_H_
#define HTTPClient_H_
#define HTTPCLIENT_1_1_COMPATIBLE
#include <memory>
#include <Arduino.h>
#include <WiFiClient.h>
@ -117,8 +119,10 @@ typedef enum {
HTTPC_TE_CHUNKED
} transferEncoding_t;
#ifdef HTTPCLIENT_1_1_COMPATIBLE
class TransportTraits;
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
#endif
class HTTPClient
{
@ -126,11 +130,20 @@ public:
HTTPClient();
~HTTPClient();
/*
* Since both begin() functions take a reference to client as a parameter, you need to
* ensure the client object lives the entire time of the HTTPClient
*/
bool begin(WiFiClient &client, String url);
bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
#ifdef HTTPCLIENT_1_1_COMPATIBLE
bool begin(String url);
bool begin(String url, const char* CAcert);
bool begin(String host, uint16_t port, String uri = "/");
bool begin(String host, uint16_t port, String uri, const char* CAcert);
bool begin(String host, uint16_t port, String uri, const char* CAcert, const char* cli_cert, const char* cli_key);
#endif
void end(void);
@ -181,6 +194,7 @@ protected:
};
bool beginInternal(String url, const char* expectedProtocol);
void disconnect();
void clear();
int returnError(int error);
bool connect(void);
@ -189,8 +203,12 @@ protected:
int writeToStreamDataBlock(Stream * stream, int len);
#ifdef HTTPCLIENT_1_1_COMPATIBLE
TransportTraitsPtr _transportTraits;
std::unique_ptr<WiFiClient> _tcp;
std::unique_ptr<WiFiClient> _tcpDeprecated;
#endif
WiFiClient* _client = nullptr;
/// request handling
String _host;

View File

@ -0,0 +1,72 @@
/**
httpUpdate.ino
Created on: 27.11.2015
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
WiFiMulti WiFiMulti;
void setup() {
Serial.begin(115200);
// Serial.setDebugOutput(true);
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
WiFiClient client;
// The line below is optional. It can be used to blink the LED on the board during flashing
// The LED will be on during download of one buffer of data from the network. The LED will
// be off during writing that buffer to flash
// On a good connection the LED should flash regularly. On a bad connection the LED will be
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
// httpUpdate.setLedPin(LED_BUILTIN, LOW);
t_httpUpdate_return ret = httpUpdate.update(client, "http://server/file.bin");
// Or:
//t_httpUpdate_return ret = httpUpdate.update(client, "server", 80, "file.bin");
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
}

View File

@ -0,0 +1,75 @@
/**
httpUpdateSPIFFS.ino
Created on: 05.12.2015
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
WiFiMulti WiFiMulti;
void setup() {
Serial.begin(115200);
// Serial.setDebugOutput(true);
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
Serial.println("Update SPIFFS...");
WiFiClient client;
// The line below is optional. It can be used to blink the LED on the board during flashing
// The LED will be on during download of one buffer of data from the network. The LED will
// be off during writing that buffer to flash
// On a good connection the LED should flash regularly. On a bad connection the LED will be
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
// httpUpdate.setLedPin(LED_BUILTIN, LOW);
t_httpUpdate_return ret = httpUpdate.updateSpiffs(client, "http://server/spiffs.bin");
if (ret == HTTP_UPDATE_OK) {
Serial.println("Update sketch...");
ret = httpUpdate.update(client, "http://server/file.bin");
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
}
}

View File

@ -0,0 +1,128 @@
/**
httpUpdateSecure.ino
Created on: 16.10.2018 as an adaptation of the ESP8266 version of httpUpdate.ino
*/
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <time.h>
WiFiMulti WiFiMulti;
// Set time via NTP, as required for x.509 validation
void setClock() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // UTC
Serial.print(F("Waiting for NTP time sync: "));
time_t now = time(nullptr);
while (now < 8 * 3600 * 2) {
yield();
delay(500);
Serial.print(F("."));
now = time(nullptr);
}
Serial.println(F(""));
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print(F("Current time: "));
Serial.print(asctime(&timeinfo));
}
/**
* This is lets-encrypt-x3-cross-signed.pem
*/
const char* rootCACertificate = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
"-----END CERTIFICATE-----\n";
void setup() {
Serial.begin(115200);
// Serial.setDebugOutput(true);
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
WiFi.mode(WIFI_STA);
WiFiMulti.addAP("SSID", "PASSWORD");
}
void loop() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
setClock();
WiFiClientSecure client;
client.setCACert(rootCACertificate);
// Reading data over SSL may be slow, use an adequate timeout
client.setTimeout(12000);
// The line below is optional. It can be used to blink the LED on the board during flashing
// The LED will be on during download of one buffer of data from the network. The LED will
// be off during writing that buffer to flash
// On a good connection the LED should flash regularly. On a bad connection the LED will be
// on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second
// value is used to put the LED on. If the LED is on with HIGH, that value should be passed
// httpUpdate.setLedPin(LED_BUILTIN, HIGH);
t_httpUpdate_return ret = httpUpdate.update(client, "https://server/file.bin");
// Or:
//t_httpUpdate_return ret = httpUpdate.update(client, "server", 443, "file.bin");
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
}

View File

@ -0,0 +1,42 @@
#######################################
# Syntax Coloring Map For ESP8266httpUpdate
#######################################
#######################################
# Library (KEYWORD3)
#######################################
ESP8266httpUpdate KEYWORD3 RESERVED_WORD
#######################################
# Datatypes (KEYWORD1)
#######################################
HTTPUpdateResult KEYWORD1 DATA_TYPE
ESPhttpUpdate KEYWORD1 DATA_TYPE
#######################################
# Methods and Functions (KEYWORD2)
#######################################
rebootOnUpdate KEYWORD2
update KEYWORD2
updateSpiffs KEYWORD2
getLastError KEYWORD2
getLastErrorString KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
HTTP_UE_TOO_LESS_SPACE LITERAL1 RESERVED_WORD_2
HTTP_UE_SERVER_NOT_REPORT_SIZE LITERAL1 RESERVED_WORD_2
HTTP_UE_SERVER_FILE_NOT_FOUND LITERAL1 RESERVED_WORD_2
HTTP_UE_SERVER_FORBIDDEN LITERAL1 RESERVED_WORD_2
HTTP_UE_SERVER_WRONG_HTTP_CODE LITERAL1 RESERVED_WORD_2
HTTP_UE_SERVER_FAULTY_MD5 LITERAL1 RESERVED_WORD_2
HTTP_UE_BIN_VERIFY_HEADER_FAILED LITERAL1 RESERVED_WORD_2
HTTP_UE_BIN_FOR_WRONG_FLASH LITERAL1 RESERVED_WORD_2
HTTP_UPDATE_FAILED LITERAL1 RESERVED_WORD_2
HTTP_UPDATE_NO_UPDATES LITERAL1 RESERVED_WORD_2
HTTP_UPDATE_OK LITERAL1 RESERVED_WORD_2

View File

@ -0,0 +1,9 @@
name=HTTPUpdate
version=1.3
author=Markus Sattler
maintainer=Markus Sattler
sentence=Http Update for ESP32
paragraph=
category=Data Processing
url=https://github.com/Links2004/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/ESP8266httpUpdate
architectures=esp32

View File

@ -0,0 +1,417 @@
/**
*
* @file HTTPUpdate.cpp based om ESP8266HTTPUpdate.cpp
* @date 16.10.2018
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP32 Http Updater.
*
* 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 "HTTPUpdate.h"
#include <StreamString.h>
#include <esp_partition.h>
#include <esp_ota_ops.h> // get running partition
// To do extern "C" uint32_t _SPIFFS_start;
// To do extern "C" uint32_t _SPIFFS_end;
HTTPUpdate::HTTPUpdate(void)
: _httpClientTimeout(8000), _ledPin(-1)
{
}
HTTPUpdate::HTTPUpdate(int httpClientTimeout)
: _httpClientTimeout(httpClientTimeout), _ledPin(-1)
{
}
HTTPUpdate::~HTTPUpdate(void)
{
}
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& url, const String& currentVersion)
{
HTTPClient http;
if(!http.begin(client, url))
{
return HTTP_UPDATE_FAILED;
}
return handleUpdate(http, currentVersion, false);
}
HTTPUpdateResult HTTPUpdate::updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion)
{
HTTPClient http;
if(!http.begin(client, url))
{
return HTTP_UPDATE_FAILED;
}
return handleUpdate(http, currentVersion, true);
}
HTTPUpdateResult HTTPUpdate::update(WiFiClient& client, const String& host, uint16_t port, const String& uri,
const String& currentVersion)
{
HTTPClient http;
if(!http.begin(client, host, port, uri))
{
return HTTP_UPDATE_FAILED;
}
return handleUpdate(http, currentVersion, false);
}
/**
* return error code as int
* @return int error code
*/
int HTTPUpdate::getLastError(void)
{
return _lastError;
}
/**
* return error code as String
* @return String error
*/
String HTTPUpdate::getLastErrorString(void)
{
if(_lastError == 0) {
return String(); // no error
}
// error from Update class
if(_lastError > 0) {
StreamString error;
Update.printError(error);
error.trim(); // remove line ending
return String("Update error: ") + error;
}
// error from http client
if(_lastError > -100) {
return String("HTTP error: ") + HTTPClient::errorToString(_lastError);
}
switch(_lastError) {
case HTTP_UE_TOO_LESS_SPACE:
return "Not Enough space";
case HTTP_UE_SERVER_NOT_REPORT_SIZE:
return "Server Did Not Report Size";
case HTTP_UE_SERVER_FILE_NOT_FOUND:
return "File Not Found (404)";
case HTTP_UE_SERVER_FORBIDDEN:
return "Forbidden (403)";
case HTTP_UE_SERVER_WRONG_HTTP_CODE:
return "Wrong HTTP Code";
case HTTP_UE_SERVER_FAULTY_MD5:
return "Wrong MD5";
case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
return "Verify Bin Header Failed";
case HTTP_UE_BIN_FOR_WRONG_FLASH:
return "New Binary Does Not Fit Flash Size";
case HTTP_UE_NO_PARTITION:
return "Partition Could Not be Found";
}
return String();
}
String getSketchSHA256() {
const size_t HASH_LEN = 32; // SHA-256 digest length
uint8_t sha_256[HASH_LEN] = { 0 };
// get sha256 digest for running partition
if(esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256) == 0) {
char buffer[2 * HASH_LEN + 1];
for(size_t index = 0; index < HASH_LEN; index++) {
uint8_t nibble = (sha_256[index] & 0xf0) >> 4;
buffer[2 * index] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
nibble = sha_256[index] & 0x0f;
buffer[2 * index + 1] = nibble < 10 ? char(nibble + '0') : char(nibble - 10 + 'A');
}
buffer[2 * HASH_LEN] = '\0';
return String(buffer);
} else {
return String();
}
}
/**
*
* @param http HTTPClient *
* @param currentVersion const char *
* @return HTTPUpdateResult
*/
HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs)
{
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
http.useHTTP10(true);
http.setTimeout(_httpClientTimeout);
http.setUserAgent("ESP32-http-Update");
http.addHeader("Cache-Control", "no-cache");
http.addHeader("x-ESP32-STA-MAC", WiFi.macAddress());
http.addHeader("x-ESP32-AP-MAC", WiFi.softAPmacAddress());
http.addHeader("x-ESP32-free-space", String(ESP.getFreeSketchSpace()));
http.addHeader("x-ESP32-sketch-size", String(ESP.getSketchSize()));
String sketchMD5 = ESP.getSketchMD5();
if(sketchMD5.length() != 0) {
http.addHeader("x-ESP32-sketch-md5", sketchMD5);
}
// Add also a SHA256
String sketchSHA256 = getSketchSHA256();
if(sketchSHA256.length() != 0) {
http.addHeader("x-ESP32-sketch-sha256", sketchSHA256);
}
http.addHeader("x-ESP32-chip-size", String(ESP.getFlashChipSize()));
http.addHeader("x-ESP32-sdk-version", ESP.getSdkVersion());
if(spiffs) {
http.addHeader("x-ESP32-mode", "spiffs");
} else {
http.addHeader("x-ESP32-mode", "sketch");
}
if(currentVersion && currentVersion[0] != 0x00) {
http.addHeader("x-ESP32-version", currentVersion);
}
const char * headerkeys[] = { "x-MD5" };
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
// track these headers
http.collectHeaders(headerkeys, headerkeyssize);
int code = http.GET();
int len = http.getSize();
if(code <= 0) {
log_e("HTTP error: %s\n", http.errorToString(code).c_str());
_lastError = code;
http.end();
return HTTP_UPDATE_FAILED;
}
log_d("Header read fin.\n");
log_d("Server header:\n");
log_d(" - code: %d\n", code);
log_d(" - len: %d\n", len);
if(http.hasHeader("x-MD5")) {
log_d(" - MD5: %s\n", http.header("x-MD5").c_str());
}
log_d("ESP32 info:\n");
log_d(" - free Space: %d\n", ESP.getFreeSketchSpace());
log_d(" - current Sketch Size: %d\n", ESP.getSketchSize());
if(currentVersion && currentVersion[0] != 0x00) {
log_d(" - current version: %s\n", currentVersion.c_str() );
}
switch(code) {
case HTTP_CODE_OK: ///< OK (Start Update)
if(len > 0) {
bool startUpdate = true;
if(spiffs) {
const esp_partition_t* _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
if(!_partition){
_lastError = HTTP_UE_NO_PARTITION;
return HTTP_UPDATE_FAILED;
}
if(len > _partition->size) {
log_e("spiffsSize to low (%d) needed: %d\n", _partition->size, len);
startUpdate = false;
}
} else {
int sketchFreeSpace = ESP.getFreeSketchSpace();
if(!sketchFreeSpace){
_lastError = HTTP_UE_NO_PARTITION;
return HTTP_UPDATE_FAILED;
}
if(len > sketchFreeSpace) {
log_e("FreeSketchSpace to low (%d) needed: %d\n", sketchFreeSpace, len);
startUpdate = false;
}
}
if(!startUpdate) {
_lastError = HTTP_UE_TOO_LESS_SPACE;
ret = HTTP_UPDATE_FAILED;
} else {
WiFiClient * tcp = http.getStreamPtr();
// To do? WiFiUDP::stopAll();
// To do? WiFiClient::stopAllExcept(tcp);
delay(100);
int command;
if(spiffs) {
command = U_SPIFFS;
log_d("runUpdate spiffs...\n");
} else {
command = U_FLASH;
log_d("runUpdate flash...\n");
}
if(!spiffs) {
/* To do
uint8_t buf[4];
if(tcp->peekBytes(&buf[0], 4) != 4) {
log_e("peekBytes magic header failed\n");
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
http.end();
return HTTP_UPDATE_FAILED;
}
*/
// check for valid first magic byte
// if(buf[0] != 0xE9) {
if(tcp->peek() != 0xE9) {
log_e("Magic header does not start with 0xE9\n");
_lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
http.end();
return HTTP_UPDATE_FAILED;
}
/* To do
uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
// check if new bin fits to SPI flash
if(bin_flash_size > ESP.getFlashChipRealSize()) {
log_e("New binary does not fit SPI Flash size\n");
_lastError = HTTP_UE_BIN_FOR_WRONG_FLASH;
http.end();
return HTTP_UPDATE_FAILED;
}
*/
}
if(runUpdate(*tcp, len, http.header("x-MD5"), command)) {
ret = HTTP_UPDATE_OK;
log_d("Update ok\n");
http.end();
if(_rebootOnUpdate && !spiffs) {
ESP.restart();
}
} else {
ret = HTTP_UPDATE_FAILED;
log_e("Update failed\n");
}
}
} else {
_lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE;
ret = HTTP_UPDATE_FAILED;
log_e("Content-Length was 0 or wasn't set by Server?!\n");
}
break;
case HTTP_CODE_NOT_MODIFIED:
///< Not Modified (No updates)
ret = HTTP_UPDATE_NO_UPDATES;
break;
case HTTP_CODE_NOT_FOUND:
_lastError = HTTP_UE_SERVER_FILE_NOT_FOUND;
ret = HTTP_UPDATE_FAILED;
break;
case HTTP_CODE_FORBIDDEN:
_lastError = HTTP_UE_SERVER_FORBIDDEN;
ret = HTTP_UPDATE_FAILED;
break;
default:
_lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE;
ret = HTTP_UPDATE_FAILED;
log_e("HTTP Code is (%d)\n", code);
break;
}
http.end();
return ret;
}
/**
* write Update to flash
* @param in Stream&
* @param size uint32_t
* @param md5 String
* @return true if Update ok
*/
bool HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command)
{
StreamString error;
if(!Update.begin(size, command, _ledPin, _ledOn)) {
_lastError = Update.getError();
Update.printError(error);
error.trim(); // remove line ending
log_e("Update.begin failed! (%s)\n", error.c_str());
return false;
}
if(md5.length()) {
if(!Update.setMD5(md5.c_str())) {
_lastError = HTTP_UE_SERVER_FAULTY_MD5;
log_e("Update.setMD5 failed! (%s)\n", md5.c_str());
return false;
}
}
// To do: the SHA256 could be checked if the server sends it
if(Update.writeStream(in) != size) {
_lastError = Update.getError();
Update.printError(error);
error.trim(); // remove line ending
log_e("Update.writeStream failed! (%s)\n", error.c_str());
return false;
}
if(!Update.end()) {
_lastError = Update.getError();
Update.printError(error);
error.trim(); // remove line ending
log_e("Update.end failed! (%s)\n", error.c_str());
return false;
}
return true;
}
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
HTTPUpdate httpUpdate;
#endif

View File

@ -0,0 +1,101 @@
/**
*
* @file HTTPUpdate.h based on ESP8266HTTPUpdate.h
* @date 16.10.2018
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP32 Http Updater.
*
* 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 ___HTTP_UPDATE_H___
#define ___HTTP_UPDATE_H___
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
#include <HTTPClient.h>
#include <Update.h>
/// note we use HTTP client errors too so we start at 100
#define HTTP_UE_TOO_LESS_SPACE (-100)
#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101)
#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102)
#define HTTP_UE_SERVER_FORBIDDEN (-103)
#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104)
#define HTTP_UE_SERVER_FAULTY_MD5 (-105)
#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106)
#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107)
#define HTTP_UE_NO_PARTITION (-108)
enum HTTPUpdateResult {
HTTP_UPDATE_FAILED,
HTTP_UPDATE_NO_UPDATES,
HTTP_UPDATE_OK
};
typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
class HTTPUpdate
{
public:
HTTPUpdate(void);
HTTPUpdate(int httpClientTimeout);
~HTTPUpdate(void);
void rebootOnUpdate(bool reboot)
{
_rebootOnUpdate = reboot;
}
void setLedPin(int ledPin = -1, uint8_t ledOn = HIGH)
{
_ledPin = ledPin;
_ledOn = ledOn;
}
t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = "");
t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/",
const String& currentVersion = "");
t_httpUpdate_return updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion = "");
int getLastError(void);
String getLastErrorString(void);
protected:
t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false);
bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH);
int _lastError;
bool _rebootOnUpdate = true;
private:
int _httpClientTimeout;
int _ledPin;
uint8_t _ledOn;
};
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
extern HTTPUpdate httpUpdate;
#endif
#endif /* ___HTTP_UPDATE_H___ */

View File

@ -468,3 +468,13 @@ size_t Preferences::getBytes(const char* key, void * buf, size_t maxLen){
}
return len;
}
size_t Preferences::freeEntries() {
nvs_stats_t nvs_stats;
esp_err_t err = nvs_get_stats(NULL, &nvs_stats);
if(err){
log_e("Failed to get nvs statistics");
return 0;
}
return nvs_stats.free_entries;
}

View File

@ -64,6 +64,7 @@ class Preferences {
size_t getString(const char* key, char* value, size_t maxLen);
String getString(const char* key, String defaultValue = String());
size_t getBytes(const char* key, void * buf, size_t maxLen);
size_t freeEntries();
};
#endif

View File

@ -16,7 +16,7 @@ extern "C" {
#include "diskio.h"
#include "ffconf.h"
#include "ff.h"
#include "esp_vfs.h"
//#include "esp_vfs.h"
#include "esp_vfs_fat.h"
char CRC7(const char* data, int length);
unsigned short CRC16(const char* data, int length);

View File

@ -50,12 +50,13 @@ bool SDMMCFS::begin(const char * mountpoint, bool mode1bit)
.init = &sdmmc_host_init,
.set_bus_width = &sdmmc_host_set_bus_width,
.get_bus_width = &sdmmc_host_get_slot_width,
.set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode,
.set_card_clk = &sdmmc_host_set_card_clk,
.do_transaction = &sdmmc_host_do_transaction,
.deinit = &sdmmc_host_deinit,
.io_int_enable = sdmmc_host_io_int_enable,
.io_int_wait = sdmmc_host_io_int_wait,
.command_timeout_ms = 0,
.io_int_enable = &sdmmc_host_io_int_enable,
.io_int_wait = &sdmmc_host_io_int_wait,
.command_timeout_ms = 0
};
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
#ifdef BOARD_HAS_1BIT_SDMMC

View File

@ -214,6 +214,11 @@ void SPIClass::writeBytes(uint8_t * data, uint32_t size)
spiEndTransaction(_spi);
}
void SPIClass::transfer(uint8_t * data, uint32_t size)
{
transferBytes(data, data, size);
}
/**
* @param data void *
* @param size uint32_t

View File

@ -65,10 +65,11 @@ public:
void beginTransaction(SPISettings settings);
void endTransaction(void);
void transfer(uint8_t * data, uint32_t size);
uint8_t transfer(uint8_t data);
uint16_t transfer16(uint16_t data);
uint32_t transfer32(uint32_t data);
void transferBytes(uint8_t * data, uint8_t * out, uint32_t size);
void transferBits(uint32_t data, uint32_t * out, uint8_t bits);

View File

@ -41,7 +41,7 @@ class UpdateClass {
Call this to check the space needed for the update
Will return false if there is not enough space
*/
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH);
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW);
/*
Writes a buffer to the flash and increments the address
@ -174,6 +174,9 @@ class UpdateClass {
String _target_md5;
MD5Builder _md5;
int _ledPin;
uint8_t _ledOn;
};
extern UpdateClass Update;

View File

@ -88,6 +88,10 @@ void UpdateClass::_reset() {
_progress = 0;
_size = 0;
_command = U_FLASH;
if(_ledPin != -1) {
digitalWrite(_ledPin, !_ledOn); // off
}
}
bool UpdateClass::canRollBack(){
@ -106,12 +110,15 @@ bool UpdateClass::rollBack(){
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
}
bool UpdateClass::begin(size_t size, int command) {
bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
if(_size > 0){
log_w("already running");
return false;
}
_ledPin = ledPin;
_ledOn = !!ledOn; // 0(LOW) or 1(HIGH)
_reset();
_error = 0;
@ -315,16 +322,32 @@ size_t UpdateClass::writeStream(Stream &data) {
if (_progress_callback) {
_progress_callback(0, _size);
}
if(_ledPin != -1) {
pinMode(_ledPin, OUTPUT);
}
while(remaining()) {
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
if(_ledPin != -1) {
digitalWrite(_ledPin, _ledOn); // Switch LED on
}
size_t bytesToRead = SPI_FLASH_SEC_SIZE - _bufferLen;
if(bytesToRead > remaining()) {
bytesToRead = remaining();
}
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
if(toRead == 0) { //Timeout
delay(100);
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
if(toRead == 0) { //Timeout
_abort(UPDATE_ERROR_STREAM);
return written;
}
}
if(_ledPin != -1) {
digitalWrite(_ledPin, !_ledOn); // Switch LED off
}
_bufferLen += toRead;
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
return written;

View File

@ -1,5 +1,5 @@
/*
FSWebServer - Example WebServer with SPIFFS backend for esp8266
FSWebServer - Example WebServer with FS backend for esp8266/esp32
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the WebServer library for Arduino environment.
@ -26,14 +26,21 @@
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <SPIFFS.h>
#define FILESYSTEM SPIFFS
#define FORMAT_FILESYSTEM true
#define DBG_OUTPUT_PORT Serial
#if FILESYSTEM == FFat
#include <FFat.h>
#endif
#if FILESYSTEM == SPIFFS
#include <SPIFFS.h>
#endif
const char* ssid = "wifi-ssid";
const char* password = "wifi-password";
const char* host = "esp32fs";
WebServer server(80);
//holds the current upload
File fsUploadFile;
@ -84,7 +91,7 @@ String getContentType(String filename) {
bool exists(String path){
bool yes = false;
File file = SPIFFS.open(path, "r");
File file = FILESYSTEM.open(path, "r");
if(!file.isDirectory()){
yes = true;
}
@ -103,7 +110,7 @@ bool handleFileRead(String path) {
if (exists(pathWithGz)) {
path += ".gz";
}
File file = SPIFFS.open(path, "r");
File file = FILESYSTEM.open(path, "r");
server.streamFile(file, contentType);
file.close();
return true;
@ -122,7 +129,7 @@ void handleFileUpload() {
filename = "/" + filename;
}
DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
fsUploadFile = SPIFFS.open(filename, "w");
fsUploadFile = FILESYSTEM.open(filename, "w");
filename = String();
} else if (upload.status == UPLOAD_FILE_WRITE) {
//DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
@ -149,7 +156,7 @@ void handleFileDelete() {
if (!exists(path)) {
return server.send(404, "text/plain", "FileNotFound");
}
SPIFFS.remove(path);
FILESYSTEM.remove(path);
server.send(200, "text/plain", "");
path = String();
}
@ -166,7 +173,7 @@ void handleFileCreate() {
if (exists(path)) {
return server.send(500, "text/plain", "FILE EXISTS");
}
File file = SPIFFS.open(path, "w");
File file = FILESYSTEM.open(path, "w");
if (file) {
file.close();
} else {
@ -186,7 +193,7 @@ void handleFileList() {
DBG_OUTPUT_PORT.println("handleFileList: " + path);
File root = SPIFFS.open(path);
File root = FILESYSTEM.open(path);
path = String();
String output = "[";
@ -212,9 +219,10 @@ void setup(void) {
DBG_OUTPUT_PORT.begin(115200);
DBG_OUTPUT_PORT.print("\n");
DBG_OUTPUT_PORT.setDebugOutput(true);
SPIFFS.begin();
if (FORMAT_FILESYSTEM) FILESYSTEM.format();
FILESYSTEM.begin();
{
File root = SPIFFS.open("/");
File root = FILESYSTEM.open("/");
File file = root.openNextFile();
while(file){
String fileName = file.name();
@ -267,7 +275,7 @@ void setup(void) {
}, handleFileUpload);
//called when the url is not defined here
//use it to load content from SPIFFS
//use it to load content from FILESYSTEM
server.onNotFound([]() {
if (!handleFileRead(server.uri())) {
server.send(404, "text/plain", "FileNotFound");

View File

@ -0,0 +1,53 @@
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
const char *ssid = "........";
const char *password = "........";
WebServer server(80);
void setup(void) {
Serial.begin(9600);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", []() {
server.send(200, "text/plain", "hello from esp32!");
});
server.on("/users/{}", []() {
String user = server.pathArg(0);
server.send(200, "text/plain", "User: '" + user + "'");
});
server.on("/users/{}/devices/{}", []() {
String user = server.pathArg(0);
String device = server.pathArg(1);
server.send(200, "text/plain", "User: '" + user + "' and Device: '" + device + "'");
});
server.begin();
Serial.println("HTTP server started");
}
void loop(void) {
server.handleClient();
}

View File

@ -509,6 +509,11 @@ void WebServer::_streamFileCore(const size_t fileSize, const String & fileName,
send(200, contentType, "");
}
String WebServer::pathArg(unsigned int i) {
if (_currentHandler != nullptr)
return _currentHandler->pathArg(i);
return "";
}
String WebServer::arg(String name) {
for (int j = 0; j < _postArgsLen; ++j) {

View File

@ -97,6 +97,7 @@ public:
virtual WiFiClient client() { return _currentClient; }
HTTPUpload& upload() { return *_currentUpload; }
String pathArg(unsigned int i); // get request path argument by number
String arg(String name); // get request argument value by name
String arg(int i); // get request argument value by number
String argName(int i); // get request argument name by number

View File

@ -1,6 +1,9 @@
#ifndef REQUESTHANDLER_H
#define REQUESTHANDLER_H
#include <vector>
#include <assert.h>
class RequestHandler {
public:
virtual ~RequestHandler() { }
@ -14,6 +17,15 @@ public:
private:
RequestHandler* _next = nullptr;
protected:
std::vector<String> pathArgs;
public:
const String& pathArg(unsigned int i) {
assert(i < pathArgs.size());
return pathArgs[i];
}
};
#endif //REQUESTHANDLER_H

View File

@ -15,16 +15,55 @@ public:
, _uri(uri)
, _method(method)
{
int numParams = 0, start = 0;
do {
start = _uri.indexOf("{}", start);
if (start > 0) {
numParams++;
start += 2;
}
} while (start > 0);
pathArgs.resize(numParams);
}
bool canHandle(HTTPMethod requestMethod, String requestUri) override {
if (_method != HTTP_ANY && _method != requestMethod)
return false;
if (requestUri != _uri)
return false;
if (_uri == requestUri)
return true;
return true;
size_t uriLength = _uri.length();
unsigned int pathArgIndex = 0;
unsigned int requestUriIndex = 0;
for (unsigned int i = 0; i < uriLength; i++, requestUriIndex++) {
char uriChar = _uri[i];
char requestUriChar = requestUri[requestUriIndex];
if (uriChar == requestUriChar)
continue;
if (uriChar != '{')
return false;
i += 2; // index of char after '}'
if (i >= uriLength) {
// there is no char after '}'
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex);
return pathArgs[pathArgIndex].indexOf("/") == -1; // path argument may not contain a '/'
}
else
{
char charEnd = _uri[i];
int uriIndex = requestUri.indexOf(charEnd, requestUriIndex);
if (uriIndex < 0)
return false;
pathArgs[pathArgIndex] = requestUri.substring(requestUriIndex, uriIndex);
requestUriIndex = (unsigned int) uriIndex;
}
pathArgIndex++;
}
return requestUriIndex >= requestUri.length();
}
bool canUpload(String requestUri) override {

View File

@ -0,0 +1,93 @@
/*
WiFiAccessPoint.ino creates a WiFi access point and provides a web server on it.
Steps:
1. Connect to the access point "yourAp"
2. Point your web browser to http://192.168.4.1/H to turn the LED on or http://192.168.4.1/L to turn it off
OR
Run raw TCP "GET /H" and "GET /L" on PuTTY terminal with 192.168.4.1 as IP address and 80 as port
Created for arduino-esp32 on 04 July, 2018
by Elochukwu Ifediora (fedy0)
*/
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#define LED_BUILTIN 2 // Set the GPIO pin where you connected your test LED or comment this line out if your dev board has a built-in LED
// Set these to your desired credentials.
const char *ssid = "yourAP";
const char *password = "yourPassword";
WiFiServer server(80);
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
Serial.println();
Serial.println("Configuring access point...");
// You can remove the password parameter if you want the AP to be open.
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.begin();
Serial.println("Server started");
}
void loop() {
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
Serial.println("New Client."); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
client.print("Click <a href=\"/H\">here</a> to turn ON the LED.<br>");
client.print("Click <a href=\"/L\">here</a> to turn OFF the LED.<br>");
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
} else { // if you got a newline, then clear currentLine:
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
// Check to see if the client request was "GET /H" or "GET /L":
if (currentLine.endsWith("GET /H")) {
digitalWrite(LED_BUILTIN, HIGH); // GET /H turns the LED on
}
if (currentLine.endsWith("GET /L")) {
digitalWrite(LED_BUILTIN, LOW); // GET /L turns the LED off
}
}
}
// close the connection:
client.stop();
Serial.println("Client Disconnected.");
}
}

View File

@ -0,0 +1,43 @@
# ESP32-Eduroam
* Eduroam wifi connection with university login identity
* Working under Eduroam networks worldwide
* Methods: PEAP + MsCHAPv2
# Format
* IDENTITY = youridentity --> if connecting from different university, use youridentity@youruniversity.domain format
* PASSWORD = yourpassword
# Usage
* Change IDENTITY
* Change password
* Upload sketch and enjoy!
* After sucessful assign of IP address, board will connect to HTTP page on internet to verify your authentification
* Board will auto reconnect to Eduroam if it lost connection
# Tested locations
|University|Board|Method|Result|
|-------------|-------------| -----|------|
|Technical University in Košice (Slovakia)|ESP32 Devkit v1|PEAP + MsCHAPv2|Working|
|Technical University in Košice (Slovakia)|ESP32 Devmodule v4|PEAP + MsCHAPv2|Working on 6th attempt in loop|
|Slovak Technical University in Bratislava (Slovakia)|ESP32 Devkit v1|PEAP + MsCHAPv2|Working|
|University of Antwerp (Belgium)|Lolin32|PEAP + MsCHAPv2|Working|
|UPV Universitat Politècnica de València (Spain)|ESP32 Devmodule v4|PEAP + MsCHAPv2|Working|
|Local Zeroshell powered network|ESP32 Devkit v1|PEAP + MsCHAPv2|*Not working*|
|Hasselt University (Belgium)|xxx|PEAP + MsCHAPv2|Working with fix sketch|
|Universidad de Granada (Spain)|Lolin D32 Pro|PEAP + MsCHAPv2|Working|
|Universidad de Granada (Spain)|Lolin D32|PEAP + MsCHAPv2|Working|
|Universidade Federal de Santa Catarina (Brazil)|xxx|EAP-TTLS + MsCHAPv2|Working|
|University of Central Florida (Orlando, Florida)|ESP32 Built-in OLED Heltec WiFi Kit 32|PEAP + MsCHAPv2|Working|
|Université de Montpellier (France)|NodeMCU-32S|PEAP + MsCHAPv2|Working|
# Common errors - Switch to Debug mode for Serial monitor prints
|Error|Appearance|Solution|
|-------------|-------------|-------------|
|wifi: Set status to INIT|Frequent|Hold EN button for few seconds|
|HANDSHAKE_TIMEOUT|Rare|Bug was found under Zeroshell RADIUS authentization - Unsucessful connection|
|AUTH_EXPIRE|Common|In the case of weak wifi network signal, this error is quite common, bring your device closer to AP|
|ASSOC_EXPIRE|Rare|-|
# Sucessful connection example
![alt text](https://i.nahraj.to/f/24Kc.png)
# Unsucessful connection example
![alt text](https://camo.githubusercontent.com/87e47d1b27f4e8ace87423e40e8edbce7983bafa/68747470733a2f2f692e6e616872616a2e746f2f662f323435572e504e47)

View File

@ -1,12 +1,10 @@
//Sketch edited by: Martin Chlebovec
//Personal website: https://arduino.php5.sk
#include "esp_wpa2.h"
#include <WiFi.h>
#define EAP_IDENTITY "login@university.domain" //eduroam login --> identity@youruniversity.domain
#include <WiFi.h> //Wifi library
#include "esp_wpa2.h" //wpa2 library for connections to Enterprise networks
#define EAP_IDENTITY "login" //if connecting from another corporation, use identity@organisation.domain in Eduroam
#define EAP_PASSWORD "password" //your Eduroam password
String line; //variable for response
const char* ssid = "eduroam"; // Eduroam SSID
const char* host = "arduino.php5.sk"; //external server domain for HTTP connection after authentification
int counter = 0;
void setup() {
Serial.begin(115200);
delay(10);
@ -14,50 +12,58 @@ void setup() {
Serial.print("Connecting to network: ");
Serial.println(ssid);
WiFi.disconnect(true); //disconnect form wifi to set new wifi connection
WiFi.mode(WIFI_STA); //init wifi mode
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide identity
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username --> identity and username is same
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD)); //provide password
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config to default (fixed for 2018 and Arduino 1.8.5+)
esp_wifi_sta_wpa2_ent_enable(&config); //set config to enable function (fixed for 2018 and Arduino 1.8.5+)
WiFi.begin(ssid); //connect to Eduroam function
WiFi.setHostname("ESP32Name"); //set Hostname for your device - not neccesary
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
WiFi.begin(ssid); //connect to wifi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
counter++;
if(counter>=60){ //after 30 seconds timeout - reset board
ESP.restart();
}
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address set: ");
Serial.println(WiFi.localIP()); //print LAN IP
}
void loop() {
delay(5000);
if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
WiFi.begin(ssid);
delay(500);
}
if (WiFi.status() == WL_CONNECTED) { //if we are connected to Eduroam network
counter = 0; //reset counter
Serial.println("Wifi is still connected with IP: ");
Serial.println(WiFi.localIP()); //inform user about his IP address
}else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
WiFi.begin(ssid);
}
while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
delay(500);
Serial.print(".");
counter++;
if(counter>=60){ //30 seconds timeout - reset board
ESP.restart();
}
}
Serial.print("Connecting to website: ");
Serial.println(host);
WiFiClient client;
if (!client.connect(host, 80)) { // HTTP connection on port 80
Serial.println("Connection lost! - Failed response");
}
String url = "/rele/rele1.txt"; //read .txt file
Serial.print("Requesting URL: ");
Serial.println(url);
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println("Client timed out! - retry");
if (client.connect(host, 80)) {
String url = "/rele/rele1.txt";
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: ESP32\r\n" + "Connection: close\r\n\r\n");
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
break;
}
}
}
while(client.available()) {
line = client.readStringUntil('\n');
Serial.println(line);
}
Serial.println();
Serial.println("End connection");
client.stop();
String line = client.readStringUntil('\n');
Serial.println(line);
}else{
Serial.println("Connection unsucessful");
}
}

View File

@ -3,34 +3,35 @@
*
*/
/*
/*
* WiFi Events
SYSTEM_EVENT_WIFI_READY < ESP32 WiFi ready
SYSTEM_EVENT_SCAN_DONE < ESP32 finish scanning AP
SYSTEM_EVENT_STA_START < ESP32 station start
SYSTEM_EVENT_STA_STOP < ESP32 station stop
SYSTEM_EVENT_STA_CONNECTED < ESP32 station connected to AP
SYSTEM_EVENT_STA_DISCONNECTED < ESP32 station disconnected from AP
SYSTEM_EVENT_STA_AUTHMODE_CHANGE < the auth mode of AP connected by ESP32 station changed
SYSTEM_EVENT_STA_GOT_IP < ESP32 station got IP from connected AP
SYSTEM_EVENT_STA_LOST_IP < ESP32 station lost IP and the IP is reset to 0
SYSTEM_EVENT_STA_WPS_ER_SUCCESS < ESP32 station wps succeeds in enrollee mode
SYSTEM_EVENT_STA_WPS_ER_FAILED < ESP32 station wps fails in enrollee mode
SYSTEM_EVENT_STA_WPS_ER_TIMEOUT < ESP32 station wps timeout in enrollee mode
SYSTEM_EVENT_STA_WPS_ER_PIN < ESP32 station wps pin code in enrollee mode
SYSTEM_EVENT_AP_START < ESP32 soft-AP start
SYSTEM_EVENT_AP_STOP < ESP32 soft-AP stop
SYSTEM_EVENT_AP_STACONNECTED < a station connected to ESP32 soft-AP
SYSTEM_EVENT_AP_STADISCONNECTED < a station disconnected from ESP32 soft-AP
SYSTEM_EVENT_AP_PROBEREQRECVED < Receive probe request packet in soft-AP interface
SYSTEM_EVENT_GOT_IP6 < ESP32 station or ap or ethernet interface v6IP addr is preferred
SYSTEM_EVENT_ETH_START < ESP32 ethernet start
SYSTEM_EVENT_ETH_STOP < ESP32 ethernet stop
SYSTEM_EVENT_ETH_CONNECTED < ESP32 ethernet phy link up
SYSTEM_EVENT_ETH_DISCONNECTED < ESP32 ethernet phy link down
SYSTEM_EVENT_ETH_GOT_IP < ESP32 ethernet got IP from connected AP
SYSTEM_EVENT_MAX
0 SYSTEM_EVENT_WIFI_READY < ESP32 WiFi ready
1 SYSTEM_EVENT_SCAN_DONE < ESP32 finish scanning AP
2 SYSTEM_EVENT_STA_START < ESP32 station start
3 SYSTEM_EVENT_STA_STOP < ESP32 station stop
4 SYSTEM_EVENT_STA_CONNECTED < ESP32 station connected to AP
5 SYSTEM_EVENT_STA_DISCONNECTED < ESP32 station disconnected from AP
6 SYSTEM_EVENT_STA_AUTHMODE_CHANGE < the auth mode of AP connected by ESP32 station changed
7 SYSTEM_EVENT_STA_GOT_IP < ESP32 station got IP from connected AP
8 SYSTEM_EVENT_STA_LOST_IP < ESP32 station lost IP and the IP is reset to 0
9 SYSTEM_EVENT_STA_WPS_ER_SUCCESS < ESP32 station wps succeeds in enrollee mode
10 SYSTEM_EVENT_STA_WPS_ER_FAILED < ESP32 station wps fails in enrollee mode
11 SYSTEM_EVENT_STA_WPS_ER_TIMEOUT < ESP32 station wps timeout in enrollee mode
12 SYSTEM_EVENT_STA_WPS_ER_PIN < ESP32 station wps pin code in enrollee mode
13 SYSTEM_EVENT_AP_START < ESP32 soft-AP start
14 SYSTEM_EVENT_AP_STOP < ESP32 soft-AP stop
15 SYSTEM_EVENT_AP_STACONNECTED < a station connected to ESP32 soft-AP
16 SYSTEM_EVENT_AP_STADISCONNECTED < a station disconnected from ESP32 soft-AP
17 SYSTEM_EVENT_AP_STAIPASSIGNED < ESP32 soft-AP assign an IP to a connected station
18 SYSTEM_EVENT_AP_PROBEREQRECVED < Receive probe request packet in soft-AP interface
19 SYSTEM_EVENT_GOT_IP6 < ESP32 station or ap or ethernet interface v6IP addr is preferred
20 SYSTEM_EVENT_ETH_START < ESP32 ethernet start
21 SYSTEM_EVENT_ETH_STOP < ESP32 ethernet stop
22 SYSTEM_EVENT_ETH_CONNECTED < ESP32 ethernet phy link up
23 SYSTEM_EVENT_ETH_DISCONNECTED < ESP32 ethernet phy link down
24 SYSTEM_EVENT_ETH_GOT_IP < ESP32 ethernet got IP from connected AP
25 SYSTEM_EVENT_MAX
*/
#include <WiFi.h>
@ -43,18 +44,84 @@ void WiFiEvent(WiFiEvent_t event)
{
Serial.printf("[WiFi-event] event: %d\n", event);
switch (event)
{
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
break;
}
}
switch (event) {
case SYSTEM_EVENT_WIFI_READY:
Serial.println("WiFi interface ready");
break;
case SYSTEM_EVENT_SCAN_DONE:
Serial.println("Completed scan for access points");
break;
case SYSTEM_EVENT_STA_START:
Serial.println("WiFi client started");
break;
case SYSTEM_EVENT_STA_STOP:
Serial.println("WiFi clients stopped");
break;
case SYSTEM_EVENT_STA_CONNECTED:
Serial.println("Connected to access point");
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("Disconnected from WiFi access point");
break;
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:
Serial.println("Authentication mode of access point has changed");
break;
case SYSTEM_EVENT_STA_GOT_IP:
Serial.print("Obtained IP address: ");
Serial.println(WiFi.localIP());
break;
case SYSTEM_EVENT_STA_LOST_IP:
Serial.println("Lost IP address and IP address is reset to 0");
break;
case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
Serial.println("WiFi Protected Setup (WPS): succeeded in enrollee mode");
break;
case SYSTEM_EVENT_STA_WPS_ER_FAILED:
Serial.println("WiFi Protected Setup (WPS): failed in enrollee mode");
break;
case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
Serial.println("WiFi Protected Setup (WPS): timeout in enrollee mode");
break;
case SYSTEM_EVENT_STA_WPS_ER_PIN:
Serial.println("WiFi Protected Setup (WPS): pin code in enrollee mode");
break;
case SYSTEM_EVENT_AP_START:
Serial.println("WiFi access point started");
break;
case SYSTEM_EVENT_AP_STOP:
Serial.println("WiFi access point stopped");
break;
case SYSTEM_EVENT_AP_STACONNECTED:
Serial.println("Client connected");
break;
case SYSTEM_EVENT_AP_STADISCONNECTED:
Serial.println("Client disconnected");
break;
case SYSTEM_EVENT_AP_STAIPASSIGNED:
Serial.println("Assigned IP address to client");
break;
case SYSTEM_EVENT_AP_PROBEREQRECVED:
Serial.println("Received probe request");
break;
case SYSTEM_EVENT_GOT_IP6:
Serial.println("IPv6 is preferred");
break;
case SYSTEM_EVENT_ETH_START:
Serial.println("Ethernet started");
break;
case SYSTEM_EVENT_ETH_STOP:
Serial.println("Ethernet stopped");
break;
case SYSTEM_EVENT_ETH_CONNECTED:
Serial.println("Ethernet connected");
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
Serial.println("Ethernet disconnected");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
Serial.println("Obtained IP address");
break;
}}
void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
{
@ -72,7 +139,7 @@ void setup()
delay(1000);
// Examples of diffrent ways to register wifi events
// Examples of different ways to register wifi events
WiFi.onEvent(WiFiEvent);
WiFi.onEvent(WiFiGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
WiFiEventId_t eventID = WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info){

View File

@ -37,7 +37,7 @@ extern "C" {
#include <esp_wifi.h>
#include <esp_event_loop.h>
#include <lwip/ip_addr.h>
#include "apps/dhcpserver_options.h"
#include "dhcpserver/dhcpserver_options.h"
}
@ -94,25 +94,28 @@ bool WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel,
if(!WiFi.enableAP(true)) {
// enable AP failed
log_e("enable AP first!");
return false;
}
if(!ssid || *ssid == 0 || strlen(ssid) > 31) {
// fail SSID too long or missing!
if(!ssid || *ssid == 0) {
// fail SSID missing
log_e("SSID missing!");
return false;
}
if(passphrase && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) {
// fail passphrase to long or short!
if(passphrase && (strlen(passphrase) > 0 && strlen(passphrase) < 8)) {
// fail passphrase too short
log_e("passphrase too short!");
return false;
}
esp_wifi_start();
wifi_config_t conf;
strcpy(reinterpret_cast<char*>(conf.ap.ssid), ssid);
strlcpy(reinterpret_cast<char*>(conf.ap.ssid), ssid, sizeof(conf.ap.ssid));
conf.ap.channel = channel;
conf.ap.ssid_len = strlen(ssid);
conf.ap.ssid_len = strlen(reinterpret_cast<char *>(conf.ap.ssid));
conf.ap.ssid_hidden = ssid_hidden;
conf.ap.max_connection = max_connection;
conf.ap.beacon_interval = 100;
@ -122,7 +125,7 @@ bool WiFiAPClass::softAP(const char* ssid, const char* passphrase, int channel,
*conf.ap.password = 0;
} else {
conf.ap.authmode = WIFI_AUTH_WPA2_PSK;
strcpy(reinterpret_cast<char*>(conf.ap.password), passphrase);
strlcpy(reinterpret_cast<char*>(conf.ap.password), passphrase, sizeof(conf.ap.password));
}
wifi_config_t conf_current;

View File

@ -240,6 +240,7 @@ int WiFiClient::setSocketOption(int option, char* value, size_t len)
int WiFiClient::setTimeout(uint32_t seconds)
{
Client::setTimeout(seconds * 1000);
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = 0;
@ -397,7 +398,8 @@ int WiFiClient::peek()
int WiFiClient::available()
{
if(!_connected) {
if(!_rxBuffer)
{
return 0;
}
int res = _rxBuffer->available();
@ -438,6 +440,8 @@ uint8_t WiFiClient::connected()
if (_connected) {
uint8_t dummy;
int res = recv(fd(), &dummy, 0, MSG_DONTWAIT);
// avoid unused var warning by gcc
(void)res;
switch (errno) {
case EWOULDBLOCK:
case ENOENT: //caused by vfs

View File

@ -23,8 +23,6 @@
#include "Arduino.h"
#include "Client.h"
#undef min
#undef max
#include <memory>
class WiFiClientSocketHandle;

View File

@ -46,9 +46,6 @@ extern "C" {
} //extern "C"
#include "esp32-hal-log.h"
#undef min
#undef max
#include <vector>
#include "sdkconfig.h"
@ -99,7 +96,7 @@ static bool _start_network_event_task(){
}
}
if(!_network_event_task_handle){
xTaskCreatePinnedToCore(_network_event_task, "network_event", 4096, NULL, 2, &_network_event_task_handle, ARDUINO_RUNNING_CORE);
xTaskCreatePinnedToCore(_network_event_task, "network_event", 4096, NULL, ESP_TASKD_EVENT_PRIO - 1, &_network_event_task_handle, ARDUINO_RUNNING_CORE);
if(!_network_event_task_handle){
log_e("Network Event Task Start Failed!");
return false;
@ -156,8 +153,8 @@ static bool espWiFiStart(bool persistent){
return false;
}
_esp_wifi_started = true;
system_event_t event;
event.event_id = SYSTEM_EVENT_WIFI_READY;
system_event_t event;
event.event_id = SYSTEM_EVENT_WIFI_READY;
WiFiGenericClass::_eventCallback(nullptr, &event);
return true;
@ -496,7 +493,7 @@ bool WiFiGenericClass::mode(wifi_mode_t m)
} else if(cm && !m){
return espWiFiStop();
}
esp_err_t err;
err = esp_wifi_set_mode(m);
if(err){

View File

@ -60,13 +60,12 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
uint8_t bestBSSID[6];
int32_t bestChannel = 0;
DEBUG_WIFI_MULTI("[WIFI] scan done\n");
delay(0);
log_i("[WIFI] scan done");
if(scanResult <= 0) {
DEBUG_WIFI_MULTI("[WIFI] no networks found\n");
log_e("[WIFI] no networks found");
} else {
DEBUG_WIFI_MULTI("[WIFI] %d networks found\n", scanResult);
log_i("[WIFI] %d networks found", scanResult);
for(int8_t i = 0; i < scanResult; ++i) {
String ssid_scan;
@ -96,24 +95,18 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
}
if(known) {
DEBUG_WIFI_MULTI(" ---> ");
log_d(" ---> %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c", i, chan_scan, BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5], ssid_scan.c_str(), rssi_scan, (sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*');
} else {
DEBUG_WIFI_MULTI(" ");
log_d(" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c", i, chan_scan, BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5], ssid_scan.c_str(), rssi_scan, (sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*');
}
DEBUG_WIFI_MULTI(" %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c\n", i, chan_scan, BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5], ssid_scan.c_str(), rssi_scan, (sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*');
delay(0);
}
}
// clean up ram
WiFi.scanDelete();
DEBUG_WIFI_MULTI("\n\n");
delay(0);
if(bestNetwork.ssid) {
DEBUG_WIFI_MULTI("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channal: %d (%d)\n", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channal: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb);
WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
status = WiFi.status();
@ -125,37 +118,33 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
status = WiFi.status();
}
IPAddress ip;
uint8_t * mac;
switch(status) {
case 3:
ip = WiFi.localIP();
mac = WiFi.BSSID();
DEBUG_WIFI_MULTI("[WIFI] Connecting done.\n");
DEBUG_WIFI_MULTI("[WIFI] SSID: %s\n", WiFi.SSID());
DEBUG_WIFI_MULTI("[WIFI] IP: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
DEBUG_WIFI_MULTI("[WIFI] MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
DEBUG_WIFI_MULTI("[WIFI] Channel: %d\n", WiFi.channel());
log_i("[WIFI] Connecting done.");
log_d("[WIFI] SSID: %s", WiFi.SSID().c_str());
log_d("[WIFI] IP: %s", WiFi.localIP().toString().c_str());
log_d("[WIFI] MAC: %s", WiFi.BSSIDstr().c_str());
log_d("[WIFI] Channel: %d", WiFi.channel());
break;
case 1:
DEBUG_WIFI_MULTI("[WIFI] Connecting Failed AP not found.\n");
log_e("[WIFI] Connecting Failed AP not found.");
break;
case 4:
DEBUG_WIFI_MULTI("[WIFI] Connecting Failed.\n");
log_e("[WIFI] Connecting Failed.");
break;
default:
DEBUG_WIFI_MULTI("[WIFI] Connecting Failed (%d).\n", status);
log_e("[WIFI] Connecting Failed (%d).", status);
break;
}
} else {
DEBUG_WIFI_MULTI("[WIFI] no matching wifi found!\n");
log_e("[WIFI] no matching wifi found!");
}
} else {
// start scan
DEBUG_WIFI_MULTI("[WIFI] delete old wifi config...\n");
log_d("[WIFI] delete old wifi config...");
WiFi.disconnect();
DEBUG_WIFI_MULTI("[WIFI] start scan\n");
log_d("[WIFI] start scan");
// scan wifi async mode
WiFi.scanNetworks(true);
}
@ -172,34 +161,34 @@ bool WiFiMulti::APlistAdd(const char* ssid, const char *passphrase)
if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
// fail SSID to long or missing!
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] no ssid or ssid to long\n");
log_e("[WIFI][APlistAdd] no ssid or ssid to long");
return false;
}
if(passphrase && strlen(passphrase) > 63) {
// fail passphrase to long!
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] passphrase to long\n");
log_e("[WIFI][APlistAdd] passphrase to long");
return false;
}
newAP.ssid = strdup(ssid);
if(!newAP.ssid) {
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.ssid == 0\n");
log_e("[WIFI][APlistAdd] fail newAP.ssid == 0");
return false;
}
if(passphrase && *passphrase != 0x00) {
newAP.passphrase = strdup(passphrase);
if(!newAP.passphrase) {
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] fail newAP.passphrase == 0\n");
log_e("[WIFI][APlistAdd] fail newAP.passphrase == 0");
free(newAP.ssid);
return false;
}
}
APlist.push_back(newAP);
DEBUG_WIFI_MULTI("[WIFI][APlistAdd] add SSID: %s\n", newAP.ssid);
log_i("[WIFI][APlistAdd] add SSID: %s", newAP.ssid);
return true;
}

View File

@ -27,20 +27,8 @@
#define WIFICLIENTMULTI_H_
#include "WiFi.h"
#undef min
#undef max
#include <vector>
#ifdef DEBUG_ESP_WIFI
#ifdef DEBUG_ESP_PORT
#define DEBUG_WIFI_MULTI(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
#endif
#endif
#ifndef DEBUG_WIFI_MULTI
#define DEBUG_WIFI_MULTI(...)
#endif
typedef struct {
char * ssid;
char * passphrase;

View File

@ -683,8 +683,10 @@ void WiFiSTAClass::_smartConfigCallback(uint32_t st, void* result) {
smartconfig_status_t status = (smartconfig_status_t) st;
log_d("Status: %s", sc_status_strings[st % 5]);
if (status == SC_STATUS_GETTING_SSID_PSWD) {
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
smartconfig_type_t * type = (smartconfig_type_t *)result;
log_d("Type: %s", sc_type_strings[*type % 3]);
#endif
} else if (status == SC_STATUS_LINK) {
wifi_sta_config_t *sta_conf = reinterpret_cast<wifi_sta_config_t *>(result);
log_d("SSID: %s", (char *)(sta_conf->ssid));
@ -694,8 +696,10 @@ void WiFiSTAClass::_smartConfigCallback(uint32_t st, void* result) {
_smartConfigDone = true;
} else if (status == SC_STATUS_LINK_OVER) {
if(result){
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
ip4_addr_t * ip = (ip4_addr_t *)result;
log_d("Sender IP: " IPSTR, IP2STR(ip));
#endif
}
WiFi.stopSmartConfig();
}

View File

@ -0,0 +1,67 @@
WiFiClientSecure
================
The WiFiClientSecure class implements support for secure connections using TLS (SSL).
It inherits from WiFiClient and thus implements a superset of that class' interface.
There are three ways to establish a secure connection using the WiFiClientSecure class:
using a root certificate authority (CA) cert, using a root CA cert plus a client cert and key,
and using a pre-shared key (PSK).
Using a root certificate authority cert
---------------------------------------
This method authenticates the server and negotiates an encrypted connection.
It is the same functionality as implemented in your web browser when you connect to HTTPS sites.
If you are accessing your own server:
- Generate a root certificate for your own certificate authority
- Generate a cert & private key using your root certificate ("self-signed cert") for your server
If you are accessing a public server:
- Obtain the cert of the public CA that signed that server's cert
Then:
- In WiFiClientSecure use setCACert (or the appropriate connect method) to set the root cert of your
CA or of the public CA
- When WiFiClientSecure connects to the target server it uses the CA cert to verify the certificate
presented by the server, and then negotiates encryption for the connection
Please see the WiFiClientSecure example.
Using a root CA cert and client cert/keys
-----------------------------------------
This method authenticates the server and additionally also authenticates
the client to the server, then negotiates an encrypted connection.
- Follow steps above
- Using your root CA generate cert/key for your client
- Register the keys with the server you will be accessing so the server can authenticate your client
- In WiFiClientSecure use setCACert (or the appropriate connect method) to set the root cert of your
CA or of the public CA, this is used to authenticate the server
- In WiFiClientSecure use setCertificate, and setPrivateKey (or the appropriate connect method) to
set your client's cert & key, this will be used to authenticate your client to the server
- When WiFiClientSecure connects to the target server it uses the CA cert to verify the certificate
presented by the server, it will use the cert/key to authenticate your client to the server, and
it will then negotiate encryption for the connection
Using Pre-Shared Keys (PSK)
---------------------------
TLS supports authentication and encryption using a pre-shared key (i.e. a key that both client and
server know) as an alternative to the public key cryptography commonly used on the web for HTTPS.
PSK is starting to be used for MQTT, e.g. in mosquitto, to simplify the set-up and avoid having to
go through the whole CA, cert, and private key process.
A pre-shared key is a binary string of up to 32 bytes and is commonly represented in hex form. In
addition to the key, clients can also present an id and typically the server allows a different key
to be associated with each client id. In effect this is very similar to username and password pairs,
except that unlike a password the key is not directly transmitted to the server, thus a connection to a
malicious server does not divulge the password. Plus the server is also authenticated to the client.
To use PSK:
- Generate a random hex string (generating an MD5 or SHA for some file is one way to do this)
- Come up with a string id for your client and configure your server to accept the id/key pair
- In WiFiClientSecure use setPreSharedKey (or the appropriate connect method) to
set the id/key combo
- When WiFiClientSecure connects to the target server it uses the id/key combo to authenticate the
server (it must prove that it has the key too), authenticate the client and then negotiate
encryption for the connection
Please see the WiFiClientPSK example.

View File

@ -0,0 +1,85 @@
/*
Wifi secure connection example for ESP32 using a pre-shared key (PSK)
This is useful with MQTT servers instead of using a self-signed cert, tested with mosquitto.
Running on TLS 1.2 using mbedTLS
To test run a test server using: openssl s_server -accept 8443 -psk 1a2b3c4d -nocert
It will show the http request made, but there's no easy way to send a reply back...
2017 - Evandro Copercini - Apache 2.0 License.
2018 - Adapted for PSK by Thorsten von Eicken
*/
#include <WiFiClientSecure.h>
#if 0
const char* ssid = "your-ssid"; // your network SSID (name of wifi network)
const char* password = "your-password"; // your network password
#else
const char* ssid = "test"; // your network SSID (name of wifi network)
const char* password = "securetest"; // your network password
#endif
//const char* server = "server.local"; // Server hostname
const IPAddress server = IPAddress(192, 168, 0, 14); // Server IP address
const int port = 8443; // server's port (8883 for MQTT)
const char* pskIdent = "Client_identity"; // PSK identity (sometimes called key hint)
const char* psKey = "1a2b3c4d"; // PSK Key (must be hex string without 0x)
WiFiClientSecure client;
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(115200);
delay(100);
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// attempt to connect to Wifi network:
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
// wait 1 second for re-trying
delay(1000);
}
Serial.print("Connected to ");
Serial.println(ssid);
client.setPreSharedKey(pskIdent, psKey);
Serial.println("\nStarting connection to server...");
if (!client.connect(server, port))
Serial.println("Connection failed!");
else {
Serial.println("Connected to server!");
// Make a HTTP request:
client.println("GET /a/check HTTP/1.0");
client.print("Host: ");
client.println(server);
client.println("Connection: close");
client.println();
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
Serial.println("headers received");
break;
}
}
// if there are incoming bytes available
// from the server, read them and print them:
while (client.available()) {
char c = client.read();
Serial.write(c);
}
client.stop();
}
}
void loop() {
// do nothing
}

View File

@ -0,0 +1,106 @@
/*|----------------------------------------------------------|*/
/*|WORKING EXAMPLE FOR HTTPS CONNECTION |*/
/*|TESTED BOARDS: Devkit v1 DOIT, Devkitc v4 |*/
/*|CORE: June 2018 |*/
/*|----------------------------------------------------------|*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "esp_wpa2.h"
#include <Wire.h>
#define EAP_IDENTITY "identity" //if connecting from another corporation, use identity@organisation.domain in Eduroam
#define EAP_PASSWORD "password" //your Eduroam password
const char* ssid = "eduroam"; // Eduroam SSID
const char* host = "arduino.php5.sk"; //external server domain for HTTP connection after authentification
int counter = 0;
const char* test_root_ca= \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \
"-----END CERTIFICATE-----\n";
// You can use x.509 client certificates if you want
//const char* test_client_key = ""; //to verify the client
//const char* test_client_cert = ""; //to verify the client
WiFiClientSecure client;
void setup() {
Serial.begin(115200);
delay(10);
Serial.println();
Serial.print("Connecting to network: ");
Serial.println(ssid);
WiFi.disconnect(true); //disconnect form wifi to set new wifi connection
WiFi.mode(WIFI_STA); //init wifi mode
esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide identity
esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EAP_IDENTITY, strlen(EAP_IDENTITY)); //provide username --> identity and username is same
esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EAP_PASSWORD, strlen(EAP_PASSWORD)); //provide password
esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); //set config settings to default
esp_wifi_sta_wpa2_ent_enable(&config); //set config settings to enable function
WiFi.begin(ssid); //connect to wifi
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
counter++;
if(counter>=60){ //after 30 seconds timeout - reset board
ESP.restart();
}
}
client.setCACert(test_root_ca);
//client.setCertificate(test_client_key); // for client verification
//client.setPrivateKey(test_client_cert); // for client verification
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address set: ");
Serial.println(WiFi.localIP()); //print LAN IP
}
void loop() {
if (WiFi.status() == WL_CONNECTED) { //if we are connected to Eduroam network
counter = 0; //reset counter
Serial.println("Wifi is still connected with IP: ");
Serial.println(WiFi.localIP()); //inform user about his IP address
}else if (WiFi.status() != WL_CONNECTED) { //if we lost connection, retry
WiFi.begin(ssid);
}
while (WiFi.status() != WL_CONNECTED) { //during lost connection, print dots
delay(500);
Serial.print(".");
counter++;
if(counter>=60){ //30 seconds timeout - reset board
ESP.restart();
}
}
Serial.print("Connecting to website: ");
Serial.println(host);
if (client.connect(host, 443)) {
String url = "/rele/rele1.txt";
client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "User-Agent: ESP32\r\n" + "Connection: close\r\n\r\n");
while (client.connected()) {
String header = client.readStringUntil('\n');
Serial.println(header);
if (header == "\r") {
break;
}
}
String line = client.readStringUntil('\n');
Serial.println(line);
}else{
Serial.println("Connection unsucessful");
}
delay(5000);
}

View File

@ -35,10 +35,12 @@ WiFiClientSecure::WiFiClientSecure()
sslclient = new sslclient_context;
ssl_init(sslclient);
sslclient->socket = -1;
sslclient->handshake_timeout = 120000;
_CA_cert = NULL;
_cert = NULL;
_private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
next = NULL;
}
@ -50,6 +52,7 @@ WiFiClientSecure::WiFiClientSecure(int sock)
sslclient = new sslclient_context;
ssl_init(sslclient);
sslclient->socket = sock;
sslclient->handshake_timeout = 120000;
if (sock >= 0) {
_connected = true;
@ -58,6 +61,8 @@ WiFiClientSecure::WiFiClientSecure(int sock)
_CA_cert = NULL;
_cert = NULL;
_private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
next = NULL;
}
@ -88,11 +93,15 @@ void WiFiClientSecure::stop()
int WiFiClientSecure::connect(IPAddress ip, uint16_t port)
{
if (_pskIdent && _psKey)
return connect(ip, port, _pskIdent, _psKey);
return connect(ip, port, _CA_cert, _cert, _private_key);
}
int WiFiClientSecure::connect(const char *host, uint16_t port)
{
if (_pskIdent && _psKey)
return connect(host, port, _pskIdent, _psKey);
return connect(host, port, _CA_cert, _cert, _private_key);
}
@ -103,7 +112,24 @@ int WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *_CA_cert,
int WiFiClientSecure::connect(const char *host, uint16_t port, const char *_CA_cert, const char *_cert, const char *_private_key)
{
int ret = start_ssl_client(sslclient, host, port, _CA_cert, _cert, _private_key);
int ret = start_ssl_client(sslclient, host, port, _CA_cert, _cert, _private_key, NULL, NULL);
_lastError = ret;
if (ret < 0) {
log_e("start_ssl_client: %d", ret);
stop();
return 0;
}
_connected = true;
return 1;
}
int WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) {
return connect(ip.toString().c_str(), port,_pskIdent, _psKey);
}
int WiFiClientSecure::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) {
log_v("start_ssl_client with PSK");
int ret = start_ssl_client(sslclient, host, port, NULL, NULL, NULL, _pskIdent, _psKey);
_lastError = ret;
if (ret < 0) {
log_e("start_ssl_client: %d", ret);
@ -130,13 +156,6 @@ size_t WiFiClientSecure::write(uint8_t data)
int WiFiClientSecure::read()
{
uint8_t data = -1;
if(_peek >= 0){
data = _peek;
_peek = -1;
return data;
}
int res = read(&data, 1);
if (res < 0) {
return res;
@ -160,7 +179,8 @@ size_t WiFiClientSecure::write(const uint8_t *buf, size_t size)
int WiFiClientSecure::read(uint8_t *buf, size_t size)
{
int peeked = 0;
if ((!buf && size) || (_peek < 0 && !available())) {
int avail = available();
if ((!buf && size) || avail <= 0) {
return -1;
}
if(!size){
@ -170,7 +190,8 @@ int WiFiClientSecure::read(uint8_t *buf, size_t size)
buf[0] = _peek;
_peek = -1;
size--;
if(!size || !available()){
avail--;
if(!size || !avail){
return 1;
}
buf++;
@ -180,23 +201,23 @@ int WiFiClientSecure::read(uint8_t *buf, size_t size)
int res = get_ssl_receive(sslclient, buf, size);
if (res < 0) {
stop();
return res;
return peeked?peeked:res;
}
return res + peeked;
}
int WiFiClientSecure::available()
{
int peeked = (_peek >= 0);
if (!_connected) {
return 0;
return peeked;
}
int res = data_to_read(sslclient);
if (res < 0 ) {
if (res < 0) {
stop();
} else if(_peek >= 0) {
res += 1;
return peeked?peeked:res;
}
return res;
return res+peeked;
}
uint8_t WiFiClientSecure::connected()
@ -222,6 +243,11 @@ void WiFiClientSecure::setPrivateKey (const char *private_key)
_private_key = private_key;
}
void WiFiClientSecure::setPreSharedKey(const char *pskIdent, const char *psKey) {
_pskIdent = pskIdent;
_psKey = psKey;
}
bool WiFiClientSecure::verify(const char* fp, const char* domain_name)
{
if (!sslclient)
@ -230,6 +256,52 @@ bool WiFiClientSecure::verify(const char* fp, const char* domain_name)
return verify_ssl_fingerprint(sslclient, fp, domain_name);
}
char *WiFiClientSecure::_streamLoad(Stream& stream, size_t size) {
static char *dest = nullptr;
if(dest) {
free(dest);
}
dest = (char*)malloc(size);
if (!dest) {
return nullptr;
}
if (size != stream.readBytes(dest, size)) {
free(dest);
dest = nullptr;
}
return dest;
}
bool WiFiClientSecure::loadCACert(Stream& stream, size_t size) {
char *dest = _streamLoad(stream, size);
bool ret = false;
if (dest) {
setCACert(dest);
ret = true;
}
return ret;
}
bool WiFiClientSecure::loadCertificate(Stream& stream, size_t size) {
char *dest = _streamLoad(stream, size);
bool ret = false;
if (dest) {
setCertificate(dest);
ret = true;
}
return ret;
}
bool WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) {
char *dest = _streamLoad(stream, size);
bool ret = false;
if (dest) {
setPrivateKey(dest);
ret = true;
}
return ret;
}
int WiFiClientSecure::lastError(char *buf, const size_t size)
{
if (!_lastError) {
@ -240,3 +312,8 @@ int WiFiClientSecure::lastError(char *buf, const size_t size)
snprintf(buf, size, "%s", error_buf);
return _lastError;
}
void WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout)
{
sslclient->handshake_timeout = handshake_timeout * 1000;
}

View File

@ -35,6 +35,8 @@ protected:
const char *_CA_cert;
const char *_cert;
const char *_private_key;
const char *_pskIdent; // identity for PSK cipher suites
const char *_psKey; // key in hex for PSK cipher suites
public:
WiFiClientSecure *next;
@ -45,6 +47,8 @@ public:
int connect(const char *host, uint16_t port);
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey);
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey);
int peek();
size_t write(uint8_t data);
size_t write(const uint8_t *buf, size_t size);
@ -55,10 +59,15 @@ public:
void stop();
uint8_t connected();
int lastError(char *buf, const size_t size);
void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex
void setCACert(const char *rootCA);
void setCertificate(const char *client_ca);
void setPrivateKey (const char *private_key);
bool loadCACert(Stream& stream, size_t size);
bool loadCertificate(Stream& stream, size_t size);
bool loadPrivateKey(Stream& stream, size_t size);
bool verify(const char* fingerprint, const char* domain_name);
void setHandshakeTimeout(unsigned long handshake_timeout);
operator bool()
{
@ -84,6 +93,9 @@ public:
return sslclient->socket = -1;
}
private:
char *_streamLoad(Stream& stream, size_t size);
//friend class WiFiServer;
using Print::write;
};

View File

@ -45,7 +45,7 @@ void ssl_init(sslclient_context *ssl_client)
}
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key)
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey)
{
char buf[512];
int ret, flags, timeout;
@ -116,6 +116,36 @@ int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t p
if (ret < 0) {
return handle_error(ret);
}
} else if (pskIdent != NULL && psKey != NULL) {
log_v("Setting up PSK");
// convert PSK from hex to binary
if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2*MBEDTLS_PSK_MAX_LEN) {
log_e("pre-shared key not valid hex or too long");
return -1;
}
unsigned char psk[MBEDTLS_PSK_MAX_LEN];
size_t psk_len = strlen(psKey)/2;
for (int j=0; j<strlen(psKey); j+= 2) {
char c = psKey[j];
if (c >= '0' && c <= '9') c -= '0';
else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
else return -1;
psk[j/2] = c<<4;
c = psKey[j+1];
if (c >= '0' && c <= '9') c -= '0';
else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
else return -1;
psk[j/2] |= c;
}
// set mbedtls config
ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len,
(const unsigned char *)pskIdent, strlen(pskIdent));
if (ret != 0) {
log_e("mbedtls_ssl_conf_psk returned %d", ret);
return handle_error(ret);
}
} else {
mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE);
log_i("WARNING: Use certificates for a more secure communication!");
@ -158,12 +188,14 @@ int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t p
mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL );
log_v("Performing the SSL/TLS handshake...");
unsigned long handshake_start_time=millis();
while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
return handle_error(ret);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
if((millis()-handshake_start_time)>ssl_client->handshake_timeout)
return -1;
vTaskDelay(10 / portTICK_PERIOD_MS);
}

View File

@ -23,11 +23,13 @@ typedef struct sslclient_context {
mbedtls_x509_crt ca_cert;
mbedtls_x509_crt client_cert;
mbedtls_pk_context client_key;
unsigned long handshake_timeout;
} sslclient_context;
void ssl_init(sslclient_context *ssl_client);
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int start_ssl_client(sslclient_context *ssl_client, const char *host, uint32_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey);
void stop_ssl_socket(sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int data_to_read(sslclient_context *ssl_client);
int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len);

View File

@ -1,5 +1,5 @@
name=Wire
version=1.0
version=1.0.1
author=Hristo Gochkov
maintainer=Hristo Gochkov <hristo@espressif.com>
sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp8266 boards.

View File

@ -58,7 +58,7 @@ TwoWire::~TwoWire()
}
}
void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
{
if(sdaPin < 0) { // default param passed
if(num == 0) {
@ -70,7 +70,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
} else {
if(sda==-1) {
log_e("no Default SDA Pin for Second Peripheral");
return; //no Default pin for Second Peripheral
return false; //no Default pin for Second Peripheral
} else {
sdaPin = sda; // reuse prior pin
}
@ -87,7 +87,7 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
} else {
if(scl == -1) {
log_e("no Default SCL Pin for Second Peripheral");
return; //no Default pin for Second Peripheral
return false; //no Default pin for Second Peripheral
} else {
sclPin = scl; // reuse prior pin
}
@ -98,10 +98,11 @@ void TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
scl = sclPin;
i2c = i2cInit(num, sdaPin, sclPin, frequency);
if(!i2c) {
return;
return false;
}
flush();
return true;
}
@ -145,6 +146,7 @@ void TwoWire::beginTransmission(uint16_t address)
txAddress = address;
txIndex = txQueued; // allow multiple beginTransmission(),write(),endTransmission(false) until endTransmission(true)
txLength = txQueued;
last_error = I2C_ERROR_OK;
}
/*stickbreaker isr
@ -152,14 +154,15 @@ void TwoWire::beginTransmission(uint16_t address)
uint8_t TwoWire::endTransmission(bool sendStop) // Assumes Wire.beginTransaction(), Wire.write()
{
if(transmitting == 1) {
last_error = writeTransmission(txAddress, &txBuffer[txQueued], txLength - txQueued, sendStop);
rxIndex = 0;
rxLength = rxQueued;
rxQueued = 0;
txQueued = 0; // the SendStop=true will restart all Queueing
if(last_error == I2C_ERROR_CONTINUE){
// txlength is howmany bytes in txbuffer have been use
last_error = writeTransmission(txAddress, &txBuffer[txQueued], txLength - txQueued, sendStop);
if(last_error == I2C_ERROR_CONTINUE){
txQueued = txLength;
} else if( last_error == I2C_ERROR_OK){
rxIndex = 0;
rxLength = rxQueued;
rxQueued = 0;
txQueued = 0; // the SendStop=true will restart all Queueing
}
} else {
last_error = I2C_ERROR_NO_BEGIN;
@ -168,7 +171,7 @@ uint8_t TwoWire::endTransmission(bool sendStop) // Assumes Wire.beginTransactio
txIndex = 0;
txLength = 0;
transmitting = 0;
return last_error;
return (last_error == I2C_ERROR_CONTINUE)?I2C_ERROR_OK:last_error; // Don't return Continue for compatibility.
}
/* @stickBreaker 11/2017 fix for ReSTART timeout, ISR
@ -189,12 +192,19 @@ uint8_t TwoWire::requestFrom(uint16_t address, uint8_t size, bool sendStop)
last_error = readTransmission(address, &rxBuffer[cnt], size, sendStop, &cnt);
rxIndex = 0;
rxLength = rxQueued;
rxQueued = 0;
txQueued = 0; // the SendStop=true will restart all Queueing
if(last_error != I2C_ERROR_OK){
rxLength = cnt;
if( last_error != I2C_ERROR_CONTINUE){ // not a buffered ReSTART operation
// so this operation actually moved data, queuing is done.
rxQueued = 0;
txQueued = 0; // the SendStop=true will restart all Queueing or error condition
}
if(last_error != I2C_ERROR_OK){ // ReSTART on read does not return any data
cnt = 0;
}
return cnt;
}
@ -202,6 +212,7 @@ size_t TwoWire::write(uint8_t data)
{
if(transmitting) {
if(txLength >= I2C_BUFFER_LENGTH) {
last_error = I2C_ERROR_MEMORY;
return 0;
}
txBuffer[txIndex] = data;
@ -209,20 +220,19 @@ size_t TwoWire::write(uint8_t data)
txLength = txIndex;
return 1;
}
last_error = I2C_ERROR_NO_BEGIN; // no begin, not transmitting
return 0;
}
size_t TwoWire::write(const uint8_t *data, size_t quantity)
{
if(transmitting) {
for(size_t i = 0; i < quantity; ++i) {
if(!write(data[i])) {
return i;
}
for(size_t i = 0; i < quantity; ++i) {
if(!write(data[i])) {
return i;
}
return quantity;
}
return 0;
return quantity;
}
int TwoWire::available(void)
@ -306,11 +316,6 @@ uint8_t TwoWire::endTransmission(void)
return endTransmission(true);
}
uint8_t TwoWire::endTransmission(uint8_t sendStop)
{
return endTransmission(static_cast<bool>(sendStop));
}
/* stickbreaker Nov2017 better error reporting
*/
uint8_t TwoWire::lastError()
@ -353,14 +358,13 @@ char * TwoWire::getErrorText(uint8_t err)
/*stickbreaker Dump i2c Interrupt buffer, i2c isr Debugging
*/
void TwoWire::dumpInts()
{
i2cDumpInts(num);
uint32_t TwoWire::setDebugFlags( uint32_t setBits, uint32_t resetBits){
return i2cDebug(i2c,setBits,resetBits);
}
void TwoWire::dumpI2C()
{
i2cDumpI2c(i2c);
bool TwoWire::busy(void){
return ((i2cGetStatus(i2c) & 16 )==16);
}
TwoWire Wire = TwoWire(0);

Some files were not shown because too many files have changed in this diff Show More