Compare commits

...

237 Commits

Author SHA1 Message Date
07390157df Fix Camera Example (#3202)
* Update app_httpd.cpp

* Stop LWIP from using PSRAM and enable OV7725
2019-09-13 01:19:53 +03:00
bab3a70f54 Added timeout to WiFiScan.cpp to prevent getting stuck at WIFI_SCAN_RUNNING (#3197)
* Added timeout to WiFiScan class to prevent haning at stucking at WIFI_SCAN_RUNNING when scan fails internally

* fixed tabs and returns, connected scanTimeout to max_scan_per_channel timeout

* Corrected tabs two

* Added static vars scanTimeout und scanStarted to WiFiScan.h protected section

* Fixed missing ; in line 64
2019-09-11 15:58:34 +03:00
cd4f9038ee Fix WiFi disconnect event not being propageted. (#3085)
Relative to #3006
2019-09-11 15:12:15 +03:00
7fe2812f7f Inline ESP::getCycleCount() to make it safe to call from ISRs (#3165)
* Inline ESP::getCycleCount() to make it safe to call from ISRs

* Attribute IRAM_ATTR ISR-safe function in addition to inlining.
2019-09-11 14:29:53 +03:00
Luc
9710fedaf0 Fix wrong values in .cvs files (#3196)
* Fix wrong values in .cvs files

Add missing upload size for partitions bigger than default one

* Add new partion to generic

Fix hardcoded partition for 16M flash
2019-09-10 22:52:51 +03:00
f5cacfee1a Remove extra connects from Client.h (#3191) 2019-09-09 09:36:22 +03:00
f71a4bd406 Bugfix/detect baudrate (#3188)
* Expose uartStartDetectBaudrate(uart_t *) in esp32-hal-uart.h and call it from HardwareSerial::begin() if baudrate detection is requested (by passing a baudrate of 0) to solve baudrate detection problems

* Avoid a division by zero error in uartGetBaudRate()
2019-09-09 00:59:32 +03:00
5f77b0108b Fix return of Print::print(timeinfo, format) (#3189) 2019-09-09 00:36:30 +03:00
717ca79ecb #3181 printf double vsnprintf() fix, malloc, va_end (#3184)
* Use loc_buf for small strings, check for error return from vsnprintf

* cleanup arg when bailing out of new

* Use malloc/free instead of new/delete in printf

* Return actual bytes written in printf

* FIX: write before free
2019-09-08 23:49:32 +03:00
07613b3158 Update URL to point to upstream repository (#3187)
Pointing to an old fork is misleading.
2019-09-08 23:15:46 +03:00
80ea521940 Add support for 160MHz rated CPUs (#3135)
Some ESP32 chips are rated only to 160MHz. This change adds support for them and does not allow frequency to be switched to 240MHz
2019-09-05 12:30:43 +03:00
5c04de6f39 Fix packager 2019-09-05 11:22:40 +03:00
0d163a1ce2 Update IDF 3.2 to 7dd492319 + WiFi fix (#3177) 2019-09-05 11:08:45 +03:00
2a7e509978 Fix long Ticker period conversion: cast ms to uint64_t when calculating us (#3175)
* Fix long Ticker period conversion

* Simplify long Ticker period conversion fix by using 1000ULL
2019-09-05 10:34:47 +03:00
1b8c7e34f9 Try bash only examples build (#3164) 2019-09-01 18:45:55 +03:00
7a574399b1 Rework the sketch builder to match the IDE (#3146)
* Rework the sketch builder to match the IDE

* Link the board to the home folder

* Rename files for clarity

* move ci files to own subfolder

* Update Github CI to use the new script locations
2019-08-28 01:28:11 +03:00
fd089d8fd3 Pull in ESP8266 String::replace() fixes, others (#3143)
Pull in bugfixes from the ESP8266 repo for problems in the SSO
implementation of replace().  See the following patches for full
details:

54240d2cc5 (diff-8d9e71e16d437343017df828f0528f63)
78a1a66e6d (diff-8d9e71e16d437343017df828f0528f63)
4e9358445a (diff-8d9e71e16d437343017df828f0528f63)

Fixes #3140
2019-08-27 20:05:36 +03:00
f356ccd54a Added the pragma line to the top (#3139)
* Removed pragme in BLERemoteService.cpp

* Added the line at the top and changed to warning
2019-08-27 10:21:27 +03:00
6daf773464 Fix travis deploy generating bad json 2019-08-21 03:29:23 +03:00
70a896481d Update Both CIs to run parallel jobs (#3120) 2019-08-21 01:56:05 +03:00
390da0d090 bump CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM to improve RX performance (#3119) 2019-08-20 22:07:56 +03:00
d5e2bb12ca Update IDF to 90747cc8b (#3118) 2019-08-20 21:11:00 +03:00
f4acac4c2b Bugfix/http client (#2867)
* Fix persistance issue found, see ESP8266 issue #6152

* Correcting the parsing of the header for HTTP1.0

* Send 'Connection: Close' header in case of HTTP1.0

* Let reuse connection depend on protocol used: HTTP1.0 or HTTP1.1

* Fixed reuse, added null ptr checks, added check for _trainsportTraits in connect() in case _client was set null

* Fix reuse connection issues, similar to ESP8266 PR #6176
2019-08-20 17:18:09 +03:00
5137fc5c80 Ble notification/indication status and timeout (#2998)
* add timed wait

* Added Notification/Indication data and status callbacks

* imply null-object pattern for BLE callback
2019-08-20 17:15:30 +03:00
03066e42ef Flush UART RX queue too (#3009) 2019-08-20 16:48:52 +03:00
cd5257ad78 ESP.getCpuFreqMHz fix (#3007)
* ESP.getCpuFreqMHz was returning the CONFIG_ variable.  Now calls the getCpuFrequencyMhz function.

* Changed the Esp function to uint32_t to match
2019-08-20 16:48:26 +03:00
ee6336a312 links http -> https (#3010)
more secure
2019-08-20 16:47:43 +03:00
61f71930e9 The progress callback is now correctly invoked with Update.write (#3024)
The progress callback was only invoked with Update.writeStream before
2019-08-20 16:45:57 +03:00
ec40c4c96f Cast TimerWakeup conversion factor as ULL (#3027) 2019-08-20 16:45:18 +03:00
2bda4a9617 Fix BLE stop advertising not working (#3034)
BLEAdvertising::handleGAPEvent -> ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT should NOT call start()!
2019-08-20 16:44:50 +03:00
91b9fae111 Add generic IP utilities (#3038)
* Add generic IP calculations

Add:
calculateNetworkID(IPAddress ip, IPAddress subnet) => Calculate the network id using the ip and subnet (e.g. 192.168.0.0)
calculateBroadcast(IPAddress ip, IPAddress subnet) => Calculate the broadcast ip using the ip and subnet (e.g. 192.168.0.255)
calculateSubnetCIDR(IPAddress subnetMask) => Calculate the subnet CIDR using the subnet (e.g. 24)

Add:
broadcastIP() => Retrieve the network id (e.g. 192.168.0.0)
networkID() => Retrieve the broadcast IP (e.g. 192.168.0.255)
subnetCIDR() => Retrieve the subnet CIDR (e.g. 24)

Add:
broadcastIP() => Retrieve the network id (e.g. 192.168.0.0)
networkID() => Retrieve the broadcast IP (e.g. 192.168.0.255)
subnetCIDR() => Retrieve the subnet CIDR (e.g. 24)

Add:
softAPBroadcastIP() => Retrieve the network id (e.g. 192.168.0.0)
softAPNetwrokID() => Retrieve the broadcast IP (e.g. 192.168.0.255)
softAPSubnetCIDR() => Retrieve the subnet CIDR (e.g. 24)
2019-08-20 16:42:55 +03:00
2a1fde7736 Include stdint for uint16_t (#3069) 2019-08-20 16:39:31 +03:00
a12d609b22 Corrected count of characteristics to get (#3082)
Only space for 1 characteristic is allocated, requesting 10 leads to stack corruption.
2019-08-20 16:38:59 +03:00
73576674b8 add core installation 1.0.2 (#3064)
* add core installation 1.0.2

* added 1.0.3
2019-08-20 16:38:34 +03:00
007a93ec7e Update Print.cpp (#3060)
#2891
2019-08-20 16:36:04 +03:00
a5f3fc6fad Magicbit Variant (#3107)
* Updated magicbit variant

* Added LED_BUILTIN
2019-08-20 15:44:12 +03:00
65e256c40a Try github CI (#3115) 2019-08-20 15:32:47 +03:00
fa55a2c91d Update stale.yml 2019-08-01 18:55:47 +03:00
a070884441 Update stale.yml 2019-08-01 11:05:05 +03:00
a9c8b46b1a Update stale.yml 2019-07-31 16:43:58 +03:00
6954150176 Create stale.yml 2019-07-31 10:04:08 +03:00
548f712df2 Fix empty libs being included with the builder (#2997) 2019-07-17 16:48:51 +03:00
56ce580b0e Fix incorrect definition of FPSTR() and move from pgmspace.h to WString.h (#1371) (#2961) 2019-07-17 10:16:47 +03:00
509d31ba51 create variant for magicbit board (#2987)
* create varient for magicbit board

* update boards.txt
2019-07-17 10:10:45 +03:00
9e32cec9a2 Update IDF to a8916daeb (#2992) 2019-07-17 10:09:43 +03:00
3376ea1bd5 Added more menu settings for TinyPICO - Fash mode and Flash speed. Set Flash mode default to QIO (#2976) 2019-07-16 17:15:13 +03:00
1c7e329140 RMT: Fix in bitshift of NeoPixel example project (#2986)
Fixed bit shift in demo application of using RMT peripheral. Init data array to be transmitted was off by one, as for the first iteration (i==0) the mask was 1<<8, which results in shifted RGB value in color variable
Closes https://github.com/espressif/arduino-esp32/issues/2921
2019-07-16 17:14:28 +03:00
c17b212cd0 WPA2 connection fix (significant improvement to connection time) (#2989)
* Config fix test

* Also force config even if equal
2019-07-16 17:12:20 +03:00
7dbda4988b Update boards.txt (#2933)
* Update boards.txt

Adding ESP32-WROOM-32 8MB flash option

* Update boards.txt

Fixing partition size and typo

* Update boards.txt
2019-07-10 09:13:09 +03:00
d1134fd45e Fix issue with semaphores (#2974) 2019-07-10 09:11:35 +03:00
d5fdd715ef WiFiClient.cpp - Fix connect() behavior (#2784)
* WiFiClient.cpp - Fix connect() behavior

* lwip_connect_r() : check return code
2019-07-09 19:48:05 +03:00
ca7106e97e Added convert method to EEPROM to transfer data from partition to nvs (#2841)
* Added convert method to transfer data from partition to nvs

* Could have sworn I changed the return when I made the label

* Empty state should be 0xFF.  Fixed some logging levels

* Set result on success
2019-07-09 19:47:06 +03:00
87e5787cf7 Added support for TinyPICO in the boards list (#2901) 2019-07-09 19:40:34 +03:00
c13d11e7d1 fix empty reply from server error (#2903)
The flush causes an empty response a client side.

see https://github.com/espressif/arduino-esp32/issues/2902
2019-07-09 19:39:12 +03:00
b0d8d4dd44 Change some WiFi buffer settings to match esp-idf. See #2899 (#2912) 2019-07-09 19:36:45 +03:00
05de017bd5 BLE Library onRead Callback fix and Client disconnect feature (#2913)
* BLEServer::disconnect()

* Invoke Read callback first, to make data update possible before read
2019-07-09 19:35:07 +03:00
e1548e9b7e Fix hang on client disconnect during upload (#2914) 2019-07-09 19:34:24 +03:00
c29ec9da3d Removed partitioning comments from EEPROM class example (#2920)
* Remove partitioning comments

* Revert "Remove partitioning comments"

This reverts commit b3b03a81572d352816819cff772231ba037dc338.

* Removed partitioning comments from EEPROM example
2019-07-09 19:34:04 +03:00
b3783fba95 fixed typo (#2927) 2019-07-09 19:31:38 +03:00
a22ec4a978 Reduce flash usage up to 214k in one click (#2929)
* std::stringstream -> std::string

* Fix small issues

* Small fix 2
2019-07-09 19:31:17 +03:00
20498cf8b1 Expand gitignore to cover files created by Visual Micro (#2934)
* Expand gitignore to cover files created by Visual Micro's Arduino IDE for Visual Studio

(cherry picked from commit d6209c79ea16a26564fe27e74e5f277160129847)

* Also ignore VS project files

(cherry picked from commit e0ec212bd41e75a3570a6098a66ee66787db58db)
2019-07-09 19:28:04 +03:00
02e51728c4 Fix Timeout Bug in WebServer (#2938) 2019-07-09 19:27:24 +03:00
d2816b2f32 Fix Unused Variable Warning (#2940)
Some Debugging variables were enabled at ERROR level instead of DEBUG.  Specifically `tAfter` and `tBefore`
2019-07-09 19:24:35 +03:00
2e32022611 ESP8266 => ESP32 tidyup, set correct default port (#2948)
Tidied up references to the ESP8266, and set the correct default port for the ESP32.
2019-07-09 19:24:10 +03:00
74ffdac74a fix for BLE Library Compile Error #2866 (#2876) 2019-07-09 19:23:01 +03:00
4ee17ec3ee Fix warning -Wempty-body by adding braces to 'if' stmt body (#2951) 2019-07-09 19:21:57 +03:00
476660f763 Cleanup WiFiMulti (#2955)
* Use macros for switch case labels

* Fixed spelling error in "too"

* Fix spacing

* Inline private functions that are only called once
2019-07-09 19:21:01 +03:00
Luc
f558e69181 Fix warnings in platformio (#2960)
Fix : warning: converting to non-pointer type 'int' from NULL [-Wconversion-null]
Fix : warning: unused variable 'res' [-Wunused-variable]
2019-07-09 19:19:16 +03:00
5bf3aab527 Make a selective menuconfig option for FFat (#2966) 2019-07-09 19:14:53 +03:00
1aced120ba docs: fix typo (#2973) 2019-07-09 19:14:27 +03:00
7d7824701f Fix persistance issue found, see ESP8266 issue #6152 (#2851) 2019-06-05 14:00:00 +02:00
89feacb813 Fix map() division by zero (#2848) 2019-06-05 13:48:13 +02:00
aae6f24a37 Update (#2838)
Added PoE-ISO and DevKit-Lipo.
For the other boards added alternative Serial (Serial1/Serial2) pins.
2019-06-05 13:44:52 +02:00
43b781a158 Update AsyncUDP library.json (#2837)
* Fix https://github.com/platformio/platform-espressif32/issues/186

* Drop library.json
2019-06-05 13:43:15 +02:00
e055b28d1a Ignore Werror-maybe-uninitialized from Azure IoT Library (#2824)
Ignore the error about a potentially uninitialized variable
in the Azure library that will occur when compiling as a component
in ESP-IDF.
2019-06-05 13:42:08 +02:00
714ba948e6 Update BLERemoteCharacteristic.cpp (#2800)
Prevents the error that is generated on free on row 186
When calling the function readValue() sometimes it gave an error on this free.
2019-06-05 13:38:49 +02:00
e57de64a3c Fixed multi_heap_free failed during setting the value of the characteristic in BLE Library. (#2789) 2019-06-05 13:37:06 +02:00
fd5a2f08f9 Fix #2750 (#2763)
* Fix #2750

* Fixes for pull comments

* latest modifications following comments from me-no-dev

* Move SPIFFSFSImpl to .cpp file
2019-05-14 00:15:35 +03:00
e9389e3122 Fix AsyncUDP buffer error
fixes: https://github.com/espressif/arduino-esp32/issues/2685
2019-05-13 23:18:38 +03:00
0acf19af8f Update IDF to v3.2 977854975 (#2771)
* Update IDF to v3.2 977854975

* Update app_httpd.cpp
2019-05-12 18:52:23 +03:00
aff2e42ac6 Add Pycom GPy board (#2754) 2019-05-11 11:24:11 +03:00
2743e7b739 Update mac.md (#2745) 2019-05-11 11:21:25 +03:00
c453a0037b Adding TTGO T1 board definition (#2744)
Fixes https://github.com/espressif/arduino-esp32/issues/2740
2019-05-11 11:20:57 +03:00
2f249edb8e Use std::abs for a float-compatible abs() function (#2738)
* Other Arduino cores uses a macro to redefine libc abs() to take any
  type, meaning abs(-3.3) == 3.3 not the normal libc result of 3.

* 1e4bf14a3 (#1783) replaced similar min, max macros with c++ stdlib. However
  this change includes <algorithm> after the line which defines the abs() macro.
  <algorithm> includes <cstdlib> which undefines abs() and re-defines it.

* This means abs() becomes the plain libc version again which only takes
  integers, so abs(-3.3) == 3. As reported here:
  https://github.com/espressif/esp-idf/issues/3405

This fix tries to keep in the spirit of #1783 by using libstdc++. The other
option would be to include <cstdlib> before defining the abs() macro, so it
doesn't get undef-ed again later on.
2019-05-11 11:18:39 +03:00
bd57ff4ab4 StreamString SSO fix (#2736)
As found by @mongozmaki in https://github.com/esp8266/Arduino/pull/6035

With SSO implementation in String, StreamString::write generates wrong
strings under some circumstances.  Reason is that String::len() returns
strlen(sso_buf) if SSO=true but with newly written data
(in StreamString::write) the null-termination missing at the time len()
is called.

Furthermore, len() is called twice which is inefficient if SSO=true.
2019-05-11 11:16:05 +03:00
43bf393dbf Fix semaphores in IDF & std::string assert (#2728)
* Fix semaphores in IDF & std::string assert 

Fixes the problem of giving a mutex from a callback with the latest IDF. Also addresses an occasional assert that happens when the btc_task callback gives the semaphore and causes an assert due to both cores potentially writing m_owner concurrently.

* Restored m_owner position in wait() as requested

* Reapply assert fix and move setting m_owner in ::give() 

Revert previous revert commit and move setting of m_owner in ::give to before giving the semaphore to prevent race condition possibility.
2019-05-11 11:03:09 +03:00
bea7bd1852 Implemented ability to change BLE address (#2690)
Implemented the ability to change the ESP32s BLE device address as
according the the BLE specification. This address is used when
advertising the ESP32 over BLE.
2019-05-11 11:02:26 +03:00
7dd537f8d3 Fix libgcc not being in rom
fixes: https://github.com/espressif/arduino-esp32/issues/2758
2019-05-09 13:07:49 +03:00
50d142950d Update esp32-hal-rmt.c 2019-05-01 20:10:41 +03:00
d13de284b8 Undo the redefinition of FPSTR from 8266 merge (#2726)
Fixes Arduino.h redefinition errors.
2019-04-30 16:52:14 +03:00
697d4ff7c4 Add board "WEMOS D1 MINI ESP32". (#2710) 2019-04-27 14:10:21 +03:00
ab309e40d5 Copy ESP8266 String w/SSO to ESP32 repo (#2715)
I redid the ESP8266 WString library to enable small string optimization
(SSO) a while back, and think it would be helpful even on the ESP32 with
its higher memory complement.

SSO avoids lots of tiny mallocs() on the heap which cause fragmentation
by using the memory in the class object itself to store the actual
string and only mallocing() for buffers that are larger than what can
fit in thie class object.  Modern C++ std::string implementations have
this optimization as well, but since we're using Arduino strings we had
to roll our own.
2019-04-26 19:41:42 +03:00
932666a03f Ensure that _size is properly set in begin (#2706)
* Ensure that _size is properly set in begin

* NULL check on _data assignment

* Changed _data to malloc in order to catch alloc fails
2019-04-26 19:39:22 +03:00
a0ad987029 Add TTGO T-Watch board definition (#2681) 2019-04-25 12:16:26 +03:00
271e5cd206 Fix Werror=reorder for idf.py build (#2707) 2019-04-25 12:02:21 +03:00
619568db5b Converted EEPROM library to use nvs instead of partition. (#2678)
* Converted EEPROM library to use nvs instead of partition.  Removed eeprom partition from all partition table CSV files.
* Changed variable names, added some comments, formatting as per me-no-dev's requests
* Checks for memory on malloc
* Moved include nvs.h from header to code
* Reworked the extra example to make it more clear how to actually use the library and persist data
2019-04-23 23:55:12 +03:00
0202ba7c21 Fix reorder error in WebServer (#2700) 2019-04-23 17:57:33 +03:00
119ece2b0f Portability from ESP8266, virtual Stream member functions. (#2701) 2019-04-23 17:56:49 +03:00
606446a830 Update ALKS board (#2694) 2019-04-23 10:52:24 +03:00
a28cf00295 add lolin32 CPU freq choice (#2682) 2019-04-22 22:00:23 +03:00
672e4faa92 Add optional support for CORS headers (#2688)
* add support for CORS headers

* remove accidental function impl

* rename setCORS to enableCORS, and add aliased function enableCrossOrigin
2019-04-22 21:52:39 +03:00
f8eebb5c39 Fix indentation 2019-04-17 23:27:32 +03:00
6bf104874a Add LGPL 2.1 License 2019-04-16 03:08:50 +02:00
9a9ff62216 Add connect with timeout to Client class 2019-04-15 18:01:43 +02:00
3d6e4e1b94 Use milliseconds for timeout 2019-04-15 17:27:54 +02:00
01d7ea7b80 Refactored use of LOG_X(LOG_TAG, ...) to log_x(...) (#2672)
* Replaced ARDUINO_VARIANT with const char

* Fixed missing return value

* Added quotes around defined value in macro (Issue #2193)

* Change logging from Error to Verbose when not found and default available

* Move Enter and Exit logging to Verbose Level

* Refactored LOG_X() into log_x()
2019-04-15 17:26:35 +02:00
Bob
af23d0bb10 Return empty string instead of "0" (#2673) 2019-04-15 17:23:42 +02:00
582e6433e9 Add proper timeout handling to WiFiClientSecure 2019-04-15 17:19:49 +02:00
ef07a84ade Update esp32-camera driver 2019-04-13 22:52:21 +02:00
ea043cdb14 Fix WiFi Multi not clearing password 2019-04-13 17:19:06 +02:00
c60092951f Add missing definitions 2019-04-13 17:18:25 +02:00
fa74767b2e Allow selecting in IDF the running core for Arduino's core tasks 2019-04-13 17:13:13 +02:00
d922557e01 Set default Serial1 pins for Adafruit Feather 32 2019-04-13 13:28:16 +02:00
33d4186b35 Add board "SparkFun LoRa Gateway 1-Channel". (#2664)
* Add board "SparkFun LoRa Gateway 1-Channel".

* Extend boards.txt.

Source: https://learn.sparkfun.com/tutorials/sparkfun-lora-gateway-1-channel-hookup-guide/all
2019-04-13 12:26:35 +02:00
6dab3f6777 Fix LEDC channels above 8 not properly clearing
Fixes: https://github.com/espressif/arduino-esp32/issues/2660
2019-04-12 17:49:44 +02:00
1efcd21ba9 replace with strerror (#2663) 2019-04-12 15:44:16 +02:00
7b5cd47d07 Set ESP-IDF to 3.2 (#2662)
* Set IDF to v3.2

* Remove BLE submodule

* Add BLE lib source

* Update Camera example to support OV3660
2019-04-12 15:43:53 +02:00
14126060a1 Properly allocate string and remove warning (#2652)
* Properly allocate string and remove warning

The former way generates the following warning:
ISO C++ forbids converting a string constant to 'char*'

This change makes a character array the size of the string with null ending. It's clearer and gets rid of the warning.

* Better way

Since this is technically immutable, the type should reflect this too.
2019-04-12 12:46:08 +02:00
25c0b52212 Alternative Improve _uploadReadByte (#2656)
* add opportunity for more than one retry to _uploadReadByte

* an alternative timeout-based method to making _uploadReadByte more resilient

* move timing variables in the correct scope

* implement and use client.getTimeout instead of hard-coded timeout in _uploadReadByte

* add missing return

* some refactoring to address respecting the timeout in a potentially deadlocked connection

* fix spelling in comment

* address review comments; move impl to cpp file for getTimeout, and remove local variable for currentMillis

* remove redundant cast

* need to check for timeout outside the inner while as well

* update WebUpdate example to print something in unexpected callback condition

* update log_e messages per review comments
2019-04-12 12:45:35 +02:00
e0beac88c9 File with more than 32,767 characters (#2566) 2019-04-11 16:54:40 +02:00
a87b2ec690 Fix AsyncUDP receive memory leak (#2607)
If _handler is set, pbuf_free is not called. ~AsyncUDPPacket() calls pbuf_free once but only after calling pbuf_ref in it's constructor. The refcount never reaches zero and the memory allocated for pbuf is never released.
2019-04-11 16:49:35 +02:00
6744565257 Also show how space is allocated for the default partitions (#2649)
Make all the partitions descriptions consistent, they now all show what space allocation you get.
2019-04-11 16:47:15 +02:00
da8b7c1b80 overload Preferences.getBytes similar to nvs so you can get size of t… (#2498)
* overload Preferences.getBytes similar to nvs so you can get size of the array.

* Cleaner implentation, with a separate function to get length.  Added an example
2019-04-10 02:45:38 +02:00
91508030d8 Hide log_v behind conditional define, reduce excessive logging (#2641)
@cyberman54 from #2250 recommended reducing unnecessary logging.
2019-04-10 02:43:21 +02:00
a0c975dfbc Reset retry counter upon successful write (#2638)
Currently  WiFiClient::write is unable to send messages over 25Kb, because of the hard-coded retry limit of 10, that is getting decremented on every successful send. Since we cannot send more than 2*MTU bytes in one go, and have only 10 retries, write() is limited to approximately 25Kb. Technically it is not a bug, as it correctly returns the number of sent bytes and the caller can set up futher retries. But not all libs are aware of this behavior, for example, WebServer is not.
I suggest improving current behavior by resetting retry counter every time we had a successful write, so the limit of 10 retries will apply to Failed writes only, and will not apply to Successful writes. This will allow to write() blobs of arbitrary sizes.
2019-04-10 02:41:29 +02:00
0906bf580f update getLocalTime(). (#2629)
* update getLocalTime().

* change count to millis in getLocalTime

* timeout logic.
2019-04-10 02:39:14 +02:00
53a4bf33b6 Added 2 ffat partition schemes for 4MB modules + improved descriptions. (#2623)
* Added 2 ffat partition schemes for 4MB modules + improved descriptions.

Existing format options were unclear on how much spiffs you were
getting. Also add missing 1MB App/3MB Spiffs split.
This addresses https://github.com/espressif/arduino-esp32/issues/1799

This makes it very clear what each option gives you, and also adds 2 options
to get ffat on 4MB chips..

* Added link to ffat howto.
2019-04-10 02:34:31 +02:00
92220b7643 Added PATCH request. (#2610) 2019-04-09 21:22:47 +02:00
dd649808d1 Add server connect timeout to HTTPClient (#2606) 2019-04-09 21:19:25 +02:00
67ee7c32e7 workaround for FS implementations that do not support fflush() (#2596)
https://github.com/espressif/arduino-esp32/issues/1293
2019-04-09 21:15:21 +02:00
8091c2cac7 Add M5Stick-C to board manager (#2594) 2019-04-09 21:13:15 +02:00
4930853edb Add support for ffat on 4MB ESP32 Dev Module and mhetesp32minikit board. (#2588)
* Update boards.txt

Add menu items for ffat on mhetesp32minikit board.

* Add partition table.

* Added ffat partition for 4MB ESP32 Dev Module.

* Added comment about partitioning schemes.
2019-04-09 21:11:17 +02:00
8e8c5035ea Added rmtDeinit to header. Fixes #2585 (#2587)
* Added rmtDeinit to header. Fixes #2585

* oops.  missing ;
2019-04-09 21:09:40 +02:00
1db72a27c4 Added ESP32-CAM board (#2572) 2019-04-09 21:09:13 +02:00
e28dce7eb7 Get Odroid-Go's PSRAM detected (#2564) 2019-04-09 21:07:53 +02:00
d5f71ce545 Added a parameter for max files in SD (#2563) 2019-04-09 21:07:25 +02:00
7df50a97d1 Update IDF to ebdcbe8c6 (#2539)
- ESP-Face to 2937054
- ESP32-Camera to 113629b
2019-03-03 17:19:11 +01:00
566f659e90 Frog Board ESP32 definition (#2515)
* Added board definition

Signed-off-by: Roman Hosek <roman.hosek@iotbakery.com>

* fix typo

* variant + pin definition
2019-03-03 15:53:49 +01:00
cb0a939a6f Adding debug flag to PIO build script (#2510)
Adding the -g3 flag that was omitted in the PIO build script but is present in Arduino IDE build scripts. This flag restores the ability to get line numbers from stack traces for elf files generated in PlatformIO IDE.
2019-03-03 15:51:59 +01:00
cebd8704c8 Default pin remap for wESP32 (#2508)
* Add default pin mapping for Serial1 and Serial2

* Default pin remap on wESP32

Improved default pin mapping for I2C0 and UART1 to avoid issues
with programming.
SDA0 was on IO2 but IO2 needs to be pulled low on reset to enable
serial programming, which conflicts with I2C pull-ups.
RX1 was on IO12, idle UART level is high which conflicts with
IO12 needing to be low on reset to select correct 3.3V flash
voltage.
New mappings:
SDA0 on IO15
RX1 on IO13
TX1 on IO12
2019-03-03 15:51:43 +01:00
ff85f3e90e connect only to provided creds (#2491) 2019-03-03 15:49:59 +01:00
dffda0bd6e log no networks found when scanResult is 0 (#2484) 2019-03-03 15:49:16 +01:00
2ceab7c279 return macaddress (#2477)
* WiFi.macaddress() returns mac address

* change description

* return macaddress when WiFi mode is WIFI_MODE_NULL
2019-03-03 15:48:30 +01:00
84e458a9e1 Add Heltec boards defintion and update board menu chooices (#2475) 2019-03-03 15:47:47 +01:00
f3c1a91f8e adding uartRxActive to expose the RX state machine status as a boolean value (#2457) 2019-03-03 15:47:24 +01:00
5af0336177 Changed the description of file (#2476) 2019-02-20 00:50:20 +02:00
628b8f0c29 Fix Olimex board definition 2019-02-18 12:56:59 +02:00
2bb32bd4b0 Unbiased random (#2468)
* An example to read high frequency analog data using i2s_adc

* Changed random() to use Lemire's method for bounding
2019-02-18 12:18:49 +02:00
89d6b895d1 Fixed missing null terminator in EEPROM.readString(address, value, maxLen) (#2439) 2019-02-14 19:31:37 +01:00
010a7c60f7 Update IDF to abea9e4c0 (#2458)
* Update IDF to abea9e4c0

* Update esptool

* Enable PSRAM for PICO D4

* Enable APP_ROLLBACK_ENABLE
2019-02-14 16:49:30 +01:00
c0345eafbf fix missing the first bit in HC589 shift in (#2448) 2019-02-14 13:11:08 +01:00
71ec3c3e31 Correct millis() error causes by micros() overflow every about 72 minutes (#2438) 2019-02-14 13:09:51 +01:00
8ec76405b9 Fix compile warning, esp32-hal-i2c.c (#2434)
@tread-Qualitrol discovered this error.
2019-02-14 13:09:05 +01:00
8806acfbf9 Added partition menu for OROCA EduBot board. (#2429) 2019-02-14 13:08:28 +01:00
2e9cb5945d default FORMAT_FILESYSTEM to false in FSBrowser.ino (#2425) 2019-02-14 13:06:21 +01:00
c001996eef Add default pin mapping for Serial1 and Serial2 for wESP32 (#2409) 2019-02-14 13:05:23 +01:00
c1344ae094 New version of the ESP32-EVB/Gateway/PoE boards (#2397)
In Board.txt file added board revision submenu to distinguish the different revisions of the same board. By selecting different revision in the Arduino menu different value of the predefined macro will be applied which is used inside the pins_arduino.h file to make different defines and values for the specific revision.
In addition to this esp32-poe pin definitions is added ethernet power enable pin and also defined macro: BOARD_HAS_1BIT_SDMMC
2019-02-14 13:03:34 +01:00
f6b9e9c2ab Clarified comments and debug messages in WiFiClient Example (#2389)
* Clarified comments and debug messages

Made debug messages and comments easier to understand/grammatical fixes

* few more fixes
2019-02-14 12:59:55 +01:00
fc737e08c6 Add connect timeout to WiFiClient (#2383)
* Add timeout to WiFiClient.connect()

* Changed default handling
2019-02-14 12:55:50 +01:00
e302a6848a Board T-Beam: Update pins_arduino.h (#2380)
* Board T-Beam: Update pins_arduino.h

DAC2 is not wired, only DAC1 is -> corrected
LORA pins 27 + 19 are only wired on pcb, but not connected to header -> deleted

* Update pins_arduino.h

Button and LED added
2019-02-14 12:53:45 +01:00
aa2393b573 remove useless condition in if statement (#2371)
* fix log error code format

* remove useless condition in if statement
2019-02-14 12:52:52 +01:00
755cd938f7 add missing EEPROM lib to CMakeLists.txt, moved the EEPROM files to proper dir (#2333)
* add missing EEPROM lib to CMakeLists.txt, moved the EEPROM files to proper directory

* add missing EEPROM lib to CMakeLists.txt, moved the EEPROM files to proper directory
2019-02-14 12:49:34 +01:00
86fdb5b23d Fix compilation errors when used as IDF component 2019-01-26 21:37:03 +01:00
ffd15e4637 fix log error code format (#2366) 2019-01-23 15:33:41 +08:00
29d59876b4 Webserver library - fix logging (#2355) (#2359)
* Webserver fix logging (#1)

* Change logging to use esp32-hal-log.h

fixes #2355

* adjust log parameter output positions, reduce lines

The DEBUG_ESP method used less lines than I originally set `log_v` to use when displaying the details of the received params ("@" and "=" indexes, and File info on a single line)
2019-01-22 17:30:10 +08:00
8cbc60edbc fixed rmt receive data pointer position. (#2353) 2019-01-22 17:29:32 +08:00
00a546ee06 python2 get.py does not work behind proxy (#2349)
Executing python2 get.py behind proxy give error "IOError: [Errno socket error] [SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:590)"
2019-01-22 17:28:27 +08:00
2ba1e66060 Fix SS pin for Adafruit Feather variant of the ESP32 board (#2344)
Pin 2 is not found on the [Adafruit docs](https://learn.adafruit.com/adafruit-huzzah32-esp32-feather) for this board and 33 was suggested in #1586 as a fix. I've tried this locally on my board and it is working correctly.
2019-01-22 17:27:42 +08:00
489feb8727 Fix BananaPi-BIT and T-Beam pin definitions (#2343)
* Fix BananaPi-BIT DAC definition

* Update t-beam too
2019-01-22 17:27:19 +08:00
3350476d1d I2C debugging and Log Dump Explaination (#2325)
* I2C debugging and Log Dump Explaination

Create a help file for I2C debugging and interpretation of log dumps

* Update i2c_debugging.md

version

* add formatting, file locations
2019-01-22 17:24:51 +08:00
fa6f75952d I2s adc (#2309)
* An example to read high frequency analog data using i2s_adc

* HiFreq_ADC.ino
2019-01-22 17:24:16 +08:00
6718da0350 Add ESP-EYE support to the camera demo 2019-01-22 14:38:24 +08:00
45fa0f8294 Fix issue with WiFi lib sending the wrong event ID when debug is on 2019-01-22 14:37:55 +08:00
6cf307dbd1 Fix copy/paste error in RMT
Fixes: https://github.com/espressif/arduino-esp32/issues/2316
2019-01-14 19:30:07 +08:00
81844f5360 Add API to feed the loop WDT (call from loop only) 2019-01-12 13:02:38 +01:00
f7cf583b87 Update README.md
Add download counter for releases
2019-01-11 10:17:45 +01:00
e8a2c16c8f Update issue templates 2019-01-11 02:21:32 +01:00
9a7946e685 I2C fix READ of zero bytes hardware hang (#2301)
The i2c peripheral will hang if a READ request is issued with a zero data length.  The peripheral
drops into a continuous timeout interrupt response.  The STOP command can not be set out to the I2C
bus. The SLAVE device correctly ACK'd the address byte, with READ bit set, it has control of the SDA
 pin.  The ESP32 send out the next SCL HIGH pulse but, since the SLAVE is in READ Mode, and the First
bit it is sending happened to be a ZERO, the ESP32 cannot send the STOP.  When it releases SDA during
the SCL HIGH, the pin does not change state.  The pin stays low because the SLAVE is outputing a LOW!
The ESP32 drops into a perminent WAIT state waiting for SDA to go HIGH (the STOP).

**esp32-hal-i2c.c**
* add databuff length checks to `i2cRead()` and `i2cWrite()`
2019-01-10 21:37:13 +01:00
566b69eda3 Test @PlatformIO with CameraWebServer example (#2300) 2019-01-10 15:54:12 +01:00
e544e67838 PlatformIO: Revert back default partition table to "default.csv" (#2299) 2019-01-10 15:53:51 +01:00
b0582e1ec8 Fix compilation failing on Windows because of liblib.a 2019-01-10 09:23:25 +01:00
a539257a38 An example to read high frequency analog data using i2s_adc (#2295) 2019-01-10 08:24:17 +01:00
70656aa129 fix leak in log_printf 2019-01-09 21:37:31 +01:00
fa61b3bffe Update IDF to e931fe9 and add esp-face (#2291)
* Update IDF to e931fe9 and add esp-face

* Fix PIO builds fail because of sketch size

* Fix example build failing for Arduino
2019-01-09 19:31:46 +01:00
452c27a74a Added ESP32 FM DevKit - Fixed unused pins definitions. (#2288)
* Added ESP32 FM DevKit

* Added ESP32 FM DevKit variant

Fixed pin definitions for unused pins.
2019-01-09 16:50:41 +01:00
2fd39b1aff Handle APB frequency change (#2250)
* Add APB change callbacks and move cpu code to own file

* Properly set esp_timer and FreeRTOS tick dividers

* Improve updated devisors

* No need to update REF_TICK yet

* Add initial handling for UART baud change

* fix uartWriteBuf and uartDetectBaudrate

* trigger callbacks even when APB did not change

* toggle UART ISR on CPU change

* add XTAL freq getter and add cpu freq validation

* Support CPU frequency changes in I2C (#2287)

**esp32-hal-i2c.c**
* add callback for cpu frequency changes
* adjust fifo thresholds based on cpu frequency and i2c bus frequency
* reduce i2c bus frequency if differential is too small
**Wire.h**
* version to 1.1.0

* Implement clock change for the other peripherals

* remove bad CPU clock values from the menu

* Add note to CPU freqs that support WiFi and BT
2019-01-09 10:07:54 +01:00
ff18a211e4 Add OROCA EduBot Board (#2264) 2019-01-06 21:47:55 +02:00
a6e3b29004 Added support for using TCPIP_ADAPTER_IF_MAX to join igmp groups on all (#2279)
interfaces
2019-01-06 21:29:38 +02:00
812d131663 Correct comment in SPI_Multiple_Buses.ino (#2272)
The default SPI bus is VSPI (see libraries/SPI/src/SPI.cpp). This change corrects a misleading comment in a code example which was stating wrongly that HSPI would be the default one.
2019-01-04 15:26:32 +02:00
00e69a28bc Redo PR #2259 because I messed up my local git (#2263) 2019-01-02 17:44:17 +02:00
229d9b7366 [WiFiClientSecure] Shows only free internal heap on logs (#2252)
* Shows only free internal heap on logs

Since Mbedtls is running only on internal heap, show internal + PSRAM available memory on logs can confuse the users

* Clarify logs
2018-12-31 11:44:57 +02:00
6dd8be3262 Use more agressive disconnect on wifi connect error 2018-12-28 21:23:17 +02:00
28ea39cf05 Add WDT API for Core 0 and disable it while SPIFFS is formatting 2018-12-28 21:14:46 +02:00
f49c854ff3 Update IDF to 97eecfa, enable reboot on WDT and add WDT API (#2248)
* Update IDF to 97eecfa and enable reboot on WDT

* Add API to enable/disable WDT for Core 1 and Arduino Loop
2018-12-28 20:37:33 +02:00
879388e170 Update esp32-hal-gpio.c
fix copy/paste error
2018-12-28 19:14:15 +02:00
1085e9a8f0 Fix bad multiplier calculation 2018-12-24 13:55:08 +02:00
bed9c96f41 update board files for LoPy, LoPy4, T-Beam (#2241)
changed LORA_IO0 -> LORA_IRQ
added LORA_RST
added SS
2018-12-24 13:08:12 +02:00
5af139bb74 HardwareSerial - add changeBaudRate method (#2223)
* Add updateBaudRate to hardware serial

* remove flush

* Fix tab
2018-12-23 21:15:06 +02:00
4f9a90fa0e fix #2232 and #2033 (#2233) 2018-12-23 19:06:43 +02:00
310e78e6fd Support CPU frequency changes (#2222)
The I2C hardware has limitations on min and max bus frequency directly related to CPU frequency, bus speed cannot be greater than 1/100 CPU clock, nor less than CPU clock / 8192.
2018-12-20 08:56:02 +01:00
c827bb4177 CPU and APB Frequency support (#2220)
* Add support to HAL for APB frequencies different than 80MHz

* Add support for CPU frequencies in the IDE board menu

* Switch to fast set_config

* Add method to uart so debug can be reassigned after apb frequency switch

* Return real APB frequency
2018-12-20 01:57:32 +01:00
1628f533a1 Add Camera WebServer Example 2018-12-20 00:41:48 +01:00
3e66aeff84 Handle better 160MHz cpu clock 2018-12-18 22:04:02 +01:00
66d33f792c Add support for CPU Frequency switching
Experimental!
2018-12-18 20:04:16 +01:00
0de0d3f79a move call to setTimeout() to after the client connects. (#2214)
This is necessary to avoid this log entry from being generated due to invalid usage of setTimeout:
[E][WiFiClient.cpp:236] setSocketOption(): 1006 : 9
2018-12-18 19:21:36 +01:00
512d0d088f Add Turta IoT Node Board (#2208) 2018-12-17 23:11:05 +01:00
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
427 changed files with 26838 additions and 4924 deletions

54
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,54 @@
---
name: Bug report
about: Please fill in the bug report carefully
title: ''
labels: ''
assignees: ''
---
Make your question, not a Statement, inclusive. Include all pertinent information:
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).
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? ?node32? ?ttgo_lora?
Core Installation version: ?1.0.0? ?1.0.1-rc4? ?1.0.1? ?1.0.1-git? ?1.0.2? ?1.0.3?
IDE name: ?Arduino IDE? ?Platform.io? ?IDF component?
Flash Frequency: ?40Mhz?
PSRAM enabled: ?no? ?yes?
Upload Speed: ?115200?
Computer OS: ?Windows 10? ?Mac OSX? ?Ubuntu?
### Description:
Describe your problem here
### 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
#include <Arduino.h>
void setup() {
}
void loop() {
}
```
### Debug Messages:
```
Enable Core debug level: Debug on tools menu of Arduino IDE, then put the serial output here
```

64
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,64 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 14
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- pinned
- security
- "to be implemented"
- "for reference"
- "move to PR"
- "enhancement"
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed in 14 days if no further activity occurs. Thank you
for your contributions.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This stale issue has been automatically closed. Thank you for your contributions.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
only: issues
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed

376
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,376 @@
name: ESP32 Arduino CI
on:
push:
branches:
- master
- release/*
pull_request:
jobs:
build-arduino-0:
name: Build Arduino IDE Tests 0
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Run CMake Check
run: ./tools/ci/check-cmakelists.sh
- name: Install Arduino IDE
env:
#ESP32_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 0 18
- name: Sketch Sizes
run: cat size.log
build-arduino-1:
name: Build Arduino IDE Tests 1
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 1 18
- name: Sketch Sizes
run: cat size.log
build-arduino-2:
name: Build Arduino IDE Tests 2
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 2 18
- name: Sketch Sizes
run: cat size.log
build-arduino-3:
name: Build Arduino IDE Tests 3
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 3 18
- name: Sketch Sizes
run: cat size.log
build-arduino-4:
name: Build Arduino IDE Tests 4
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 4 18
- name: Sketch Sizes
run: cat size.log
build-arduino-5:
name: Build Arduino IDE Tests 5
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 5 18
- name: Sketch Sizes
run: cat size.log
build-arduino-6:
name: Build Arduino IDE Tests 6
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 6 18
- name: Sketch Sizes
run: cat size.log
build-arduino-7:
name: Build Arduino IDE Tests 7
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 7 18
- name: Sketch Sizes
run: cat size.log
build-arduino-8:
name: Build Arduino IDE Tests 8
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 8 18
- name: Sketch Sizes
run: cat size.log
build-arduino-9:
name: Build Arduino IDE Tests 9
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 9 18
- name: Sketch Sizes
run: cat size.log
build-arduino-10:
name: Build Arduino IDE Tests 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 10 18
- name: Sketch Sizes
run: cat size.log
build-arduino-11:
name: Build Arduino IDE Tests 11
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 11 18
- name: Sketch Sizes
run: cat size.log
build-arduino-12:
name: Build Arduino IDE Tests 12
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 12 18
- name: Sketch Sizes
run: cat size.log
build-arduino-13:
name: Build Arduino IDE Tests 13
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 13 18
- name: Sketch Sizes
run: cat size.log
build-arduino-14:
name: Build Arduino IDE Tests 14
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 14 18
- name: Sketch Sizes
run: cat size.log
build-arduino-15:
name: Build Arduino IDE Tests 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 15 18
- name: Sketch Sizes
run: cat size.log
build-arduino-16:
name: Build Arduino IDE Tests 16
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 16 18
- name: Sketch Sizes
run: cat size.log
build-arduino-17:
name: Build Arduino IDE Tests 17
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-arduino-ide.sh
- name: Test Arduino IDE
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/test-arduino-ide.sh 17 18
- name: Sketch Sizes
run: cat size.log
build-platformio:
name: Build PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pull submodules
run: git submodule update --init --recursive
- name: Install Python Wheel
run: pip install wheel
- name: Install GCC Toolchain
run: cd tools && python get.py
- name: Install PlatformIO
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
run: ./tools/ci/prep-platformio.sh
- name: Test PlatformIO
run: ./tools/ci/test-platformio.sh

7
.gitignore vendored
View File

@ -5,3 +5,10 @@ tools/esptool.exe
tools/mkspiffs/mkspiffs tools/mkspiffs/mkspiffs
tools/mkspiffs/mkspiffs.exe tools/mkspiffs/mkspiffs.exe
.DS_Store .DS_Store
#Ignore files built by Visual Studio/Visual Micro
[Dd]ebug*/
[Rr]elease*/
.vs/
__vm/
*.vcxproj*

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "libraries/BLE"]
path = libraries/BLE
url = https://github.com/nkolban/ESP32_BLE_Arduino.git
[submodule "libraries/AzureIoT"] [submodule "libraries/AzureIoT"]
path = libraries/AzureIoT path = libraries/AzureIoT
url = https://github.com/VSChina/ESP32_AzureIoT_Arduino url = https://github.com/VSChina/ESP32_AzureIoT_Arduino

View File

@ -1,28 +1,62 @@
sudo: false sudo: false
language: python language: python
python:
- "2.7"
os: os:
- linux - linux
env: git:
global: depth: false
before_install:
- git submodule update --init --recursive
stages:
- build
- deploy
jobs:
include:
- name: "Build Arduino 0"
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
stage: build
script: $TRAVIS_BUILD_DIR/tools/ci/build-tests.sh 0 4
- name: "Build Arduino 1"
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
stage: build
script: $TRAVIS_BUILD_DIR/tools/ci/build-tests.sh 1 4
- name: "Build Arduino 2"
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
stage: build
script: $TRAVIS_BUILD_DIR/tools/ci/build-tests.sh 2 4
- name: "Build Arduino 3"
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
stage: build
script: $TRAVIS_BUILD_DIR/tools/ci/build-tests.sh 3 4
- name: "Build PlatformIO"
if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
stage: build
script: $TRAVIS_BUILD_DIR/tools/ci/build-tests.sh 4 4
- name: "Package & Deploy"
if: tag IS present
stage: deploy
env:
- 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=" - 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="
- REMOTE_URL=https://github.com/$TRAVIS_REPO_SLUG/releases/download/$TRAVIS_TAG - REMOTE_URL=https://github.com/$TRAVIS_REPO_SLUG/releases/download/$TRAVIS_TAG
script: bash $TRAVIS_BUILD_DIR/tools/ci/build-release.sh -a$ESP32_GITHUB_TOKEN
script: before_deploy: git submodule update --init
- bash $TRAVIS_BUILD_DIR/tools/build.sh deploy:
- provider: script
deploy: skip_cleanup: true
provider: script script: bash $TRAVIS_BUILD_DIR/tools/ci/deploy-release.sh -t$TRAVIS_TAG -a$ESP32_GITHUB_TOKEN -s$TRAVIS_REPO_SLUG -drelease
skip_cleanup: true on:
script: bash $TRAVIS_BUILD_DIR/tools/deploy.sh -t$TRAVIS_TAG -a$ESP32_GITHUB_TOKEN -s$TRAVIS_REPO_SLUG -drelease tags: true
on:
tags: true
notifications: notifications:
email: email:

View File

@ -3,6 +3,7 @@ set(CORE_SRCS
cores/esp32/cbuf.cpp cores/esp32/cbuf.cpp
cores/esp32/esp32-hal-adc.c cores/esp32/esp32-hal-adc.c
cores/esp32/esp32-hal-bt.c cores/esp32/esp32-hal-bt.c
cores/esp32/esp32-hal-cpu.c
cores/esp32/esp32-hal-dac.c cores/esp32/esp32-hal-dac.c
cores/esp32/esp32-hal-gpio.c cores/esp32/esp32-hal-gpio.c
cores/esp32/esp32-hal-i2c.c cores/esp32/esp32-hal-i2c.c
@ -41,7 +42,7 @@ set(LIBRARY_SRCS
libraries/AsyncUDP/src/AsyncUDP.cpp libraries/AsyncUDP/src/AsyncUDP.cpp
libraries/BluetoothSerial/src/BluetoothSerial.cpp libraries/BluetoothSerial/src/BluetoothSerial.cpp
libraries/DNSServer/src/DNSServer.cpp libraries/DNSServer/src/DNSServer.cpp
libraries/EEPROM/EEPROM.cpp libraries/EEPROM/src/EEPROM.cpp
libraries/ESPmDNS/src/ESPmDNS.cpp libraries/ESPmDNS/src/ESPmDNS.cpp
libraries/FFat/src/FFat.cpp libraries/FFat/src/FFat.cpp
libraries/FS/src/FS.cpp libraries/FS/src/FS.cpp
@ -150,6 +151,8 @@ set(BLE_SRCS
libraries/BLE/src/BLEDescriptor.cpp libraries/BLE/src/BLEDescriptor.cpp
libraries/BLE/src/BLEDescriptorMap.cpp libraries/BLE/src/BLEDescriptorMap.cpp
libraries/BLE/src/BLEDevice.cpp libraries/BLE/src/BLEDevice.cpp
libraries/BLE/src/BLEEddystoneTLM.cpp
libraries/BLE/src/BLEEddystoneURL.cpp
libraries/BLE/src/BLEExceptions.cpp libraries/BLE/src/BLEExceptions.cpp
libraries/BLE/src/BLEHIDDevice.cpp libraries/BLE/src/BLEHIDDevice.cpp
libraries/BLE/src/BLERemoteCharacteristic.cpp libraries/BLE/src/BLERemoteCharacteristic.cpp
@ -178,6 +181,7 @@ set(COMPONENT_ADD_INCLUDEDIRS
libraries/BLE/src libraries/BLE/src
libraries/BluetoothSerial/src libraries/BluetoothSerial/src
libraries/DNSServer/src libraries/DNSServer/src
libraries/EEPROM/src
libraries/ESP32/src libraries/ESP32/src
libraries/ESPmDNS/src libraries/ESPmDNS/src
libraries/FFat/src libraries/FFat/src
@ -205,3 +209,8 @@ set(COMPONENT_REQUIRES spi_flash mbedtls mdns ethernet)
set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl bt) set(COMPONENT_PRIV_REQUIRES fatfs nvs_flash app_update spiffs bootloader_support openssl bt)
register_component() register_component()
set_source_files_properties(libraries/AzureIoT/src/az_iot/iothub_client/src/iothubtransport_mqtt_common.c
PROPERTIES COMPILE_FLAGS
-Wno-maybe-uninitialized
)

View File

@ -19,6 +19,70 @@ config AUTOSTART_ARDUINO
If disabled, you can call initArduino() to run any preparations If disabled, you can call initArduino() to run any preparations
required by the framework required by the framework
choice ARDUINO_RUNNING_CORE
bool "Core on which Arduino's setup() and loop() are running"
default ARDUINO_RUN_CORE1
help
Select on which core Arduino's setup() and loop() functions run
config ARDUINO_RUN_CORE0
bool "CORE 0"
config ARDUINO_RUN_CORE1
bool "CORE 1"
config ARDUINO_RUN_NO_AFFINITY
bool "BOTH"
endchoice
config ARDUINO_RUNNING_CORE
int
default 0 if ARDUINO_RUN_CORE0
default 1 if ARDUINO_RUN_CORE1
default -1 if ARDUINO_RUN_NO_AFFINITY
choice ARDUINO_EVENT_RUNNING_CORE
bool "Core on which Arduino's event handler is running"
default ARDUINO_EVENT_RUN_CORE1
help
Select on which core Arduino's WiFi.onEvent() run
config ARDUINO_EVENT_RUN_CORE0
bool "CORE 0"
config ARDUINO_EVENT_RUN_CORE1
bool "CORE 1"
config ARDUINO_EVENT_RUN_NO_AFFINITY
bool "BOTH"
endchoice
config ARDUINO_EVENT_RUNNING_CORE
int
default 0 if ARDUINO_EVENT_RUN_CORE0
default 1 if ARDUINO_EVENT_RUN_CORE1
default -1 if ARDUINO_EVENT_RUN_NO_AFFINITY
choice ARDUINO_UDP_RUNNING_CORE
bool "Core on which Arduino's UDP is running"
default ARDUINO_UDP_RUN_CORE1
help
Select on which core Arduino's UDP run
config ARDUINO_UDP_RUN_CORE0
bool "CORE 0"
config ARDUINO_UDP_RUN_CORE1
bool "CORE 1"
config ARDUINO_UDP_RUN_NO_AFFINITY
bool "BOTH"
endchoice
config ARDUINO_UDP_RUNNING_CORE
int
default 0 if ARDUINO_UDP_RUN_CORE0
default 1 if ARDUINO_UDP_RUN_CORE1
default -1 if ARDUINO_UDP_RUN_NO_AFFINITY
config DISABLE_HAL_LOCKS config DISABLE_HAL_LOCKS
bool "Disable mutex locks for HAL" bool "Disable mutex locks for HAL"
default "n" default "n"
@ -90,6 +154,8 @@ config ARDUHAL_PARTITION_SCHEME_MINIMAL
bool "Minimal (for 2MB FLASH)" bool "Minimal (for 2MB FLASH)"
config ARDUHAL_PARTITION_SCHEME_NO_OTA config ARDUHAL_PARTITION_SCHEME_NO_OTA
bool "No OTA (for large apps)" bool "No OTA (for large apps)"
config ARDUHAL_PARTITION_SCHEME_HUGE_APP
bool "Huge App (for very large apps)"
config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
bool "Minimal SPIFFS (for large apps with OTA)" bool "Minimal SPIFFS (for large apps with OTA)"
endchoice endchoice
@ -99,6 +165,7 @@ config ARDUHAL_PARTITION_SCHEME
default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT
default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL
default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA
default "huge_app" if ARDUHAL_PARTITION_SCHEME_HUGE_APP
default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
@ -165,6 +232,12 @@ config ARDUINO_SELECTIVE_ESPmDNS
select ARDUINO_SELECTIVE_WiFi select ARDUINO_SELECTIVE_WiFi
default y default y
config ARDUINO_SELECTIVE_FFat
bool "Enable FFat"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_FS config ARDUINO_SELECTIVE_FS
bool "Enable FS" bool "Enable FS"
depends on ARDUINO_SELECTIVE_COMPILATION depends on ARDUINO_SELECTIVE_COMPILATION

503
LICENSE.md Normal file
View File

@ -0,0 +1,503 @@
### GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
### Preamble
The licenses for most software are designed to take away your freedom
to share and change it. By contrast, the GNU General Public Licenses
are intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations
below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that there
is no warranty for the free library. Also, if the library is modified
by someone else and passed on, the recipients should know that what
they have is not the original version, so that the original author's
reputation will not be affected by problems that might be introduced
by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using a
shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it
becomes a de-facto standard. To achieve this, non-free programs must
be allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
**0.** This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License"). Each
licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control
compilation and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does and
what the program that uses the Library does.
**1.** You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a
fee.
**2.** You may modify your copy or copies of the Library or any
portion of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
- **a)** The modified work must itself be a software library.
- **b)** You must cause the files modified to carry prominent
notices stating that you changed the files and the date of
any change.
- **c)** You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
- **d)** If a facility in the modified Library refers to a function
or a table of data to be supplied by an application program that
uses the facility, other than as an argument passed when the
facility is invoked, then you must make a good faith effort to
ensure that, in the event an application does not supply such
function or table, the facility still operates, and performs
whatever part of its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of
the application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
**3.** You may opt to apply the terms of the ordinary GNU General
Public License instead of this License to a given copy of the Library.
To do this, you must alter all the notices that refer to this License,
so that they refer to the ordinary GNU General Public License, version
2, instead of to this License. (If a newer version than version 2 of
the ordinary GNU General Public License has appeared, then you can
specify that version instead if you wish.) Do not make any other
change in these notices.
Once this change is made in a given copy, it is irreversible for that
copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of the
Library into a program that is not a library.
**4.** You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy from
a designated place, then offering equivalent access to copy the source
code from the same place satisfies the requirement to distribute the
source code, even though third parties are not compelled to copy the
source along with the object code.
**5.** A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a work,
in isolation, is not a derivative work of the Library, and therefore
falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License. Section
6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data structure
layouts and accessors, and small macros and small inline functions
(ten lines or less in length), then the use of the object file is
unrestricted, regardless of whether it is legally a derivative work.
(Executables containing this object code plus portions of the Library
will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
**6.** As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a work
containing portions of the Library, and distribute that work under
terms of your choice, provided that the terms permit modification of
the work for the customer's own use and reverse engineering for
debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
- **a)** Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood that
the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
- **b)** Use a suitable shared library mechanism for linking with
the Library. A suitable mechanism is one that (1) uses at run time
a copy of the library already present on the user's computer
system, rather than copying library functions into the executable,
and (2) will operate properly with a modified version of the
library, if the user installs one, as long as the modified version
is interface-compatible with the version that the work was
made with.
- **c)** Accompany the work with a written offer, valid for at least
three years, to give the same user the materials specified in
Subsection 6a, above, for a charge no more than the cost of
performing this distribution.
- **d)** If distribution of the work is made by offering access to
copy from a designated place, offer equivalent access to copy the
above specified materials from the same place.
- **e)** Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
**7.** You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
- **a)** Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other
library facilities. This must be distributed under the terms of
the Sections above.
- **b)** Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
**8.** You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
**9.** You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
**10.** Each time you redistribute the Library (or any work based on
the Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
**11.** If, as a consequence of a court judgment or allegation of
patent infringement or for any other reason (not limited to patent
issues), conditions are imposed on you (whether by court order,
agreement or otherwise) that contradict the conditions of this
License, they do not excuse you from the conditions of this License.
If you cannot distribute so as to satisfy simultaneously your
obligations under this License and any other pertinent obligations,
then as a consequence you may not distribute the Library at all. For
example, if a patent license would not permit royalty-free
redistribution of the Library by all those who receive copies directly
or indirectly through you, then the only way you could satisfy both it
and this License would be to refrain entirely from distribution of the
Library.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply, and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
**12.** If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
**13.** The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time. Such
new versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
**14.** If you wish to incorporate parts of the Library into other
free programs whose distribution conditions are incompatible with
these, write to the author to ask for permission. For software which
is copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
**NO WARRANTY**
**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
### END OF TERMS AND CONDITIONS
### How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms
of the ordinary General Public License).
To apply these terms, attach the following notices to the library. It
is safest to attach them to the start of each source file to most
effectively convey the exclusion of warranty; and each file should
have at least the "copyright" line and a pointer to where the full
notice is found.
one line to give the library's name and an idea of what it does.
Copyright (C) year name of author
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper
mail.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the library,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in
the library `Frob' (a library for tweaking knobs) written
by James Random Hacker.
signature of Ty Coon, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -10,11 +10,11 @@
- [ESP32Dev Board PINMAP](#esp32dev-board-pinmap) - [ESP32Dev Board PINMAP](#esp32dev-board-pinmap)
## Development Status ## 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 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/) ![Downloads](https://img.shields.io/github/downloads/espressif/arduino-esp32/latest/total.svg?style=plastic)
[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/) [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/) ![Downloads](https://img.shields.io/github/downloads-pre/espressif/arduino-esp32/latest/total.svg?style=plastic)
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: Most of the framework is implemented. Most noticeable 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 - 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 - 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 - 2 channels [DAC](cores/esp32/esp32-hal-dac.h) which gives real analog output

1604
boards.txt

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,36 @@
ARDUINO_LIBRARIES_LIST := $(patsubst $(COMPONENT_PATH)/libraries/%,%,$(wildcard $(COMPONENT_PATH)/libraries/*)) ARDUINO_ALL_LIBRARIES := $(patsubst $(COMPONENT_PATH)/libraries/%,%,$(wildcard $(COMPONENT_PATH)/libraries/*))
ARDUINO_SINGLE_LIBRARY_FILES = $(patsubst $(COMPONENT_PATH)/%,%,$(sort $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/*/*/)) $(dir $(wildcard $(COMPONENT_PATH)/libraries/$(MODULE)/*/*/*/*/*/))))
ARDUINO_CORE_LIBS := $(foreach MODULE,$(ARDUINO_LIBRARIES_LIST),$(if $(CONFIG_ARDUINO_SELECTIVE_COMPILATION),$(if $(CONFIG_ARDUINO_SELECTIVE_$(MODULE)),$(ARDUINO_SINGLE_LIBRARY_FILES)),$(ARDUINO_SINGLE_LIBRARY_FILES)))
COMPONENT_ADD_INCLUDEDIRS := cores/esp32 variants/esp32 $(ARDUINO_CORE_LIBS) # Macro returns non-empty if Arduino library $(1) should be included in the build
# (either because selective compilation is of, or this library is enabled
define ARDUINO_LIBRARY_ENABLED
$(if $(CONFIG_ARDUINO_SELECTIVE_COMPILATION),$(CONFIG_ARDUINO_SELECTIVE_$(1)),y)
endef
ARDUINO_ENABLED_LIBRARIES := $(foreach LIBRARY,$(sort $(ARDUINO_ALL_LIBRARIES)),$(if $(call ARDUINO_LIBRARY_ENABLED,$(LIBRARY)),$(LIBRARY)))
$(info Arduino libraries in build: $(ARDUINO_ENABLED_LIBRARIES))
# Expand all subdirs under $(1)
define EXPAND_SUBDIRS
$(sort $(dir $(wildcard $(1)/* $(1)/*/* $(1)/*/*/* $(1)/*/*/*/* $(1)/*/*/*/*/*)))
endef
# Macro returns SRCDIRS for library
define ARDUINO_LIBRARY_GET_SRCDIRS
$(if $(wildcard $(COMPONENT_PATH)/libraries/$(1)/src/.), \
$(call EXPAND_SUBDIRS,$(COMPONENT_PATH)/libraries/$(1)/src), \
$(filter-out $(call EXPAND_SUBDIRS,$(COMPONENT_PATH)/libraries/$(1)/examples), \
$(call EXPAND_SUBDIRS,$(COMPONENT_PATH)/libraries/$(1)) \
) \
)
endef
# Make a list of all srcdirs in enabled libraries
ARDUINO_LIBRARY_SRCDIRS := $(patsubst $(COMPONENT_PATH)/%,%,$(foreach LIBRARY,$(ARDUINO_ENABLED_LIBRARIES),$(call ARDUINO_LIBRARY_GET_SRCDIRS,$(LIBRARY))))
#$(info Arduino libraries src dirs: $(ARDUINO_LIBRARY_SRCDIRS))
COMPONENT_ADD_INCLUDEDIRS := cores/esp32 variants/esp32 $(ARDUINO_LIBRARY_SRCDIRS)
COMPONENT_PRIV_INCLUDEDIRS := cores/esp32/libb64 COMPONENT_PRIV_INCLUDEDIRS := cores/esp32/libb64
COMPONENT_SRCDIRS := cores/esp32/libb64 cores/esp32 variants/esp32 $(ARDUINO_CORE_LIBS) COMPONENT_SRCDIRS := cores/esp32/libb64 cores/esp32 variants/esp32 $(ARDUINO_LIBRARY_SRCDIRS)
CXXFLAGS += -fno-rtti CXXFLAGS += -fno-rtti

View File

@ -68,12 +68,6 @@
#define __STRINGIFY(a) #a #define __STRINGIFY(a) #a
#endif #endif
// undefine stdlib's abs if encountered
#ifdef abs
#undef abs
#endif
#define abs(x) ((x)>0?(x):-(x))
#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
#define radians(deg) ((deg)*DEG_TO_RAD) #define radians(deg) ((deg)*DEG_TO_RAD)
#define degrees(rad) ((rad)*RAD_TO_DEG) #define degrees(rad) ((rad)*RAD_TO_DEG)
@ -160,6 +154,7 @@ void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
#include "HardwareSerial.h" #include "HardwareSerial.h"
#include "Esp.h" #include "Esp.h"
using std::abs;
using std::isinf; using std::isinf;
using std::isnan; using std::isnan;
using std::max; using std::max;

View File

@ -26,10 +26,11 @@
#include <soc/soc.h> #include <soc/soc.h>
#include <soc/efuse_reg.h> #include <soc/efuse_reg.h>
#include <esp_partition.h> #include <esp_partition.h>
#include <esp_ota_ops.h>
extern "C" { extern "C" {
#include <esp_image_format.h> #include "esp_ota_ops.h"
#include "esp_image_format.h"
} }
#include <MD5Builder.h>
/** /**
* User-defined Literals * User-defined Literals
@ -91,13 +92,6 @@ void EspClass::deepSleep(uint32_t time_us)
esp_deep_sleep(time_us); esp_deep_sleep(time_us);
} }
uint32_t EspClass::getCycleCount()
{
uint32_t ccount;
__asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount));
return ccount;
}
void EspClass::restart(void) void EspClass::restart(void)
{ {
esp_restart(); esp_restart();
@ -156,11 +150,11 @@ static uint32_t sketchSize(sketchSize_t response) {
.size = running->size, .size = running->size,
}; };
data.start_addr = running_pos.offset; data.start_addr = running_pos.offset;
esp_image_load(ESP_IMAGE_VERIFY, &running_pos, &data); esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
if (response) { if (response) {
return running_pos.size - data.image_len; return running_pos.size - data.image_len;
} else { } else {
return data.image_len; return data.image_len;
} }
} }
@ -168,8 +162,53 @@ uint32_t EspClass::getSketchSize () {
return sketchSize(SKETCH_SIZE_TOTAL); 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 () { uint32_t EspClass::getFreeSketchSpace () {
return sketchSize(SKETCH_SIZE_FREE); const esp_partition_t* _partition = esp_ota_get_next_update_partition(NULL);
if(!_partition){
return 0;
}
return _partition->size;
} }
uint8_t EspClass::getChipRevision(void) uint8_t EspClass::getChipRevision(void)

View File

@ -75,8 +75,8 @@ public:
uint32_t getMaxAllocPsram(); uint32_t getMaxAllocPsram();
uint8_t getChipRevision(); uint8_t getChipRevision();
uint8_t getCpuFreqMHz(){ return CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; } uint32_t getCpuFreqMHz(){ return getCpuFrequencyMhz(); }
uint32_t getCycleCount(); inline uint32_t getCycleCount() __attribute__((always_inline));
const char * getSdkVersion(); const char * getSdkVersion();
void deepSleep(uint32_t time_us); void deepSleep(uint32_t time_us);
@ -90,6 +90,7 @@ public:
FlashMode_t magicFlashChipMode(uint8_t byte); FlashMode_t magicFlashChipMode(uint8_t byte);
uint32_t getSketchSize(); uint32_t getSketchSize();
String getSketchMD5();
uint32_t getFreeSketchSpace(); uint32_t getFreeSketchSpace();
bool flashEraseSector(uint32_t sector); bool flashEraseSector(uint32_t sector);
@ -100,6 +101,13 @@ public:
}; };
uint32_t IRAM_ATTR EspClass::getCycleCount()
{
uint32_t ccount;
__asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount));
return ccount;
}
extern EspClass ESP; extern EspClass ESP;
#endif //ESP_H #endif //ESP_H

View File

@ -55,6 +55,7 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert); _uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert);
if(!baud) { if(!baud) {
uartStartDetectBaudrate(_uart);
time_t startMillis = millis(); time_t startMillis = millis();
unsigned long detectedBaudRate = 0; unsigned long detectedBaudRate = 0;
while(millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) { while(millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) {
@ -73,6 +74,11 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in
} }
} }
void HardwareSerial::updateBaudRate(unsigned long baud)
{
uartSetBaudRate(_uart, baud);
}
void HardwareSerial::end() void HardwareSerial::end()
{ {
if(uartGetDebug() == _uart_nr) { if(uartGetDebug() == _uart_nr) {

View File

@ -57,6 +57,7 @@ public:
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 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(); void end();
void updateBaudRate(unsigned long baud);
int available(void); int available(void);
int availableForWrite(void); int availableForWrite(void);
int peek(void); int peek(void);

View File

@ -52,19 +52,24 @@ size_t Print::printf(const char *format, ...)
va_list copy; va_list copy;
va_start(arg, format); va_start(arg, format);
va_copy(copy, arg); va_copy(copy, arg);
size_t len = vsnprintf(NULL, 0, format, arg); int len = vsnprintf(temp, sizeof(loc_buf), format, copy);
va_end(copy); va_end(copy);
if(len < 0) {
va_end(arg);
return 0;
};
if(len >= sizeof(loc_buf)){ if(len >= sizeof(loc_buf)){
temp = new char[len+1]; temp = (char*) malloc(len+1);
if(temp == NULL) { if(temp == NULL) {
va_end(arg);
return 0; return 0;
} }
len = vsnprintf(temp, len+1, format, arg);
} }
len = vsnprintf(temp, len+1, format, arg);
write((uint8_t*)temp, len);
va_end(arg); va_end(arg);
if(len > 64){ len = write((uint8_t*)temp, len);
delete[] temp; if(temp != loc_buf){
free(temp);
} }
return len; return len;
} }
@ -154,8 +159,10 @@ size_t Print::print(struct tm * timeinfo, const char * format)
} }
char buf[64]; char buf[64];
size_t written = strftime(buf, 64, f, timeinfo); size_t written = strftime(buf, 64, f, timeinfo);
print(buf); if(written == 0){
return written; return written;
}
return print(buf);
} }
size_t Print::println(void) size_t Print::println(void)

View File

@ -82,6 +82,9 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi
{ {
_timeout = timeout; _timeout = timeout;
} }
unsigned long Stream::getTimeout(void) {
return _timeout;
}
// find returns true if the target string is found // find returns true if the target string is found
bool Stream::find(const char *target) bool Stream::find(const char *target)

View File

@ -59,7 +59,7 @@ public:
// parsing methods // parsing methods
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
unsigned long getTimeout(void);
bool find(const char *target); // reads data from the stream until the target string is found bool find(const char *target); // reads data from the stream until the target string is found
bool find(uint8_t *target) bool find(uint8_t *target)
{ {
@ -97,8 +97,8 @@ public:
float parseFloat(); // float version of parseInt float parseFloat(); // float version of parseInt
size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
size_t readBytes(uint8_t *buffer, size_t length) virtual size_t readBytes(uint8_t *buffer, size_t length)
{ {
return readBytes((char *) buffer, length); return readBytes((char *) buffer, length);
} }
@ -114,7 +114,7 @@ public:
// returns the number of characters placed in the buffer (0 means no valid data found) // returns the number of characters placed in the buffer (0 means no valid data found)
// Arduino String functions to be added here // Arduino String functions to be added here
String readString(); virtual String readString();
String readStringUntil(char terminator); String readStringUntil(char terminator);
protected: protected:

View File

@ -2,6 +2,7 @@
StreamString.cpp StreamString.cpp
Copyright (c) 2015 Markus Sattler. All rights reserved. Copyright (c) 2015 Markus Sattler. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
@ -22,31 +23,28 @@
#include <Arduino.h> #include <Arduino.h>
#include "StreamString.h" #include "StreamString.h"
size_t StreamString::write(const uint8_t *data, size_t size) size_t StreamString::write(const uint8_t *data, size_t size) {
{
if(size && data) { if(size && data) {
if(reserve(length() + size + 1)) { const unsigned int newlen = length() + size;
memcpy((void *) (buffer + len), (const void *) data, size); if(reserve(newlen + 1)) {
len += size; memcpy((void *) (wbuffer() + len()), (const void *) data, size);
*(buffer + len) = 0x00; // add null for string end setLen(newlen);
*(wbuffer() + newlen) = 0x00; // add null for string end
return size; return size;
} }
} }
return 0; return 0;
} }
size_t StreamString::write(uint8_t data) size_t StreamString::write(uint8_t data) {
{
return concat((char) data); return concat((char) data);
} }
int StreamString::available() int StreamString::available() {
{
return length(); return length();
} }
int StreamString::read() int StreamString::read() {
{
if(length()) { if(length()) {
char c = charAt(0); char c = charAt(0);
remove(0, 1); remove(0, 1);
@ -56,8 +54,7 @@ int StreamString::read()
return -1; return -1;
} }
int StreamString::peek() int StreamString::peek() {
{
if(length()) { if(length()) {
char c = charAt(0); char c = charAt(0);
return c; return c;
@ -65,7 +62,6 @@ int StreamString::peek()
return -1; return -1;
} }
void StreamString::flush() void StreamString::flush() {
{
} }

View File

@ -37,10 +37,23 @@ void randomSeed(unsigned long seed)
long random(long howbig) long random(long howbig)
{ {
if(howbig == 0) { uint32_t x = esp_random();
return 0; uint64_t m = uint64_t(x) * uint64_t(howbig);
uint32_t l = uint32_t(m);
if (l < howbig) {
uint32_t t = -howbig;
if (t >= howbig) {
t -= howbig;
if (t >= howbig)
t %= howbig;
}
while (l < t) {
x = esp_random();
m = uint64_t(x) * uint64_t(howbig);
l = uint32_t(m);
}
} }
return esp_random() % howbig; return m >> 32;
} }
long random(long howsmall, long howbig) long random(long howsmall, long howbig)
@ -52,9 +65,12 @@ long random(long howsmall, long howbig)
return random(diff) + howsmall; return random(diff) + howsmall;
} }
long map(long x, long in_min, long in_max, long out_min, long out_max) long map(long x, long in_min, long in_max, long out_min, long out_max) {
{ long divisor = (in_max - in_min);
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; if(divisor == 0){
return -1; //AVR returns -1, SAM returns 0
}
return (x - in_min) * (out_max - out_min) / divisor + out_min;
} }
unsigned int makeWord(unsigned int w) unsigned int makeWord(unsigned int w)

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,7 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <pgmspace.h> #include <pgmspace.h>
#include <stdint.h>
// An inherited class for holding the result of a concatenation. These // An inherited class for holding the result of a concatenation. These
// result objects are assumed to be writable by subsequent concatenations. // result objects are assumed to be writable by subsequent concatenations.
@ -35,295 +36,300 @@ class StringSumHelper;
// an abstract class used as a means to proide a unique pointer type // an abstract class used as a means to proide a unique pointer type
// but really has no body // but really has no body
class __FlashStringHelper; class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal))) #define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
// The string class // The string class
class String class String {
{ // use a function pointer to allow for "if (s)" without the
// use a function pointer to allow for "if (s)" without the // complications of an operator bool(). for more information, see:
// complications of an operator bool(). for more information, see: // http://www.artima.com/cppsource/safebool.html
// http://www.artima.com/cppsource/safebool.html typedef void (String::*StringIfHelperType)() const;
typedef void (String::*StringIfHelperType)() const; void StringIfHelper() const {
void StringIfHelper() const
{
}
public:
// constructors
// creates a copy of the initial value.
// if the initial value is null or invalid, or if memory allocation
// fails, the string will be marked as invalid (i.e. "if (s)" will
// be false).
String(const char *cstr = "");
String(const String &str);
String(const __FlashStringHelper *str) : String(reinterpret_cast<const char *>(str)) {};
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String(String &&rval);
String(StringSumHelper &&rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base = 10);
explicit String(int, unsigned char base = 10);
explicit String(unsigned int, unsigned char base = 10);
explicit String(long, unsigned char base = 10);
explicit String(unsigned long, unsigned char base = 10);
explicit String(float, unsigned char decimalPlaces = 2);
explicit String(double, unsigned char decimalPlaces = 2);
~String(void);
// memory management
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const
{
if(buffer) {
return len;
} else {
return 0;
} }
}
// creates a copy of the assigned value. if the value is null or public:
// invalid, or if the memory allocation fails, the string will be // constructors
// marked as invalid ("if (s)" will be false). // creates a copy of the initial value.
String & operator =(const String &rhs); // if the initial value is null or invalid, or if memory allocation
String & operator =(const char *cstr); // fails, the string will be marked as invalid (i.e. "if (s)" will
String & operator = (const __FlashStringHelper *str); // be false).
String(const char *cstr = "");
String(const String &str);
String(const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
String & operator =(String &&rval); String(String &&rval);
String & operator =(StringSumHelper &&rval); String(StringSumHelper &&rval);
#endif
explicit String(char c);
explicit String(unsigned char, unsigned char base = 10);
explicit String(int, unsigned char base = 10);
explicit String(unsigned int, unsigned char base = 10);
explicit String(long, unsigned char base = 10);
explicit String(unsigned long, unsigned char base = 10);
explicit String(float, unsigned char decimalPlaces = 2);
explicit String(double, unsigned char decimalPlaces = 2);
~String(void);
// memory management
// return true on success, false on failure (in which case, the string
// is left unchanged). reserve(0), if successful, will validate an
// invalid string (i.e., "if (s)" will be true afterwards)
unsigned char reserve(unsigned int size);
inline unsigned int length(void) const {
if(buffer()) {
return len();
} else {
return 0;
}
}
inline void clear(void) {
setLen(0);
}
inline bool isEmpty(void) const {
return length() == 0;
}
// creates a copy of the assigned value. if the value is null or
// invalid, or if the memory allocation fails, the string will be
// marked as invalid ("if (s)" will be false).
String & operator =(const String &rhs);
String & operator =(const char *cstr);
String & operator = (const __FlashStringHelper *str);
#ifdef __GXX_EXPERIMENTAL_CXX0X__
String & operator =(String &&rval);
String & operator =(StringSumHelper &&rval);
#endif #endif
// concatenate (works w/ built-in types) // concatenate (works w/ built-in types)
// returns true on success, false on failure (in which case, the string // returns true on success, false on failure (in which case, the string
// is left unchanged). if the argument is null or invalid, the // is left unchanged). if the argument is null or invalid, the
// concatenation is considered unsucessful. // concatenation is considered unsuccessful.
unsigned char concat(const String &str); unsigned char concat(const String &str);
unsigned char concat(const char *cstr); unsigned char concat(const char *cstr);
unsigned char concat(char c); unsigned char concat(char c);
unsigned char concat(unsigned char c); unsigned char concat(unsigned char c);
unsigned char concat(int num); unsigned char concat(int num);
unsigned char concat(unsigned int num); unsigned char concat(unsigned int num);
unsigned char concat(long num); unsigned char concat(long num);
unsigned char concat(unsigned long num); unsigned char concat(unsigned long num);
unsigned char concat(float num); unsigned char concat(float num);
unsigned char concat(double num); unsigned char concat(double num);
unsigned char concat(const __FlashStringHelper * str); unsigned char concat(const __FlashStringHelper * str);
// if there's not enough memory for the concatenated value, the string // if there's not enough memory for the concatenated value, the string
// will be left unchanged (but this isn't signalled in any way) // will be left unchanged (but this isn't signalled in any way)
String & operator +=(const String &rhs) String & operator +=(const String &rhs) {
{ concat(rhs);
concat(rhs); return (*this);
return (*this); }
} String & operator +=(const char *cstr) {
String & operator +=(const char *cstr) concat(cstr);
{ return (*this);
concat(cstr); }
return (*this); String & operator +=(char c) {
} concat(c);
String & operator +=(char c) return (*this);
{ }
concat(c); String & operator +=(unsigned char num) {
return (*this); concat(num);
} return (*this);
String & operator +=(unsigned char num) }
{ String & operator +=(int num) {
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(int num) String & operator +=(unsigned int num) {
{ concat(num);
concat(num); return (*this);
return (*this); }
} String & operator +=(long num) {
String & operator +=(unsigned int num) concat(num);
{ return (*this);
concat(num); }
return (*this); String & operator +=(unsigned long num) {
} concat(num);
String & operator +=(long num) return (*this);
{ }
concat(num); String & operator +=(float num) {
return (*this); concat(num);
} return (*this);
String & operator +=(unsigned long num) }
{ String & operator +=(double num) {
concat(num); concat(num);
return (*this); return (*this);
} }
String & operator +=(float num) String & operator += (const __FlashStringHelper *str){
{ concat(str);
concat(num); return (*this);
return (*this); }
}
String & operator +=(double num)
{
concat(num);
return (*this);
}
String & operator += (const __FlashStringHelper *str)
{
concat(str);
return (*this);
}
friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs); friend StringSumHelper & operator +(const StringSumHelper &lhs, const String &rhs);
friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr); friend StringSumHelper & operator +(const StringSumHelper &lhs, const char *cstr);
friend StringSumHelper & operator +(const StringSumHelper &lhs, char c); friend StringSumHelper & operator +(const StringSumHelper &lhs, char c);
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num); friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned char num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, int num); friend StringSumHelper & operator +(const StringSumHelper &lhs, int num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num); friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned int num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, long num); friend StringSumHelper & operator +(const StringSumHelper &lhs, long num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num); friend StringSumHelper & operator +(const StringSumHelper &lhs, unsigned long num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, float num); friend StringSumHelper & operator +(const StringSumHelper &lhs, float num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, double num); friend StringSumHelper & operator +(const StringSumHelper &lhs, double num);
friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs); friend StringSumHelper & operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs);
// comparison (only works w/ Strings and "strings") // comparison (only works w/ Strings and "strings")
operator StringIfHelperType() const operator StringIfHelperType() const {
{ return buffer() ? &String::StringIfHelper : 0;
return buffer ? &String::StringIfHelper : 0; }
} int compareTo(const String &s) const;
int compareTo(const String &s) const; unsigned char equals(const String &s) const;
unsigned char equals(const String &s) const; unsigned char equals(const char *cstr) const;
unsigned char equals(const char *cstr) const; unsigned char operator ==(const String &rhs) const {
unsigned char operator ==(const String &rhs) const return equals(rhs);
{ }
return equals(rhs); unsigned char operator ==(const char *cstr) const {
} return equals(cstr);
unsigned char operator ==(const char *cstr) const }
{ unsigned char operator !=(const String &rhs) const {
return equals(cstr); return !equals(rhs);
} }
unsigned char operator !=(const String &rhs) const unsigned char operator !=(const char *cstr) const {
{ return !equals(cstr);
return !equals(rhs); }
} unsigned char operator <(const String &rhs) const;
unsigned char operator !=(const char *cstr) const unsigned char operator >(const String &rhs) const;
{ unsigned char operator <=(const String &rhs) const;
return !equals(cstr); unsigned char operator >=(const String &rhs) const;
} unsigned char equalsIgnoreCase(const String &s) const;
unsigned char operator <(const String &rhs) const; unsigned char equalsConstantTime(const String &s) const;
unsigned char operator >(const String &rhs) const; unsigned char startsWith(const String &prefix) const;
unsigned char operator <=(const String &rhs) const; unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char operator >=(const String &rhs) const; unsigned char endsWith(const String &suffix) const;
unsigned char equalsIgnoreCase(const String &s) const;
unsigned char equalsConstantTime(const String &s) const;
unsigned char startsWith(const String &prefix) const;
unsigned char startsWith(const String &prefix, unsigned int offset) const;
unsigned char endsWith(const String &suffix) const;
// character acccess // character access
char charAt(unsigned int index) const; char charAt(unsigned int index) const;
void setCharAt(unsigned int index, char c); void setCharAt(unsigned int index, char c);
char operator [](unsigned int index) const; char operator [](unsigned int index) const;
char& operator [](unsigned int index); char& operator [](unsigned int index);
void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const; void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index = 0) const;
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
{ getBytes((unsigned char *) buf, bufsize, index);
getBytes((unsigned char *) buf, bufsize, index); }
} const char* c_str() const { return buffer(); }
const char * c_str() const char* begin() { return wbuffer(); }
{ char* end() { return wbuffer() + length(); }
return buffer; const char* begin() const { return c_str(); }
} const char* end() const { return c_str() + length(); }
// search // search
int indexOf(char ch) const; int indexOf(char ch) const;
int indexOf(char ch, unsigned int fromIndex) const; int indexOf(char ch, unsigned int fromIndex) const;
int indexOf(const String &str) const; int indexOf(const String &str) const;
int indexOf(const String &str, unsigned int fromIndex) const; int indexOf(const String &str, unsigned int fromIndex) const;
int lastIndexOf(char ch) const; int lastIndexOf(char ch) const;
int lastIndexOf(char ch, unsigned int fromIndex) const; int lastIndexOf(char ch, unsigned int fromIndex) const;
int lastIndexOf(const String &str) const; int lastIndexOf(const String &str) const;
int lastIndexOf(const String &str, unsigned int fromIndex) const; int lastIndexOf(const String &str, unsigned int fromIndex) const;
String substring(unsigned int beginIndex) const String substring(unsigned int beginIndex) const {
{ return substring(beginIndex, len());
return substring(beginIndex, len); }
} ;
; String substring(unsigned int beginIndex, unsigned int endIndex) const;
String substring(unsigned int beginIndex, unsigned int endIndex) const;
// modification // modification
void replace(char find, char replace); void replace(char find, char replace);
void replace(const String& find, const String& replace); void replace(const String& find, const String& replace);
void remove(unsigned int index); void remove(unsigned int index);
void remove(unsigned int index, unsigned int count); void remove(unsigned int index, unsigned int count);
void toLowerCase(void); void toLowerCase(void);
void toUpperCase(void); void toUpperCase(void);
void trim(void); void trim(void);
// parsing/conversion // parsing/conversion
long toInt(void) const; long toInt(void) const;
float toFloat(void) const; float toFloat(void) const;
double toDouble(void) const; double toDouble(void) const;
protected: protected:
char *buffer; // the actual char array // Contains the string info when we're not in SSO mode
unsigned int capacity; // the array length minus one (for the '\0') struct _ptr {
unsigned int len; // the String length (not counting the '\0') char * buff;
protected: uint16_t cap;
void init(void); uint16_t len;
void invalidate(void); };
unsigned char changeBuffer(unsigned int maxStrLen); // This allows strings up up to 11 (10 + \0 termination) without any extra space.
unsigned char concat(const char *cstr, unsigned int length); enum { SSOSIZE = sizeof(struct _ptr) + 4 - 1 }; // Characters to allocate space for SSO, must be 12 or more
struct _sso {
char buff[SSOSIZE];
unsigned char len : 7; // Ensure only one byte is allocated by GCC for the bitfields
unsigned char isSSO : 1;
} __attribute__((packed)); // Ensure that GCC doesn't expand the flag byte to a 32-bit word for alignment issues
enum { CAPACITY_MAX = 65535 }; // If typeof(cap) changed from uint16_t, be sure to update this enum to the max value storable in the type
union {
struct _ptr ptr;
struct _sso sso;
};
// Accessor functions
inline bool isSSO() const { return sso.isSSO; }
inline unsigned int len() const { return isSSO() ? sso.len : ptr.len; }
inline unsigned int capacity() const { return isSSO() ? (unsigned int)SSOSIZE - 1 : ptr.cap; } // Size of max string not including terminal NUL
inline void setSSO(bool set) { sso.isSSO = set; }
inline void setLen(int len) { if (isSSO()) sso.len = len; else ptr.len = len; }
inline void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; }
inline void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; }
// Buffer accessor functions
inline const char *buffer() const { return (const char *)(isSSO() ? sso.buff : ptr.buff); }
inline char *wbuffer() const { return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff; } // Writable version of buffer
// copy and move protected:
String & copy(const char *cstr, unsigned int length); void init(void);
String & copy(const __FlashStringHelper *pstr, unsigned int length); void invalidate(void);
unsigned char changeBuffer(unsigned int maxStrLen);
unsigned char concat(const char *cstr, unsigned int length);
// copy and move
String & copy(const char *cstr, unsigned int length);
String & copy(const __FlashStringHelper *pstr, unsigned int length);
#ifdef __GXX_EXPERIMENTAL_CXX0X__ #ifdef __GXX_EXPERIMENTAL_CXX0X__
void move(String &rhs); void move(String &rhs);
#endif #endif
}; };
class StringSumHelper: public String class StringSumHelper: public String {
{ public:
public: StringSumHelper(const String &s) :
StringSumHelper(const String &s) : String(s) {
String(s) }
{ StringSumHelper(const char *p) :
} String(p) {
StringSumHelper(const char *p) : }
String(p) StringSumHelper(char c) :
{ String(c) {
} }
StringSumHelper(char c) : StringSumHelper(unsigned char num) :
String(c) String(num) {
{ }
} StringSumHelper(int num) :
StringSumHelper(unsigned char num) : String(num) {
String(num) }
{ StringSumHelper(unsigned int num) :
} String(num) {
StringSumHelper(int num) : }
String(num) StringSumHelper(long num) :
{ String(num) {
} }
StringSumHelper(unsigned int num) : StringSumHelper(unsigned long num) :
String(num) String(num) {
{ }
} StringSumHelper(float num) :
StringSumHelper(long num) : String(num) {
String(num) }
{ StringSumHelper(double num) :
} String(num) {
StringSumHelper(unsigned long num) : }
String(num)
{
}
StringSumHelper(float num) :
String(num)
{
}
StringSumHelper(double num) :
String(num)
{
}
}; };
extern const String emptyString;
#endif // __cplusplus #endif // __cplusplus
#endif // String_class_h #endif // String_class_h

219
cores/esp32/esp32-hal-cpu.c Normal file
View File

@ -0,0 +1,219 @@
// 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 "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "freertos/xtensa_timer.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "rom/rtc.h"
#include "soc/apb_ctrl_reg.h"
#include "soc/efuse_reg.h"
#include "esp32-hal.h"
#include "esp32-hal-cpu.h"
typedef struct apb_change_cb_s {
struct apb_change_cb_s * next;
void * arg;
apb_change_cb_t cb;
} apb_change_t;
const uint32_t MHZ = 1000000;
static apb_change_t * apb_change_callbacks = NULL;
static xSemaphoreHandle apb_change_lock = NULL;
static void initApbChangeCallback(){
static volatile bool initialized = false;
if(!initialized){
initialized = true;
apb_change_lock = xSemaphoreCreateMutex();
if(!apb_change_lock){
initialized = false;
}
}
}
static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
initApbChangeCallback();
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
apb_change_t * r = apb_change_callbacks;
while(r != NULL){
r->cb(r->arg, ev_type, old_apb, new_apb);
r=r->next;
}
xSemaphoreGive(apb_change_lock);
}
bool addApbChangeCallback(void * arg, apb_change_cb_t cb){
initApbChangeCallback();
apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t));
if(!c){
log_e("Callback Object Malloc Failed");
return false;
}
c->next = NULL;
c->arg = arg;
c->cb = cb;
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
if(apb_change_callbacks == NULL){
apb_change_callbacks = c;
} else {
apb_change_t * r = apb_change_callbacks;
if(r->cb != cb || r->arg != arg){
while(r->next){
r = r->next;
if(r->cb == cb && r->arg == arg){
free(c);
goto unlock_and_exit;
}
}
r->next = c;
}
}
unlock_and_exit:
xSemaphoreGive(apb_change_lock);
return true;
}
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){
initApbChangeCallback();
xSemaphoreTake(apb_change_lock, portMAX_DELAY);
apb_change_t * r = apb_change_callbacks;
if(r == NULL){
xSemaphoreGive(apb_change_lock);
return false;
}
if(r->cb == cb && r->arg == arg){
apb_change_callbacks = r->next;
free(r);
} else {
while(r->next && (r->next->cb != cb || r->next->arg != arg)){
r = r->next;
}
if(r->next == NULL || r->next->cb != cb || r->next->arg != arg){
xSemaphoreGive(apb_change_lock);
return false;
}
apb_change_t * c = r->next;
r->next = c->next;
free(c);
}
xSemaphoreGive(apb_change_lock);
return true;
}
static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){
if(conf->freq_mhz >= 80){
return 80 * MHZ;
}
return (conf->source_freq_mhz * MHZ) / conf->div;
}
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz){
rtc_cpu_freq_config_t conf, cconf;
uint32_t capb, apb;
//Get XTAL Frequency and calculate min CPU MHz
rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get();
if(xtal > RTC_XTAL_FREQ_AUTO){
if(xtal < RTC_XTAL_FREQ_40M) {
if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2)){
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
return false;
}
} else if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2) && cpu_freq_mhz != (xtal/4)){
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
return false;
}
}
if(cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 80){
if(xtal >= RTC_XTAL_FREQ_40M){
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4);
} else {
log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2);
}
return false;
}
//check if cpu supports the frequency
if(cpu_freq_mhz == 240){
//Check if ESP32 is rated for a CPU frequency of 160MHz only
if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) &&
REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) {
log_e("Can not switch to 240 MHz! Chip CPU frequency rated for 160MHz.");
cpu_freq_mhz = 160;
}
}
//Get current CPU clock configuration
rtc_clk_cpu_freq_get_config(&cconf);
//return if frequency has not changed
if(cconf.freq_mhz == cpu_freq_mhz){
return true;
}
//Get configuration for the new CPU frequency
if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){
log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz);
return false;
}
//Current APB
capb = calculateApb(&cconf);
//New APB
apb = calculateApb(&conf);
log_d("%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), conf.source_freq_mhz, conf.div, conf.freq_mhz, apb);
//Call peripheral functions before the APB change
if(apb_change_callbacks){
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb);
}
//Make the frequency change
rtc_clk_cpu_freq_set_config_fast(&conf);
if(capb != apb){
//Update REF_TICK (uncomment if REF_TICK is different than 1MHz)
//if(conf.freq_mhz < 80){
// ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1;
//}
//Update APB Freq REG
rtc_clk_apb_freq_update(apb);
//Update esp_timer divisor
esp_timer_impl_update_apb_freq(apb / MHZ);
}
//Update FreeRTOS Tick Divisor
uint32_t fcpu = (conf.freq_mhz >= 80)?(conf.freq_mhz * MHZ):(apb);
_xt_tick_divisor = fcpu / XT_TICK_PER_SEC;
//Call peripheral functions after the APB change
if(apb_change_callbacks){
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb);
}
return true;
}
uint32_t getCpuFrequencyMhz(){
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
return conf.freq_mhz;
}
uint32_t getXtalFrequencyMhz(){
return rtc_clk_xtal_freq_get();
}
uint32_t getApbFrequency(){
rtc_cpu_freq_config_t conf;
rtc_clk_cpu_freq_get_config(&conf);
return calculateApb(&conf);
}

View File

@ -0,0 +1,48 @@
// 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 _ESP32_HAL_CPU_H_
#define _ESP32_HAL_CPU_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t;
typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
bool addApbChangeCallback(void * arg, apb_change_cb_t cb);
bool removeApbChangeCallback(void * arg, apb_change_cb_t cb);
//function takes the following frequencies as valid values:
// 240, 160, 80 <<< For all XTAL types
// 40, 20, 10 <<< For 40MHz XTAL
// 26, 13 <<< For 26MHz XTAL
// 24, 12 <<< For 24MHz XTAL
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz);
uint32_t getCpuFrequencyMhz(); // In MHz
uint32_t getXtalFrequencyMhz(); // In MHz
uint32_t getApbFrequency(); // In Hz
#ifdef __cplusplus
}
#endif
#endif /* _ESP32_HAL_CPU_H_ */

View File

@ -287,7 +287,7 @@ extern void __detachInterrupt(uint8_t pin)
} }
__pinInterruptHandlers[pin].fn = NULL; __pinInterruptHandlers[pin].fn = NULL;
__pinInterruptHandlers[pin].arg = NULL; __pinInterruptHandlers[pin].arg = NULL;
__pinInterruptHandlers[pin].arg = false; __pinInterruptHandlers[pin].functional = false;
GPIO.pin[pin].int_ena = 0; GPIO.pin[pin].int_ena = 0;
GPIO.pin[pin].int_type = 0; GPIO.pin[pin].int_type = 0;

View File

@ -24,7 +24,7 @@
#include "soc/i2c_struct.h" #include "soc/i2c_struct.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "esp32-hal-cpu.h" // cpu clock change support 31DEC2018
//#define I2C_DEV(i) (volatile i2c_dev_t *)((i)?DR_REG_I2C1_EXT_BASE:DR_REG_I2C_EXT_BASE) //#define I2C_DEV(i) (volatile i2c_dev_t *)((i)?DR_REG_I2C1_EXT_BASE:DR_REG_I2C_EXT_BASE)
//#define I2C_DEV(i) ((i2c_dev_t *)(REG_I2C_BASE(i))) //#define I2C_DEV(i) ((i2c_dev_t *)(REG_I2C_BASE(i)))
#define I2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0)) #define I2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0))
@ -206,8 +206,8 @@ static i2c_t _i2c_bus_array[2] = {
{(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} {(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 #else
#define I2C_MUTEX_LOCK() do {} while (xSemaphoreTake(i2c->lock, portMAX_DELAY) != pdPASS) #define I2C_MUTEX_LOCK() do {} while (xSemaphoreTakeRecursive(i2c->lock, portMAX_DELAY) != pdPASS)
#define I2C_MUTEX_UNLOCK() xSemaphoreGive(i2c->lock) #define I2C_MUTEX_UNLOCK() xSemaphoreGiveRecursive(i2c->lock)
static i2c_t _i2c_bus_array[2] = { 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,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},
@ -248,9 +248,10 @@ static void IRAM_ATTR i2cDumpCmdQueue(i2c_t *i2c)
/* Stickbreaker ISR mode debug support /* Stickbreaker ISR mode debug support
*/ */
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
static void i2cDumpDqData(i2c_t * i2c) static void i2cDumpDqData(i2c_t * i2c)
{ {
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) #if defined (ENABLE_I2C_DEBUG_BUFFER)
uint16_t a=0; uint16_t a=0;
char buff[140]; char buff[140];
I2C_DATA_QUEUE_t *tdq; I2C_DATA_QUEUE_t *tdq;
@ -306,9 +307,12 @@ static void i2cDumpDqData(i2c_t * i2c)
} }
a++; a++;
} }
#else
log_i("Debug Buffer not Enabled");
#endif #endif
} }
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
static void i2cDumpI2c(i2c_t * i2c) static void i2cDumpI2c(i2c_t * i2c)
{ {
log_e("i2c=%p",i2c); log_e("i2c=%p",i2c);
@ -332,11 +336,12 @@ static void i2cDumpI2c(i2c_t * i2c)
i2cDumpDqData(i2c); i2cDumpDqData(i2c);
} }
} }
#endif
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
static void i2cDumpInts(uint8_t num) static void i2cDumpInts(uint8_t num)
{ {
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO) && (defined ENABLE_I2C_DEBUG_BUFFER) #if defined (ENABLE_I2C_DEBUG_BUFFER)
uint32_t b; uint32_t b;
log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num); log_i("%u row\tcount\tINTR\tTX\tRX\tTick ",num);
for(uint32_t a=1; a<=INTBUFFMAX; a++) { for(uint32_t a=1; a<=INTBUFFMAX; a++) {
@ -349,9 +354,10 @@ static void i2cDumpInts(uint8_t num)
log_i("Debug Buffer not Enabled"); log_i("Debug Buffer not Enabled");
#endif #endif
} }
#endif
static void IRAM_ATTR i2cDumpStatus(i2c_t * i2c){
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
static void IRAM_ATTR i2cDumpStatus(i2c_t * i2c){
typedef union { typedef union {
struct { struct {
uint32_t ack_rec: 1; /*This register stores the value of ACK bit.*/ uint32_t ack_rec: 1; /*This register stores the value of ACK bit.*/
@ -377,11 +383,11 @@ static void IRAM_ATTR i2cDumpStatus(i2c_t * i2c){
sr.val= i2c->dev->status_reg.val; 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); 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
} }
#endif
static void i2cDumpFifo(i2c_t * i2c){
#if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER) #if (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
static void i2cDumpFifo(i2c_t * i2c){
char buf[64]; char buf[64];
uint16_t k = 0; uint16_t k = 0;
uint16_t i = fifoPos+1; uint16_t i = fifoPos+1;
@ -422,8 +428,8 @@ if(i != fifoPos){// actual data
} }
}while( i!= fifoPos); }while( i!= fifoPos);
} }
#endif
} }
#endif
static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char locus[]){ 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 (ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)&&(defined ENABLE_I2C_DEBUG_BUFFER)
@ -439,6 +445,39 @@ static void IRAM_ATTR i2cTriggerDumps(i2c_t * i2c, uint8_t trigger, const char l
} }
// end of debug support routines // end of debug support routines
/* Start of CPU Clock change Support
*/
static void i2cApbChangeCallback(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
i2c_t* i2c = (i2c_t*) arg; // recover data
if(i2c == NULL) { // point to peripheral control block does not exits
return;
}
uint32_t oldFreq=0;
switch(ev_type){
case APB_BEFORE_CHANGE :
if(new_apb < 3000000) {// too slow
log_e("apb speed %d too slow",new_apb);
break;
}
I2C_MUTEX_LOCK(); // lock will spin until current transaction is completed
break;
case APB_AFTER_CHANGE :
oldFreq = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); //read old apbCycles
if(oldFreq>0) { // was configured with value
oldFreq = old_apb / oldFreq;
i2cSetFrequency(i2c,oldFreq);
}
I2C_MUTEX_UNLOCK();
break;
default :
log_e("unk ev %u",ev_type);
I2C_MUTEX_UNLOCK();
}
return;
}
/* End of CPU Clock change Support
*/
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) 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; I2C_COMMAND_t cmd;
@ -1142,7 +1181,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
if(tdq->ctrl.addrReq ==2) { // 10bit address if(tdq->ctrl.addrReq ==2) { // 10bit address
taddr =((tdq->ctrl.addr >> 7) & 0xFE) taddr =((tdq->ctrl.addr >> 7) & 0xFE)
|tdq->ctrl.mode; |tdq->ctrl.mode;
taddr = (taddr <<8) || (tdq->ctrl.addr&0xFF); taddr = (taddr <<8) | (tdq->ctrl.addr&0xFF);
} else { // 7bit address } else { // 7bit address
taddr = ((tdq->ctrl.addr<<1)&0xFE) taddr = ((tdq->ctrl.addr<<1)&0xFE)
|tdq->ctrl.mode; |tdq->ctrl.mode;
@ -1215,6 +1254,12 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
return I2C_ERROR_MEMORY; return I2C_ERROR_MEMORY;
} }
if( !addApbChangeCallback( i2c, i2cApbChangeCallback)) {
log_e("install apb Callback failed");
I2C_MUTEX_UNLOCK();
return I2C_ERROR_DEV;
}
} }
//hang until it completes. //hang until it completes.
@ -1225,7 +1270,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
i2c->dev->ctr.trans_start=1; // go for it i2c->dev->ctr.trans_start=1; // go for it
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
portTickType tBefore=xTaskGetTickCount(); portTickType tBefore=xTaskGetTickCount();
#endif #endif
@ -1233,7 +1278,7 @@ i2c_err_t i2cProcQueue(i2c_t * i2c, uint32_t *readCount, uint16_t timeOutMillis)
uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdFALSE,pdTRUE,ticksTimeOut); uint32_t eBits = xEventGroupWaitBits(i2c->i2c_event,EVENT_DONE,pdFALSE,pdTRUE,ticksTimeOut);
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
portTickType tAfter=xTaskGetTickCount(); portTickType tAfter=xTaskGetTickCount();
#endif #endif
@ -1346,6 +1391,9 @@ static void i2cReleaseISR(i2c_t * i2c)
if(i2c->intr_handle) { if(i2c->intr_handle) {
esp_intr_free(i2c->intr_handle); esp_intr_free(i2c->intr_handle);
i2c->intr_handle=NULL; i2c->intr_handle=NULL;
if (!removeApbChangeCallback( i2c, i2cApbChangeCallback)) {
log_e("unable to release apbCallback");
}
} }
} }
@ -1431,15 +1479,17 @@ i2c_err_t i2cDetachSDA(i2c_t * i2c, int8_t sda)
* PUBLIC API * PUBLIC API
* */ * */
// 24Nov17 only supports Master Mode // 24Nov17 only supports Master Mode
i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //before this is called, pins should be detached, else glitch i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) {
{ #ifdef ENABLE_I2C_DEBUG_BUFFER
log_v("num=%d sda=%d scl=%d freq=%d",i2c_num, sda, scl, frequency); log_v("num=%d sda=%d scl=%d freq=%d",i2c_num, sda, scl, frequency);
#endif
if(i2c_num > 1) { if(i2c_num > 1) {
return NULL; return NULL;
} }
i2c_t * i2c = &_i2c_bus_array[i2c_num]; i2c_t * i2c = &_i2c_bus_array[i2c_num];
// pins should be detached, else glitch
if(i2c->sda >= 0){ if(i2c->sda >= 0){
i2cDetachSDA(i2c, i2c->sda); i2cDetachSDA(i2c, i2c->sda);
} }
@ -1451,7 +1501,7 @@ i2c_t * i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) //b
#if !CONFIG_DISABLE_HAL_LOCKS #if !CONFIG_DISABLE_HAL_LOCKS
if(i2c->lock == NULL) { if(i2c->lock == NULL) {
i2c->lock = xSemaphoreCreateMutex(); i2c->lock = xSemaphoreCreateRecursiveMutex();
if(i2c->lock == NULL) { if(i2c->lock == NULL) {
return NULL; return NULL;
} }
@ -1561,6 +1611,9 @@ i2c_err_t i2cFlush(i2c_t * i2c)
} }
i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis){ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis){
if((i2c==NULL)||((size>0)&&(buff==NULL))) { // need to have location to store requested data
return I2C_ERROR_DEV;
}
i2c_err_t last_error = i2cAddQueueWrite(i2c, address, buff, size, sendStop, NULL); i2c_err_t last_error = i2cAddQueueWrite(i2c, address, buff, size, sendStop, NULL);
if(last_error == I2C_ERROR_OK) { //queued if(last_error == I2C_ERROR_OK) { //queued
@ -1580,6 +1633,9 @@ i2c_err_t i2cWrite(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size,
} }
i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount){ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, bool sendStop, uint16_t timeOutMillis, uint32_t *readCount){
if((size == 0)||(i2c == NULL)||(buff==NULL)){ // hardware will hang if no data requested on READ
return I2C_ERROR_DEV;
}
i2c_err_t last_error=i2cAddQueueRead(i2c, address, buff, size, sendStop, NULL); i2c_err_t last_error=i2cAddQueueRead(i2c, address, buff, size, sendStop, NULL);
if(last_error == I2C_ERROR_OK) { //queued if(last_error == I2C_ERROR_OK) { //queued
@ -1598,28 +1654,55 @@ i2c_err_t i2cRead(i2c_t * i2c, uint16_t address, uint8_t* buff, uint16_t size, b
return last_error; return last_error;
} }
#define MIN_I2C_CLKS 100 // minimum ratio between cpu and i2c Bus clocks
#define INTERRUPT_CYCLE_OVERHEAD 16000 // number of cpu clocks necessary to respond to interrupt
i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed) i2c_err_t i2cSetFrequency(i2c_t * i2c, uint32_t clk_speed)
{ {
if(i2c == NULL) { if(i2c == NULL) {
return I2C_ERROR_DEV; return I2C_ERROR_DEV;
} }
I2C_FIFO_CONF_t f; uint32_t apb = getApbFrequency();
uint32_t period = (apb/clk_speed) / 2;
uint32_t period = (APB_CLK_FREQ/clk_speed) / 2;
if((apb/8192 > clk_speed)||(apb/MIN_I2C_CLKS < clk_speed)){ //out of bounds
log_d("i2c freq(%d) out of bounds.vs APB Clock(%d), min=%d, max=%d",clk_speed,apb,(apb/8192),(apb/MIN_I2C_CLKS));
}
if(period < (MIN_I2C_CLKS/2) ){
period = (MIN_I2C_CLKS/2);
clk_speed = apb/(period*2);
log_d("APB Freq too slow, Reducing i2c Freq to %d Hz",clk_speed);
} else if ( period> 4095) {
period = 4095;
clk_speed = apb/(period*2);
log_d("APB Freq too fast, Increasing i2c Freq to %d Hz",clk_speed);
}
#ifdef ENABLE_I2C_DEBUG_BUFFER
log_v("freq=%dHz",clk_speed);
#endif
uint32_t halfPeriod = period/2; uint32_t halfPeriod = period/2;
uint32_t quarterPeriod = period/4; uint32_t quarterPeriod = period/4;
I2C_MUTEX_LOCK(); I2C_MUTEX_LOCK();
// Adjust Fifo thresholds based on frequency I2C_FIFO_CONF_t f;
f.val = i2c->dev->fifo_conf.val;
uint32_t a = (clk_speed / 50000L )+1;
if (a > 24) a=24;
f.rx_fifo_full_thrhd = 32 - a;
f.tx_fifo_empty_thrhd = a;
i2c->dev->fifo_conf.val = f.val; // set thresholds
log_v("Fifo threshold=%d",a);
f.val = i2c->dev->fifo_conf.val;
/* Adjust Fifo thresholds based on differential between cpu frequency and bus clock.
The fifo_delta is calculated such that at least INTERRUPT_CYCLE_OVERHEAD cpu clocks are
available when a Fifo interrupt is triggered. This allows enough room in the Fifo so that
interrupt latency does not cause a Fifo overflow/underflow event.
*/
#ifdef ENABLE_I2C_DEBUG_BUFFER
log_v("cpu Freq=%dMhz, i2c Freq=%dHz",getCpuFrequencyMhz(),clk_speed);
#endif
uint32_t fifo_delta = (INTERRUPT_CYCLE_OVERHEAD/((getCpuFrequencyMhz()*1000000 / clk_speed)*10))+1;
if (fifo_delta > 24) fifo_delta=24;
f.rx_fifo_full_thrhd = 32 - fifo_delta;
f.tx_fifo_empty_thrhd = fifo_delta;
i2c->dev->fifo_conf.val = f.val; // set thresholds
#ifdef ENABLE_I2C_DEBUG_BUFFER
log_v("Fifo delta=%d",fifo_delta);
#endif
//the clock num during SCL is low level //the clock num during SCL is low level
i2c->dev->scl_low_period.period = period; i2c->dev->scl_low_period.period = period;
//the clock num during SCL is high level //the clock num during SCL is high level
@ -1651,7 +1734,7 @@ uint32_t i2cGetFrequency(i2c_t * i2c)
uint32_t result = 0; uint32_t result = 0;
uint32_t old_count = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period); uint32_t old_count = (i2c->dev->scl_low_period.period+i2c->dev->scl_high_period.period);
if(old_count>0) { if(old_count>0) {
result = APB_CLK_FREQ / old_count; result = getApbFrequency() / old_count;
} else { } else {
result = 0; result = 0;
} }
@ -1674,6 +1757,8 @@ uint32_t i2cGetStatus(i2c_t * i2c){
} }
else return 0; else return 0;
} }
/* todo /* todo
22JUL18 22JUL18
need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads need to add multi-thread capability, use dq.queueEvent as the group marker. When multiple threads

View File

@ -53,6 +53,33 @@ xSemaphoreHandle _ledc_sys_lock;
#define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)] #define LEDC_CHAN(g,c) LEDC.channel_group[(g)].channel[(c)]
#define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)] #define LEDC_TIMER(g,t) LEDC.timer_group[(g)].timer[(t)]
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
if(ev_type == APB_AFTER_CHANGE && old_apb != new_apb){
uint32_t iarg = (uint32_t)arg;
uint8_t chan = iarg;
uint8_t group=(chan/8), timer=((chan/2)%4);
old_apb /= 1000000;
new_apb /= 1000000;
if(LEDC_TIMER(group, timer).conf.tick_sel){
LEDC_MUTEX_LOCK();
uint32_t old_div = LEDC_TIMER(group, timer).conf.clock_divider;
uint32_t div_num = (new_apb * old_div) / old_apb;
if(div_num > LEDC_DIV_NUM_HSTIMER0_V){
new_apb = REF_CLK_FREQ / 1000000;
div_num = (new_apb * old_div) / old_apb;
if(div_num > LEDC_DIV_NUM_HSTIMER0_V) {
div_num = LEDC_DIV_NUM_HSTIMER0_V;//lowest clock possible
}
LEDC_TIMER(group, timer).conf.tick_sel = 0;
} else if(div_num < 256) {
div_num = 256;//highest clock possible
}
LEDC_TIMER(group, timer).conf.clock_divider = div_num;
LEDC_MUTEX_UNLOCK();
}
}
}
//uint32_t frequency = (80MHz or 1MHz)/((div_num / 256.0)*(1 << bit_num)); //uint32_t frequency = (80MHz or 1MHz)/((div_num / 256.0)*(1 << bit_num));
static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, bool apb_clk) static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, bool apb_clk)
{ {
@ -78,13 +105,15 @@ static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, boo
LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset. LEDC_TIMER(group, timer).conf.rst = 1;//This bit is used to reset timer the counter will be 0 after reset.
LEDC_TIMER(group, timer).conf.rst = 0; LEDC_TIMER(group, timer).conf.rst = 0;
LEDC_MUTEX_UNLOCK(); LEDC_MUTEX_UNLOCK();
uint32_t iarg = chan;
addApbChangeCallback((void*)iarg, _on_apb_change);
} }
//max div_num 0x3FFFF (262143) //max div_num 0x3FFFF (262143)
//max bit_num 0x1F (31) //max bit_num 0x1F (31)
static double _ledcSetupTimerFreq(uint8_t chan, double freq, uint8_t bit_num) static double _ledcSetupTimerFreq(uint8_t chan, double freq, uint8_t bit_num)
{ {
uint64_t clk_freq = APB_CLK_FREQ; uint64_t clk_freq = getApbFrequency();
clk_freq <<= 8;//div_num is 8 bit decimal clk_freq <<= 8;//div_num is 8 bit decimal
uint32_t div_num = (clk_freq >> bit_num) / freq; uint32_t div_num = (clk_freq >> bit_num) / freq;
bool apb_clk = true; bool apb_clk = true;
@ -117,7 +146,7 @@ static double _ledcTimerRead(uint8_t chan)
LEDC_MUTEX_UNLOCK(); LEDC_MUTEX_UNLOCK();
uint64_t clk_freq = 1000000; uint64_t clk_freq = 1000000;
if(apb_clk) { if(apb_clk) {
clk_freq *= 80; clk_freq = getApbFrequency();
} }
clk_freq <<= 8;//div_num is 8 bit decimal clk_freq <<= 8;//div_num is 8 bit decimal
return (clk_freq >> bit_num) / (double)div_num; return (clk_freq >> bit_num) / (double)div_num;
@ -138,7 +167,7 @@ static void _ledcSetupChannel(uint8_t chan, uint8_t idle_level)
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware. LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
if(group) { if(group) {
LEDC_CHAN(group, channel).conf0.val &= ~BIT(4); LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
} else { } else {
LEDC_CHAN(group, channel).conf0.clk_en = 0; LEDC_CHAN(group, channel).conf0.clk_en = 0;
} }
@ -167,7 +196,7 @@ void ledcWrite(uint8_t chan, uint32_t duty)
LEDC_CHAN(group, channel).conf0.sig_out_en = 1;//This is the output enable control bit for channel LEDC_CHAN(group, channel).conf0.sig_out_en = 1;//This is the output enable control bit for channel
LEDC_CHAN(group, channel).conf1.duty_start = 1;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware. LEDC_CHAN(group, channel).conf1.duty_start = 1;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
if(group) { if(group) {
LEDC_CHAN(group, channel).conf0.val |= BIT(4); LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
} else { } else {
LEDC_CHAN(group, channel).conf0.clk_en = 1; LEDC_CHAN(group, channel).conf0.clk_en = 1;
} }
@ -175,7 +204,7 @@ void ledcWrite(uint8_t chan, uint32_t duty)
LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel LEDC_CHAN(group, channel).conf0.sig_out_en = 0;//This is the output enable control bit for channel
LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware. LEDC_CHAN(group, channel).conf1.duty_start = 0;//When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware.
if(group) { if(group) {
LEDC_CHAN(group, channel).conf0.val &= ~BIT(4); LEDC_CHAN(group, channel).conf0.low_speed_update = 1;
} else { } else {
LEDC_CHAN(group, channel).conf0.clk_en = 0; LEDC_CHAN(group, channel).conf0.clk_en = 0;
} }

View File

@ -21,8 +21,18 @@
#include "esp_partition.h" #include "esp_partition.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_timer.h" #include "esp_timer.h"
#ifdef CONFIG_APP_ROLLBACK_ENABLE
#include "esp_ota_ops.h"
#endif //CONFIG_APP_ROLLBACK_ENABLE
#ifdef CONFIG_BT_ENABLED
#include "esp_bt.h" #include "esp_bt.h"
#endif //CONFIG_BT_ENABLED
#include <sys/time.h> #include <sys/time.h>
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/apb_ctrl_reg.h"
#include "rom/rtc.h"
#include "esp_task_wdt.h"
#include "esp32-hal.h" #include "esp32-hal.h"
//Undocumented!!! Get chip temperature in Farenheit //Undocumented!!! Get chip temperature in Farenheit
@ -39,14 +49,94 @@ void yield()
vPortYield(); vPortYield();
} }
#if CONFIG_AUTOSTART_ARDUINO
extern TaskHandle_t loopTaskHandle;
extern bool loopTaskWDTEnabled;
void enableLoopWDT(){
if(loopTaskHandle != NULL){
if(esp_task_wdt_add(loopTaskHandle) != ESP_OK){
log_e("Failed to add loop task to WDT");
} else {
loopTaskWDTEnabled = true;
}
}
}
void disableLoopWDT(){
if(loopTaskHandle != NULL && loopTaskWDTEnabled){
loopTaskWDTEnabled = false;
if(esp_task_wdt_delete(loopTaskHandle) != ESP_OK){
log_e("Failed to remove loop task from WDT");
}
}
}
void feedLoopWDT(){
esp_err_t err = esp_task_wdt_reset();
if(err != ESP_OK){
log_e("Failed to feed WDT! Error: %d", err);
}
}
#endif
void enableCore0WDT(){
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
if(idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK){
log_e("Failed to add Core 0 IDLE task to WDT");
}
}
void disableCore0WDT(){
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0);
if(idle_0 == NULL || esp_task_wdt_delete(idle_0) != ESP_OK){
log_e("Failed to remove Core 0 IDLE task from WDT");
}
}
#ifndef CONFIG_FREERTOS_UNICORE
void enableCore1WDT(){
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
if(idle_1 == NULL || esp_task_wdt_add(idle_1) != ESP_OK){
log_e("Failed to add Core 1 IDLE task to WDT");
}
}
void disableCore1WDT(){
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1);
if(idle_1 == NULL || esp_task_wdt_delete(idle_1) != ESP_OK){
log_e("Failed to remove Core 1 IDLE task from WDT");
}
}
#endif
BaseType_t xTaskCreateUniversal( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
const BaseType_t xCoreID ){
#ifndef CONFIG_FREERTOS_UNICORE
if(xCoreID >= 0 && xCoreID < 2) {
return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID);
} else {
#endif
return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask);
#ifndef CONFIG_FREERTOS_UNICORE
}
#endif
}
unsigned long IRAM_ATTR micros() unsigned long IRAM_ATTR micros()
{ {
return (unsigned long) esp_timer_get_time(); return (unsigned long) (esp_timer_get_time());
} }
unsigned long IRAM_ATTR millis() unsigned long IRAM_ATTR millis()
{ {
return (unsigned long) (esp_timer_get_time() / 1000); return (unsigned long) (esp_timer_get_time() / 1000ULL);
} }
void delay(uint32_t ms) void delay(uint32_t ms)
@ -76,6 +166,9 @@ void initVariant() {}
void init() __attribute__((weak)); void init() __attribute__((weak));
void init() {} void init() {}
bool verifyOta() __attribute__((weak));
bool verifyOta() { return true; }
#ifdef CONFIG_BT_ENABLED #ifdef CONFIG_BT_ENABLED
//overwritten in esp32-hal-bt.c //overwritten in esp32-hal-bt.c
bool btInUse() __attribute__((weak)); bool btInUse() __attribute__((weak));
@ -84,6 +177,25 @@ bool btInUse(){ return false; }
void initArduino() void initArduino()
{ {
#ifdef CONFIG_APP_ROLLBACK_ENABLE
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
if (verifyOta()) {
esp_ota_mark_app_valid_cancel_rollback();
} else {
log_e("OTA verification failed! Start rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
#endif
//init proper ref tick value for PLL (uncomment if REF_TICK is different than 1MHz)
//ESP_REG(APB_CTRL_PLL_TICK_CONF_REG) = APB_CLK_FREQ / REF_CLK_FREQ - 1;
#ifdef F_CPU
setCpuFrequencyMhz(F_CPU/1000000);
#endif
#if CONFIG_SPIRAM_SUPPORT #if CONFIG_SPIRAM_SUPPORT
psramInit(); psramInit();
#endif #endif

View File

@ -19,7 +19,7 @@ bool psramInit(){
} }
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
uint32_t pkg_ver = chip_ver & 0x7; uint32_t pkg_ver = chip_ver & 0x7;
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
spiramFailed = true; spiramFailed = true;
log_w("PSRAM not supported!"); log_w("PSRAM not supported!");
return false; return false;

View File

@ -156,7 +156,7 @@ bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t
RMT_MUTEX_LOCK(channel); RMT_MUTEX_LOCK(channel);
RMT.carrier_duty_ch[channel].low = low; RMT.carrier_duty_ch[channel].low = low;
RMT.carrier_duty_ch[channel].low = high; RMT.carrier_duty_ch[channel].high = high;
RMT.conf_ch[channel].conf0.carrier_en = carrier_en; RMT.conf_ch[channel].conf0.carrier_en = carrier_en;
RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level;
@ -637,13 +637,13 @@ static void IRAM_ATTR _rmt_isr(void* arg)
data += MAX_DATA_PER_CHANNEL*(g_rmt_objects[ch].buffers); data += MAX_DATA_PER_CHANNEL*(g_rmt_objects[ch].buffers);
} }
} }
uint32_t *data_received = data;
for (i = 0; i < g_rmt_objects[ch].data_size; i++ ) { for (i = 0; i < g_rmt_objects[ch].data_size; i++ ) {
*data++ = RMTMEM.chan[ch].data32[i].val; *data++ = RMTMEM.chan[ch].data32[i].val;
} }
if (g_rmt_objects[ch].cb) { if (g_rmt_objects[ch].cb) {
// actually received data ptr // actually received data ptr
uint32_t * data = g_rmt_objects[ch].data_ptr; (g_rmt_objects[ch].cb)(data_received, _rmt_get_mem_len(ch));
(g_rmt_objects[ch].cb)(data, _rmt_get_mem_len(ch));
// restart the reception // restart the reception
RMT.conf_ch[ch].conf1.mem_owner = 1; RMT.conf_ch[ch].conf1.mem_owner = 1;
@ -665,7 +665,6 @@ static void IRAM_ATTR _rmt_isr(void* arg)
} }
if (intr_val & _INT_ERROR(ch)) { if (intr_val & _INT_ERROR(ch)) {
digitalWrite(2, 1);
// clear the flag // clear the flag
RMT.int_clr.val = _INT_ERROR(ch); RMT.int_clr.val = _INT_ERROR(ch);
RMT.int_ena.val &= ~_INT_ERROR(ch); RMT.int_ena.val &= ~_INT_ERROR(ch);

View File

@ -122,6 +122,10 @@ bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t
*/ */
bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level); bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level);
/**
* Deinitialize the driver
*/
bool rmtDeinit(rmt_obj_t *rmt);
// TODO: // TODO:
// * uninstall interrupt when all channels are deinit // * uninstall interrupt when all channels are deinit

View File

@ -31,6 +31,26 @@
xSemaphoreHandle _sd_sys_lock; xSemaphoreHandle _sd_sys_lock;
#endif #endif
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
if(old_apb == new_apb){
return;
}
uint32_t iarg = (uint32_t)arg;
uint8_t channel = iarg;
if(ev_type == APB_BEFORE_CHANGE){
SIGMADELTA.cg.clk_en = 0;
} else {
old_apb /= 1000000;
new_apb /= 1000000;
SD_MUTEX_LOCK();
uint32_t old_prescale = SIGMADELTA.channel[channel].prescale + 1;
SIGMADELTA.channel[channel].prescale = ((new_apb * old_prescale) / old_apb) - 1;
SIGMADELTA.cg.clk_en = 0;
SIGMADELTA.cg.clk_en = 1;
SD_MUTEX_UNLOCK();
}
}
uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-312500 uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-312500
{ {
if(channel > 7) { if(channel > 7) {
@ -43,7 +63,8 @@ uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-31
_sd_sys_lock = xSemaphoreCreateMutex(); _sd_sys_lock = xSemaphoreCreateMutex();
} }
#endif #endif
uint32_t prescale = (10000000/(freq*32)) - 1; uint32_t apb_freq = getApbFrequency();
uint32_t prescale = (apb_freq/(freq*256)) - 1;
if(prescale > 0xFF) { if(prescale > 0xFF) {
prescale = 0xFF; prescale = 0xFF;
} }
@ -52,7 +73,9 @@ uint32_t sigmaDeltaSetup(uint8_t channel, uint32_t freq) //chan 0-7 freq 1220-31
SIGMADELTA.cg.clk_en = 0; SIGMADELTA.cg.clk_en = 0;
SIGMADELTA.cg.clk_en = 1; SIGMADELTA.cg.clk_en = 1;
SD_MUTEX_UNLOCK(); SD_MUTEX_UNLOCK();
return 10000000/((prescale + 1) * 32); uint32_t iarg = channel;
addApbChangeCallback((void*)iarg, _on_apb_change);
return apb_freq/((prescale + 1) * 256);
} }
void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-7 duty 8 bit void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-7 duty 8 bit

View File

@ -26,6 +26,7 @@
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
#include "soc/rtc.h"
#define SPI_CLK_IDX(p) ((p==0)?SPICLK_OUT_IDX:((p==1)?SPICLK_OUT_IDX:((p==2)?HSPICLK_OUT_IDX:((p==3)?VSPICLK_OUT_IDX:0)))) #define SPI_CLK_IDX(p) ((p==0)?SPICLK_OUT_IDX:((p==1)?SPICLK_OUT_IDX:((p==2)?HSPICLK_OUT_IDX:((p==3)?VSPICLK_OUT_IDX:0))))
#define SPI_MISO_IDX(p) ((p==0)?SPIQ_OUT_IDX:((p==1)?SPIQ_OUT_IDX:((p==2)?HSPIQ_OUT_IDX:((p==3)?VSPIQ_OUT_IDX:0)))) #define SPI_MISO_IDX(p) ((p==0)?SPIQ_OUT_IDX:((p==1)?SPIQ_OUT_IDX:((p==2)?HSPIQ_OUT_IDX:((p==3)?VSPIQ_OUT_IDX:0))))
@ -371,6 +372,18 @@ void spiSetBitOrder(spi_t * spi, uint8_t bitOrder)
SPI_MUTEX_UNLOCK(); SPI_MUTEX_UNLOCK();
} }
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
{
spi_t * spi = (spi_t *)arg;
if(ev_type == APB_BEFORE_CHANGE){
SPI_MUTEX_LOCK();
while(spi->dev->cmd.usr);
} else {
spi->dev->clock.val = spiFrequencyToClockDiv(old_apb / ((spi->dev->clock.clkdiv_pre + 1) * (spi->dev->clock.clkcnt_n + 1)));
SPI_MUTEX_UNLOCK();
}
}
void spiStopBus(spi_t * spi) void spiStopBus(spi_t * spi)
{ {
if(!spi) { if(!spi) {
@ -387,6 +400,7 @@ void spiStopBus(spi_t * spi)
spi->dev->ctrl2.val = 0; spi->dev->ctrl2.val = 0;
spi->dev->clock.val = 0; spi->dev->clock.val = 0;
SPI_MUTEX_UNLOCK(); SPI_MUTEX_UNLOCK();
removeApbChangeCallback(spi, _on_apb_change);
} }
spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder)
@ -433,6 +447,7 @@ spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_
} }
SPI_MUTEX_UNLOCK(); SPI_MUTEX_UNLOCK();
addApbChangeCallback(spi, _on_apb_change);
return spi; return spi;
} }
@ -750,7 +765,7 @@ void spiEndTransaction(spi_t * spi)
SPI_MUTEX_UNLOCK(); SPI_MUTEX_UNLOCK();
} }
void spiWriteByteNL(spi_t * spi, uint8_t data) void IRAM_ATTR spiWriteByteNL(spi_t * spi, uint8_t data)
{ {
if(!spi) { if(!spi) {
return; return;
@ -776,7 +791,7 @@ uint8_t spiTransferByteNL(spi_t * spi, uint8_t data)
return data; return data;
} }
void spiWriteShortNL(spi_t * spi, uint16_t data) void IRAM_ATTR spiWriteShortNL(spi_t * spi, uint16_t data)
{ {
if(!spi) { if(!spi) {
return; return;
@ -811,7 +826,7 @@ uint16_t spiTransferShortNL(spi_t * spi, uint16_t data)
return data; return data;
} }
void spiWriteLongNL(spi_t * spi, uint32_t data) void IRAM_ATTR spiWriteLongNL(spi_t * spi, uint32_t data)
{ {
if(!spi) { if(!spi) {
return; return;
@ -959,7 +974,7 @@ void spiTransferBitsNL(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits)
} }
} }
void spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){ void IRAM_ATTR spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){
size_t longs = len >> 2; size_t longs = len >> 2;
if(len & 3){ if(len & 3){
longs++; longs++;
@ -1007,35 +1022,37 @@ void spiWritePixelsNL(spi_t * spi, const void * data_in, size_t len){
* */ * */
typedef union { typedef union {
uint32_t regValue; uint32_t value;
struct { struct {
unsigned regL :6; uint32_t clkcnt_l: 6; /*it must be equal to spi_clkcnt_N.*/
unsigned regH :6; uint32_t clkcnt_h: 6; /*it must be floor((spi_clkcnt_N+1)/2-1).*/
unsigned regN :6; uint32_t clkcnt_n: 6; /*it is the divider of spi_clk. So spi_clk frequency is system/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)*/
unsigned regPre :13; uint32_t clkdiv_pre: 13; /*it is pre-divider of spi_clk.*/
unsigned regEQU :1; uint32_t clk_equ_sysclk: 1; /*1: spi_clk is eqaul to system 0: spi_clk is divided from system clock.*/
}; };
} spiClk_t; } spiClk_t;
#define ClkRegToFreq(reg) (CPU_CLK_FREQ / (((reg)->regPre + 1) * ((reg)->regN + 1))) #define ClkRegToFreq(reg) (apb_freq / (((reg)->clkdiv_pre + 1) * ((reg)->clkcnt_n + 1)))
uint32_t spiClockDivToFrequency(uint32_t clockDiv) uint32_t spiClockDivToFrequency(uint32_t clockDiv)
{ {
uint32_t apb_freq = getApbFrequency();
spiClk_t reg = { clockDiv }; spiClk_t reg = { clockDiv };
return ClkRegToFreq(&reg); return ClkRegToFreq(&reg);
} }
uint32_t spiFrequencyToClockDiv(uint32_t freq) uint32_t spiFrequencyToClockDiv(uint32_t freq)
{ {
uint32_t apb_freq = getApbFrequency();
if(freq >= CPU_CLK_FREQ) { if(freq >= apb_freq) {
return SPI_CLK_EQU_SYSCLK; return SPI_CLK_EQU_SYSCLK;
} }
const spiClk_t minFreqReg = { 0x7FFFF000 }; const spiClk_t minFreqReg = { 0x7FFFF000 };
uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg); uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg);
if(freq < minFreq) { if(freq < minFreq) {
return minFreqReg.regValue; return minFreqReg.value;
} }
uint8_t calN = 1; uint8_t calN = 1;
@ -1048,18 +1065,18 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq)
int32_t calPre; int32_t calPre;
int8_t calPreVari = -2; int8_t calPreVari = -2;
reg.regN = calN; reg.clkcnt_n = calN;
while(calPreVari++ <= 1) { while(calPreVari++ <= 1) {
calPre = (((CPU_CLK_FREQ / (reg.regN + 1)) / freq) - 1) + calPreVari; calPre = (((apb_freq / (reg.clkcnt_n + 1)) / freq) - 1) + calPreVari;
if(calPre > 0x1FFF) { if(calPre > 0x1FFF) {
reg.regPre = 0x1FFF; reg.clkdiv_pre = 0x1FFF;
} else if(calPre <= 0) { } else if(calPre <= 0) {
reg.regPre = 0; reg.clkdiv_pre = 0;
} else { } else {
reg.regPre = calPre; reg.clkdiv_pre = calPre;
} }
reg.regL = ((reg.regN + 1) / 2); reg.clkcnt_l = ((reg.clkcnt_n + 1) / 2);
calFreq = ClkRegToFreq(&reg); calFreq = ClkRegToFreq(&reg);
if(calFreq == (int32_t) freq) { if(calFreq == (int32_t) freq) {
memcpy(&bestReg, &reg, sizeof(bestReg)); memcpy(&bestReg, &reg, sizeof(bestReg));
@ -1076,6 +1093,6 @@ uint32_t spiFrequencyToClockDiv(uint32_t freq)
} }
calN++; calN++;
} }
return bestReg.regValue; return bestReg.value;
} }

View File

@ -17,9 +17,9 @@
static void setTimeZone(long offset, int daylight) static void setTimeZone(long offset, int daylight)
{ {
char cst[16] = {0}; char cst[17] = {0};
char cdt[16] = "DST"; char cdt[17] = "DST";
char tz[32] = {0}; char tz[33] = {0};
if(offset % 3600){ if(offset % 3600){
sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60)); sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
@ -76,23 +76,15 @@ void configTzTime(const char* tz, const char* server1, const char* server2, cons
bool getLocalTime(struct tm * info, uint32_t ms) bool getLocalTime(struct tm * info, uint32_t ms)
{ {
uint32_t count = ms / 10; uint32_t start = millis();
time_t now; time_t now;
while((millis()-start) <= ms) {
time(&now);
localtime_r(&now, info);
if(info->tm_year > (2016 - 1900)){
return true;
}
while(count--) {
delay(10);
time(&now); time(&now);
localtime_r(&now, info); localtime_r(&now, info);
if(info->tm_year > (2016 - 1900)){ if(info->tm_year > (2016 - 1900)){
return true; return true;
} }
delay(10);
} }
return false; return false;
} }

View File

@ -84,7 +84,7 @@ void IRAM_ATTR __timerISR(void * arg){
i = 4; i = 4;
//call callbacks //call callbacks
while(i--){ while(i--){
if(__timerInterruptHandlers[i] && status & (1 << i)){ if(__timerInterruptHandlers[i] && (status & (1 << i))){
__timerInterruptHandlers[i](); __timerInterruptHandlers[i]();
} }
} }
@ -184,6 +184,18 @@ bool timerAlarmEnabled(hw_timer_t *timer){
return timer->dev->config.alarm_en; return timer->dev->config.alarm_en;
} }
static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){
hw_timer_t * timer = (hw_timer_t *)arg;
if(ev_type == APB_BEFORE_CHANGE){
timer->dev->config.enable = 0;
} else {
old_apb /= 1000000;
new_apb /= 1000000;
timer->dev->config.divider = (new_apb * timer->dev->config.divider) / old_apb;
timer->dev->config.enable = 1;
}
}
hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){ hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
if(num > 3){ if(num > 3){
return NULL; return NULL;
@ -205,12 +217,14 @@ hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){
timerAttachInterrupt(timer, NULL, false); timerAttachInterrupt(timer, NULL, false);
timerWrite(timer, 0); timerWrite(timer, 0);
timer->dev->config.enable = 1; timer->dev->config.enable = 1;
addApbChangeCallback(timer, _on_apb_change);
return timer; return timer;
} }
void timerEnd(hw_timer_t *timer){ void timerEnd(hw_timer_t *timer){
timer->dev->config.enable = 0; timer->dev->config.enable = 0;
timerAttachInterrupt(timer, NULL, false); timerAttachInterrupt(timer, NULL, false);
removeApbChangeCallback(timer, _on_apb_change);
} }
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){ void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){
@ -271,23 +285,23 @@ void timerDetachInterrupt(hw_timer_t *timer){
uint64_t timerReadMicros(hw_timer_t *timer){ uint64_t timerReadMicros(hw_timer_t *timer){
uint64_t timer_val = timerRead(timer); uint64_t timer_val = timerRead(timer);
uint16_t div = timerGetDivider(timer); uint16_t div = timerGetDivider(timer);
return timer_val * div / 80; return timer_val * div / (getApbFrequency() / 1000000);
} }
double timerReadSeconds(hw_timer_t *timer){ double timerReadSeconds(hw_timer_t *timer){
uint64_t timer_val = timerRead(timer); uint64_t timer_val = timerRead(timer);
uint16_t div = timerGetDivider(timer); uint16_t div = timerGetDivider(timer);
return (double)timer_val * div / 80000000; return (double)timer_val * div / getApbFrequency();
} }
uint64_t timerAlarmReadMicros(hw_timer_t *timer){ uint64_t timerAlarmReadMicros(hw_timer_t *timer){
uint64_t timer_val = timerAlarmRead(timer); uint64_t timer_val = timerAlarmRead(timer);
uint16_t div = timerGetDivider(timer); uint16_t div = timerGetDivider(timer);
return timer_val * div / 80; return timer_val * div / (getApbFrequency() / 1000000);
} }
double timerAlarmReadSeconds(hw_timer_t *timer){ double timerAlarmReadSeconds(hw_timer_t *timer){
uint64_t timer_val = timerAlarmRead(timer); uint64_t timer_val = timerAlarmRead(timer);
uint16_t div = timerGetDivider(timer); uint16_t div = timerGetDivider(timer);
return (double)timer_val * div / 80000000; return (double)timer_val * div / getApbFrequency();
} }

View File

@ -27,6 +27,7 @@
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
#include "soc/rtc.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:( (u==2)?DR_REG_UART2_BASE:0))) #define UART_REG_BASE(u) ((u==0)?DR_REG_UART_BASE:( (u==1)?DR_REG_UART1_BASE:( (u==2)?DR_REG_UART2_BASE:0)))
@ -66,6 +67,8 @@ static uart_t _uart_bus_array[3] = {
}; };
#endif #endif
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
static void IRAM_ATTR _uart_isr(void *arg) static void IRAM_ATTR _uart_isr(void *arg)
{ {
uint8_t i, c; uint8_t i, c;
@ -215,6 +218,7 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx
uartAttachTx(uart, txPin, inverted); uartAttachTx(uart, txPin, inverted);
} }
addApbChangeCallback(uart, uart_on_apb_change);
return uart; return uart;
} }
@ -223,11 +227,10 @@ void uartEnd(uart_t* uart)
if(uart == NULL) { if(uart == NULL) {
return; return;
} }
removeApbChangeCallback(uart, uart_on_apb_change);
UART_MUTEX_LOCK(); UART_MUTEX_LOCK();
if(uart->queue != NULL) { if(uart->queue != NULL) {
uint8_t c;
while(xQueueReceive(uart->queue, &c, 0));
vQueueDelete(uart->queue); vQueueDelete(uart->queue);
uart->queue = NULL; uart->queue = NULL;
} }
@ -247,8 +250,6 @@ size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
UART_MUTEX_LOCK(); UART_MUTEX_LOCK();
if(uart->queue != NULL) { if(uart->queue != NULL) {
uint8_t c;
while(xQueueReceive(uart->queue, &c, 0));
vQueueDelete(uart->queue); vQueueDelete(uart->queue);
uart->queue = xQueueCreate(new_size, sizeof(uint8_t)); uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
if(uart->queue == NULL) { if(uart->queue == NULL) {
@ -318,10 +319,9 @@ void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len)
} }
UART_MUTEX_LOCK(); UART_MUTEX_LOCK();
while(len) { while(len) {
while(len && uart->dev->status.txfifo_cnt < 0x7F) { while(uart->dev->status.txfifo_cnt == 0x7F);
uart->dev->fifo.rw_byte = *data++; uart->dev->fifo.rw_byte = *data++;
len--; len--;
}
} }
UART_MUTEX_UNLOCK(); UART_MUTEX_UNLOCK();
} }
@ -343,6 +343,8 @@ void uartFlush(uart_t* uart)
READ_PERI_REG(UART_FIFO_REG(uart->num)); READ_PERI_REG(UART_FIFO_REG(uart->num));
} }
xQueueReset(uart->queue);
UART_MUTEX_UNLOCK(); UART_MUTEX_UNLOCK();
} }
@ -352,19 +354,60 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
return; return;
} }
UART_MUTEX_LOCK(); UART_MUTEX_LOCK();
uint32_t clk_div = ((UART_CLK_FREQ<<4)/baud_rate); uint32_t clk_div = ((getApbFrequency()<<4)/baud_rate);
uart->dev->clk_div.div_int = clk_div>>4 ; uart->dev->clk_div.div_int = clk_div>>4 ;
uart->dev->clk_div.div_frag = clk_div & 0xf; uart->dev->clk_div.div_frag = clk_div & 0xf;
UART_MUTEX_UNLOCK(); UART_MUTEX_UNLOCK();
} }
static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
{
uart_t* uart = (uart_t*)arg;
if(ev_type == APB_BEFORE_CHANGE){
UART_MUTEX_LOCK();
//disabple interrupt
uart->dev->int_ena.val = 0;
uart->dev->int_clr.val = 0xffffffff;
// read RX fifo
uint8_t c;
BaseType_t xHigherPriorityTaskWoken;
while(uart->dev->status.rxfifo_cnt != 0 || (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);
}
}
// wait TX empty
while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
} else {
//todo:
// set baudrate
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
uint32_t baud_rate = ((old_apb<<4)/clk_div);
clk_div = ((new_apb<<4)/baud_rate);
uart->dev->clk_div.div_int = clk_div>>4 ;
uart->dev->clk_div.div_frag = clk_div & 0xf;
//enable interrupts
uart->dev->int_ena.rxfifo_full = 1;
uart->dev->int_ena.frm_err = 1;
uart->dev->int_ena.rxfifo_tout = 1;
uart->dev->int_clr.val = 0xffffffff;
UART_MUTEX_UNLOCK();
}
}
uint32_t uartGetBaudRate(uart_t* uart) uint32_t uartGetBaudRate(uart_t* uart)
{ {
if(uart == NULL) { if(uart == NULL) {
return 0; return 0;
} }
uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F); uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
return ((UART_CLK_FREQ<<4)/clk_div); if(!clk_div) {
return 0;
}
return ((getApbFrequency()<<4)/clk_div);
} }
static void IRAM_ATTR uart0_write_char(char c) static void IRAM_ATTR uart0_write_char(char c)
@ -385,17 +428,8 @@ static void IRAM_ATTR uart2_write_char(char c)
ESP_REG(DR_REG_UART2_BASE) = c; ESP_REG(DR_REG_UART2_BASE) = c;
} }
void uartSetDebug(uart_t* uart) void uart_install_putc()
{ {
if(uart == NULL || uart->num > 2) {
s_uart_debug_nr = -1;
ets_install_putc1(NULL);
return;
}
if(s_uart_debug_nr == uart->num) {
return;
}
s_uart_debug_nr = uart->num;
switch(s_uart_debug_nr) { switch(s_uart_debug_nr) {
case 0: case 0:
ets_install_putc1((void (*)(char)) &uart0_write_char); ets_install_putc1((void (*)(char)) &uart0_write_char);
@ -412,6 +446,20 @@ void uartSetDebug(uart_t* uart)
} }
} }
void uartSetDebug(uart_t* uart)
{
if(uart == NULL || uart->num > 2) {
s_uart_debug_nr = -1;
//ets_install_putc1(NULL);
//return;
} else
if(s_uart_debug_nr == uart->num) {
return;
} else
s_uart_debug_nr = uart->num;
uart_install_putc();
}
int uartGetDebug() int uartGetDebug()
{ {
return s_uart_debug_nr; return s_uart_debug_nr;
@ -440,7 +488,7 @@ int log_printf(const char *format, ...)
vsnprintf(temp, len+1, format, arg); vsnprintf(temp, len+1, format, arg);
#if !CONFIG_DISABLE_HAL_LOCKS #if !CONFIG_DISABLE_HAL_LOCKS
if(_uart_bus_array[s_uart_debug_nr].lock){ if(_uart_bus_array[s_uart_debug_nr].lock){
while (xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY) != pdPASS); xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY);
ets_printf("%s", temp); ets_printf("%s", temp);
xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock); xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock);
} else { } else {
@ -450,7 +498,7 @@ int log_printf(const char *format, ...)
ets_printf("%s", temp); ets_printf("%s", temp);
#endif #endif
va_end(arg); va_end(arg);
if(len > 64){ if(len >= sizeof(loc_buf)){
free(temp); free(temp);
} }
return len; return len;
@ -479,6 +527,14 @@ unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
* detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is * detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is
* rounded to the closed real baudrate. * rounded to the closed real baudrate.
*/ */
void uartStartDetectBaudrate(uart_t *uart) {
if(!uart) return;
uart->dev->auto_baud.glitch_filt = 0x08;
uart->dev->auto_baud.en = 0;
uart->dev->auto_baud.en = 1;
}
unsigned long unsigned long
uartDetectBaudrate(uart_t *uart) uartDetectBaudrate(uart_t *uart)
{ {
@ -499,7 +555,7 @@ uartDetectBaudrate(uart_t *uart)
uart->dev->auto_baud.en = 0; uart->dev->auto_baud.en = 0;
uartStateDetectingBaudrate = false; // Initialize for the next round uartStateDetectingBaudrate = false; // Initialize for the next round
unsigned long baudrate = UART_CLK_FREQ / divisor; unsigned long baudrate = getApbFrequency() / divisor;
static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400}; static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
@ -517,3 +573,10 @@ uartDetectBaudrate(uart_t *uart)
return default_rates[i]; return default_rates[i];
} }
/*
* Returns the status of the RX state machine, if the value is non-zero the state machine is active.
*/
bool uartRxActive(uart_t* uart) {
return uart->dev->status.st_urx_out != 0;
}

View File

@ -72,8 +72,11 @@ size_t uartResizeRxBuffer(uart_t* uart, size_t new_size);
void uartSetDebug(uart_t* uart); void uartSetDebug(uart_t* uart);
int uartGetDebug(); int uartGetDebug();
void uartStartDetectBaudrate(uart_t *uart);
unsigned long uartDetectBaudrate(uart_t *uart); unsigned long uartDetectBaudrate(uart_t *uart);
bool uartRxActive(uart_t* uart);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -62,6 +62,7 @@ void yield(void);
#include "esp32-hal-timer.h" #include "esp32-hal-timer.h"
#include "esp32-hal-bt.h" #include "esp32-hal-bt.h"
#include "esp32-hal-psram.h" #include "esp32-hal-psram.h"
#include "esp32-hal-cpu.h"
#ifndef BOARD_HAS_PSRAM #ifndef BOARD_HAS_PSRAM
#ifdef CONFIG_SPIRAM_SUPPORT #ifdef CONFIG_SPIRAM_SUPPORT
@ -72,6 +73,33 @@ void yield(void);
//returns chip temperature in Celsius //returns chip temperature in Celsius
float temperatureRead(); float temperatureRead();
#if CONFIG_AUTOSTART_ARDUINO
//enable/disable WDT for Arduino's setup and loop functions
void enableLoopWDT();
void disableLoopWDT();
//feed WDT for the loop task
void feedLoopWDT();
#endif
//enable/disable WDT for the IDLE task on Core 0 (SYSTEM)
void enableCore0WDT();
void disableCore0WDT();
#ifndef CONFIG_FREERTOS_UNICORE
//enable/disable WDT for the IDLE task on Core 1 (Arduino)
void enableCore1WDT();
void disableCore1WDT();
#endif
//if xCoreID < 0 or CPU is unicore, it will use xTaskCreate, else xTaskCreatePinnedToCore
//allows to easily handle all possible situations without repetitive code
BaseType_t xTaskCreateUniversal( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
const BaseType_t xCoreID );
unsigned long micros(); unsigned long micros();
unsigned long millis(); unsigned long millis();
void delay(uint32_t); void delay(uint32_t);

View File

@ -1,27 +1,30 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_task_wdt.h"
#include "Arduino.h" #include "Arduino.h"
TaskHandle_t loopTaskHandle = NULL;
#if CONFIG_AUTOSTART_ARDUINO #if CONFIG_AUTOSTART_ARDUINO
#if CONFIG_FREERTOS_UNICORE bool loopTaskWDTEnabled;
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
void loopTask(void *pvParameters) void loopTask(void *pvParameters)
{ {
setup(); setup();
for(;;) { for(;;) {
if(loopTaskWDTEnabled){
esp_task_wdt_reset();
}
loop(); loop();
} }
} }
extern "C" void app_main() extern "C" void app_main()
{ {
loopTaskWDTEnabled = false;
initArduino(); initArduino();
xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, ARDUINO_RUNNING_CORE); xTaskCreateUniversal(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, CONFIG_ARDUINO_RUNNING_CORE);
} }
#endif #endif

View File

@ -32,7 +32,6 @@ typedef unsigned long prog_uint32_t;
#define PROGMEM #define PROGMEM
#define PGM_P const char * #define PGM_P const char *
#define PGM_VOID_P const void * #define PGM_VOID_P const void *
#define FPSTR(p) ((const char *)(p))
#define PSTR(s) (s) #define PSTR(s) (s)
#define _SFR_BYTE(n) (n) #define _SFR_BYTE(n) (n)

View File

@ -25,11 +25,12 @@ uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
uint8_t i; uint8_t i;
for(i = 0; i < 8; ++i) { for(i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH); //digitalWrite(clockPin, HIGH);
if(bitOrder == LSBFIRST) if(bitOrder == LSBFIRST)
value |= digitalRead(dataPin) << i; value |= digitalRead(dataPin) << i;
else else
value |= digitalRead(dataPin) << (7 - i); value |= digitalRead(dataPin) << (7 - i);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW); digitalWrite(clockPin, LOW);
} }
return value; return value;

View File

@ -16,7 +16,7 @@ Installation instructions for Debian / Ubuntu OS
cd esp32 && \ cd esp32 && \
git submodule update --init --recursive && \ git submodule update --init --recursive && \
cd tools && \ cd tools && \
python2 get.py python3 get.py
``` ```
- Restart Arduino IDE - Restart Arduino IDE
@ -32,5 +32,5 @@ Installation instructions for Debian / Ubuntu OS
cd esp32 && \ cd esp32 && \
git submodule update --init --recursive && \ git submodule update --init --recursive && \
cd tools && \ cd tools && \
python2 get.py python3 get.py
``` ```

View File

@ -23,5 +23,7 @@ Installation instructions for Mac OS
- 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` - 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`
- If you get the following error when running `python get.py` urllib.error.URLError: <urlopen error SSL: CERTIFICATE_VERIFY_FAILED, go to Macintosh HD > Applications > Python3.6 folder (or any other python version), and run the following scripts: Install Certificates.command and Update Shell Profile.command
- Restart Arduino IDE - Restart Arduino IDE

View File

@ -1,11 +1,11 @@
Installation instructions for using PlatformIO Installation instructions for using PlatformIO
================================================= =================================================
- [What is PlatformIO?](http://docs.platformio.org/en/latest/what-is-platformio.html?utm_source=github&utm_medium=arduino-esp32) - [What is PlatformIO?](https://docs.platformio.org/en/latest/what-is-platformio.html?utm_source=github&utm_medium=arduino-esp32)
- [PlatformIO IDE](http://platformio.org/platformio-ide?utm_source=github&utm_medium=arduino-esp32) - [PlatformIO IDE](https://platformio.org/platformio-ide?utm_source=github&utm_medium=arduino-esp32)
- [PlatformIO Core](http://docs.platformio.org/en/latest/core.html?utm_source=github&utm_medium=arduino-esp32) (command line tool) - [PlatformIO Core](https://docs.platformio.org/en/latest/core.html?utm_source=github&utm_medium=arduino-esp32) (command line tool)
- [Advanced usage](http://docs.platformio.org/en/latest/platforms/espressif32.html?utm_source=github&utm_medium=arduino-esp32) - - [Advanced usage](https://docs.platformio.org/en/latest/platforms/espressif32.html?utm_source=github&utm_medium=arduino-esp32) -
custom settings, uploading to SPIFFS, Over-the-Air (OTA), staging version custom settings, uploading to SPIFFS, Over-the-Air (OTA), staging version
- [Integration with Cloud and Standalone IDEs](http://docs.platformio.org/en/latest/ide.html?utm_source=github&utm_medium=arduino-esp32) - - [Integration with Cloud and Standalone IDEs](https://docs.platformio.org/en/latest/ide.html?utm_source=github&utm_medium=arduino-esp32) -
Cloud9, Codeanywhere, Eclipse Che (Codenvy), Atom, CLion, Eclipse, Emacs, NetBeans, Qt Creator, Sublime Text, VIM, Visual Studio, and VSCode Cloud9, Codeanywhere, Eclipse Che (Codenvy), Atom, CLion, Eclipse, Emacs, NetBeans, Qt Creator, Sublime Text, VIM, Visual Studio, and VSCode
- [Project Examples](http://docs.platformio.org/en/latest/platforms/espressif32.html?utm_source=github&utm_medium=arduino-esp32#examples) - [Project Examples](https://docs.platformio.org/en/latest/platforms/espressif32.html?utm_source=github&utm_medium=arduino-esp32#examples)

View File

@ -1,17 +0,0 @@
{
"name":"AsyncUDP",
"description":"Asynchronous UDP Library for ESP32",
"keywords":"async,udp,server,client,multicast,broadcast",
"authors":
{
"name": "Hristo Gochkov",
"maintainer": true
},
"repository":
{
"type": "git",
"url": "https://github.com/me-no-dev/ESPAsyncUDP.git"
},
"frameworks": "arduino",
"platforms":"espressif"
}

View File

@ -150,7 +150,7 @@ static bool _udp_task_start(){
} }
} }
if(!_udp_task_handle){ if(!_udp_task_handle){
xTaskCreate(_udp_task, "async_udp", 4096, NULL, 3, (TaskHandle_t*)&_udp_task_handle); xTaskCreateUniversal(_udp_task, "async_udp", 4096, NULL, 3, (TaskHandle_t*)&_udp_task_handle, CONFIG_ARDUINO_UDP_RUNNING_CORE);
if(!_udp_task_handle){ if(!_udp_task_handle){
return false; return false;
} }
@ -561,26 +561,48 @@ static esp_err_t joinMulticastGroup(const ip_addr_t *addr, bool join, tcpip_adap
return ESP_ERR_INVALID_ARG; return ESP_ERR_INVALID_ARG;
} }
netif = (struct netif *)nif; netif = (struct netif *)nif;
}
if (addr->type == IPADDR_TYPE_V4) { if (addr->type == IPADDR_TYPE_V4) {
if(join){ if(join){
if (igmp_joingroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) { if (igmp_joingroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
}
} else {
if (igmp_leavegroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) {
return ESP_ERR_INVALID_STATE;
}
} }
} else { } else {
if (igmp_leavegroup_netif(netif, (const ip4_addr *)&(addr->u_addr.ip4))) { if(join){
return ESP_ERR_INVALID_STATE; 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;
}
} }
} }
} else { } else {
if(join){ if (addr->type == IPADDR_TYPE_V4) {
if (mld6_joingroup_netif(netif, &(addr->u_addr.ip6))) { if(join){
return ESP_ERR_INVALID_STATE; if (igmp_joingroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)&(addr->u_addr.ip4))) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (igmp_leavegroup((const ip4_addr *)IP4_ADDR_ANY, (const ip4_addr *)&(addr->u_addr.ip4))) {
return ESP_ERR_INVALID_STATE;
}
} }
} else { } else {
if (mld6_leavegroup_netif(netif, &(addr->u_addr.ip6))) { if(join){
return ESP_ERR_INVALID_STATE; if (mld6_joingroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) {
return ESP_ERR_INVALID_STATE;
}
} else {
if (mld6_leavegroup((const ip6_addr *)IP6_ADDR_ANY, &(addr->u_addr.ip6))) {
return ESP_ERR_INVALID_STATE;
}
} }
} }
} }

Submodule libraries/BLE deleted from 7951347ed6

15
libraries/BLE/README.md Normal file
View File

@ -0,0 +1,15 @@
# ESP32 BLE for Arduino
The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino.
The actual source of the project which is being maintained can be found here:
https://github.com/nkolban/esp32-snippets
Issues and questions should be raised here:
https://github.com/nkolban/esp32-snippets/issues
Documentation for using the library can be found here:
https://github.com/nkolban/esp32-snippets/tree/master/Documentation

View File

@ -0,0 +1,160 @@
/**
* A BLE client example that is rich in capabilities.
* There is a lot new capabilities implemented.
* author unknown
* updated by chegewara
*/
#include "BLEDevice.h"
//#include "BLEScan.h"
// The remote service we wish to connect to.
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
static void notifyCallback(
BLERemoteCharacteristic* pBLERemoteCharacteristic,
uint8_t* pData,
size_t length,
bool isNotify) {
Serial.print("Notify callback for characteristic ");
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
Serial.print(" of data length ");
Serial.println(length);
Serial.print("data: ");
Serial.println((char*)pData);
}
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
}
};
bool connectToServer() {
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our characteristic");
// Read the value of the characteristic.
if(pRemoteCharacteristic->canRead()) {
std::string value = pRemoteCharacteristic->readValue();
Serial.print("The characteristic value was: ");
Serial.println(value.c_str());
}
if(pRemoteCharacteristic->canNotify())
pRemoteCharacteristic->registerForNotify(notifyCallback);
connected = true;
}
/**
* Scan for BLE servers and find the first one that advertises the service we are looking for.
*/
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
/**
* Called for each advertising BLE server.
*/
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void setup() {
Serial.begin(115200);
Serial.println("Starting Arduino BLE Client application...");
BLEDevice::init("");
// Retrieve a Scanner and set the callback we want to use to be informed when we
// have detected a new device. Specify that we want active scanning and start the
// scan to run for 5 seconds.
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
} // End of setup.
// This is the Arduino main loop function.
void loop() {
// If the flag "doConnect" is true then we have scanned for and found the desired
// BLE Server with which we wish to connect. Now we connect to it. Once we are
// connected we set the connected flag to be true.
if (doConnect == true) {
if (connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
}
doConnect = false;
}
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
// with the current time since boot.
if (connected) {
String newValue = "Time since boot: " + String(millis()/1000);
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
// Set the characteristic's value to be the array of bytes that is actually a string.
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
}else if(doScan){
BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino
}
delay(1000); // Delay a second between loops.
} // End of loop

View File

@ -0,0 +1,103 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by pcbreflux
*/
/*
Create a BLE server that will send periodic iBeacon frames.
The design of creating the BLE server is:
1. Create a BLE Server
2. Create advertising data
3. Start advertising.
4. wait
5. Stop advertising.
6. deep sleep
*/
#include "sys/time.h"
#include "BLEDevice.h"
#include "BLEUtils.h"
#include "BLEBeacon.h"
#include "esp_sleep.h"
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
#ifdef __cplusplus
extern "C" {
#endif
uint8_t temprature_sens_read();
//uint8_t g_phyFuns;
#ifdef __cplusplus
}
#endif
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
BLEAdvertising *pAdvertising;
struct timeval now;
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
void setBeacon() {
BLEBeacon oBeacon = BLEBeacon();
oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!)
oBeacon.setProximityUUID(BLEUUID(BEACON_UUID));
oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16);
oBeacon.setMinor(bootcount&0xFFFF);
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04
std::string strServiceData = "";
strServiceData += (char)26; // Len
strServiceData += (char)0xFF; // Type
strServiceData += oBeacon.getData();
oAdvertisementData.addData(strServiceData);
pAdvertising->setAdvertisementData(oAdvertisementData);
pAdvertising->setScanResponseData(oScanResponseData);
}
void setup() {
Serial.begin(115200);
gettimeofday(&now, NULL);
Serial.printf("start ESP32 %d\n",bootcount++);
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last);
last = now.tv_sec;
// Create the BLE Device
BLEDevice::init("");
// Create the BLE Server
// BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage
pAdvertising = BLEDevice::getAdvertising();
setBeacon();
// Start advertising
pAdvertising->start();
Serial.println("Advertizing started...");
delay(100);
pAdvertising->stop();
Serial.printf("enter deep sleep\n");
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
Serial.printf("in deep sleep\n");
}
void loop() {
}

View File

@ -0,0 +1,110 @@
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
updated by chegewara
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
A connect hander associated with the server starts a background task that performs notification
every couple of seconds.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("ESP32");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
pCharacteristic->addDescriptor(new BLE2902());
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
// notify changed value
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
value++;
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}

View File

@ -0,0 +1,40 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
int scanTime = 5; //In seconds
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
}
void loop() {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
delay(2000);
}

View File

@ -0,0 +1,45 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
Ported to Arduino ESP32 by Evandro Copercini
updates by chegewara
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE work!");
BLEDevice::init("Long name works now");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setValue("Hello World says Neil");
pService->start();
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}
void loop() {
// put your main code here, to run repeatedly:
delay(2000);
}

View File

@ -0,0 +1,111 @@
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
updated by chegewara
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
A connect hander associated with the server starts a background task that performs notification
every couple of seconds.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
BLEDevice::startAdvertising();
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("ESP32");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
pCharacteristic->addDescriptor(new BLE2902());
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
// notify changed value
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
value++;
delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}

View File

@ -0,0 +1,125 @@
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
In this example rxValue is the data received (only accessible inside that function).
And txValue is the data to be sent, in this example just a byte incremented every second.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 0;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++)
Serial.print(rxValue[i]);
Serial.println();
Serial.println("*********");
}
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("UART Service");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
pTxCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pRxCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
if (deviceConnected) {
pTxCharacteristic->setValue(&txValue, 1);
pTxCharacteristic->notify();
txValue++;
delay(10); // bluetooth stack will go into congestion, if too many packets are sent
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}

View File

@ -0,0 +1,65 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string value = pCharacteristic->getValue();
if (value.length() > 0) {
Serial.println("*********");
Serial.print("New value: ");
for (int i = 0; i < value.length(); i++)
Serial.print(value[i]);
Serial.println();
Serial.println("*********");
}
}
};
void setup() {
Serial.begin(115200);
Serial.println("1- Download and install an BLE scanner app in your phone");
Serial.println("2- Scan for BLE devices in the app");
Serial.println("3- Connect to MyESP32");
Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
Serial.println("5- See the magic =)");
BLEDevice::init("MyESP32");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new MyCallbacks());
pCharacteristic->setValue("Hello World");
pService->start();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();
}
void loop() {
// put your main code here, to run repeatedly:
delay(2000);
}

View File

@ -0,0 +1,10 @@
name=ESP32 BLE Arduino
version=1.0.1
author=Neil Kolban <kolban1@kolban.com>
maintainer=Dariusz Krempa <esp32@esp32.eu.org>
sentence=BLE functions for ESP32
paragraph=This library provides an implementation Bluetooth Low Energy support for the ESP32 using the Arduino platform.
category=Communication
url=https://github.com/nkolban/ESP32_BLE_Arduino
architectures=esp32
includes=BLEDevice.h, BLEUtils.h, BLEScan.h, BLEAdvertisedDevice.h

View File

@ -0,0 +1,62 @@
/*
* BLE2902.cpp
*
* Created on: Jun 25, 2017
* Author: kolban
*/
/*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLE2902.h"
BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t) 0x2902)) {
uint8_t data[2] = { 0, 0 };
setValue(data, 2);
} // BLE2902
/**
* @brief Get the notifications value.
* @return The notifications value. True if notifications are enabled and false if not.
*/
bool BLE2902::getNotifications() {
return (getValue()[0] & (1 << 0)) != 0;
} // getNotifications
/**
* @brief Get the indications value.
* @return The indications value. True if indications are enabled and false if not.
*/
bool BLE2902::getIndications() {
return (getValue()[0] & (1 << 1)) != 0;
} // getIndications
/**
* @brief Set the indications flag.
* @param [in] flag The indications flag.
*/
void BLE2902::setIndications(bool flag) {
uint8_t *pValue = getValue();
if (flag) pValue[0] |= 1 << 1;
else pValue[0] &= ~(1 << 1);
} // setIndications
/**
* @brief Set the notifications flag.
* @param [in] flag The notifications flag.
*/
void BLE2902::setNotifications(bool flag) {
uint8_t *pValue = getValue();
if (flag) pValue[0] |= 1 << 0;
else pValue[0] &= ~(1 << 0);
} // setNotifications
#endif

View File

@ -0,0 +1,34 @@
/*
* BLE2902.h
*
* Created on: Jun 25, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLE2902_H_
#define COMPONENTS_CPP_UTILS_BLE2902_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLEDescriptor.h"
/**
* @brief Descriptor for Client Characteristic Configuration.
*
* This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
*/
class BLE2902: public BLEDescriptor {
public:
BLE2902();
bool getNotifications();
bool getIndications();
void setNotifications(bool flag);
void setIndications(bool flag);
}; // BLE2902
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */

View File

@ -0,0 +1,74 @@
/*
* BLE2904.cpp
*
* Created on: Dec 23, 2017
* Author: kolban
*/
/*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLE2904.h"
BLE2904::BLE2904() : BLEDescriptor(BLEUUID((uint16_t) 0x2904)) {
m_data.m_format = 0;
m_data.m_exponent = 0;
m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers
m_data.m_unit = 0;
m_data.m_description = 0;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // BLE2902
/**
* @brief Set the description.
*/
void BLE2904::setDescription(uint16_t description) {
m_data.m_description = description;
setValue((uint8_t*) &m_data, sizeof(m_data));
}
/**
* @brief Set the exponent.
*/
void BLE2904::setExponent(int8_t exponent) {
m_data.m_exponent = exponent;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // setExponent
/**
* @brief Set the format.
*/
void BLE2904::setFormat(uint8_t format) {
m_data.m_format = format;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // setFormat
/**
* @brief Set the namespace.
*/
void BLE2904::setNamespace(uint8_t namespace_value) {
m_data.m_namespace = namespace_value;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // setNamespace
/**
* @brief Set the units for this value. It should be one of the encoded values defined here:
* https://www.bluetooth.com/specifications/assigned-numbers/units
* @param [in] unit The type of units of this characteristic as defined by assigned numbers.
*/
void BLE2904::setUnit(uint16_t unit) {
m_data.m_unit = unit;
setValue((uint8_t*) &m_data, sizeof(m_data));
} // setUnit
#endif

View File

@ -0,0 +1,74 @@
/*
* BLE2904.h
*
* Created on: Dec 23, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLE2904_H_
#define COMPONENTS_CPP_UTILS_BLE2904_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLEDescriptor.h"
struct BLE2904_Data {
uint8_t m_format;
int8_t m_exponent;
uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units
uint8_t m_namespace;
uint16_t m_description;
} __attribute__((packed));
/**
* @brief Descriptor for Characteristic Presentation Format.
*
* This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml
*/
class BLE2904: public BLEDescriptor {
public:
BLE2904();
static const uint8_t FORMAT_BOOLEAN = 1;
static const uint8_t FORMAT_UINT2 = 2;
static const uint8_t FORMAT_UINT4 = 3;
static const uint8_t FORMAT_UINT8 = 4;
static const uint8_t FORMAT_UINT12 = 5;
static const uint8_t FORMAT_UINT16 = 6;
static const uint8_t FORMAT_UINT24 = 7;
static const uint8_t FORMAT_UINT32 = 8;
static const uint8_t FORMAT_UINT48 = 9;
static const uint8_t FORMAT_UINT64 = 10;
static const uint8_t FORMAT_UINT128 = 11;
static const uint8_t FORMAT_SINT8 = 12;
static const uint8_t FORMAT_SINT12 = 13;
static const uint8_t FORMAT_SINT16 = 14;
static const uint8_t FORMAT_SINT24 = 15;
static const uint8_t FORMAT_SINT32 = 16;
static const uint8_t FORMAT_SINT48 = 17;
static const uint8_t FORMAT_SINT64 = 18;
static const uint8_t FORMAT_SINT128 = 19;
static const uint8_t FORMAT_FLOAT32 = 20;
static const uint8_t FORMAT_FLOAT64 = 21;
static const uint8_t FORMAT_SFLOAT16 = 22;
static const uint8_t FORMAT_SFLOAT32 = 23;
static const uint8_t FORMAT_IEEE20601 = 24;
static const uint8_t FORMAT_UTF8 = 25;
static const uint8_t FORMAT_UTF16 = 26;
static const uint8_t FORMAT_OPAQUE = 27;
void setDescription(uint16_t);
void setExponent(int8_t exponent);
void setFormat(uint8_t format);
void setNamespace(uint8_t namespace_value);
void setUnit(uint16_t unit);
private:
BLE2904_Data m_data;
}; // BLE2904
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */

View File

@ -0,0 +1,94 @@
/*
* BLEAddress.cpp
*
* Created on: Jul 2, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLEAddress.h"
#include <string>
#include <sstream>
#include <iomanip>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#endif
/**
* @brief Create an address from the native ESP32 representation.
* @param [in] address The native representation.
*/
BLEAddress::BLEAddress(esp_bd_addr_t address) {
memcpy(m_address, address, ESP_BD_ADDR_LEN);
} // BLEAddress
/**
* @brief Create an address from a hex string
*
* A hex string is of the format:
* ```
* 00:00:00:00:00:00
* ```
* which is 17 characters in length.
*
* @param [in] stringAddress The hex representation of the address.
*/
BLEAddress::BLEAddress(std::string stringAddress) {
if (stringAddress.length() != 17) return;
int data[6];
sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]);
m_address[0] = (uint8_t) data[0];
m_address[1] = (uint8_t) data[1];
m_address[2] = (uint8_t) data[2];
m_address[3] = (uint8_t) data[3];
m_address[4] = (uint8_t) data[4];
m_address[5] = (uint8_t) data[5];
} // BLEAddress
/**
* @brief Determine if this address equals another.
* @param [in] otherAddress The other address to compare against.
* @return True if the addresses are equal.
*/
bool BLEAddress::equals(BLEAddress otherAddress) {
return memcmp(otherAddress.getNative(), m_address, 6) == 0;
} // equals
/**
* @brief Return the native representation of the address.
* @return The native representation of the address.
*/
esp_bd_addr_t *BLEAddress::getNative() {
return &m_address;
} // getNative
/**
* @brief Convert a BLE address to a string.
*
* A string representation of an address is in the format:
*
* ```
* xx:xx:xx:xx:xx:xx
* ```
*
* @return The string representation of the address.
*/
std::string BLEAddress::toString() {
auto size = 18;
char *res = (char*)malloc(size);
snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[0], m_address[1], m_address[2], m_address[3], m_address[4], m_address[5]);
std::string ret(res);
free(res);
return ret;
} // toString
#endif

View File

@ -0,0 +1,34 @@
/*
* BLEAddress.h
*
* Created on: Jul 2, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_
#define COMPONENTS_CPP_UTILS_BLEADDRESS_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gap_ble_api.h> // ESP32 BLE
#include <string>
/**
* @brief A %BLE device address.
*
* Every %BLE device has a unique address which can be used to identify it and form connections.
*/
class BLEAddress {
public:
BLEAddress(esp_bd_addr_t address);
BLEAddress(std::string stringAddress);
bool equals(BLEAddress otherAddress);
esp_bd_addr_t* getNative();
std::string toString();
private:
esp_bd_addr_t m_address;
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */

View File

@ -0,0 +1,529 @@
/*
* BLEAdvertisedDevice.cpp
*
* During the scanning procedure, we will be finding advertised BLE devices. This class
* models a found device.
*
*
* See also:
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*
* Created on: Jul 3, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <sstream>
#include "BLEAdvertisedDevice.h"
#include "BLEUtils.h"
#include "esp32-hal-log.h"
BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_adFlag = 0;
m_appearance = 0;
m_deviceType = 0;
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
m_serviceData = "";
m_txPower = 0;
m_pScan = nullptr;
m_haveAppearance = false;
m_haveManufacturerData = false;
m_haveName = false;
m_haveRSSI = false;
m_haveServiceData = false;
m_haveServiceUUID = false;
m_haveTXPower = false;
} // BLEAdvertisedDevice
/**
* @brief Get the address.
*
* Every %BLE device exposes an address that is used to identify it and subsequently connect to it.
* Call this function to obtain the address of the advertised device.
*
* @return The address of the advertised device.
*/
BLEAddress BLEAdvertisedDevice::getAddress() {
return m_address;
} // getAddress
/**
* @brief Get the appearance.
*
* A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user
* typcially in the form of an icon.
*
* @return The appearance of the advertised device.
*/
uint16_t BLEAdvertisedDevice::getAppearance() {
return m_appearance;
} // getAppearance
/**
* @brief Get the manufacturer data.
* @return The manufacturer data of the advertised device.
*/
std::string BLEAdvertisedDevice::getManufacturerData() {
return m_manufacturerData;
} // getManufacturerData
/**
* @brief Get the name.
* @return The name of the advertised device.
*/
std::string BLEAdvertisedDevice::getName() {
return m_name;
} // getName
/**
* @brief Get the RSSI.
* @return The RSSI of the advertised device.
*/
int BLEAdvertisedDevice::getRSSI() {
return m_rssi;
} // getRSSI
/**
* @brief Get the scan object that created this advertisement.
* @return The scan object.
*/
BLEScan* BLEAdvertisedDevice::getScan() {
return m_pScan;
} // getScan
/**
* @brief Get the service data.
* @return The ServiceData of the advertised device.
*/
std::string BLEAdvertisedDevice::getServiceData() {
return m_serviceData;
} //getServiceData
/**
* @brief Get the service data UUID.
* @return The service data UUID.
*/
BLEUUID BLEAdvertisedDevice::getServiceDataUUID() {
return m_serviceDataUUID;
} // getServiceDataUUID
/**
* @brief Get the Service UUID.
* @return The Service UUID of the advertised device.
*/
BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful
return m_serviceUUIDs[0];
} // getServiceUUID
/**
* @brief Check advertised serviced for existence required UUID
* @return Return true if service is advertised
*/
bool BLEAdvertisedDevice::isAdvertisingService(BLEUUID uuid){
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
if (m_serviceUUIDs[i].equals(uuid)) return true;
}
return false;
}
/**
* @brief Get the TX Power.
* @return The TX Power of the advertised device.
*/
int8_t BLEAdvertisedDevice::getTXPower() {
return m_txPower;
} // getTXPower
/**
* @brief Does this advertisement have an appearance value?
* @return True if there is an appearance value present.
*/
bool BLEAdvertisedDevice::haveAppearance() {
return m_haveAppearance;
} // haveAppearance
/**
* @brief Does this advertisement have manufacturer data?
* @return True if there is manufacturer data present.
*/
bool BLEAdvertisedDevice::haveManufacturerData() {
return m_haveManufacturerData;
} // haveManufacturerData
/**
* @brief Does this advertisement have a name value?
* @return True if there is a name value present.
*/
bool BLEAdvertisedDevice::haveName() {
return m_haveName;
} // haveName
/**
* @brief Does this advertisement have a signal strength value?
* @return True if there is a signal strength value present.
*/
bool BLEAdvertisedDevice::haveRSSI() {
return m_haveRSSI;
} // haveRSSI
/**
* @brief Does this advertisement have a service data value?
* @return True if there is a service data value present.
*/
bool BLEAdvertisedDevice::haveServiceData() {
return m_haveServiceData;
} // haveServiceData
/**
* @brief Does this advertisement have a service UUID value?
* @return True if there is a service UUID value present.
*/
bool BLEAdvertisedDevice::haveServiceUUID() {
return m_haveServiceUUID;
} // haveServiceUUID
/**
* @brief Does this advertisement have a transmission power value?
* @return True if there is a transmission power value present.
*/
bool BLEAdvertisedDevice::haveTXPower() {
return m_haveTXPower;
} // haveTXPower
/**
* @brief Parse the advertising pay load.
*
* The pay load is a buffer of bytes that is either 31 bytes long or terminated by
* a 0 length value. Each entry in the buffer has the format:
* [length][type][data...]
*
* The length does not include itself but does include everything after it until the next record. A record
* with a length value of 0 indicates a terminator.
*
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*/
void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, size_t total_len) {
uint8_t length;
uint8_t ad_type;
uint8_t sizeConsumed = 0;
bool finished = false;
m_payload = payload;
m_payloadLength = total_len;
while(!finished) {
length = *payload; // Retrieve the length of the record.
payload++; // Skip to type
sizeConsumed += 1 + length; // increase the size consumed.
if (length != 0) { // A length of 0 indicates that we have reached the end.
ad_type = *payload;
payload++;
length--;
char* pHex = BLEUtils::buildHexData(nullptr, payload, length);
log_d("Type: 0x%.2x (%s), length: %d, data: %s",
ad_type, BLEUtils::advTypeToString(ad_type), length, pHex);
free(pHex);
switch(ad_type) {
case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09
setName(std::string(reinterpret_cast<char*>(payload), length));
break;
} // ESP_BLE_AD_TYPE_NAME_CMPL
case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A
setTXPower(*payload);
break;
} // ESP_BLE_AD_TYPE_TX_PWR
case ESP_BLE_AD_TYPE_APPEARANCE: { // Adv Data Type: 0x19
setAppearance(*reinterpret_cast<uint16_t*>(payload));
break;
} // ESP_BLE_AD_TYPE_APPEARANCE
case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01
setAdFlag(*payload);
break;
} // ESP_BLE_AD_TYPE_FLAG
case ESP_BLE_AD_TYPE_16SRV_CMPL:
case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02
for (int var = 0; var < length/2; ++var) {
setServiceUUID(BLEUUID(*reinterpret_cast<uint16_t*>(payload + var * 2)));
}
break;
} // ESP_BLE_AD_TYPE_16SRV_PART
case ESP_BLE_AD_TYPE_32SRV_CMPL:
case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04
for (int var = 0; var < length/4; ++var) {
setServiceUUID(BLEUUID(*reinterpret_cast<uint32_t*>(payload + var * 4)));
}
break;
} // ESP_BLE_AD_TYPE_32SRV_PART
case ESP_BLE_AD_TYPE_128SRV_CMPL: { // Adv Data Type: 0x07
setServiceUUID(BLEUUID(payload, 16, false));
break;
} // ESP_BLE_AD_TYPE_128SRV_CMPL
case ESP_BLE_AD_TYPE_128SRV_PART: { // Adv Data Type: 0x06
setServiceUUID(BLEUUID(payload, 16, false));
break;
} // ESP_BLE_AD_TYPE_128SRV_PART
// See CSS Part A 1.4 Manufacturer Specific Data
case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: {
setManufacturerData(std::string(reinterpret_cast<char*>(payload), length));
break;
} // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE
case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) - 2 byte UUID
if (length < 2) {
log_e("Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
break;
}
uint16_t uuid = *(uint16_t*)payload;
setServiceDataUUID(BLEUUID(uuid));
if (length > 2) {
setServiceData(std::string(reinterpret_cast<char*>(payload + 2), length - 2));
}
break;
} //ESP_BLE_AD_TYPE_SERVICE_DATA
case ESP_BLE_AD_TYPE_32SERVICE_DATA: { // Adv Data Type: 0x20 (Service Data) - 4 byte UUID
if (length < 4) {
log_e("Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
break;
}
uint32_t uuid = *(uint32_t*) payload;
setServiceDataUUID(BLEUUID(uuid));
if (length > 4) {
setServiceData(std::string(reinterpret_cast<char*>(payload + 4), length - 4));
}
break;
} //ESP_BLE_AD_TYPE_32SERVICE_DATA
case ESP_BLE_AD_TYPE_128SERVICE_DATA: { // Adv Data Type: 0x21 (Service Data) - 16 byte UUID
if (length < 16) {
log_e("Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
break;
}
setServiceDataUUID(BLEUUID(payload, (size_t)16, false));
if (length > 16) {
setServiceData(std::string(reinterpret_cast<char*>(payload + 16), length - 16));
}
break;
} //ESP_BLE_AD_TYPE_32SERVICE_DATA
default: {
log_d("Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type);
break;
}
} // switch
payload += length;
} // Length <> 0
if (sizeConsumed >= total_len)
finished = true;
} // !finished
} // parseAdvertisement
/**
* @brief Set the address of the advertised device.
* @param [in] address The address of the advertised device.
*/
void BLEAdvertisedDevice::setAddress(BLEAddress address) {
m_address = address;
} // setAddress
/**
* @brief Set the adFlag for this device.
* @param [in] The discovered adFlag.
*/
void BLEAdvertisedDevice::setAdFlag(uint8_t adFlag) {
m_adFlag = adFlag;
} // setAdFlag
/**
* @brief Set the appearance for this device.
* @param [in] The discovered appearance.
*/
void BLEAdvertisedDevice::setAppearance(uint16_t appearance) {
m_appearance = appearance;
m_haveAppearance = true;
log_d("- appearance: %d", m_appearance);
} // setAppearance
/**
* @brief Set the manufacturer data for this device.
* @param [in] The discovered manufacturer data.
*/
void BLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
m_manufacturerData = manufacturerData;
m_haveManufacturerData = true;
char* pHex = BLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length());
log_d("- manufacturer data: %s", pHex);
free(pHex);
} // setManufacturerData
/**
* @brief Set the name for this device.
* @param [in] name The discovered name.
*/
void BLEAdvertisedDevice::setName(std::string name) {
m_name = name;
m_haveName = true;
log_d("- setName(): name: %s", m_name.c_str());
} // setName
/**
* @brief Set the RSSI for this device.
* @param [in] rssi The discovered RSSI.
*/
void BLEAdvertisedDevice::setRSSI(int rssi) {
m_rssi = rssi;
m_haveRSSI = true;
log_d("- setRSSI(): rssi: %d", m_rssi);
} // setRSSI
/**
* @brief Set the Scan that created this advertised device.
* @param pScan The Scan that created this advertised device.
*/
void BLEAdvertisedDevice::setScan(BLEScan* pScan) {
m_pScan = pScan;
} // setScan
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
*/
void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
return setServiceUUID(BLEUUID(serviceUUID));
} // setServiceUUID
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
*/
void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) {
m_serviceUUIDs.push_back(serviceUUID);
m_haveServiceUUID = true;
log_d("- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str());
} // setServiceUUID
/**
* @brief Set the ServiceData value.
* @param [in] data ServiceData value.
*/
void BLEAdvertisedDevice::setServiceData(std::string serviceData) {
m_haveServiceData = true; // Set the flag that indicates we have service data.
m_serviceData = serviceData; // Save the service data that we received.
} //setServiceData
/**
* @brief Set the ServiceDataUUID value.
* @param [in] data ServiceDataUUID value.
*/
void BLEAdvertisedDevice::setServiceDataUUID(BLEUUID uuid) {
m_haveServiceData = true; // Set the flag that indicates we have service data.
m_serviceDataUUID = uuid;
} // setServiceDataUUID
/**
* @brief Set the power level for this device.
* @param [in] txPower The discovered power level.
*/
void BLEAdvertisedDevice::setTXPower(int8_t txPower) {
m_txPower = txPower;
m_haveTXPower = true;
log_d("- txPower: %d", m_txPower);
} // setTXPower
/**
* @brief Create a string representation of this device.
* @return A string representation of this device.
*/
std::string BLEAdvertisedDevice::toString() {
std::string res = "Name: " + getName() + ", Address: " + getAddress().toString();
if (haveAppearance()) {
char val[6];
snprintf(val, sizeof(val), "%d", getAppearance());
res += ", appearance: ";
res += val;
}
if (haveManufacturerData()) {
char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length());
res += ", manufacturer data: ";
res += pHex;
free(pHex);
}
if (haveServiceUUID()) {
res += ", serviceUUID: " + getServiceUUID().toString();
}
if (haveTXPower()) {
char val[4];
snprintf(val, sizeof(val), "%d", getTXPower());
res += ", txPower: ";
res += val;
}
return res;
} // toString
uint8_t* BLEAdvertisedDevice::getPayload() {
return m_payload;
}
esp_ble_addr_type_t BLEAdvertisedDevice::getAddressType() {
return m_addressType;
}
void BLEAdvertisedDevice::setAddressType(esp_ble_addr_type_t type) {
m_addressType = type;
}
size_t BLEAdvertisedDevice::getPayloadLength() {
return m_payloadLength;
}
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,123 @@
/*
* BLEAdvertisedDevice.h
*
* Created on: Jul 3, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
#define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gattc_api.h>
#include <map>
#include "BLEAddress.h"
#include "BLEScan.h"
#include "BLEUUID.h"
class BLEScan;
/**
* @brief A representation of a %BLE advertised device found by a scan.
*
* When we perform a %BLE scan, the result will be a set of devices that are advertising. This
* class provides a model of a detected device.
*/
class BLEAdvertisedDevice {
public:
BLEAdvertisedDevice();
BLEAddress getAddress();
uint16_t getAppearance();
std::string getManufacturerData();
std::string getName();
int getRSSI();
BLEScan* getScan();
std::string getServiceData();
BLEUUID getServiceDataUUID();
BLEUUID getServiceUUID();
int8_t getTXPower();
uint8_t* getPayload();
size_t getPayloadLength();
esp_ble_addr_type_t getAddressType();
void setAddressType(esp_ble_addr_type_t type);
bool isAdvertisingService(BLEUUID uuid);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
bool haveRSSI();
bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
std::string toString();
private:
friend class BLEScan;
void parseAdvertisement(uint8_t* payload, size_t total_len=62);
void setAddress(BLEAddress address);
void setAdFlag(uint8_t adFlag);
void setAdvertizementResult(uint8_t* payload);
void setAppearance(uint16_t appearance);
void setManufacturerData(std::string manufacturerData);
void setName(std::string name);
void setRSSI(int rssi);
void setScan(BLEScan* pScan);
void setServiceData(std::string data);
void setServiceDataUUID(BLEUUID uuid);
void setServiceUUID(const char* serviceUUID);
void setServiceUUID(BLEUUID serviceUUID);
void setTXPower(int8_t txPower);
bool m_haveAppearance;
bool m_haveManufacturerData;
bool m_haveName;
bool m_haveRSSI;
bool m_haveServiceData;
bool m_haveServiceUUID;
bool m_haveTXPower;
BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0");
uint8_t m_adFlag;
uint16_t m_appearance;
int m_deviceType;
std::string m_manufacturerData;
std::string m_name;
BLEScan* m_pScan;
int m_rssi;
std::vector<BLEUUID> m_serviceUUIDs;
int8_t m_txPower;
std::string m_serviceData;
BLEUUID m_serviceDataUUID;
uint8_t* m_payload;
size_t m_payloadLength = 0;
esp_ble_addr_type_t m_addressType;
};
/**
* @brief A callback handler for callbacks associated device scanning.
*
* When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising
* has been found. This class can be sub-classed and registered such that when a scan is performed and
* a new advertised device has been found, we will be called back to be notified.
*/
class BLEAdvertisedDeviceCallbacks {
public:
virtual ~BLEAdvertisedDeviceCallbacks() {}
/**
* @brief Called when a new scan result is detected.
*
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
* device that was found. During any individual scan, a device will only be detected one time.
*/
virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0;
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */

View File

@ -0,0 +1,517 @@
/*
* BLEAdvertising.cpp
*
* This class encapsulates advertising a BLE Server.
* Created on: Jun 21, 2017
* Author: kolban
*
* The ESP-IDF provides a framework for BLE advertising. It has determined that there are a common set
* of properties that are advertised and has built a data structure that can be populated by the programmer.
* This means that the programmer doesn't have to "mess with" the low level construction of a low level
* BLE advertising frame. Many of the fields are determined for us while others we can set before starting
* to advertise.
*
* Should we wish to construct our own payload, we can use the BLEAdvertisementData class and call the setters
* upon it. Once it is populated, we can then associate it with the advertising and what ever the programmer
* set in the data will be advertised.
*
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLEAdvertising.h"
#include <esp_err.h>
#include "BLEUtils.h"
#include "GeneralUtils.h"
#include "esp32-hal-log.h"
/**
* @brief Construct a default advertising object.
*
*/
BLEAdvertising::BLEAdvertising() {
m_advData.set_scan_rsp = false;
m_advData.include_name = true;
m_advData.include_txpower = true;
m_advData.min_interval = 0x20;
m_advData.max_interval = 0x40;
m_advData.appearance = 0x00;
m_advData.manufacturer_len = 0;
m_advData.p_manufacturer_data = nullptr;
m_advData.service_data_len = 0;
m_advData.p_service_data = nullptr;
m_advData.service_uuid_len = 0;
m_advData.p_service_uuid = nullptr;
m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
m_advParams.adv_int_min = 0x20;
m_advParams.adv_int_max = 0x40;
m_advParams.adv_type = ADV_TYPE_IND;
m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
m_advParams.channel_map = ADV_CHNL_ALL;
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
m_advParams.peer_addr_type = BLE_ADDR_TYPE_PUBLIC;
m_customAdvData = false; // No custom advertising data
m_customScanResponseData = false; // No custom scan response data
} // BLEAdvertising
/**
* @brief Add a service uuid to exposed list of services.
* @param [in] serviceUUID The UUID of the service to expose.
*/
void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) {
m_serviceUUIDs.push_back(serviceUUID);
} // addServiceUUID
/**
* @brief Add a service uuid to exposed list of services.
* @param [in] serviceUUID The string representation of the service to expose.
*/
void BLEAdvertising::addServiceUUID(const char* serviceUUID) {
addServiceUUID(BLEUUID(serviceUUID));
} // addServiceUUID
/**
* @brief Set the device appearance in the advertising data.
* The appearance attribute is of type 0x19. The codes for distinct appearances can be found here:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
* @param [in] appearance The appearance of the device in the advertising data.
* @return N/A.
*/
void BLEAdvertising::setAppearance(uint16_t appearance) {
m_advData.appearance = appearance;
} // setAppearance
void BLEAdvertising::setMinInterval(uint16_t mininterval) {
m_advParams.adv_int_min = mininterval;
} // setMinInterval
void BLEAdvertising::setMaxInterval(uint16_t maxinterval) {
m_advParams.adv_int_max = maxinterval;
} // setMaxInterval
void BLEAdvertising::setMinPreferred(uint16_t mininterval) {
m_advData.min_interval = mininterval;
} //
void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
m_advData.max_interval = maxinterval;
} //
void BLEAdvertising::setScanResponse(bool set) {
m_scanResp = set;
}
/**
* @brief Set the filtering for the scan filter.
* @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list.
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
*/
void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
log_v(">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
log_v("<< setScanFilter");
return;
}
if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY;
log_v("<< setScanFilter");
return;
}
if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST;
log_v("<< setScanFilter");
return;
}
if (scanRequestWhitelistOnly && connectWhitelistOnly) {
m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST;
log_v("<< setScanFilter");
return;
}
} // setScanFilter
/**
* @brief Set the advertisement data that is to be published in a regular advertisement.
* @param [in] advertisementData The data to be advertised.
*/
void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) {
log_v(">> setAdvertisementData");
esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw(
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (errRc != ESP_OK) {
log_e("esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
}
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
log_v("<< setAdvertisementData");
} // setAdvertisementData
/**
* @brief Set the advertisement data that is to be published in a scan response.
* @param [in] advertisementData The data to be advertised.
*/
void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) {
log_v(">> setScanResponseData");
esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw(
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (errRc != ESP_OK) {
log_e("esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
}
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
log_v("<< setScanResponseData");
} // setScanResponseData
/**
* @brief Start advertising.
* Start advertising.
* @return N/A.
*/
void BLEAdvertising::start() {
log_v(">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
// We have a vector of service UUIDs that we wish to advertise. In order to use the
// ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte)
// representations. If we have 1 or more services to advertise then we allocate enough
// storage to host them and then copy them in one at a time into the contiguous storage.
int numServices = m_serviceUUIDs.size();
if (numServices > 0) {
m_advData.service_uuid_len = 16 * numServices;
m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len];
uint8_t* p = m_advData.p_service_uuid;
for (int i = 0; i < numServices; i++) {
log_d("- advertising service: %s", m_serviceUUIDs[i].toString().c_str());
BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128();
memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16);
p += 16;
}
} else {
m_advData.service_uuid_len = 0;
log_d("- no services advertised");
}
esp_err_t errRc;
if (!m_customAdvData) {
// Set the configuration for advertising.
m_advData.set_scan_rsp = false;
m_advData.include_name = !m_scanResp;
m_advData.include_txpower = !m_scanResp;
errRc = ::esp_ble_gap_config_adv_data(&m_advData);
if (errRc != ESP_OK) {
log_e("<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
}
if (!m_customScanResponseData && m_scanResp) {
m_advData.set_scan_rsp = true;
m_advData.include_name = m_scanResp;
m_advData.include_txpower = m_scanResp;
errRc = ::esp_ble_gap_config_adv_data(&m_advData);
if (errRc != ESP_OK) {
log_e("<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
}
// If we had services to advertise then we previously allocated some storage for them.
// Here we release that storage.
if (m_advData.service_uuid_len > 0) {
delete[] m_advData.p_service_uuid;
m_advData.p_service_uuid = nullptr;
}
// Start advertising.
errRc = ::esp_ble_gap_start_advertising(&m_advParams);
if (errRc != ESP_OK) {
log_e("<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
log_v("<< start");
} // start
/**
* @brief Stop advertising.
* Stop advertising.
* @return N/A.
*/
void BLEAdvertising::stop() {
log_v(">> stop");
esp_err_t errRc = ::esp_ble_gap_stop_advertising();
if (errRc != ESP_OK) {
log_e("esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
log_v("<< stop");
} // stop
/**
* @brief Set BLE address.
* @param [in] Bluetooth address.
* @param [in] Bluetooth address type.
* Set BLE address.
*/
void BLEAdvertising::setDeviceAddress(esp_bd_addr_t addr, esp_ble_addr_type_t type)
{
log_v(">> setPrivateAddress");
m_advParams.own_addr_type = type;
esp_err_t errRc = esp_ble_gap_set_rand_addr((uint8_t*)addr);
if (errRc != ESP_OK)
{
log_e("esp_ble_gap_set_rand_addr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
log_v("<< setPrivateAddress");
} // setPrivateAddress
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
*/
void BLEAdvertisementData::addData(std::string data) {
if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) {
return;
}
m_payload.append(data);
} // addData
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
*
* See also:
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
*/
void BLEAdvertisementData::setAppearance(uint16_t appearance) {
char cdata[2];
cdata[0] = 3;
cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19
addData(std::string(cdata, 2) + std::string((char*) &appearance, 2));
} // setAppearance
/**
* @brief Set the complete services.
* @param [in] uuid The single service to advertise.
*/
void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2));
break;
}
case 32: {
// [Len] [0x04] [LL] [LL] [HH] [HH]
cdata[0] = 5;
cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4));
break;
}
case 128: {
// [Len] [0x04] [0] [1] ... [15]
cdata[0] = 17;
cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07
addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16));
break;
}
default:
return;
}
} // setCompleteServices
/**
* @brief Set the advertisement flags.
* @param [in] The flags to be set in the advertisement.
*
* * ESP_BLE_ADV_FLAG_LIMIT_DISC
* * ESP_BLE_ADV_FLAG_GEN_DISC
* * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT
* * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT
* * ESP_BLE_ADV_FLAG_DMT_HOST_SPT
* * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC
*/
void BLEAdvertisementData::setFlags(uint8_t flag) {
char cdata[3];
cdata[0] = 2;
cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01
cdata[2] = flag;
addData(std::string(cdata, 3));
} // setFlag
/**
* @brief Set manufacturer specific data.
* @param [in] data Manufacturer data.
*/
void BLEAdvertisementData::setManufacturerData(std::string data) {
log_d("BLEAdvertisementData", ">> setManufacturerData");
char cdata[2];
cdata[0] = data.length() + 1;
cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff
addData(std::string(cdata, 2) + data);
log_d("BLEAdvertisementData", "<< setManufacturerData");
} // setManufacturerData
/**
* @brief Set the name.
* @param [in] The complete name of the device.
*/
void BLEAdvertisementData::setName(std::string name) {
log_d("BLEAdvertisementData", ">> setName: %s", name.c_str());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09
addData(std::string(cdata, 2) + name);
log_d("BLEAdvertisementData", "<< setName");
} // setName
/**
* @brief Set the partial services.
* @param [in] uuid The single service to advertise.
*/
void BLEAdvertisementData::setPartialServices(BLEUUID uuid) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x02] [LL] [HH]
cdata[0] = 3;
cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2));
break;
}
case 32: {
// [Len] [0x04] [LL] [LL] [HH] [HH]
cdata[0] = 5;
cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4));
break;
}
case 128: {
// [Len] [0x04] [0] [1] ... [15]
cdata[0] = 17;
cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06
addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16));
break;
}
default:
return;
}
} // setPartialServices
/**
* @brief Set the service data (UUID + data)
* @param [in] uuid The UUID to set with the service data. Size of UUID will be used.
* @param [in] data The data to be associated with the service data advert.
*/
void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) {
char cdata[2];
switch (uuid.bitSize()) {
case 16: {
// [Len] [0x16] [UUID16] data
cdata[0] = data.length() + 3;
cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data);
break;
}
case 32: {
// [Len] [0x20] [UUID32] data
cdata[0] = data.length() + 5;
cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data);
break;
}
case 128: {
// [Len] [0x21] [UUID128] data
cdata[0] = data.length() + 17;
cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21
addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data);
break;
}
default:
return;
}
} // setServiceData
/**
* @brief Set the short name.
* @param [in] The short name of the device.
*/
void BLEAdvertisementData::setShortName(std::string name) {
log_d("BLEAdvertisementData", ">> setShortName: %s", name.c_str());
char cdata[2];
cdata[0] = name.length() + 1;
cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08
addData(std::string(cdata, 2) + name);
log_d("BLEAdvertisementData", "<< setShortName");
} // setShortName
/**
* @brief Retrieve the payload that is to be advertised.
* @return The payload that is to be advertised.
*/
std::string BLEAdvertisementData::getPayload() {
return m_payload;
} // getPayload
void BLEAdvertising::handleGAPEvent(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param) {
log_d("handleGAPEvent [event no: %d]", (int)event);
switch(event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: {
// m_semaphoreSetAdv.give();
break;
}
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: {
// m_semaphoreSetAdv.give();
break;
}
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: {
// m_semaphoreSetAdv.give();
break;
}
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: {
log_i("STOP advertising");
//start();
break;
}
default:
break;
}
}
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,79 @@
/*
* BLEAdvertising.h
*
* Created on: Jun 21, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
#define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gap_ble_api.h>
#include "BLEUUID.h"
#include <vector>
#include "FreeRTOS.h"
/**
* @brief Advertisement data set by the programmer to be published by the %BLE server.
*/
class BLEAdvertisementData {
// Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will
// be exposed on demand/request or as time permits.
//
public:
void setAppearance(uint16_t appearance);
void setCompleteServices(BLEUUID uuid);
void setFlags(uint8_t);
void setManufacturerData(std::string data);
void setName(std::string name);
void setPartialServices(BLEUUID uuid);
void setServiceData(BLEUUID uuid, std::string data);
void setShortName(std::string name);
void addData(std::string data); // Add data to the payload.
std::string getPayload(); // Retrieve the current advert payload.
private:
friend class BLEAdvertising;
std::string m_payload; // The payload of the advertisement.
}; // BLEAdvertisementData
/**
* @brief Perform and manage %BLE advertising.
*
* A %BLE server will want to perform advertising in order to make itself known to %BLE clients.
*/
class BLEAdvertising {
public:
BLEAdvertising();
void addServiceUUID(BLEUUID serviceUUID);
void addServiceUUID(const char* serviceUUID);
void start();
void stop();
void setAppearance(uint16_t appearance);
void setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval);
void setAdvertisementData(BLEAdvertisementData& advertisementData);
void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly);
void setScanResponseData(BLEAdvertisementData& advertisementData);
void setPrivateAddress(esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM);
void setDeviceAddress(esp_bd_addr_t addr, esp_ble_addr_type_t type = BLE_ADDR_TYPE_RANDOM);
void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
void setMinPreferred(uint16_t);
void setMaxPreferred(uint16_t);
void setScanResponse(bool);
private:
esp_ble_adv_data_t m_advData;
esp_ble_adv_params_t m_advParams;
std::vector<BLEUUID> m_serviceUUIDs;
bool m_customAdvData = false; // Are we using custom advertising data?
bool m_customScanResponseData = false; // Are we using custom scan response data?
FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert");
bool m_scanResp = true;
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */

View File

@ -0,0 +1,83 @@
/*
* BLEBeacon.cpp
*
* Created on: Jan 4, 2018
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <string.h>
#include "BLEBeacon.h"
#include "esp32-hal-log.h"
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
BLEBeacon::BLEBeacon() {
m_beaconData.manufacturerId = 0x4c00;
m_beaconData.subType = 0x02;
m_beaconData.subTypeLength = 0x15;
m_beaconData.major = 0;
m_beaconData.minor = 0;
m_beaconData.signalPower = 0;
memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID));
} // BLEBeacon
std::string BLEBeacon::getData() {
return std::string((char*) &m_beaconData, sizeof(m_beaconData));
} // getData
uint16_t BLEBeacon::getMajor() {
return m_beaconData.major;
}
uint16_t BLEBeacon::getManufacturerId() {
return m_beaconData.manufacturerId;
}
uint16_t BLEBeacon::getMinor() {
return m_beaconData.minor;
}
BLEUUID BLEBeacon::getProximityUUID() {
return BLEUUID(m_beaconData.proximityUUID, 16, false);
}
int8_t BLEBeacon::getSignalPower() {
return m_beaconData.signalPower;
}
/**
* Set the raw data for the beacon record.
*/
void BLEBeacon::setData(std::string data) {
if (data.length() != sizeof(m_beaconData)) {
log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData));
return;
}
memcpy(&m_beaconData, data.data(), sizeof(m_beaconData));
} // setData
void BLEBeacon::setMajor(uint16_t major) {
m_beaconData.major = ENDIAN_CHANGE_U16(major);
} // setMajor
void BLEBeacon::setManufacturerId(uint16_t manufacturerId) {
m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId);
} // setManufacturerId
void BLEBeacon::setMinor(uint16_t minor) {
m_beaconData.minor = ENDIAN_CHANGE_U16(minor);
} // setMinior
void BLEBeacon::setProximityUUID(BLEUUID uuid) {
uuid = uuid.to128();
memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16);
} // setProximityUUID
void BLEBeacon::setSignalPower(int8_t signalPower) {
m_beaconData.signalPower = signalPower;
} // setSignalPower
#endif

View File

@ -0,0 +1,43 @@
/*
* BLEBeacon2.h
*
* Created on: Jan 4, 2018
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_
#define COMPONENTS_CPP_UTILS_BLEBEACON_H_
#include "BLEUUID.h"
/**
* @brief Representation of a beacon.
* See:
* * https://en.wikipedia.org/wiki/IBeacon
*/
class BLEBeacon {
private:
struct {
uint16_t manufacturerId;
uint8_t subType;
uint8_t subTypeLength;
uint8_t proximityUUID[16];
uint16_t major;
uint16_t minor;
int8_t signalPower;
} __attribute__((packed)) m_beaconData;
public:
BLEBeacon();
std::string getData();
uint16_t getMajor();
uint16_t getMinor();
uint16_t getManufacturerId();
BLEUUID getProximityUUID();
int8_t getSignalPower();
void setData(std::string data);
void setMajor(uint16_t major);
void setMinor(uint16_t minor);
void setManufacturerId(uint16_t manufacturerId);
void setProximityUUID(BLEUUID uuid);
void setSignalPower(int8_t signalPower);
}; // BLEBeacon
#endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */

View File

@ -0,0 +1,801 @@
/*
* BLECharacteristic.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <sstream>
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include "sdkconfig.h"
#include <esp_err.h>
#include "BLECharacteristic.h"
#include "BLEService.h"
#include "BLEDevice.h"
#include "BLEUtils.h"
#include "BLE2902.h"
#include "GeneralUtils.h"
#include "esp32-hal-log.h"
#define NULL_HANDLE (0xffff)
static BLECharacteristicCallbacks defaultCallback; //null-object-pattern
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic.
* @param [in] properties - Properties for the characteristic.
*/
BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BLECharacteristic(BLEUUID(uuid), properties) {
}
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID for the characteristic.
* @param [in] properties - Properties for the characteristic.
*/
BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) {
m_bleUUID = uuid;
m_handle = NULL_HANDLE;
m_properties = (esp_gatt_char_prop_t)0;
m_pCallbacks = &defaultCallback;
setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0);
setReadProperty((properties & PROPERTY_READ) != 0);
setWriteProperty((properties & PROPERTY_WRITE) != 0);
setNotifyProperty((properties & PROPERTY_NOTIFY) != 0);
setIndicateProperty((properties & PROPERTY_INDICATE) != 0);
setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0);
} // BLECharacteristic
/**
* @brief Destructor.
*/
BLECharacteristic::~BLECharacteristic() {
//free(m_value.attr_value); // Release the storage for the value.
} // ~BLECharacteristic
/**
* @brief Associate a descriptor with this characteristic.
* @param [in] pDescriptor
* @return N/A.
*/
void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) {
log_v(">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str());
m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor);
log_v("<< addDescriptor()");
} // addDescriptor
/**
* @brief Register a new characteristic with the ESP runtime.
* @param [in] pService The service with which to associate this characteristic.
*/
void BLECharacteristic::executeCreate(BLEService* pService) {
log_v(">> executeCreate()");
if (m_handle != NULL_HANDLE) {
log_e("Characteristic already has a handle.");
return;
}
m_pService = pService; // Save the service to which this characteristic belongs.
log_d("Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s",
getUUID().toString().c_str(),
m_pService->toString().c_str());
esp_attr_control_t control;
control.auto_rsp = ESP_GATT_RSP_BY_APP;
m_semaphoreCreateEvt.take("executeCreate");
esp_err_t errRc = ::esp_ble_gatts_add_char(
m_pService->getHandle(),
getUUID().getNative(),
static_cast<esp_gatt_perm_t>(m_permissions),
getProperties(),
nullptr,
&control); // Whether to auto respond or not.
if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
m_semaphoreCreateEvt.wait("executeCreate");
BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
while (pDescriptor != nullptr) {
pDescriptor->executeCreate(this);
pDescriptor = m_descriptorMap.getNext();
} // End while
log_v("<< executeCreate");
} // executeCreate
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) {
return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID));
} // getDescriptorByUUID
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
* @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
* @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
BLEDescriptor* BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) {
return m_descriptorMap.getByUUID(descriptorUUID);
} // getDescriptorByUUID
/**
* @brief Get the handle of the characteristic.
* @return The handle of the characteristic.
*/
uint16_t BLECharacteristic::getHandle() {
return m_handle;
} // getHandle
void BLECharacteristic::setAccessPermissions(esp_gatt_perm_t perm) {
m_permissions = perm;
}
esp_gatt_char_prop_t BLECharacteristic::getProperties() {
return m_properties;
} // getProperties
/**
* @brief Get the service associated with this characteristic.
*/
BLEService* BLECharacteristic::getService() {
return m_pService;
} // getService
/**
* @brief Get the UUID of the characteristic.
* @return The UUID of the characteristic.
*/
BLEUUID BLECharacteristic::getUUID() {
return m_bleUUID;
} // getUUID
/**
* @brief Retrieve the current value of the characteristic.
* @return A pointer to storage containing the current characteristic value.
*/
std::string BLECharacteristic::getValue() {
return m_value.getValue();
} // getValue
/**
* @brief Retrieve the current raw data of the characteristic.
* @return A pointer to storage containing the current characteristic data.
*/
uint8_t* BLECharacteristic::getData() {
return m_value.getData();
} // getData
/**
* Handle a GATT server event.
*/
void BLECharacteristic::handleGATTServerEvent(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param) {
log_v(">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str());
switch(event) {
// Events handled:
//
// ESP_GATTS_ADD_CHAR_EVT
// ESP_GATTS_CONF_EVT
// ESP_GATTS_CONNECT_EVT
// ESP_GATTS_DISCONNECT_EVT
// ESP_GATTS_EXEC_WRITE_EVT
// ESP_GATTS_READ_EVT
// ESP_GATTS_WRITE_EVT
//
// ESP_GATTS_EXEC_WRITE_EVT
// When we receive this event it is an indication that a previous write long needs to be committed.
//
// exec_write:
// - uint16_t conn_id
// - uint32_t trans_id
// - esp_bd_addr_t bda
// - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL
//
case ESP_GATTS_EXEC_WRITE_EVT: {
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
m_value.commit();
m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
} else {
m_value.cancel();
}
// ???
esp_err_t errRc = ::esp_ble_gatts_send_response(
gatts_if,
param->write.conn_id,
param->write.trans_id, ESP_GATT_OK, nullptr);
if (errRc != ESP_OK) {
log_e("esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
break;
} // ESP_GATTS_EXEC_WRITE_EVT
// ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
// add_char:
// - esp_gatt_status_t status
// - uint16_t attr_handle
// - uint16_t service_handle
// - esp_bt_uuid_t char_uuid
case ESP_GATTS_ADD_CHAR_EVT: {
if (getHandle() == param->add_char.attr_handle) {
// we have created characteristic, now we can create descriptors
// BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
// while (pDescriptor != nullptr) {
// pDescriptor->executeCreate(this);
// pDescriptor = m_descriptorMap.getNext();
// } // End while
m_semaphoreCreateEvt.give();
}
break;
} // ESP_GATTS_ADD_CHAR_EVT
// ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived.
//
// write:
// - uint16_t conn_id
// - uint16_t trans_id
// - esp_bd_addr_t bda
// - uint16_t handle
// - uint16_t offset
// - bool need_rsp
// - bool is_prep
// - uint16_t len
// - uint8_t *value
//
case ESP_GATTS_WRITE_EVT: {
// We check if this write request is for us by comparing the handles in the event. If it is for us
// we save the new value. Next we look at the need_rsp flag which indicates whether or not we need
// to send a response. If we do, then we formulate a response and send it.
if (param->write.handle == m_handle) {
if (param->write.is_prep) {
m_value.addPart(param->write.value, param->write.len);
} else {
setValue(param->write.value, param->write.len);
}
log_d(" - Response to write event: New value: handle: %.2x, uuid: %s",
getHandle(), getUUID().toString().c_str());
char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len);
log_d(" - Data: length: %d, data: %s", param->write.len, pHexData);
free(pHexData);
if (param->write.need_rsp) {
esp_gatt_rsp_t rsp;
rsp.attr_value.len = param->write.len;
rsp.attr_value.handle = m_handle;
rsp.attr_value.offset = param->write.offset;
rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
memcpy(rsp.attr_value.value, param->write.value, param->write.len);
esp_err_t errRc = ::esp_ble_gatts_send_response(
gatts_if,
param->write.conn_id,
param->write.trans_id, ESP_GATT_OK, &rsp);
if (errRc != ESP_OK) {
log_e("esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
} // Response needed
if (param->write.is_prep != true) {
m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
}
} // Match on handles.
break;
} // ESP_GATTS_WRITE_EVT
// ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived.
//
// read:
// - uint16_t conn_id
// - uint32_t trans_id
// - esp_bd_addr_t bda
// - uint16_t handle
// - uint16_t offset
// - bool is_long
// - bool need_rsp
//
case ESP_GATTS_READ_EVT: {
if (param->read.handle == m_handle) {
// Here's an interesting thing. The read request has the option of saying whether we need a response
// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like
// a very strange read.
//
// We have to handle the case where the data we wish to send back to the client is greater than the maximum
// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes.
// The apparent algorithm is as follows:
//
// If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes.
// If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than
// 22 bytes, then we "just" send it and thats the end of the story.
// If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request.
// If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request.
// Because of follow on request processing, we need to maintain an offset of how much data we have already sent
// so that when a follow on request arrives, we know where to start in the data to send the next sequence.
// Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response.
// If our payload is divisible by 22 then the last response will be a response of 0 bytes in length.
//
// The following code has deliberately not been factored to make it fewer statements because this would cloud the
// the logic flow comprehension.
//
// get mtu for peer device that we are sending read request to
uint16_t maxOffset = getService()->getServer()->getPeerMTU(param->read.conn_id) - 1;
log_d("mtu value: %d", maxOffset);
if (param->read.need_rsp) {
log_d("Sending a response (esp_ble_gatts_send_response)");
esp_gatt_rsp_t rsp;
if (param->read.is_long) {
std::string value = m_value.getValue();
if (value.length() - m_value.getReadOffset() < maxOffset) {
// This is the last in the chain
rsp.attr_value.len = value.length() - m_value.getReadOffset();
rsp.attr_value.offset = m_value.getReadOffset();
memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
m_value.setReadOffset(0);
} else {
// There will be more to come.
rsp.attr_value.len = maxOffset;
rsp.attr_value.offset = m_value.getReadOffset();
memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
m_value.setReadOffset(rsp.attr_value.offset + maxOffset);
}
} else { // read.is_long == false
// If is.long is false then this is the first (or only) request to read data, so invoke the callback
// Invoke the read callback.
m_pCallbacks->onRead(this);
std::string value = m_value.getValue();
if (value.length() + 1 > maxOffset) {
// Too big for a single shot entry.
m_value.setReadOffset(maxOffset);
rsp.attr_value.len = maxOffset;
rsp.attr_value.offset = 0;
memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
} else {
// Will fit in a single packet with no callbacks required.
rsp.attr_value.len = value.length();
rsp.attr_value.offset = 0;
memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
}
}
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
char *pHexData = BLEUtils::buildHexData(nullptr, rsp.attr_value.value, rsp.attr_value.len);
log_d(" - Data: length=%d, data=%s, offset=%d", rsp.attr_value.len, pHexData, rsp.attr_value.offset);
free(pHexData);
esp_err_t errRc = ::esp_ble_gatts_send_response(
gatts_if, param->read.conn_id,
param->read.trans_id,
ESP_GATT_OK,
&rsp);
if (errRc != ESP_OK) {
log_e("esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
} // Response needed
} // Handle matches this characteristic.
break;
} // ESP_GATTS_READ_EVT
// ESP_GATTS_CONF_EVT
//
// conf:
// - esp_gatt_status_t status The status code.
// - uint16_t conn_id The connection used.
//
case ESP_GATTS_CONF_EVT: {
// log_d("m_handle = %d, conf->handle = %d", m_handle, param->conf.handle);
if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet
m_semaphoreConfEvt.give(param->conf.status);
break;
}
case ESP_GATTS_CONNECT_EVT: {
break;
}
case ESP_GATTS_DISCONNECT_EVT: {
m_semaphoreConfEvt.give();
break;
}
default: {
break;
} // default
} // switch event
// Give each of the descriptors associated with this characteristic the opportunity to handle the
// event.
m_descriptorMap.handleGATTServerEvent(event, gatts_if, param);
log_v("<< handleGATTServerEvent");
} // handleGATTServerEvent
/**
* @brief Send an indication.
* An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
* will block waiting a positive confirmation from the client.
* @return N/A
*/
void BLECharacteristic::indicate() {
log_v(">> indicate: length: %d", m_value.getValue().length());
notify(false);
log_v("<< indicate");
} // indicate
/**
* @brief Send a notify.
* A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
* will not block; it is a fire and forget.
* @return N/A.
*/
void BLECharacteristic::notify(bool is_notification) {
log_v(">> notify: length: %d", m_value.getValue().length());
assert(getService() != nullptr);
assert(getService()->getServer() != nullptr);
m_pCallbacks->onNotify(this); // Invoke the notify callback.
GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length());
if (getService()->getServer()->getConnectedCount() == 0) {
log_v("<< notify: No connected clients.");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NO_CLIENT, 0);
return;
}
// Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled
// and, if not, prevent the notification.
BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902);
if(is_notification) {
if (p2902 != nullptr && !p2902->getNotifications()) {
log_v("<< notifications disabled; ignoring");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_NOTIFY_DISABLED, 0); // Invoke the notify callback.
return;
}
}
else{
if (p2902 != nullptr && !p2902->getIndications()) {
log_v("<< indications disabled; ignoring");
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED, 0); // Invoke the notify callback.
return;
}
}
for (auto &myPair : getService()->getServer()->getPeerDevices(false)) {
uint16_t _mtu = (myPair.second.mtu);
if (m_value.getValue().length() > _mtu - 3) {
log_w("- Truncating to %d bytes (maximum notify size)", _mtu - 3);
}
size_t length = m_value.getValue().length();
if(!is_notification) // is indication
m_semaphoreConfEvt.take("indicate");
esp_err_t errRc = ::esp_ble_gatts_send_indicate(
getService()->getServer()->getGattsIf(),
myPair.first,
getHandle(), length, (uint8_t*)m_value.getValue().data(), !is_notification); // The need_confirm = false makes this a notify.
if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc));
m_semaphoreConfEvt.give();
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_GATT, errRc); // Invoke the notify callback.
return;
}
if(!is_notification){ // is indication
if(!m_semaphoreConfEvt.timedWait("indicate", indicationTimeout)){
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, 0); // Invoke the notify callback.
} else {
auto code = (esp_gatt_status_t) m_semaphoreConfEvt.value();
if(code == ESP_GATT_OK) {
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_INDICATE, code); // Invoke the notify callback.
} else {
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, code);
}
}
} else {
m_pCallbacks->onStatus(this, BLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); // Invoke the notify callback.
}
}
log_v("<< notify");
} // Notify
/**
* @brief Set the permission to broadcast.
* A characteristics has properties associated with it which define what it is capable of doing.
* One of these is the broadcast flag.
* @param [in] value The flag value of the property.
* @return N/A
*/
void BLECharacteristic::setBroadcastProperty(bool value) {
//log_d("setBroadcastProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
}
} // setBroadcastProperty
/**
* @brief Set the callback handlers for this characteristic.
* @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
*/
void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) {
log_v(">> setCallbacks: 0x%x", (uint32_t)pCallbacks);
if (pCallbacks != nullptr){
m_pCallbacks = pCallbacks;
} else {
m_pCallbacks = &defaultCallback;
}
log_v("<< setCallbacks");
} // setCallbacks
/**
* @brief Set the BLE handle associated with this characteristic.
* A user program will request that a characteristic be created against a service. When the characteristic has been
* registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the
* server/service but it is told to the service, not the characteristic associated with the service. This internally
* exposed function can be invoked by the service against this model of the characteristic to allow the characteristic
* to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events
* that will be propagated down to it which contain a handle value and now know that the event is destined for it.
* @param [in] handle The handle associated with this characteristic.
*/
void BLECharacteristic::setHandle(uint16_t handle) {
log_v(">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str());
m_handle = handle;
log_v("<< setHandle");
} // setHandle
/**
* @brief Set the Indicate property value.
* @param [in] value Set to true if we are to allow indicate messages.
*/
void BLECharacteristic::setIndicateProperty(bool value) {
//log_d("setIndicateProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
}
} // setIndicateProperty
/**
* @brief Set the Notify property value.
* @param [in] value Set to true if we are to allow notification messages.
*/
void BLECharacteristic::setNotifyProperty(bool value) {
//log_d("setNotifyProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
}
} // setNotifyProperty
/**
* @brief Set the Read property value.
* @param [in] value Set to true if we are to allow reads.
*/
void BLECharacteristic::setReadProperty(bool value) {
//log_d("setReadProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ);
}
} // setReadProperty
/**
* @brief Set the value of the characteristic.
* @param [in] data The data to set for the characteristic.
* @param [in] length The length of the data in bytes.
*/
void BLECharacteristic::setValue(uint8_t* data, size_t length) {
char* pHex = BLEUtils::buildHexData(nullptr, data, length);
log_v(">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
free(pHex);
if (length > ESP_GATT_MAX_ATTR_LEN) {
log_e("Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
return;
}
m_semaphoreSetValue.take();
m_value.setValue(data, length);
m_semaphoreSetValue.give();
log_v("<< setValue");
} // setValue
/**
* @brief Set the value of the characteristic from string data.
* We set the value of the characteristic from the bytes contained in the
* string.
* @param [in] Set the value of the characteristic.
* @return N/A.
*/
void BLECharacteristic::setValue(std::string value) {
setValue((uint8_t*)(value.data()), value.length());
} // setValue
void BLECharacteristic::setValue(uint16_t& data16) {
uint8_t temp[2];
temp[0] = data16;
temp[1] = data16 >> 8;
setValue(temp, 2);
} // setValue
void BLECharacteristic::setValue(uint32_t& data32) {
uint8_t temp[4];
temp[0] = data32;
temp[1] = data32 >> 8;
temp[2] = data32 >> 16;
temp[3] = data32 >> 24;
setValue(temp, 4);
} // setValue
void BLECharacteristic::setValue(int& data32) {
uint8_t temp[4];
temp[0] = data32;
temp[1] = data32 >> 8;
temp[2] = data32 >> 16;
temp[3] = data32 >> 24;
setValue(temp, 4);
} // setValue
void BLECharacteristic::setValue(float& data32) {
uint8_t temp[4];
*((float*)temp) = data32;
setValue(temp, 4);
} // setValue
void BLECharacteristic::setValue(double& data64) {
uint8_t temp[8];
*((double*)temp) = data64;
setValue(temp, 8);
} // setValue
/**
* @brief Set the Write No Response property value.
* @param [in] value Set to true if we are to allow writes with no response.
*/
void BLECharacteristic::setWriteNoResponseProperty(bool value) {
//log_d("setWriteNoResponseProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
}
} // setWriteNoResponseProperty
/**
* @brief Set the Write property value.
* @param [in] value Set to true if we are to allow writes.
*/
void BLECharacteristic::setWriteProperty(bool value) {
//log_d("setWriteProperty(%d)", value);
if (value) {
m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE);
} else {
m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
}
} // setWriteProperty
/**
* @brief Return a string representation of the characteristic.
* @return A string representation of the characteristic.
*/
std::string BLECharacteristic::toString() {
std::string res = "UUID: " + m_bleUUID.toString() + ", handle : 0x";
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
res += hex;
res += " ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_READ) res += "Read ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE) res += "Write ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) res += "WriteNoResponse ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST) res += "Broadcast ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) res += "Notify ";
if (m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE) res += "Indicate ";
return res;
} // toString
BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {}
/**
* @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
void BLECharacteristicCallbacks::onRead(BLECharacteristic* pCharacteristic) {
log_d("BLECharacteristicCallbacks", ">> onRead: default");
log_d("BLECharacteristicCallbacks", "<< onRead");
} // onRead
/**
* @brief Callback function to support a write request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
void BLECharacteristicCallbacks::onWrite(BLECharacteristic* pCharacteristic) {
log_d("BLECharacteristicCallbacks", ">> onWrite: default");
log_d("BLECharacteristicCallbacks", "<< onWrite");
} // onWrite
/**
* @brief Callback function to support a Notify request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
*/
void BLECharacteristicCallbacks::onNotify(BLECharacteristic* pCharacteristic) {
log_d("BLECharacteristicCallbacks", ">> onNotify: default");
log_d("BLECharacteristicCallbacks", "<< onNotify");
} // onNotify
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
* @param [in] s Status of the notification/indication
* @param [in] code Additional code of underlying errors
*/
void BLECharacteristicCallbacks::onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code) {
log_d("BLECharacteristicCallbacks", ">> onStatus: default");
log_d("BLECharacteristicCallbacks", "<< onStatus");
} // onStatus
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,153 @@
/*
* BLECharacteristic.h
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
#define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <string>
#include <map>
#include "BLEUUID.h"
#include <esp_gatts_api.h>
#include <esp_gap_ble_api.h>
#include "BLEDescriptor.h"
#include "BLEValue.h"
#include "FreeRTOS.h"
class BLEService;
class BLEDescriptor;
class BLECharacteristicCallbacks;
/**
* @brief A management structure for %BLE descriptors.
*/
class BLEDescriptorMap {
public:
void setByUUID(const char* uuid, BLEDescriptor* pDescriptor);
void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor);
void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor);
BLEDescriptor* getByUUID(const char* uuid);
BLEDescriptor* getByUUID(BLEUUID uuid);
BLEDescriptor* getByHandle(uint16_t handle);
std::string toString();
void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param);
BLEDescriptor* getFirst();
BLEDescriptor* getNext();
private:
std::map<BLEDescriptor*, std::string> m_uuidMap;
std::map<uint16_t, BLEDescriptor*> m_handleMap;
std::map<BLEDescriptor*, std::string>::iterator m_iterator;
};
/**
* @brief The model of a %BLE Characteristic.
*
* A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and
* can be read and written to by a %BLE client.
*/
class BLECharacteristic {
public:
BLECharacteristic(const char* uuid, uint32_t properties = 0);
BLECharacteristic(BLEUUID uuid, uint32_t properties = 0);
virtual ~BLECharacteristic();
void addDescriptor(BLEDescriptor* pDescriptor);
BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID);
BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID);
BLEUUID getUUID();
std::string getValue();
uint8_t* getData();
void indicate();
void notify(bool is_notification = true);
void setBroadcastProperty(bool value);
void setCallbacks(BLECharacteristicCallbacks* pCallbacks);
void setIndicateProperty(bool value);
void setNotifyProperty(bool value);
void setReadProperty(bool value);
void setValue(uint8_t* data, size_t size);
void setValue(std::string value);
void setValue(uint16_t& data16);
void setValue(uint32_t& data32);
void setValue(int& data32);
void setValue(float& data32);
void setValue(double& data64);
void setWriteProperty(bool value);
void setWriteNoResponseProperty(bool value);
std::string toString();
uint16_t getHandle();
void setAccessPermissions(esp_gatt_perm_t perm);
static const uint32_t PROPERTY_READ = 1<<0;
static const uint32_t PROPERTY_WRITE = 1<<1;
static const uint32_t PROPERTY_NOTIFY = 1<<2;
static const uint32_t PROPERTY_BROADCAST = 1<<3;
static const uint32_t PROPERTY_INDICATE = 1<<4;
static const uint32_t PROPERTY_WRITE_NR = 1<<5;
static const uint32_t indicationTimeout = 1000;
private:
friend class BLEServer;
friend class BLEService;
friend class BLEDescriptor;
friend class BLECharacteristicMap;
BLEUUID m_bleUUID;
BLEDescriptorMap m_descriptorMap;
uint16_t m_handle;
esp_gatt_char_prop_t m_properties;
BLECharacteristicCallbacks* m_pCallbacks;
BLEService* m_pService;
BLEValue m_value;
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
void handleGATTServerEvent(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param);
void executeCreate(BLEService* pService);
esp_gatt_char_prop_t getProperties();
BLEService* getService();
void setHandle(uint16_t handle);
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt");
FreeRTOS::Semaphore m_semaphoreSetValue = FreeRTOS::Semaphore("SetValue");
}; // BLECharacteristic
/**
* @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
*
* When a server application creates a %BLE characteristic, we may wish to be informed when there is either
* a read or write request to the characteristic's value. An application can register a
* sub-classed instance of this class and will be notified when such an event happens.
*/
class BLECharacteristicCallbacks {
public:
typedef enum {
SUCCESS_INDICATE,
SUCCESS_NOTIFY,
ERROR_INDICATE_DISABLED,
ERROR_NOTIFY_DISABLED,
ERROR_GATT,
ERROR_NO_CLIENT,
ERROR_INDICATE_TIMEOUT,
ERROR_INDICATE_FAILURE
}Status;
virtual ~BLECharacteristicCallbacks();
virtual void onRead(BLECharacteristic* pCharacteristic);
virtual void onWrite(BLECharacteristic* pCharacteristic);
virtual void onNotify(BLECharacteristic* pCharacteristic);
virtual void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code);
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */

View File

@ -0,0 +1,134 @@
/*
* BLECharacteristicMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <sstream>
#include <iomanip>
#include "BLEService.h"
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#endif
/**
* @brief Return the characteristic by handle.
* @param [in] handle The handle to look up the characteristic.
* @return The characteristic.
*/
BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
/**
* @brief Return the characteristic by UUID.
* @param [in] UUID The UUID to look up the characteristic.
* @return The characteristic.
*/
BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) {
return getByUUID(BLEUUID(uuid));
}
/**
* @brief Return the characteristic by UUID.
* @param [in] UUID The UUID to look up the characteristic.
* @return The characteristic.
*/
BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
//return m_uuidMap.at(uuid.toString());
return nullptr;
} // getByUUID
/**
* @brief Get the first characteristic in the map.
* @return The first characteristic in the map.
*/
BLECharacteristic* BLECharacteristicMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
BLECharacteristic* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next characteristic in the map.
* @return The next characteristic in the map.
*/
BLECharacteristic* BLECharacteristicMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
BLECharacteristic* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
/**
* @brief Pass the GATT server event onwards to each of the characteristics found in the mapping
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*/
void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) {
// Invoke the handler for every Service we have.
for (auto& myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(event, gatts_if, param);
}
} // handleGATTServerEvent
/**
* @brief Set the characteristic by handle.
* @param [in] handle The handle of the characteristic.
* @param [in] characteristic The characteristic to cache.
* @return N/A.
*/
void BLECharacteristicMap::setByHandle(uint16_t handle, BLECharacteristic* characteristic) {
m_handleMap.insert(std::pair<uint16_t, BLECharacteristic*>(handle, characteristic));
} // setByHandle
/**
* @brief Set the characteristic by UUID.
* @param [in] uuid The uuid of the characteristic.
* @param [in] characteristic The characteristic to cache.
* @return N/A.
*/
void BLECharacteristicMap::setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid) {
m_uuidMap.insert(std::pair<BLECharacteristic*, std::string>(pCharacteristic, uuid.toString()));
} // setByUUID
/**
* @brief Return a string representation of the characteristic map.
* @return A string representation of the characteristic map.
*/
std::string BLECharacteristicMap::toString() {
std::string res;
int count = 0;
char hex[5];
for (auto &myPair: m_uuidMap) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + myPair.first->getUUID().toString();
}
return res;
} // toString
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,529 @@
/*
* BLEDevice.cpp
*
* Created on: Mar 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_bt.h>
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include "BLEClient.h"
#include "BLEUtils.h"
#include "BLEService.h"
#include "GeneralUtils.h"
#include <string>
#include <sstream>
#include <unordered_set>
#include "BLEDevice.h"
#include "esp32-hal-log.h"
/*
* Design
* ------
* When we perform a searchService() requests, we are asking the BLE server to return each of the services
* that it exposes. For each service, we received an ESP_GATTC_SEARCH_RES_EVT event which contains details
* of the exposed service including its UUID.
*
* The objects we will invent for a BLEClient will be as follows:
* * BLERemoteService - A model of a remote service.
* * BLERemoteCharacteristic - A model of a remote characteristic
* * BLERemoteDescriptor - A model of a remote descriptor.
*
* Since there is a hierarchical relationship here, we will have the idea that from a BLERemoteService will own
* zero or more remote characteristics and a BLERemoteCharacteristic will own zero or more remote BLEDescriptors.
*
* We will assume that a BLERemoteService contains a map that maps BLEUUIDs to the set of owned characteristics
* and that a BLECharacteristic contains a map that maps BLEUUIDs to the set of owned descriptors.
*
*
*/
BLEClient::BLEClient() {
m_pClientCallbacks = nullptr;
m_conn_id = ESP_GATT_IF_NONE;
m_gattc_if = ESP_GATT_IF_NONE;
m_haveServices = false;
m_isConnected = false; // Initially, we are flagged as not connected.
} // BLEClient
/**
* @brief Destructor.
*/
BLEClient::~BLEClient() {
// We may have allocated service references associated with this client. Before we are finished
// with the client, we must release resources.
for (auto &myPair : m_servicesMap) {
delete myPair.second;
}
m_servicesMap.clear();
} // ~BLEClient
/**
* @brief Clear any existing services.
*
*/
void BLEClient::clearServices() {
log_v(">> clearServices");
// Delete all the services.
for (auto &myPair : m_servicesMap) {
delete myPair.second;
}
m_servicesMap.clear();
m_haveServices = false;
log_v("<< clearServices");
} // clearServices
/**
* Add overloaded function to ease connect to peer device with not public address
*/
bool BLEClient::connect(BLEAdvertisedDevice* device) {
BLEAddress address = device->getAddress();
esp_ble_addr_type_t type = device->getAddressType();
return connect(address, type);
}
/**
* @brief Connect to the partner (BLE Server).
* @param [in] address The address of the partner.
* @return True on success.
*/
bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) {
log_v(">> connect(%s)", address.toString().c_str());
// We need the connection handle that we get from registering the application. We register the app
// and then block on its completion. When the event has arrived, we will have the handle.
m_appId = BLEDevice::m_appId++;
BLEDevice::addPeerDevice(this, true, m_appId);
m_semaphoreRegEvt.take("connect");
// clearServices(); // we dont need to delete services since every client is unique?
esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
m_semaphoreRegEvt.wait("connect");
m_peerAddress = address;
// Perform the open connection request against the target BLE Server.
m_semaphoreOpenEvt.take("connect");
errRc = ::esp_ble_gattc_open(
m_gattc_if,
*getPeerAddress().getNative(), // address
type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature.
1 // direct connection <-- maybe needs to be changed in case of direct indirect connection???
);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
log_v("<< connect(), rc=%d", rc==ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
/**
* @brief Disconnect from the peer.
* @return N/A.
*/
void BLEClient::disconnect() {
log_v(">> disconnect()");
esp_err_t errRc = ::esp_ble_gattc_close(getGattcIf(), getConnId());
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
log_v("<< disconnect()");
} // disconnect
/**
* @brief Handle GATT Client events
*/
void BLEClient::gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* evtParam) {
log_d("gattClientEventHandler [esp_gatt_if: %d] ... %s",
gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str());
// Execute handler code based on the type of event received.
switch(event) {
case ESP_GATTC_SRVC_CHG_EVT:
log_i("SERVICE CHANGED");
break;
case ESP_GATTC_CLOSE_EVT: {
// esp_ble_gattc_app_unregister(m_appId);
// BLEDevice::removePeerDevice(m_gattc_if, true);
break;
}
//
// ESP_GATTC_DISCONNECT_EVT
//
// disconnect:
// - esp_gatt_status_t status
// - uint16_t conn_id
// - esp_bd_addr_t remote_bda
case ESP_GATTC_DISCONNECT_EVT: {
// If we receive a disconnect event, set the class flag that indicates that we are
// no longer connected.
m_isConnected = false;
if (m_pClientCallbacks != nullptr) {
m_pClientCallbacks->onDisconnect(this);
}
esp_ble_gattc_app_unregister(m_gattc_if);
m_semaphoreOpenEvt.give(ESP_GATT_IF_NONE);
m_semaphoreRssiCmplEvt.give();
m_semaphoreSearchCmplEvt.give(1);
BLEDevice::removePeerDevice(m_appId, true);
break;
} // ESP_GATTC_DISCONNECT_EVT
//
// ESP_GATTC_OPEN_EVT
//
// open:
// - esp_gatt_status_t status
// - uint16_t conn_id
// - esp_bd_addr_t remote_bda
//
case ESP_GATTC_OPEN_EVT: {
m_conn_id = evtParam->open.conn_id;
if (m_pClientCallbacks != nullptr) {
m_pClientCallbacks->onConnect(this);
}
if (evtParam->open.status == ESP_GATT_OK) {
m_isConnected = true; // Flag us as connected.
}
m_semaphoreOpenEvt.give(evtParam->open.status);
break;
} // ESP_GATTC_OPEN_EVT
//
// ESP_GATTC_REG_EVT
//
// reg:
// esp_gatt_status_t status
// uint16_t app_id
//
case ESP_GATTC_REG_EVT: {
m_gattc_if = gattc_if;
m_semaphoreRegEvt.give();
break;
} // ESP_GATTC_REG_EVT
case ESP_GATTC_CFG_MTU_EVT:
if(evtParam->cfg_mtu.status != ESP_GATT_OK) {
log_e("Config mtu failed");
}
m_mtu = evtParam->cfg_mtu.mtu;
break;
case ESP_GATTC_CONNECT_EVT: {
BLEDevice::updatePeerDevice(this, true, m_gattc_if);
esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, evtParam->connect.conn_id);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
if(BLEDevice::m_securityLevel){
esp_ble_set_encryption(evtParam->connect.remote_bda, BLEDevice::m_securityLevel);
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
} // ESP_GATTC_CONNECT_EVT
//
// ESP_GATTC_SEARCH_CMPL_EVT
//
// search_cmpl:
// - esp_gatt_status_t status
// - uint16_t conn_id
//
case ESP_GATTC_SEARCH_CMPL_EVT: {
esp_ble_gattc_cb_param_t* p_data = (esp_ble_gattc_cb_param_t*)evtParam;
if (p_data->search_cmpl.status != ESP_GATT_OK){
log_e("search service failed, error status = %x", p_data->search_cmpl.status);
break;
}
#ifndef ARDUINO_ARCH_ESP32
// commented out just for now to keep backward compatibility
// if(p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE) {
// log_i("Get service information from remote device");
// } else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH) {
// log_i("Get service information from flash");
// } else {
// log_i("unknown service source");
// }
#endif
m_semaphoreSearchCmplEvt.give(0);
break;
} // ESP_GATTC_SEARCH_CMPL_EVT
//
// ESP_GATTC_SEARCH_RES_EVT
//
// search_res:
// - uint16_t conn_id
// - uint16_t start_handle
// - uint16_t end_handle
// - esp_gatt_id_t srvc_id
//
case ESP_GATTC_SEARCH_RES_EVT: {
BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id);
BLERemoteService* pRemoteService = new BLERemoteService(
evtParam->search_res.srvc_id,
this,
evtParam->search_res.start_handle,
evtParam->search_res.end_handle
);
m_servicesMap.insert(std::pair<std::string, BLERemoteService*>(uuid.toString(), pRemoteService));
m_servicesMapByInstID.insert(std::pair<BLERemoteService *, uint16_t>(pRemoteService, evtParam->search_res.srvc_id.inst_id));
break;
} // ESP_GATTC_SEARCH_RES_EVT
default: {
break;
}
} // Switch
// Pass the request on to all services.
for (auto &myPair : m_servicesMap) {
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
} // gattClientEventHandler
uint16_t BLEClient::getConnId() {
return m_conn_id;
} // getConnId
esp_gatt_if_t BLEClient::getGattcIf() {
return m_gattc_if;
} // getGattcIf
/**
* @brief Retrieve the address of the peer.
*
* Returns the Bluetooth device address of the %BLE peer to which this client is connected.
*/
BLEAddress BLEClient::getPeerAddress() {
return m_peerAddress;
} // getAddress
/**
* @brief Ask the BLE server for the RSSI value.
* @return The RSSI value.
*/
int BLEClient::getRssi() {
log_v(">> getRssi()");
if (!isConnected()) {
log_v("<< getRssi(): Not connected");
return 0;
}
// We make the API call to read the RSSI value which is an asynchronous operation. We expect to receive
// an ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT to indicate completion.
//
m_semaphoreRssiCmplEvt.take("getRssi");
esp_err_t rc = ::esp_ble_gap_read_rssi(*getPeerAddress().getNative());
if (rc != ESP_OK) {
log_e("<< getRssi: esp_ble_gap_read_rssi: rc=%d %s", rc, GeneralUtils::errorToString(rc));
return 0;
}
int rssiValue = m_semaphoreRssiCmplEvt.wait("getRssi");
log_v("<< getRssi(): %d", rssiValue);
return rssiValue;
} // getRssi
/**
* @brief Get the service BLE Remote Service instance corresponding to the uuid.
* @param [in] uuid The UUID of the service being sought.
* @return A reference to the Service or nullptr if don't know about it.
*/
BLERemoteService* BLEClient::getService(const char* uuid) {
return getService(BLEUUID(uuid));
} // getService
/**
* @brief Get the service object corresponding to the uuid.
* @param [in] uuid The UUID of the service being sought.
* @return A reference to the Service or nullptr if don't know about it.
* @throws BLEUuidNotFound
*/
BLERemoteService* BLEClient::getService(BLEUUID uuid) {
log_v(">> getService: uuid: %s", uuid.toString().c_str());
// Design
// ------
// We wish to retrieve the service given its UUID. It is possible that we have not yet asked the
// device what services it has in which case we have nothing to match against. If we have not
// asked the device about its services, then we do that now. Once we get the results we can then
// examine the services map to see if it has the service we are looking for.
if (!m_haveServices) {
getServices();
}
std::string uuidStr = uuid.toString();
for (auto &myPair : m_servicesMap) {
if (myPair.first == uuidStr) {
log_v("<< getService: found the service with uuid: %s", uuid.toString().c_str());
return myPair.second;
}
} // End of each of the services.
log_v("<< getService: not found");
return nullptr;
} // getService
/**
* @brief Ask the remote %BLE server for its services.
* A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of
* services and wait until we have received them all.
* @return N/A
*/
std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
/*
* Design
* ------
* We invoke esp_ble_gattc_search_service. This will request a list of the service exposed by the
* peer BLE partner to be returned as events. Each event will be an an instance of ESP_GATTC_SEARCH_RES_EVT
* and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received.
*/
log_v(">> getServices");
// TODO implement retrieving services from cache
clearServices(); // Clear any services that may exist.
esp_err_t errRc = esp_ble_gattc_search_service(
getGattcIf(),
getConnId(),
NULL // Filter UUID
);
m_semaphoreSearchCmplEvt.take("getServices");
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return &m_servicesMap;
}
// If sucessfull, remember that we now have services.
m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0);
log_v("<< getServices");
return &m_servicesMap;
} // getServices
/**
* @brief Get the value of a specific characteristic associated with a specific service.
* @param [in] serviceUUID The service that owns the characteristic.
* @param [in] characteristicUUID The characteristic whose value we wish to read.
* @throws BLEUuidNotFound
*/
std::string BLEClient::getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID) {
log_v(">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
std::string ret = getService(serviceUUID)->getCharacteristic(characteristicUUID)->readValue();
log_v("<<getValue");
return ret;
} // getValue
/**
* @brief Handle a received GAP event.
*
* @param [in] event
* @param [in] param
*/
void BLEClient::handleGAPEvent(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param) {
log_d("BLEClient ... handling GAP event!");
switch (event) {
//
// ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
//
// read_rssi_cmpl
// - esp_bt_status_t status
// - int8_t rssi
// - esp_bd_addr_t remote_addr
//
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: {
m_semaphoreRssiCmplEvt.give((uint32_t) param->read_rssi_cmpl.rssi);
break;
} // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
default:
break;
}
} // handleGAPEvent
/**
* @brief Are we connected to a partner?
* @return True if we are connected and false if we are not connected.
*/
bool BLEClient::isConnected() {
return m_isConnected;
} // isConnected
/**
* @brief Set the callbacks that will be invoked.
*/
void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) {
m_pClientCallbacks = pClientCallbacks;
} // setClientCallbacks
/**
* @brief Set the value of a specific characteristic associated with a specific service.
* @param [in] serviceUUID The service that owns the characteristic.
* @param [in] characteristicUUID The characteristic whose value we wish to write.
* @throws BLEUuidNotFound
*/
void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
log_v(">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
getService(serviceUUID)->getCharacteristic(characteristicUUID)->writeValue(value);
log_v("<< setValue");
} // setValue
uint16_t BLEClient::getMTU() {
return m_mtu;
}
/**
* @brief Return a string representation of this client.
* @return A string representation of this client.
*/
std::string BLEClient::toString() {
std::string res = "peer address: " + m_peerAddress.toString();
res += "\nServices:\n";
for (auto &myPair : m_servicesMap) {
res += myPair.second->toString() + "\n";
// myPair.second is the value
}
return res;
} // toString
#endif // CONFIG_BT_ENABLED

View File

@ -0,0 +1,103 @@
/*
* BLEDevice.h
*
* Created on: Mar 22, 2017
* Author: kolban
*/
#ifndef MAIN_BLEDEVICE_H_
#define MAIN_BLEDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gattc_api.h>
#include <string.h>
#include <map>
#include <string>
//#include "BLEExceptions.h"
#include "BLERemoteService.h"
#include "BLEService.h"
#include "BLEAddress.h"
#include "BLEAdvertisedDevice.h"
class BLERemoteService;
class BLEClientCallbacks;
class BLEAdvertisedDevice;
/**
* @brief A model of a %BLE client.
*/
class BLEClient {
public:
BLEClient();
~BLEClient();
bool connect(BLEAdvertisedDevice* device);
bool connect(BLEAddress address, esp_ble_addr_type_t type = BLE_ADDR_TYPE_PUBLIC); // Connect to the remote BLE Server
void disconnect(); // Disconnect from the remote BLE Server
BLEAddress getPeerAddress(); // Get the address of the remote BLE Server
int getRssi(); // Get the RSSI of the remote BLE Server
std::map<std::string, BLERemoteService*>* getServices(); // Get a map of the services offered by the remote BLE Server
BLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server.
BLERemoteService* getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server.
std::string getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service.
void handleGAPEvent(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param);
bool isConnected(); // Return true if we are connected.
void setClientCallbacks(BLEClientCallbacks *pClientCallbacks);
void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service.
std::string toString(); // Return a string representation of this client.
uint16_t getConnId();
esp_gatt_if_t getGattcIf();
uint16_t getMTU();
uint16_t m_appId;
private:
friend class BLEDevice;
friend class BLERemoteService;
friend class BLERemoteCharacteristic;
friend class BLERemoteDescriptor;
void gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* param);
BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server.
uint16_t m_conn_id;
// int m_deviceType;
esp_gatt_if_t m_gattc_if;
bool m_haveServices = false; // Have we previously obtain the set of services from the remote server.
bool m_isConnected = false; // Are we currently connected.
BLEClientCallbacks* m_pClientCallbacks;
FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt");
FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt");
FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt");
std::map<std::string, BLERemoteService*> m_servicesMap;
std::map<BLERemoteService*, uint16_t> m_servicesMapByInstID;
void clearServices(); // Clear any existing services.
uint16_t m_mtu = 23;
}; // class BLEDevice
/**
* @brief Callbacks associated with a %BLE client.
*/
class BLEClientCallbacks {
public:
virtual ~BLEClientCallbacks() {};
virtual void onConnect(BLEClient *pClient) = 0;
virtual void onDisconnect(BLEClient *pClient) = 0;
};
#endif // CONFIG_BT_ENABLED
#endif /* MAIN_BLEDEVICE_H_ */

View File

@ -0,0 +1,287 @@
/*
* BLEDescriptor.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <sstream>
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include "sdkconfig.h"
#include <esp_err.h>
#include "BLEService.h"
#include "BLEDescriptor.h"
#include "GeneralUtils.h"
#include "esp32-hal-log.h"
#define NULL_HANDLE (0xffff)
/**
* @brief BLEDescriptor constructor.
*/
BLEDescriptor::BLEDescriptor(const char* uuid, uint16_t len) : BLEDescriptor(BLEUUID(uuid), len) {
}
/**
* @brief BLEDescriptor constructor.
*/
BLEDescriptor::BLEDescriptor(BLEUUID uuid, uint16_t max_len) {
m_bleUUID = uuid;
m_value.attr_len = 0; // Initial length is 0.
m_value.attr_max_len = max_len; // Maximum length of the data.
m_handle = NULL_HANDLE; // Handle is initially unknown.
m_pCharacteristic = nullptr; // No initial characteristic.
m_pCallback = nullptr; // No initial callback.
m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value.
} // BLEDescriptor
/**
* @brief BLEDescriptor destructor.
*/
BLEDescriptor::~BLEDescriptor() {
free(m_value.attr_value); // Release the storage we created in the constructor.
} // ~BLEDescriptor
/**
* @brief Execute the creation of the descriptor with the BLE runtime in ESP.
* @param [in] pCharacteristic The characteristic to which to register this descriptor.
*/
void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) {
log_v(">> executeCreate(): %s", toString().c_str());
if (m_handle != NULL_HANDLE) {
log_e("Descriptor already has a handle.");
return;
}
m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service.
esp_attr_control_t control;
control.auto_rsp = ESP_GATT_AUTO_RSP;
m_semaphoreCreateEvt.take("executeCreate");
esp_err_t errRc = ::esp_ble_gatts_add_char_descr(
pCharacteristic->getService()->getHandle(),
getUUID().getNative(),
(esp_gatt_perm_t)m_permissions,
&m_value,
&control);
if (errRc != ESP_OK) {
log_e("<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
m_semaphoreCreateEvt.wait("executeCreate");
log_v("<< executeCreate");
} // executeCreate
/**
* @brief Get the BLE handle for this descriptor.
* @return The handle for this descriptor.
*/
uint16_t BLEDescriptor::getHandle() {
return m_handle;
} // getHandle
/**
* @brief Get the length of the value of this descriptor.
* @return The length (in bytes) of the value of this descriptor.
*/
size_t BLEDescriptor::getLength() {
return m_value.attr_len;
} // getLength
/**
* @brief Get the UUID of the descriptor.
*/
BLEUUID BLEDescriptor::getUUID() {
return m_bleUUID;
} // getUUID
/**
* @brief Get the value of this descriptor.
* @return A pointer to the value of this descriptor.
*/
uint8_t* BLEDescriptor::getValue() {
return m_value.attr_value;
} // getValue
/**
* @brief Handle GATT server events for the descripttor.
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*/
void BLEDescriptor::handleGATTServerEvent(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param) {
switch (event) {
// ESP_GATTS_ADD_CHAR_DESCR_EVT
//
// add_char_descr:
// - esp_gatt_status_t status
// - uint16_t attr_handle
// - uint16_t service_handle
// - esp_bt_uuid_t char_uuid
case ESP_GATTS_ADD_CHAR_DESCR_EVT: {
if (m_pCharacteristic != nullptr &&
m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) &&
m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle &&
m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) {
setHandle(param->add_char_descr.attr_handle);
m_semaphoreCreateEvt.give();
}
break;
} // ESP_GATTS_ADD_CHAR_DESCR_EVT
// ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived.
//
// write:
// - uint16_t conn_id
// - uint16_t trans_id
// - esp_bd_addr_t bda
// - uint16_t handle
// - uint16_t offset
// - bool need_rsp
// - bool is_prep
// - uint16_t len
// - uint8_t *value
case ESP_GATTS_WRITE_EVT: {
if (param->write.handle == m_handle) {
setValue(param->write.value, param->write.len); // Set the value of the descriptor.
if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now.
m_pCallback->onWrite(this); // Invoke the onWrite callback handler.
}
} // End of ... this is our handle.
break;
} // ESP_GATTS_WRITE_EVT
// ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived.
//
// read:
// - uint16_t conn_id
// - uint32_t trans_id
// - esp_bd_addr_t bda
// - uint16_t handle
// - uint16_t offset
// - bool is_long
// - bool need_rsp
//
case ESP_GATTS_READ_EVT: {
if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it
if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now.
m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler.
}
} // End of this is our handle
break;
} // ESP_GATTS_READ_EVT
default:
break;
} // switch event
} // handleGATTServerEvent
/**
* @brief Set the callback handlers for this descriptor.
* @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
*/
void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) {
log_v(">> setCallbacks: 0x%x", (uint32_t) pCallback);
m_pCallback = pCallback;
log_v("<< setCallbacks");
} // setCallbacks
/**
* @brief Set the handle of this descriptor.
* Set the handle of this descriptor to be the supplied value.
* @param [in] handle The handle to be associated with this descriptor.
* @return N/A.
*/
void BLEDescriptor::setHandle(uint16_t handle) {
log_v(">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
m_handle = handle;
log_v("<< setHandle()");
} // setHandle
/**
* @brief Set the value of the descriptor.
* @param [in] data The data to set for the descriptor.
* @param [in] length The length of the data in bytes.
*/
void BLEDescriptor::setValue(uint8_t* data, size_t length) {
if (length > ESP_GATT_MAX_ATTR_LEN) {
log_e("Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
return;
}
m_value.attr_len = length;
memcpy(m_value.attr_value, data, length);
} // setValue
/**
* @brief Set the value of the descriptor.
* @param [in] value The value of the descriptor in string form.
*/
void BLEDescriptor::setValue(std::string value) {
setValue((uint8_t*) value.data(), value.length());
} // setValue
void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) {
m_permissions = perm;
}
/**
* @brief Return a string representation of the descriptor.
* @return A string representation of the descriptor.
*/
std::string BLEDescriptor::toString() {
char hex[5];
snprintf(hex, sizeof(hex), "%04x", m_handle);
std::string res = "UUID: " + m_bleUUID.toString() + ", handle: 0x" + hex;
return res;
} // toString
BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {}
/**
* @brief Callback function to support a read request.
* @param [in] pDescriptor The descriptor that is the source of the event.
*/
void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) {
log_d("BLEDescriptorCallbacks", ">> onRead: default");
log_d("BLEDescriptorCallbacks", "<< onRead");
} // onRead
/**
* @brief Callback function to support a write request.
* @param [in] pDescriptor The descriptor that is the source of the event.
*/
void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) {
log_d("BLEDescriptorCallbacks", ">> onWrite: default");
log_d("BLEDescriptorCallbacks", "<< onWrite");
} // onWrite
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,77 @@
/*
* BLEDescriptor.h
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
#define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <string>
#include "BLEUUID.h"
#include "BLECharacteristic.h"
#include <esp_gatts_api.h>
#include "FreeRTOS.h"
class BLEService;
class BLECharacteristic;
class BLEDescriptorCallbacks;
/**
* @brief A model of a %BLE descriptor.
*/
class BLEDescriptor {
public:
BLEDescriptor(const char* uuid, uint16_t max_len = 100);
BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100);
virtual ~BLEDescriptor();
uint16_t getHandle(); // Get the handle of the descriptor.
size_t getLength(); // Get the length of the value of the descriptor.
BLEUUID getUUID(); // Get the UUID of the descriptor.
uint8_t* getValue(); // Get a pointer to the value of the descriptor.
void handleGATTServerEvent(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param);
void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor.
void setCallbacks(BLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor.
void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data.
void setValue(std::string value); // Set the value of the descriptor as a data buffer.
std::string toString(); // Convert the descriptor to a string representation.
private:
friend class BLEDescriptorMap;
friend class BLECharacteristic;
BLEUUID m_bleUUID;
uint16_t m_handle;
BLEDescriptorCallbacks* m_pCallback;
BLECharacteristic* m_pCharacteristic;
esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
esp_attr_value_t m_value;
void executeCreate(BLECharacteristic* pCharacteristic);
void setHandle(uint16_t handle);
}; // BLEDescriptor
/**
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
*
* When a server application creates a %BLE descriptor, we may wish to be informed when there is either
* a read or write request to the descriptors value. An application can register a
* sub-classed instance of this class and will be notified when such an event happens.
*/
class BLEDescriptorCallbacks {
public:
virtual ~BLEDescriptorCallbacks();
virtual void onRead(BLEDescriptor* pDescriptor);
virtual void onWrite(BLEDescriptor* pDescriptor);
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */

View File

@ -0,0 +1,148 @@
/*
* BLEDescriptorMap.cpp
*
* Created on: Jun 22, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <sstream>
#include <iomanip>
#include "BLECharacteristic.h"
#include "BLEDescriptor.h"
#include <esp_gatts_api.h> // ESP32 BLE
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#endif
/**
* @brief Return the descriptor by UUID.
* @param [in] UUID The UUID to look up the descriptor.
* @return The descriptor. If not present, then nullptr is returned.
*/
BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) {
return getByUUID(BLEUUID(uuid));
}
/**
* @brief Return the descriptor by UUID.
* @param [in] UUID The UUID to look up the descriptor.
* @return The descriptor. If not present, then nullptr is returned.
*/
BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) {
for (auto &myPair : m_uuidMap) {
if (myPair.first->getUUID().equals(uuid)) {
return myPair.first;
}
}
//return m_uuidMap.at(uuid.toString());
return nullptr;
} // getByUUID
/**
* @brief Return the descriptor by handle.
* @param [in] handle The handle to look up the descriptor.
* @return The descriptor.
*/
BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) {
return m_handleMap.at(handle);
} // getByHandle
/**
* @brief Set the descriptor by UUID.
* @param [in] uuid The uuid of the descriptor.
* @param [in] characteristic The descriptor to cache.
* @return N/A.
*/
void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor* pDescriptor){
m_uuidMap.insert(std::pair<BLEDescriptor*, std::string>(pDescriptor, uuid));
} // setByUUID
/**
* @brief Set the descriptor by UUID.
* @param [in] uuid The uuid of the descriptor.
* @param [in] characteristic The descriptor to cache.
* @return N/A.
*/
void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor) {
m_uuidMap.insert(std::pair<BLEDescriptor*, std::string>(pDescriptor, uuid.toString()));
} // setByUUID
/**
* @brief Set the descriptor by handle.
* @param [in] handle The handle of the descriptor.
* @param [in] descriptor The descriptor to cache.
* @return N/A.
*/
void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor* pDescriptor) {
m_handleMap.insert(std::pair<uint16_t, BLEDescriptor*>(handle, pDescriptor));
} // setByHandle
/**
* @brief Return a string representation of the descriptor map.
* @return A string representation of the descriptor map.
*/
std::string BLEDescriptorMap::toString() {
std::string res;
char hex[5];
int count = 0;
for (auto &myPair : m_uuidMap) {
if (count > 0) {res += "\n";}
snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle());
count++;
res += "handle: 0x";
res += hex;
res += ", uuid: " + myPair.first->getUUID().toString();
}
return res;
} // toString
/**
* @breif Pass the GATT server event onwards to each of the descriptors found in the mapping
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*/
void BLEDescriptorMap::handleGATTServerEvent(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param) {
// Invoke the handler for every descriptor we have.
for (auto &myPair : m_uuidMap) {
myPair.first->handleGATTServerEvent(event, gatts_if, param);
}
} // handleGATTServerEvent
/**
* @brief Get the first descriptor in the map.
* @return The first descriptor in the map.
*/
BLEDescriptor* BLEDescriptorMap::getFirst() {
m_iterator = m_uuidMap.begin();
if (m_iterator == m_uuidMap.end()) return nullptr;
BLEDescriptor* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getFirst
/**
* @brief Get the next descriptor in the map.
* @return The next descriptor in the map.
*/
BLEDescriptor* BLEDescriptorMap::getNext() {
if (m_iterator == m_uuidMap.end()) return nullptr;
BLEDescriptor* pRet = m_iterator->first;
m_iterator++;
return pRet;
} // getNext
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,649 @@
/*
* BLE.cpp
*
* Created on: Mar 16, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <freertos/task.h>
#include <esp_err.h>
#include <nvs_flash.h>
#include <esp_bt.h> // ESP32 BLE
#include <esp_bt_device.h> // ESP32 BLE
#include <esp_bt_main.h> // ESP32 BLE
#include <esp_gap_ble_api.h> // ESP32 BLE
#include <esp_gatts_api.h> // ESP32 BLE
#include <esp_gattc_api.h> // ESP32 BLE
#include <esp_gatt_common_api.h>// ESP32 BLE
#include <esp_err.h> // ESP32 ESP-IDF
#include <map> // Part of C++ Standard library
#include <sstream> // Part of C++ Standard library
#include <iomanip> // Part of C++ Standard library
#include "BLEDevice.h"
#include "BLEClient.h"
#include "BLEUtils.h"
#include "GeneralUtils.h"
#if defined(ARDUINO_ARCH_ESP32)
#include "esp32-hal-bt.h"
#endif
#if defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
#else
#include "esp_log.h"
static const char* LOG_TAG = "BLEDevice";
#endif
/**
* Singletons for the BLEDevice.
*/
BLEServer* BLEDevice::m_pServer = nullptr;
BLEScan* BLEDevice::m_pScan = nullptr;
BLEClient* BLEDevice::m_pClient = nullptr;
bool initialized = false;
esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0;
BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr;
uint16_t BLEDevice::m_localMTU = 23; // not sure if this variable is useful
BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr;
uint16_t BLEDevice::m_appId = 0;
std::map<uint16_t, conn_status_t> BLEDevice::m_connectedClientsMap;
gap_event_handler BLEDevice::m_customGapHandler = nullptr;
gattc_event_handler BLEDevice::m_customGattcHandler = nullptr;
gatts_event_handler BLEDevice::m_customGattsHandler = nullptr;
/**
* @brief Create a new instance of a client.
* @return A new instance of the client.
*/
/* STATIC */ BLEClient* BLEDevice::createClient() {
log_v(">> createClient");
#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig
log_e("BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined");
abort();
#endif // CONFIG_GATTC_ENABLE
m_pClient = new BLEClient();
log_v("<< createClient");
return m_pClient;
} // createClient
/**
* @brief Create a new instance of a server.
* @return A new instance of the server.
*/
/* STATIC */ BLEServer* BLEDevice::createServer() {
log_v(">> createServer");
#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig
log_e("BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined");
abort();
#endif // CONFIG_GATTS_ENABLE
m_pServer = new BLEServer();
m_pServer->createApp(m_appId++);
log_v("<< createServer");
return m_pServer;
} // createServer
/**
* @brief Handle GATT server events.
*
* @param [in] event The event that has been newly received.
* @param [in] gatts_if The connection to the GATT interface.
* @param [in] param Parameters for the event.
*/
/* STATIC */ void BLEDevice::gattServerEventHandler(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param
) {
log_d("gattServerEventHandler [esp_gatt_if: %d] ... %s",
gatts_if,
BLEUtils::gattServerEventTypeToString(event).c_str());
BLEUtils::dumpGattServerEvent(event, gatts_if, param);
switch (event) {
case ESP_GATTS_CONNECT_EVT: {
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
if(BLEDevice::m_securityLevel){
esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
} // ESP_GATTS_CONNECT_EVT
default: {
break;
}
} // switch
if (BLEDevice::m_pServer != nullptr) {
BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param);
}
if(m_customGattsHandler != nullptr) {
m_customGattsHandler(event, gatts_if, param);
}
} // gattServerEventHandler
/**
* @brief Handle GATT client events.
*
* Handler for the GATT client events.
*
* @param [in] event
* @param [in] gattc_if
* @param [in] param
*/
/* STATIC */ void BLEDevice::gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* param) {
log_d("gattClientEventHandler [esp_gatt_if: %d] ... %s",
gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str());
BLEUtils::dumpGattClientEvent(event, gattc_if, param);
switch(event) {
case ESP_GATTC_CONNECT_EVT: {
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
if(BLEDevice::m_securityLevel){
esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
} // ESP_GATTS_CONNECT_EVT
default:
break;
} // switch
for(auto &myPair : BLEDevice::getPeerDevices(true)) {
conn_status_t conn_status = (conn_status_t)myPair.second;
if(((BLEClient*)conn_status.peer_device)->getGattcIf() == gattc_if || ((BLEClient*)conn_status.peer_device)->getGattcIf() == ESP_GATT_IF_NONE || gattc_if == ESP_GATT_IF_NONE){
((BLEClient*)conn_status.peer_device)->gattClientEventHandler(event, gattc_if, param);
}
}
if(m_customGattcHandler != nullptr) {
m_customGattcHandler(event, gattc_if, param);
}
} // gattClientEventHandler
/**
* @brief Handle GAP events.
*/
/* STATIC */ void BLEDevice::gapEventHandler(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t *param) {
BLEUtils::dumpGapEvent(event, param);
switch(event) {
case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */
log_i("ESP_GAP_BLE_OOB_REQ_EVT");
break;
case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */
log_i("ESP_GAP_BLE_LOCAL_IR_EVT");
break;
case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */
log_i("ESP_GAP_BLE_LOCAL_ER_EVT");
break;
case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */
log_i("ESP_GAP_BLE_NC_REQ_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
if(BLEDevice::m_securityCallbacks != nullptr){
esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey));
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */
log_i("ESP_GAP_BLE_PASSKEY_REQ_EVT: ");
// esp_log_buffer_hex(m_remote_bda, sizeof(m_remote_bda));
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
if(BLEDevice::m_securityCallbacks != nullptr){
esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest());
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
/*
* TODO should we add white/black list comparison?
*/
case ESP_GAP_BLE_SEC_REQ_EVT:
/* send the positive(true) security response to the peer device to accept the security request.
If not accept the security request, should sent the security response with negative(false) accept value*/
log_i("ESP_GAP_BLE_SEC_REQ_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
if(BLEDevice::m_securityCallbacks!=nullptr){
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest());
}
else{
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
/*
*
*/
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: //the app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
//display the passkey number to the user to input it in the peer deivce within 30 seconds
log_i("ESP_GAP_BLE_PASSKEY_NOTIF_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
log_i("passKey = %d", param->ble_security.key_notif.passkey);
if(BLEDevice::m_securityCallbacks!=nullptr){
BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey);
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
case ESP_GAP_BLE_KEY_EVT:
//shows the ble key type info share with peer device to the user.
log_d("ESP_GAP_BLE_KEY_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
log_i("key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type));
#endif // CONFIG_BLE_SMP_ENABLE
break;
case ESP_GAP_BLE_AUTH_CMPL_EVT:
log_i("ESP_GAP_BLE_AUTH_CMPL_EVT");
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
if(BLEDevice::m_securityCallbacks != nullptr){
BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl);
}
#endif // CONFIG_BLE_SMP_ENABLE
break;
default: {
break;
}
} // switch
if (BLEDevice::m_pClient != nullptr) {
BLEDevice::m_pClient->handleGAPEvent(event, param);
}
if (BLEDevice::m_pScan != nullptr) {
BLEDevice::getScan()->handleGAPEvent(event, param);
}
if(m_bleAdvertising != nullptr) {
BLEDevice::getAdvertising()->handleGAPEvent(event, param);
}
if(m_customGapHandler != nullptr) {
BLEDevice::m_customGapHandler(event, param);
}
} // gapEventHandler
/**
* @brief Get the BLE device address.
* @return The BLE device address.
*/
/* STATIC*/ BLEAddress BLEDevice::getAddress() {
const uint8_t* bdAddr = esp_bt_dev_get_address();
esp_bd_addr_t addr;
memcpy(addr, bdAddr, sizeof(addr));
return BLEAddress(addr);
} // getAddress
/**
* @brief Retrieve the Scan object that we use for scanning.
* @return The scanning object reference. This is a singleton object. The caller should not
* try and release/delete it.
*/
/* STATIC */ BLEScan* BLEDevice::getScan() {
//log_v(">> getScan");
if (m_pScan == nullptr) {
m_pScan = new BLEScan();
//log_d(" - creating a new scan object");
}
//log_v("<< getScan: Returning object at 0x%x", (uint32_t)m_pScan);
return m_pScan;
} // getScan
/**
* @brief Get the value of a characteristic of a service on a remote device.
* @param [in] bdAddress
* @param [in] serviceUUID
* @param [in] characteristicUUID
*/
/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) {
log_v(">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
BLEClient* pClient = createClient();
pClient->connect(bdAddress);
std::string ret = pClient->getValue(serviceUUID, characteristicUUID);
pClient->disconnect();
log_v("<< getValue");
return ret;
} // getValue
/**
* @brief Initialize the %BLE environment.
* @param deviceName The device name of the device.
*/
/* STATIC */ void BLEDevice::init(std::string deviceName) {
if(!initialized){
initialized = true; // Set the initialization flag to ensure we are only initialized once.
esp_err_t errRc = ESP_OK;
#ifdef ARDUINO_ARCH_ESP32
if (!btStart()) {
errRc = ESP_FAIL;
return;
}
#else
errRc = ::nvs_flash_init();
if (errRc != ESP_OK) {
log_e("nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
#ifndef CLASSIC_BT_ENABLED
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
#endif
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
errRc = esp_bt_controller_init(&bt_cfg);
if (errRc != ESP_OK) {
log_e("esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
#ifndef CLASSIC_BT_ENABLED
errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (errRc != ESP_OK) {
log_e("esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
#else
errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
if (errRc != ESP_OK) {
log_e("esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
#endif
#endif
esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
errRc = esp_bluedroid_init();
if (errRc != ESP_OK) {
log_e("esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
}
if (bt_state != ESP_BLUEDROID_STATUS_ENABLED) {
errRc = esp_bluedroid_enable();
if (errRc != ESP_OK) {
log_e("esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
}
errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler);
if (errRc != ESP_OK) {
log_e("esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig
errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
#endif // CONFIG_GATTC_ENABLE
#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig
errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler);
if (errRc != ESP_OK) {
log_e("esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
#endif // CONFIG_GATTS_ENABLE
errRc = ::esp_ble_gap_set_device_name(deviceName.c_str());
if (errRc != ESP_OK) {
log_e("esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
};
#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
if (errRc != ESP_OK) {
log_e("esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
};
#endif // CONFIG_BLE_SMP_ENABLE
}
vTaskDelay(200 / portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue.
} // init
/**
* @brief Set the transmission power.
* The power level can be one of:
* * ESP_PWR_LVL_N14
* * ESP_PWR_LVL_N11
* * ESP_PWR_LVL_N8
* * ESP_PWR_LVL_N5
* * ESP_PWR_LVL_N2
* * ESP_PWR_LVL_P1
* * ESP_PWR_LVL_P4
* * ESP_PWR_LVL_P7
* @param [in] powerLevel.
*/
/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) {
log_v(">> setPower: %d", powerLevel);
esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel);
if (errRc != ESP_OK) {
log_e("esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
};
log_v("<< setPower");
} // setPower
/**
* @brief Set the value of a characteristic of a service on a remote device.
* @param [in] bdAddress
* @param [in] serviceUUID
* @param [in] characteristicUUID
*/
/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
log_v(">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
BLEClient* pClient = createClient();
pClient->connect(bdAddress);
pClient->setValue(serviceUUID, characteristicUUID, value);
pClient->disconnect();
} // setValue
/**
* @brief Return a string representation of the nature of this device.
* @return A string representation of the nature of this device.
*/
/* STATIC */ std::string BLEDevice::toString() {
std::string res = "BD Address: " + getAddress().toString();
return res;
} // toString
/**
* @brief Add an entry to the BLE white list.
* @param [in] address The address to add to the white list.
*/
void BLEDevice::whiteListAdd(BLEAddress address) {
log_v(">> whiteListAdd: %s", address.toString().c_str());
esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry.
if (errRc != ESP_OK) {
log_e("esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
log_v("<< whiteListAdd");
} // whiteListAdd
/**
* @brief Remove an entry from the BLE white list.
* @param [in] address The address to remove from the white list.
*/
void BLEDevice::whiteListRemove(BLEAddress address) {
log_v(">> whiteListRemove: %s", address.toString().c_str());
esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry.
if (errRc != ESP_OK) {
log_e("esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
log_v("<< whiteListRemove");
} // whiteListRemove
/*
* @brief Set encryption level that will be negotiated with peer device durng connection
* @param [in] level Requested encryption level
*/
void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) {
BLEDevice::m_securityLevel = level;
}
/*
* @brief Set callbacks that will be used to handle encryption negotiation events and authentication events
* @param [in] cllbacks Pointer to BLESecurityCallbacks class callback
*/
void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) {
BLEDevice::m_securityCallbacks = callbacks;
}
/*
* @brief Setup local mtu that will be used to negotiate mtu during request from client peer
* @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517
*/
esp_err_t BLEDevice::setMTU(uint16_t mtu) {
log_v(">> setLocalMTU: %d", mtu);
esp_err_t err = esp_ble_gatt_set_local_mtu(mtu);
if (err == ESP_OK) {
m_localMTU = mtu;
} else {
log_e("can't set local mtu value: %d", mtu);
}
log_v("<< setLocalMTU");
return err;
}
/*
* @brief Get local MTU value set during mtu request or default value
*/
uint16_t BLEDevice::getMTU() {
return m_localMTU;
}
bool BLEDevice::getInitialized() {
return initialized;
}
BLEAdvertising* BLEDevice::getAdvertising() {
if(m_bleAdvertising == nullptr) {
m_bleAdvertising = new BLEAdvertising();
log_i("create advertising");
}
log_d("get advertising");
return m_bleAdvertising;
}
void BLEDevice::startAdvertising() {
log_v(">> startAdvertising");
getAdvertising()->start();
log_v("<< startAdvertising");
} // startAdvertising
/* multi connect support */
/* requires a little more work */
std::map<uint16_t, conn_status_t> BLEDevice::getPeerDevices(bool _client) {
return m_connectedClientsMap;
}
BLEClient* BLEDevice::getClientByGattIf(uint16_t conn_id) {
return (BLEClient*)m_connectedClientsMap.find(conn_id)->second.peer_device;
}
void BLEDevice::updatePeerDevice(void* peer, bool _client, uint16_t conn_id) {
log_d("update conn_id: %d, GATT role: %s", conn_id, _client? "client":"server");
std::map<uint16_t, conn_status_t>::iterator it = m_connectedClientsMap.find(ESP_GATT_IF_NONE);
if (it != m_connectedClientsMap.end()) {
std::swap(m_connectedClientsMap[conn_id], it->second);
m_connectedClientsMap.erase(it);
}else{
it = m_connectedClientsMap.find(conn_id);
if (it != m_connectedClientsMap.end()) {
conn_status_t _st = it->second;
_st.peer_device = peer;
std::swap(m_connectedClientsMap[conn_id], _st);
}
}
}
void BLEDevice::addPeerDevice(void* peer, bool _client, uint16_t conn_id) {
log_i("add conn_id: %d, GATT role: %s", conn_id, _client? "client":"server");
conn_status_t status = {
.peer_device = peer,
.connected = true,
.mtu = 23
};
m_connectedClientsMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
}
void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) {
log_i("remove: %d, GATT role %s", conn_id, _client?"client":"server");
if(m_connectedClientsMap.find(conn_id) != m_connectedClientsMap.end())
m_connectedClientsMap.erase(conn_id);
}
/* multi connect support */
/**
* @brief de-Initialize the %BLE environment.
* @param release_memory release the internal BT stack memory
*/
/* STATIC */ void BLEDevice::deinit(bool release_memory) {
if (!initialized) return;
esp_bluedroid_disable();
esp_bluedroid_deinit();
esp_bt_controller_disable();
esp_bt_controller_deinit();
#ifndef ARDUINO_ARCH_ESP32
if (release_memory) {
esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); // <-- require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it)
} else {
initialized = false;
}
#endif
}
void BLEDevice::setCustomGapHandler(gap_event_handler handler) {
m_customGapHandler = handler;
}
void BLEDevice::setCustomGattcHandler(gattc_event_handler handler) {
m_customGattcHandler = handler;
}
void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) {
m_customGattsHandler = handler;
}
#endif // CONFIG_BT_ENABLED

View File

@ -0,0 +1,99 @@
/*
* BLEDevice.h
*
* Created on: Mar 16, 2017
* Author: kolban
*/
#ifndef MAIN_BLEDevice_H_
#define MAIN_BLEDevice_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gap_ble_api.h> // ESP32 BLE
#include <esp_gattc_api.h> // ESP32 BLE
#include <map> // Part of C++ STL
#include <string>
#include <esp_bt.h>
#include "BLEServer.h"
#include "BLEClient.h"
#include "BLEUtils.h"
#include "BLEScan.h"
#include "BLEAddress.h"
/**
* @brief BLE functions.
*/
typedef void (*gap_event_handler)(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param);
typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param);
typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param);
class BLEDevice {
public:
static BLEClient* createClient(); // Create a new BLE client.
static BLEServer* createServer(); // Cretae a new BLE server.
static BLEAddress getAddress(); // Retrieve our own local BD address.
static BLEScan* getScan(); // Get the scan object
static std::string getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server.
static void init(std::string deviceName); // Initialize the local BLE environment.
static void setPower(esp_power_level_t powerLevel); // Set our power level.
static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server.
static std::string toString(); // Return a string representation of our device.
static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list.
static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list.
static void setEncryptionLevel(esp_ble_sec_act_t level);
static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks);
static esp_err_t setMTU(uint16_t mtu);
static uint16_t getMTU();
static bool getInitialized(); // Returns the state of the device, is it initialized or not?
/* move advertising to BLEDevice for saving ram and flash in beacons */
static BLEAdvertising* getAdvertising();
static void startAdvertising();
static uint16_t m_appId;
/* multi connect */
static std::map<uint16_t, conn_status_t> getPeerDevices(bool client);
static void addPeerDevice(void* peer, bool is_client, uint16_t conn_id);
static void updatePeerDevice(void* peer, bool _client, uint16_t conn_id);
static void removePeerDevice(uint16_t conn_id, bool client);
static BLEClient* getClientByGattIf(uint16_t conn_id);
static void setCustomGapHandler(gap_event_handler handler);
static void setCustomGattcHandler(gattc_event_handler handler);
static void setCustomGattsHandler(gatts_event_handler handler);
static void deinit(bool release_memory = false);
static uint16_t m_localMTU;
static esp_ble_sec_act_t m_securityLevel;
private:
static BLEServer* m_pServer;
static BLEScan* m_pScan;
static BLEClient* m_pClient;
static BLESecurityCallbacks* m_securityCallbacks;
static BLEAdvertising* m_bleAdvertising;
static esp_gatt_if_t getGattcIF();
static std::map<uint16_t, conn_status_t> m_connectedClientsMap;
static void gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* param);
static void gattServerEventHandler(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param);
static void gapEventHandler(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param);
public:
/* custom gap and gatt handlers for flexibility */
static gap_event_handler m_customGapHandler;
static gattc_event_handler m_customGattcHandler;
static gatts_event_handler m_customGattsHandler;
}; // class BLE
#endif // CONFIG_BT_ENABLED
#endif /* MAIN_BLEDevice_H_ */

View File

@ -0,0 +1,132 @@
/*
* BLEEddystoneTLM.cpp
*
* Created on: Mar 12, 2018
* Author: pcbreflux
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <string.h>
#include <stdio.h>
#include "esp32-hal-log.h"
#include "BLEEddystoneTLM.h"
static const char LOG_TAG[] = "BLEEddystoneTLM";
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24))
BLEEddystoneTLM::BLEEddystoneTLM() {
beaconUUID = 0xFEAA;
m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE;
m_eddystoneData.version = 0;
m_eddystoneData.volt = 3300; // 3300mV = 3.3V
m_eddystoneData.temp = (uint16_t) ((float) 23.00);
m_eddystoneData.advCount = 0;
m_eddystoneData.tmil = 0;
} // BLEEddystoneTLM
std::string BLEEddystoneTLM::getData() {
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
} // getData
BLEUUID BLEEddystoneTLM::getUUID() {
return BLEUUID(beaconUUID);
} // getUUID
uint8_t BLEEddystoneTLM::getVersion() {
return m_eddystoneData.version;
} // getVersion
uint16_t BLEEddystoneTLM::getVolt() {
return m_eddystoneData.volt;
} // getVolt
float BLEEddystoneTLM::getTemp() {
return (float)m_eddystoneData.temp;
} // getTemp
uint32_t BLEEddystoneTLM::getCount() {
return m_eddystoneData.advCount;
} // getCount
uint32_t BLEEddystoneTLM::getTime() {
return m_eddystoneData.tmil;
} // getTime
std::string BLEEddystoneTLM::toString() {
std::string out = "";
uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil);
char val[6];
out += "Version " + m_eddystoneData.version;
out += "\n";
out += "Battery Voltage " + ENDIAN_CHANGE_U16(m_eddystoneData.volt);
out += " mV\n";
out += "Temperature ";
snprintf(val, sizeof(val), "%d", m_eddystoneData.temp);
out += val;
out += ".0 °C\n";
out += "Adv. Count ";
snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount));
out += val;
out += "\n";
out += "Time ";
snprintf(val, sizeof(val), "%04d", rawsec / 864000);
out += val;
out += ".";
snprintf(val, sizeof(val), "%02d", (rawsec / 36000) % 24);
out += val;
out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 600) % 60);
out += val;
out += ":";
snprintf(val, sizeof(val), "%02d", (rawsec / 10) % 60);
out += val;
out += "\n";
return out;
} // toString
/**
* Set the raw data for the beacon record.
*/
void BLEEddystoneTLM::setData(std::string data) {
if (data.length() != sizeof(m_eddystoneData)) {
log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData));
return;
}
memcpy(&m_eddystoneData, data.data(), data.length());
} // setData
void BLEEddystoneTLM::setUUID(BLEUUID l_uuid) {
beaconUUID = l_uuid.getNative()->uuid.uuid16;
} // setUUID
void BLEEddystoneTLM::setVersion(uint8_t version) {
m_eddystoneData.version = version;
} // setVersion
void BLEEddystoneTLM::setVolt(uint16_t volt) {
m_eddystoneData.volt = volt;
} // setVolt
void BLEEddystoneTLM::setTemp(float temp) {
m_eddystoneData.temp = (uint16_t)temp;
} // setTemp
void BLEEddystoneTLM::setCount(uint32_t advCount) {
m_eddystoneData.advCount = advCount;
} // setCount
void BLEEddystoneTLM::setTime(uint32_t tmil) {
m_eddystoneData.tmil = tmil;
} // setTime
#endif

View File

@ -0,0 +1,51 @@
/*
* BLEEddystoneTLM.cpp
*
* Created on: Mar 12, 2018
* Author: pcbreflux
*/
#ifndef _BLEEddystoneTLM_H_
#define _BLEEddystoneTLM_H_
#include "BLEUUID.h"
#define EDDYSTONE_TLM_FRAME_TYPE 0x20
/**
* @brief Representation of a beacon.
* See:
* * https://github.com/google/eddystone
*/
class BLEEddystoneTLM {
public:
BLEEddystoneTLM();
std::string getData();
BLEUUID getUUID();
uint8_t getVersion();
uint16_t getVolt();
float getTemp();
uint32_t getCount();
uint32_t getTime();
std::string toString();
void setData(std::string data);
void setUUID(BLEUUID l_uuid);
void setVersion(uint8_t version);
void setVolt(uint16_t volt);
void setTemp(float temp);
void setCount(uint32_t advCount);
void setTime(uint32_t tmil);
private:
uint16_t beaconUUID;
struct {
uint8_t frameType;
uint8_t version;
uint16_t volt;
uint16_t temp;
uint32_t advCount;
uint32_t tmil;
} __attribute__((packed)) m_eddystoneData;
}; // BLEEddystoneTLM
#endif /* _BLEEddystoneTLM_H_ */

View File

@ -0,0 +1,148 @@
/*
* BLEEddystoneURL.cpp
*
* Created on: Mar 12, 2018
* Author: pcbreflux
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <string.h>
#include "esp32-hal-log.h"
#include "BLEEddystoneURL.h"
static const char LOG_TAG[] = "BLEEddystoneURL";
BLEEddystoneURL::BLEEddystoneURL() {
beaconUUID = 0xFEAA;
lengthURL = 0;
m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE;
m_eddystoneData.advertisedTxPower = 0;
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
} // BLEEddystoneURL
std::string BLEEddystoneURL::getData() {
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
} // getData
BLEUUID BLEEddystoneURL::getUUID() {
return BLEUUID(beaconUUID);
} // getUUID
int8_t BLEEddystoneURL::getPower() {
return m_eddystoneData.advertisedTxPower;
} // getPower
std::string BLEEddystoneURL::getURL() {
return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url));
} // getURL
std::string BLEEddystoneURL::getDecodedURL() {
std::string decodedURL = "";
switch (m_eddystoneData.url[0]) {
case 0x00:
decodedURL += "http://www.";
break;
case 0x01:
decodedURL += "https://www.";
break;
case 0x02:
decodedURL += "http://";
break;
case 0x03:
decodedURL += "https://";
break;
default:
decodedURL += m_eddystoneData.url[0];
}
for (int i = 1; i < lengthURL; i++) {
if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) {
decodedURL += m_eddystoneData.url[i];
} else {
switch (m_eddystoneData.url[i]) {
case 0x00:
decodedURL += ".com/";
break;
case 0x01:
decodedURL += ".org/";
break;
case 0x02:
decodedURL += ".edu/";
break;
case 0x03:
decodedURL += ".net/";
break;
case 0x04:
decodedURL += ".info/";
break;
case 0x05:
decodedURL += ".biz/";
break;
case 0x06:
decodedURL += ".gov/";
break;
case 0x07:
decodedURL += ".com";
break;
case 0x08:
decodedURL += ".org";
break;
case 0x09:
decodedURL += ".edu";
break;
case 0x0A:
decodedURL += ".net";
break;
case 0x0B:
decodedURL += ".info";
break;
case 0x0C:
decodedURL += ".biz";
break;
case 0x0D:
decodedURL += ".gov";
break;
default:
break;
}
}
}
return decodedURL;
} // getDecodedURL
/**
* Set the raw data for the beacon record.
*/
void BLEEddystoneURL::setData(std::string data) {
if (data.length() > sizeof(m_eddystoneData)) {
log_e("Unable to set the data ... length passed in was %d and max expected %d", data.length(), sizeof(m_eddystoneData));
return;
}
memset(&m_eddystoneData, 0, sizeof(m_eddystoneData));
memcpy(&m_eddystoneData, data.data(), data.length());
lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url));
} // setData
void BLEEddystoneURL::setUUID(BLEUUID l_uuid) {
beaconUUID = l_uuid.getNative()->uuid.uuid16;
} // setUUID
void BLEEddystoneURL::setPower(int8_t advertisedTxPower) {
m_eddystoneData.advertisedTxPower = advertisedTxPower;
} // setPower
void BLEEddystoneURL::setURL(std::string url) {
if (url.length() > sizeof(m_eddystoneData.url)) {
log_e("Unable to set the url ... length passed in was %d and max expected %d", url.length(), sizeof(m_eddystoneData.url));
return;
}
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
memcpy(m_eddystoneData.url, url.data(), url.length());
lengthURL = url.length();
} // setURL
#endif

View File

@ -0,0 +1,43 @@
/*
* BLEEddystoneURL.cpp
*
* Created on: Mar 12, 2018
* Author: pcbreflux
*/
#ifndef _BLEEddystoneURL_H_
#define _BLEEddystoneURL_H_
#include "BLEUUID.h"
#define EDDYSTONE_URL_FRAME_TYPE 0x10
/**
* @brief Representation of a beacon.
* See:
* * https://github.com/google/eddystone
*/
class BLEEddystoneURL {
public:
BLEEddystoneURL();
std::string getData();
BLEUUID getUUID();
int8_t getPower();
std::string getURL();
std::string getDecodedURL();
void setData(std::string data);
void setUUID(BLEUUID l_uuid);
void setPower(int8_t advertisedTxPower);
void setURL(std::string url);
private:
uint16_t beaconUUID;
uint8_t lengthURL;
struct {
uint8_t frameType;
int8_t advertisedTxPower;
uint8_t url[16];
} __attribute__((packed)) m_eddystoneData;
}; // BLEEddystoneURL
#endif /* _BLEEddystoneURL_H_ */

View File

@ -0,0 +1,9 @@
/*
* BLExceptions.cpp
*
* Created on: Nov 27, 2017
* Author: kolban
*/
//#include "BLEExceptions.h"

View File

@ -0,0 +1,31 @@
/*
* BLExceptions.h
*
* Created on: Nov 27, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
#define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
#include "sdkconfig.h"
#if CONFIG_CXX_EXCEPTIONS != 1
#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions."
#endif
#include <exception>
class BLEDisconnectedException : public std::exception {
const char* what() const throw () {
return "BLE Disconnected";
}
};
class BLEUuidNotFoundException : public std::exception {
const char* what() const throw () {
return "No such UUID";
}
};
#endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */

View File

@ -0,0 +1,243 @@
/*
* BLEHIDDevice.cpp
*
* Created on: Jan 03, 2018
* Author: chegewara
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLEHIDDevice.h"
#include "BLE2904.h"
BLEHIDDevice::BLEHIDDevice(BLEServer* server) {
/*
* Here we create mandatory services described in bluetooth specification
*/
m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a));
m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40);
m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f));
/*
* Mandatory characteristic for device info service
*/
m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, BLECharacteristic::PROPERTY_READ);
/*
* Mandatory characteristics for HID service
*/
m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, BLECharacteristic::PROPERTY_READ);
m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, BLECharacteristic::PROPERTY_READ);
m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR);
m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ);
/*
* Mandatory battery level characteristic with notification and presence descriptor
*/
BLE2904* batteryLevelDescriptor = new BLE2904();
batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8);
batteryLevelDescriptor->setNamespace(1);
batteryLevelDescriptor->setUnit(0x27ad);
m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor);
m_batteryLevelCharacteristic->addDescriptor(new BLE2902());
/*
* This value is setup here because its default value in most usage cases, its very rare to use boot mode
* and we want to simplify library using as much as possible
*/
const uint8_t pMode[] = { 0x01 };
protocolMode()->setValue((uint8_t*) pMode, 1);
}
BLEHIDDevice::~BLEHIDDevice() {
}
/*
* @brief
*/
void BLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
m_reportMapCharacteristic->setValue(map, size);
}
/*
* @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service
*/
void BLEHIDDevice::startServices() {
m_deviceInfoService->start();
m_hidService->start();
m_batteryService->start();
}
/*
* @brief Create manufacturer characteristic (this characteristic is optional)
*/
BLECharacteristic* BLEHIDDevice::manufacturer() {
m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, BLECharacteristic::PROPERTY_READ);
return m_manufacturerCharacteristic;
}
/*
* @brief Set manufacturer name
* @param [in] name manufacturer name
*/
void BLEHIDDevice::manufacturer(std::string name) {
m_manufacturerCharacteristic->setValue(name);
}
/*
* @brief
*/
void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version };
m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
}
/*
* @brief
*/
void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
uint8_t info[] = { 0x11, 0x1, country, flags };
m_hidInfoCharacteristic->setValue(info, sizeof(info));
}
/*
* @brief Create input report characteristic that need to be saved as new characteristic object so can be further used
* @param [in] reportID input report ID, the same as in report map for input object related to created characteristic
* @return pointer to new input report characteristic
*/
BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) {
BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908));
BLE2902* p2902 = new BLE2902();
inputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
inputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
p2902->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
uint8_t desc1_val[] = { reportID, 0x01 };
inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
inputReportCharacteristic->addDescriptor(p2902);
inputReportCharacteristic->addDescriptor(inputReportDescriptor);
return inputReportCharacteristic;
}
/*
* @brief Create output report characteristic that need to be saved as new characteristic object so can be further used
* @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic
* @return Pointer to new output report characteristic
*/
BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) {
BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908));
outputReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
outputReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
uint8_t desc1_val[] = { reportID, 0x02 };
outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
outputReportCharacteristic->addDescriptor(outputReportDescriptor);
return outputReportCharacteristic;
}
/*
* @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used
* @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic
* @return Pointer to new feature report characteristic
*/
BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) {
BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t) 0x2908));
featureReportCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
featureReportDescriptor->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
uint8_t desc1_val[] = { reportID, 0x03 };
featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
featureReportCharacteristic->addDescriptor(featureReportDescriptor);
return featureReportCharacteristic;
}
/*
* @brief
*/
BLECharacteristic* BLEHIDDevice::bootInput() {
BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a22, BLECharacteristic::PROPERTY_NOTIFY);
bootInputCharacteristic->addDescriptor(new BLE2902());
return bootInputCharacteristic;
}
/*
* @brief
*/
BLECharacteristic* BLEHIDDevice::bootOutput() {
return m_hidService->createCharacteristic((uint16_t) 0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
}
/*
* @brief
*/
BLECharacteristic* BLEHIDDevice::hidControl() {
return m_hidControlCharacteristic;
}
/*
* @brief
*/
BLECharacteristic* BLEHIDDevice::protocolMode() {
return m_protocolModeCharacteristic;
}
void BLEHIDDevice::setBatteryLevel(uint8_t level) {
m_batteryLevelCharacteristic->setValue(&level, 1);
}
/*
* @brief Returns battery level characteristic
* @ return battery level characteristic
*//*
BLECharacteristic* BLEHIDDevice::batteryLevel() {
return m_batteryLevelCharacteristic;
}
BLECharacteristic* BLEHIDDevice::reportMap() {
return m_reportMapCharacteristic;
}
BLECharacteristic* BLEHIDDevice::pnp() {
return m_pnpCharacteristic;
}
BLECharacteristic* BLEHIDDevice::hidInfo() {
return m_hidInfoCharacteristic;
}
*/
/*
* @brief
*/
BLEService* BLEHIDDevice::deviceInfo() {
return m_deviceInfoService;
}
/*
* @brief
*/
BLEService* BLEHIDDevice::hidService() {
return m_hidService;
}
/*
* @brief
*/
BLEService* BLEHIDDevice::batteryService() {
return m_batteryService;
}
#endif // CONFIG_BT_ENABLED

View File

@ -0,0 +1,75 @@
/*
* BLEHIDDevice.h
*
* Created on: Jan 03, 2018
* Author: chegewara
*/
#ifndef _BLEHIDDEVICE_H_
#define _BLEHIDDEVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include "BLECharacteristic.h"
#include "BLEService.h"
#include "BLEDescriptor.h"
#include "BLE2902.h"
#include "HIDTypes.h"
#define GENERIC_HID 0x03C0
#define HID_KEYBOARD 0x03C1
#define HID_MOUSE 0x03C2
#define HID_JOYSTICK 0x03C3
#define HID_GAMEPAD 0x03C4
#define HID_TABLET 0x03C5
#define HID_CARD_READER 0x03C6
#define HID_DIGITAL_PEN 0x03C7
#define HID_BARCODE 0x03C8
class BLEHIDDevice {
public:
BLEHIDDevice(BLEServer*);
virtual ~BLEHIDDevice();
void reportMap(uint8_t* map, uint16_t);
void startServices();
BLEService* deviceInfo();
BLEService* hidService();
BLEService* batteryService();
BLECharacteristic* manufacturer();
void manufacturer(std::string name);
//BLECharacteristic* pnp();
void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
//BLECharacteristic* hidInfo();
void hidInfo(uint8_t country, uint8_t flags);
//BLECharacteristic* batteryLevel();
void setBatteryLevel(uint8_t level);
//BLECharacteristic* reportMap();
BLECharacteristic* hidControl();
BLECharacteristic* inputReport(uint8_t reportID);
BLECharacteristic* outputReport(uint8_t reportID);
BLECharacteristic* featureReport(uint8_t reportID);
BLECharacteristic* protocolMode();
BLECharacteristic* bootInput();
BLECharacteristic* bootOutput();
private:
BLEService* m_deviceInfoService; //0x180a
BLEService* m_hidService; //0x1812
BLEService* m_batteryService = 0; //0x180f
BLECharacteristic* m_manufacturerCharacteristic; //0x2a29
BLECharacteristic* m_pnpCharacteristic; //0x2a50
BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
BLECharacteristic* m_reportMapCharacteristic; //0x2a4b
BLECharacteristic* m_hidControlCharacteristic; //0x2a4c
BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
};
#endif // CONFIG_BT_ENABLED
#endif /* _BLEHIDDEVICE_H_ */

View File

@ -0,0 +1,587 @@
/*
* BLERemoteCharacteristic.cpp
*
* Created on: Jul 8, 2017
* Author: kolban
*/
#include "BLERemoteCharacteristic.h"
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gattc_api.h>
#include <esp_err.h>
#include <sstream>
//#include "BLEExceptions.h"
#include "BLEUtils.h"
#include "GeneralUtils.h"
#include "BLERemoteDescriptor.h"
#include "esp32-hal-log.h"
/**
* @brief Constructor.
* @param [in] handle The BLE server side handle of this characteristic.
* @param [in] uuid The UUID of this characteristic.
* @param [in] charProp The properties of this characteristic.
* @param [in] pRemoteService A reference to the remote service to which this remote characteristic pertains.
*/
BLERemoteCharacteristic::BLERemoteCharacteristic(
uint16_t handle,
BLEUUID uuid,
esp_gatt_char_prop_t charProp,
BLERemoteService* pRemoteService) {
log_v(">> BLERemoteCharacteristic: handle: %d 0x%d, uuid: %s", handle, handle, uuid.toString().c_str());
m_handle = handle;
m_uuid = uuid;
m_charProp = charProp;
m_pRemoteService = pRemoteService;
m_notifyCallback = nullptr;
m_rawData = nullptr;
retrieveDescriptors(); // Get the descriptors for this characteristic
log_v("<< BLERemoteCharacteristic");
} // BLERemoteCharacteristic
/**
*@brief Destructor.
*/
BLERemoteCharacteristic::~BLERemoteCharacteristic() {
removeDescriptors(); // Release resources for any descriptor information we may have allocated.
} // ~BLERemoteCharacteristic
/**
* @brief Does the characteristic support broadcasting?
* @return True if the characteristic supports broadcasting.
*/
bool BLERemoteCharacteristic::canBroadcast() {
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_BROADCAST) != 0;
} // canBroadcast
/**
* @brief Does the characteristic support indications?
* @return True if the characteristic supports indications.
*/
bool BLERemoteCharacteristic::canIndicate() {
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_INDICATE) != 0;
} // canIndicate
/**
* @brief Does the characteristic support notifications?
* @return True if the characteristic supports notifications.
*/
bool BLERemoteCharacteristic::canNotify() {
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0;
} // canNotify
/**
* @brief Does the characteristic support reading?
* @return True if the characteristic supports reading.
*/
bool BLERemoteCharacteristic::canRead() {
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_READ) != 0;
} // canRead
/**
* @brief Does the characteristic support writing?
* @return True if the characteristic supports writing.
*/
bool BLERemoteCharacteristic::canWrite() {
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE) != 0;
} // canWrite
/**
* @brief Does the characteristic support writing with no response?
* @return True if the characteristic supports writing with no response.
*/
bool BLERemoteCharacteristic::canWriteNoResponse() {
return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) != 0;
} // canWriteNoResponse
/*
static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) {
if (id1.id.inst_id != id2.id.inst_id) {
return false;
}
if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) {
return false;
}
return true;
} // compareSrvcId
*/
/*
static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) {
if (id1.inst_id != id2.inst_id) {
return false;
}
if (!BLEUUID(id1.uuid).equals(BLEUUID(id2.uuid))) {
return false;
}
return true;
} // compareCharId
*/
/**
* @brief Handle GATT Client events.
* When an event arrives for a GATT client we give this characteristic the opportunity to
* take a look at it to see if there is interest in it.
* @param [in] event The type of event.
* @param [in] gattc_if The interface on which the event was received.
* @param [in] evtParam Payload data for the event.
* @returns N/A
*/
void BLERemoteCharacteristic::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam) {
switch(event) {
// ESP_GATTC_NOTIFY_EVT
//
// notify
// - uint16_t conn_id - The connection identifier of the server.
// - esp_bd_addr_t remote_bda - The device address of the BLE server.
// - uint16_t handle - The handle of the characteristic for which the event is being received.
// - uint16_t value_len - The length of the received data.
// - uint8_t* value - The received data.
// - bool is_notify - True if this is a notify, false if it is an indicate.
//
// We have received a notification event which means that the server wishes us to know about a notification
// piece of data. What we must now do is find the characteristic with the associated handle and then
// invoke its notification callback (if it has one).
case ESP_GATTC_NOTIFY_EVT: {
if (evtParam->notify.handle != getHandle()) break;
if (m_notifyCallback != nullptr) {
log_d("Invoking callback for notification on characteristic %s", toString().c_str());
m_notifyCallback(this, evtParam->notify.value, evtParam->notify.value_len, evtParam->notify.is_notify);
} // End we have a callback function ...
break;
} // ESP_GATTC_NOTIFY_EVT
// ESP_GATTC_READ_CHAR_EVT
// This event indicates that the server has responded to the read request.
//
// read:
// - esp_gatt_status_t status
// - uint16_t conn_id
// - uint16_t handle
// - uint8_t* value
// - uint16_t value_len
case ESP_GATTC_READ_CHAR_EVT: {
// If this event is not for us, then nothing further to do.
if (evtParam->read.handle != getHandle()) break;
// At this point, we have determined that the event is for us, so now we save the value
// and unlock the semaphore to ensure that the requestor of the data can continue.
if (evtParam->read.status == ESP_GATT_OK) {
m_value = std::string((char*) evtParam->read.value, evtParam->read.value_len);
if(m_rawData != nullptr) free(m_rawData);
m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t));
memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len);
} else {
m_value = "";
}
m_semaphoreReadCharEvt.give();
break;
} // ESP_GATTC_READ_CHAR_EVT
// ESP_GATTC_REG_FOR_NOTIFY_EVT
//
// reg_for_notify:
// - esp_gatt_status_t status
// - uint16_t handle
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
// If the request is not for this BLERemoteCharacteristic then move on to the next.
if (evtParam->reg_for_notify.handle != getHandle()) break;
// We have processed the notify registration and can unlock the semaphore.
m_semaphoreRegForNotifyEvt.give();
break;
} // ESP_GATTC_REG_FOR_NOTIFY_EVT
// ESP_GATTC_UNREG_FOR_NOTIFY_EVT
//
// unreg_for_notify:
// - esp_gatt_status_t status
// - uint16_t handle
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
if (evtParam->unreg_for_notify.handle != getHandle()) break;
// We have processed the notify un-registration and can unlock the semaphore.
m_semaphoreRegForNotifyEvt.give();
break;
} // ESP_GATTC_UNREG_FOR_NOTIFY_EVT:
// ESP_GATTC_WRITE_CHAR_EVT
//
// write:
// - esp_gatt_status_t status
// - uint16_t conn_id
// - uint16_t handle
case ESP_GATTC_WRITE_CHAR_EVT: {
// Determine if this event is for us and, if not, pass onwards.
if (evtParam->write.handle != getHandle()) break;
// There is nothing further we need to do here. This is merely an indication
// that the write has completed and we can unlock the caller.
m_semaphoreWriteCharEvt.give();
break;
} // ESP_GATTC_WRITE_CHAR_EVT
default:
break;
} // End switch
}; // gattClientEventHandler
/**
* @brief Populate the descriptors (if any) for this characteristic.
*/
void BLERemoteCharacteristic::retrieveDescriptors() {
log_v(">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
removeDescriptors(); // Remove any existing descriptors.
// Loop over each of the descriptors within the service associated with this characteristic.
// For each descriptor we find, create a BLERemoteDescriptor instance.
uint16_t offset = 0;
esp_gattc_descr_elem_t result;
while(true) {
uint16_t count = 10;
esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr(
getRemoteService()->getClient()->getGattcIf(),
getRemoteService()->getClient()->getConnId(),
getHandle(),
&result,
&count,
offset
);
if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
break;
}
if (status != ESP_GATT_OK) {
log_e("esp_ble_gattc_get_all_descr: %s", BLEUtils::gattStatusToString(status).c_str());
break;
}
if (count == 0) break;
log_d("Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str());
// We now have a new characteristic ... let us add that to our set of known characteristics
BLERemoteDescriptor* pNewRemoteDescriptor = new BLERemoteDescriptor(
result.handle,
BLEUUID(result.uuid),
this
);
m_descriptorMap.insert(std::pair<std::string, BLERemoteDescriptor*>(pNewRemoteDescriptor->getUUID().toString(), pNewRemoteDescriptor));
offset++;
} // while true
//m_haveCharacteristics = true; // Remember that we have received the characteristics.
log_v("<< retrieveDescriptors(): Found %d descriptors.", offset);
} // getDescriptors
/**
* @brief Retrieve the map of descriptors keyed by UUID.
*/
std::map<std::string, BLERemoteDescriptor*>* BLERemoteCharacteristic::getDescriptors() {
return &m_descriptorMap;
} // getDescriptors
/**
* @brief Get the handle for this characteristic.
* @return The handle for this characteristic.
*/
uint16_t BLERemoteCharacteristic::getHandle() {
//log_v(">> getHandle: Characteristic: %s", getUUID().toString().c_str());
//log_v("<< getHandle: %d 0x%.2x", m_handle, m_handle);
return m_handle;
} // getHandle
/**
* @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
* @param [in] uuid The UUID of the descriptor to find.
* @return The Remote descriptor (if present) or null if not present.
*/
BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) {
log_v(">> getDescriptor: uuid: %s", uuid.toString().c_str());
std::string v = uuid.toString();
for (auto &myPair : m_descriptorMap) {
if (myPair.first == v) {
log_v("<< getDescriptor: found");
return myPair.second;
}
}
log_v("<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
/**
* @brief Get the remote service associated with this characteristic.
* @return The remote service associated with this characteristic.
*/
BLERemoteService* BLERemoteCharacteristic::getRemoteService() {
return m_pRemoteService;
} // getRemoteService
/**
* @brief Get the UUID for this characteristic.
* @return The UUID for this characteristic.
*/
BLEUUID BLERemoteCharacteristic::getUUID() {
return m_uuid;
} // getUUID
/**
* @brief Read an unsigned 16 bit value
* @return The unsigned 16 bit value.
*/
uint16_t BLERemoteCharacteristic::readUInt16() {
std::string value = readValue();
if (value.length() >= 2) {
return *(uint16_t*)(value.data());
}
return 0;
} // readUInt16
/**
* @brief Read an unsigned 32 bit value.
* @return the unsigned 32 bit value.
*/
uint32_t BLERemoteCharacteristic::readUInt32() {
std::string value = readValue();
if (value.length() >= 4) {
return *(uint32_t*)(value.data());
}
return 0;
} // readUInt32
/**
* @brief Read a byte value
* @return The value as a byte
*/
uint8_t BLERemoteCharacteristic::readUInt8() {
std::string value = readValue();
if (value.length() >= 1) {
return (uint8_t)value[0];
}
return 0;
} // readUInt8
/**
* @brief Read the value of the remote characteristic.
* @return The value of the remote characteristic.
*/
std::string BLERemoteCharacteristic::readValue() {
log_v(">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle());
// Check to see that we are connected.
if (!getRemoteService()->getClient()->isConnected()) {
log_e("Disconnected");
return std::string();
}
m_semaphoreReadCharEvt.take("readValue");
// Ask the BLE subsystem to retrieve the value for the remote hosted characteristic.
// This is an asynchronous request which means that we must block waiting for the response
// to become available.
esp_err_t errRc = ::esp_ble_gattc_read_char(
m_pRemoteService->getClient()->getGattcIf(),
m_pRemoteService->getClient()->getConnId(), // The connection ID to the BLE server
getHandle(), // The handle of this characteristic
ESP_GATT_AUTH_REQ_NONE); // Security
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return "";
}
// Block waiting for the event that indicates that the read has completed. When it has, the std::string found
// in m_value will contain our data.
m_semaphoreReadCharEvt.wait("readValue");
log_v("<< readValue(): length: %d", m_value.length());
return m_value;
} // readValue
/**
* @brief Register for notifications.
* @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are
* unregistering a notification.
* @return N/A.
*/
void BLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications) {
log_v(">> registerForNotify(): %s", toString().c_str());
m_notifyCallback = notifyCallback; // Save the notification callback.
m_semaphoreRegForNotifyEvt.take("registerForNotify");
if (notifyCallback != nullptr) { // If we have a callback function, then this is a registration.
esp_err_t errRc = ::esp_ble_gattc_register_for_notify(
m_pRemoteService->getClient()->getGattcIf(),
*m_pRemoteService->getClient()->getPeerAddress().getNative(),
getHandle()
);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
uint8_t val[] = {0x01, 0x00};
if(!notifications) val[0] = 0x02;
BLERemoteDescriptor* desc = getDescriptor(BLEUUID((uint16_t)0x2902));
desc->writeValue(val, 2);
} // End Register
else { // If we weren't passed a callback function, then this is an unregistration.
esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify(
m_pRemoteService->getClient()->getGattcIf(),
*m_pRemoteService->getClient()->getPeerAddress().getNative(),
getHandle()
);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
}
uint8_t val[] = {0x00, 0x00};
BLERemoteDescriptor* desc = getDescriptor((uint16_t)0x2902);
desc->writeValue(val, 2);
} // End Unregister
m_semaphoreRegForNotifyEvt.wait("registerForNotify");
log_v("<< registerForNotify()");
} // registerForNotify
/**
* @brief Delete the descriptors in the descriptor map.
* We maintain a map called m_descriptorMap that contains pointers to BLERemoteDescriptors
* object references. Since we allocated these in this class, we are also responsible for deleteing
* them. This method does just that.
* @return N/A.
*/
void BLERemoteCharacteristic::removeDescriptors() {
// Iterate through all the descriptors releasing their storage and erasing them from the map.
for (auto &myPair : m_descriptorMap) {
m_descriptorMap.erase(myPair.first);
delete myPair.second;
}
m_descriptorMap.clear(); // Technically not neeeded, but just to be sure.
} // removeCharacteristics
/**
* @brief Convert a BLERemoteCharacteristic to a string representation;
* @return a String representation.
*/
std::string BLERemoteCharacteristic::toString() {
std::string res = "Characteristic: uuid: " + m_uuid.toString();
char val[6];
res += ", handle: ";
snprintf(val, sizeof(val), "%d", getHandle());
res += val;
res += " 0x";
snprintf(val, sizeof(val), "%04x", getHandle());
res += val;
res += ", props: " + BLEUtils::characteristicPropertiesToString(m_charProp);
return res;
} // toString
/**
* @brief Write the new value for the characteristic.
* @param [in] newValue The new value to write.
* @param [in] response Do we expect a response?
* @return N/A.
*/
void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) {
writeValue((uint8_t*)newValue.c_str(), strlen(newValue.c_str()), response);
} // writeValue
/**
* @brief Write the new value for the characteristic.
*
* This is a convenience function. Many BLE characteristics are a single byte of data.
* @param [in] newValue The new byte value to write.
* @param [in] response Whether we require a response from the write.
* @return N/A.
*/
void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) {
writeValue(&newValue, 1, response);
} // writeValue
/**
* @brief Write the new value for the characteristic from a data buffer.
* @param [in] data A pointer to a data buffer.
* @param [in] length The length of the data in the data buffer.
* @param [in] response Whether we require a response from the write.
*/
void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) {
// writeValue(std::string((char*)data, length), response);
log_v(">> writeValue(), length: %d", length);
// Check to see that we are connected.
if (!getRemoteService()->getClient()->isConnected()) {
log_e("Disconnected");
return;
}
m_semaphoreWriteCharEvt.take("writeValue");
// Invoke the ESP-IDF API to perform the write.
esp_err_t errRc = ::esp_ble_gattc_write_char(
m_pRemoteService->getClient()->getGattcIf(),
m_pRemoteService->getClient()->getConnId(),
getHandle(),
length,
data,
response?ESP_GATT_WRITE_TYPE_RSP:ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE
);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_write_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
m_semaphoreWriteCharEvt.wait("writeValue");
log_v("<< writeValue");
} // writeValue
/**
* @brief Read raw data from remote characteristic as hex bytes
* @return return pointer data read
*/
uint8_t* BLERemoteCharacteristic::readRawData() {
return m_rawData;
}
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,84 @@
/*
* BLERemoteCharacteristic.h
*
* Created on: Jul 8, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_
#define COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <string>
#include <esp_gattc_api.h>
#include "BLERemoteService.h"
#include "BLERemoteDescriptor.h"
#include "BLEUUID.h"
#include "FreeRTOS.h"
class BLERemoteService;
class BLERemoteDescriptor;
typedef void (*notify_callback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify);
/**
* @brief A model of a remote %BLE characteristic.
*/
class BLERemoteCharacteristic {
public:
~BLERemoteCharacteristic();
// Public member functions
bool canBroadcast();
bool canIndicate();
bool canNotify();
bool canRead();
bool canWrite();
bool canWriteNoResponse();
BLERemoteDescriptor* getDescriptor(BLEUUID uuid);
std::map<std::string, BLERemoteDescriptor*>* getDescriptors();
uint16_t getHandle();
BLEUUID getUUID();
std::string readValue();
uint8_t readUInt8();
uint16_t readUInt16();
uint32_t readUInt32();
void registerForNotify(notify_callback _callback, bool notifications = true);
void writeValue(uint8_t* data, size_t length, bool response = false);
void writeValue(std::string newValue, bool response = false);
void writeValue(uint8_t newValue, bool response = false);
std::string toString();
uint8_t* readRawData();
private:
BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService);
friend class BLEClient;
friend class BLERemoteService;
friend class BLERemoteDescriptor;
// Private member functions
void gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* evtParam);
BLERemoteService* getRemoteService();
void removeDescriptors();
void retrieveDescriptors();
// Private properties
BLEUUID m_uuid;
esp_gatt_char_prop_t m_charProp;
uint16_t m_handle;
BLERemoteService* m_pRemoteService;
FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt");
FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt");
FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt");
std::string m_value;
uint8_t *m_rawData;
notify_callback m_notifyCallback;
// We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID.
std::map<std::string, BLERemoteDescriptor*> m_descriptorMap;
}; // BLERemoteCharacteristic
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */

View File

@ -0,0 +1,175 @@
/*
* BLERemoteDescriptor.cpp
*
* Created on: Jul 8, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <sstream>
#include "BLERemoteDescriptor.h"
#include "GeneralUtils.h"
#include "esp32-hal-log.h"
BLERemoteDescriptor::BLERemoteDescriptor(
uint16_t handle,
BLEUUID uuid,
BLERemoteCharacteristic* pRemoteCharacteristic) {
m_handle = handle;
m_uuid = uuid;
m_pRemoteCharacteristic = pRemoteCharacteristic;
}
/**
* @brief Retrieve the handle associated with this remote descriptor.
* @return The handle associated with this remote descriptor.
*/
uint16_t BLERemoteDescriptor::getHandle() {
return m_handle;
} // getHandle
/**
* @brief Get the characteristic that owns this descriptor.
* @return The characteristic that owns this descriptor.
*/
BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() {
return m_pRemoteCharacteristic;
} // getRemoteCharacteristic
/**
* @brief Retrieve the UUID associated this remote descriptor.
* @return The UUID associated this remote descriptor.
*/
BLEUUID BLERemoteDescriptor::getUUID() {
return m_uuid;
} // getUUID
std::string BLERemoteDescriptor::readValue() {
log_v(">> readValue: %s", toString().c_str());
// Check to see that we are connected.
if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) {
log_e("Disconnected");
return std::string();
}
m_semaphoreReadDescrEvt.take("readValue");
// Ask the BLE subsystem to retrieve the value for the remote hosted characteristic.
esp_err_t errRc = ::esp_ble_gattc_read_char_descr(
m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(),
m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server
getHandle(), // The handle of this characteristic
ESP_GATT_AUTH_REQ_NONE); // Security
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return "";
}
// Block waiting for the event that indicates that the read has completed. When it has, the std::string found
// in m_value will contain our data.
m_semaphoreReadDescrEvt.wait("readValue");
log_v("<< readValue(): length: %d", m_value.length());
return m_value;
} // readValue
uint8_t BLERemoteDescriptor::readUInt8() {
std::string value = readValue();
if (value.length() >= 1) {
return (uint8_t) value[0];
}
return 0;
} // readUInt8
uint16_t BLERemoteDescriptor::readUInt16() {
std::string value = readValue();
if (value.length() >= 2) {
return *(uint16_t*) value.data();
}
return 0;
} // readUInt16
uint32_t BLERemoteDescriptor::readUInt32() {
std::string value = readValue();
if (value.length() >= 4) {
return *(uint32_t*) value.data();
}
return 0;
} // readUInt32
/**
* @brief Return a string representation of this BLE Remote Descriptor.
* @retun A string representation of this BLE Remote Descriptor.
*/
std::string BLERemoteDescriptor::toString() {
char val[6];
snprintf(val, sizeof(val), "%d", getHandle());
std::string res = "handle: ";
res += val;
res += ", uuid: " + getUUID().toString();
return res;
} // toString
/**
* @brief Write data to the BLE Remote Descriptor.
* @param [in] data The data to send to the remote descriptor.
* @param [in] length The length of the data to send.
* @param [in] response True if we expect a response.
*/
void BLERemoteDescriptor::writeValue(uint8_t* data, size_t length, bool response) {
log_v(">> writeValue: %s", toString().c_str());
// Check to see that we are connected.
if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) {
log_e("Disconnected");
return;
}
esp_err_t errRc = ::esp_ble_gattc_write_char_descr(
m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(),
m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(),
getHandle(),
length, // Data length
data, // Data
response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE
);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_write_char_descr: %d", errRc);
}
log_v("<< writeValue");
} // writeValue
/**
* @brief Write data represented as a string to the BLE Remote Descriptor.
* @param [in] newValue The data to send to the remote descriptor.
* @param [in] response True if we expect a response.
*/
void BLERemoteDescriptor::writeValue(std::string newValue, bool response) {
writeValue((uint8_t*) newValue.data(), newValue.length(), response);
} // writeValue
/**
* @brief Write a byte value to the Descriptor.
* @param [in] The single byte to write.
* @param [in] True if we expect a response.
*/
void BLERemoteDescriptor::writeValue(uint8_t newValue, bool response) {
writeValue(&newValue, 1, response);
} // writeValue
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,55 @@
/*
* BLERemoteDescriptor.h
*
* Created on: Jul 8, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_
#define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <string>
#include <esp_gattc_api.h>
#include "BLERemoteCharacteristic.h"
#include "BLEUUID.h"
#include "FreeRTOS.h"
class BLERemoteCharacteristic;
/**
* @brief A model of remote %BLE descriptor.
*/
class BLERemoteDescriptor {
public:
uint16_t getHandle();
BLERemoteCharacteristic* getRemoteCharacteristic();
BLEUUID getUUID();
std::string readValue(void);
uint8_t readUInt8(void);
uint16_t readUInt16(void);
uint32_t readUInt32(void);
std::string toString(void);
void writeValue(uint8_t* data, size_t length, bool response = false);
void writeValue(std::string newValue, bool response = false);
void writeValue(uint8_t newValue, bool response = false);
private:
friend class BLERemoteCharacteristic;
BLERemoteDescriptor(
uint16_t handle,
BLEUUID uuid,
BLERemoteCharacteristic* pRemoteCharacteristic
);
uint16_t m_handle; // Server handle of this descriptor.
BLEUUID m_uuid; // UUID of this descriptor.
std::string m_value; // Last received value of the descriptor.
BLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated.
FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt");
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */

View File

@ -0,0 +1,343 @@
/*
* BLERemoteService.cpp
*
* Created on: Jul 8, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <sstream>
#include "BLERemoteService.h"
#include "BLEUtils.h"
#include "GeneralUtils.h"
#include <esp_err.h>
#include "esp32-hal-log.h"
#pragma GCC diagnostic warning "-Wunused-but-set-parameter"
BLERemoteService::BLERemoteService(
esp_gatt_id_t srvcId,
BLEClient* pClient,
uint16_t startHandle,
uint16_t endHandle
) {
log_v(">> BLERemoteService()");
m_srvcId = srvcId;
m_pClient = pClient;
m_uuid = BLEUUID(m_srvcId);
m_haveCharacteristics = false;
m_startHandle = startHandle;
m_endHandle = endHandle;
log_v("<< BLERemoteService()");
}
BLERemoteService::~BLERemoteService() {
removeCharacteristics();
}
/*
static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) {
if (id1.id.inst_id != id2.id.inst_id) {
return false;
}
if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) {
return false;
}
return true;
} // compareSrvcId
*/
/**
* @brief Handle GATT Client events
*/
void BLERemoteService::gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* evtParam) {
switch (event) {
//
// ESP_GATTC_GET_CHAR_EVT
//
// get_char:
// - esp_gatt_status_t status
// - uin1t6_t conn_id
// - esp_gatt_srvc_id_t srvc_id
// - esp_gatt_id_t char_id
// - esp_gatt_char_prop_t char_prop
//
/*
case ESP_GATTC_GET_CHAR_EVT: {
// Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be
// the same.
if (compareSrvcId(m_srvcId, evtParam->get_char.srvc_id) == false) {
break;
}
// If the status is NOT OK then we have a problem and continue.
if (evtParam->get_char.status != ESP_GATT_OK) {
m_semaphoreGetCharEvt.give();
break;
}
// This is an indication that we now have the characteristic details for a characteristic owned
// by this service so remember it.
m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(
BLEUUID(evtParam->get_char.char_id.uuid).toString(),
new BLERemoteCharacteristic(evtParam->get_char.char_id, evtParam->get_char.char_prop, this) ));
// Now that we have received a characteristic, lets ask for the next one.
esp_err_t errRc = ::esp_ble_gattc_get_characteristic(
m_pClient->getGattcIf(),
m_pClient->getConnId(),
&m_srvcId,
&evtParam->get_char.char_id);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
break;
}
//m_semaphoreGetCharEvt.give();
break;
} // ESP_GATTC_GET_CHAR_EVT
*/
default:
break;
} // switch
// Send the event to each of the characteristics owned by this service.
for (auto &myPair : m_characteristicMapByHandle) {
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
} // gattClientEventHandler
/**
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
* @return Reference to the remote characteristic object.
* @throws BLEUuidNotFoundException
*/
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) {
return getCharacteristic(BLEUUID(uuid));
} // getCharacteristic
/**
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
* @return Reference to the characteristic object.
* @throws BLEUuidNotFoundException
*/
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) {
// Design
// ------
// We wish to retrieve the characteristic given its UUID. It is possible that we have not yet asked the
// device what characteristics it has in which case we have nothing to match against. If we have not
// asked the device about its characteristics, then we do that now. Once we get the results we can then
// examine the characteristics map to see if it has the characteristic we are looking for.
if (!m_haveCharacteristics) {
retrieveCharacteristics();
}
std::string v = uuid.toString();
for (auto &myPair : m_characteristicMap) {
if (myPair.first == v) {
return myPair.second;
}
}
// throw new BLEUuidNotFoundException(); // <-- we dont want exception here, which will cause app crash, we want to search if any characteristic can be found one after another
return nullptr;
} // getCharacteristic
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return N/A
*/
void BLERemoteService::retrieveCharacteristics() {
log_v(">> getCharacteristics() for service: %s", getUUID().toString().c_str());
removeCharacteristics(); // Forget any previous characteristics.
uint16_t offset = 0;
esp_gattc_char_elem_t result;
while (true) {
uint16_t count = 1; // only room for 1 result allocated, so go one by one
esp_gatt_status_t status = ::esp_ble_gattc_get_all_char(
getClient()->getGattcIf(),
getClient()->getConnId(),
m_startHandle,
m_endHandle,
&result,
&count,
offset
);
if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
break;
}
if (status != ESP_GATT_OK) { // If we got an error, end.
log_e("esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str());
break;
}
if (count == 0) { // If we failed to get any new records, end.
break;
}
log_d("Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str());
// We now have a new characteristic ... let us add that to our set of known characteristics
BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic(
result.char_handle,
BLEUUID(result.uuid),
result.properties,
this
);
m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic));
m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic*>(result.char_handle, pNewRemoteCharacteristic));
offset++; // Increment our count of number of descriptors found.
} // Loop forever (until we break inside the loop).
m_haveCharacteristics = true; // Remember that we have received the characteristics.
log_v("<< getCharacteristics()");
} // getCharacteristics
/**
* @brief Retrieve a map of all the characteristics of this service.
* @return A map of all the characteristics of this service.
*/
std::map<std::string, BLERemoteCharacteristic*>* BLERemoteService::getCharacteristics() {
log_v(">> getCharacteristics() for service: %s", getUUID().toString().c_str());
// If is possible that we have not read the characteristics associated with the service so do that
// now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking
// call and does not return until all the characteristics are available.
if (!m_haveCharacteristics) {
retrieveCharacteristics();
}
log_v("<< getCharacteristics() for service: %s", getUUID().toString().c_str());
return &m_characteristicMap;
} // getCharacteristics
/**
* @brief This function is designed to get characteristics map when we have multiple characteristics with the same UUID
*/
void BLERemoteService::getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* pCharacteristicMap) {
pCharacteristicMap = &m_characteristicMapByHandle;
} // Get the characteristics map.
/**
* @brief Get the client associated with this service.
* @return A reference to the client associated with this service.
*/
BLEClient* BLERemoteService::getClient() {
return m_pClient;
} // getClient
uint16_t BLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
esp_gatt_id_t* BLERemoteService::getSrvcId() {
return &m_srvcId;
} // getSrvcId
uint16_t BLERemoteService::getStartHandle() {
return m_startHandle;
} // getStartHandle
uint16_t BLERemoteService::getHandle() {
log_v(">> getHandle: service: %s", getUUID().toString().c_str());
log_v("<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle());
return getStartHandle();
} // getHandle
BLEUUID BLERemoteService::getUUID() {
return m_uuid;
}
/**
* @brief Read the value of a characteristic associated with this service.
*/
std::string BLERemoteService::getValue(BLEUUID characteristicUuid) {
log_v(">> readValue: uuid: %s", characteristicUuid.toString().c_str());
std::string ret = getCharacteristic(characteristicUuid)->readValue();
log_v("<< readValue");
return ret;
} // readValue
/**
* @brief Delete the characteristics in the characteristics map.
* We maintain a map called m_characteristicsMap that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleteing
* them. This method does just that.
* @return N/A.
*/
void BLERemoteService::removeCharacteristics() {
for (auto &myPair : m_characteristicMap) {
delete myPair.second;
//m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear
}
m_characteristicMap.clear(); // Clear the map
for (auto &myPair : m_characteristicMapByHandle) {
delete myPair.second;
}
m_characteristicMapByHandle.clear(); // Clear the map
} // removeCharacteristics
/**
* @brief Set the value of a characteristic.
* @param [in] characteristicUuid The characteristic to set.
* @param [in] value The value to set.
* @throws BLEUuidNotFound
*/
void BLERemoteService::setValue(BLEUUID characteristicUuid, std::string value) {
log_v(">> setValue: uuid: %s", characteristicUuid.toString().c_str());
getCharacteristic(characteristicUuid)->writeValue(value);
log_v("<< setValue");
} // setValue
/**
* @brief Create a string representation of this remote service.
* @return A string representation of this remote service.
*/
std::string BLERemoteService::toString() {
std::string res = "Service: uuid: " + m_uuid.toString();
char val[6];
res += ", start_handle: ";
snprintf(val, sizeof(val), "%d", m_startHandle);
res += val;
snprintf(val, sizeof(val), "%04x", m_startHandle);
res += " 0x";
res += val;
res += ", end_handle: ";
snprintf(val, sizeof(val), "%d", m_endHandle);
res += val;
snprintf(val, sizeof(val), "%04x", m_endHandle);
res += " 0x";
res += val;
for (auto &myPair : m_characteristicMap) {
res += "\n" + myPair.second->toString();
// myPair.second is the value
}
return res;
} // toString
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,85 @@
/*
* BLERemoteService.h
*
* Created on: Jul 8, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_
#define COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <map>
#include "BLEClient.h"
#include "BLERemoteCharacteristic.h"
#include "BLEUUID.h"
#include "FreeRTOS.h"
class BLEClient;
class BLERemoteCharacteristic;
/**
* @brief A model of a remote %BLE service.
*/
class BLERemoteService {
public:
virtual ~BLERemoteService();
// Public methods
BLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference.
BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference.
BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference.
std::map<std::string, BLERemoteCharacteristic*>* getCharacteristics();
std::map<uint16_t, BLERemoteCharacteristic*>* getCharacteristicsByHandle(); // Get the characteristics map.
void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* pCharacteristicMap);
BLEClient* getClient(void); // Get a reference to the client associated with this service.
uint16_t getHandle(); // Get the handle of this service.
BLEUUID getUUID(void); // Get the UUID of this service.
std::string getValue(BLEUUID characteristicUuid); // Get the value of a characteristic.
void setValue(BLEUUID characteristicUuid, std::string value); // Set the value of a characteristic.
std::string toString(void);
private:
// Private constructor ... never meant to be created by a user application.
BLERemoteService(esp_gatt_id_t srvcId, BLEClient* pClient, uint16_t startHandle, uint16_t endHandle);
// Friends
friend class BLEClient;
friend class BLERemoteCharacteristic;
// Private methods
void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server.
esp_gatt_id_t* getSrvcId(void);
uint16_t getStartHandle(); // Get the start handle for this service.
uint16_t getEndHandle(); // Get the end handle for this service.
void gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* evtParam);
void removeCharacteristics();
// Properties
// We maintain a map of characteristics owned by this service keyed by a string representation of the UUID.
std::map<std::string, BLERemoteCharacteristic*> m_characteristicMap;
// We maintain a map of characteristics owned by this service keyed by a handle.
std::map<uint16_t, BLERemoteCharacteristic*> m_characteristicMapByHandle;
bool m_haveCharacteristics; // Have we previously obtained the characteristics.
BLEClient* m_pClient;
FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt");
esp_gatt_id_t m_srvcId;
BLEUUID m_uuid; // The UUID of this service.
uint16_t m_startHandle; // The starting handle of this service.
uint16_t m_endHandle; // The ending handle of this service.
}; // BLERemoteService
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */

View File

@ -0,0 +1,322 @@
/*
* BLEScan.cpp
*
* Created on: Jul 1, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_err.h>
#include <map>
#include "BLEAdvertisedDevice.h"
#include "BLEScan.h"
#include "BLEUtils.h"
#include "GeneralUtils.h"
#include "esp32-hal-log.h"
/**
* Constructor
*/
BLEScan::BLEScan() {
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan.
m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
m_pAdvertisedDeviceCallbacks = nullptr;
m_stopped = true;
m_wantDuplicates = false;
setInterval(100);
setWindow(100);
} // BLEScan
/**
* @brief Handle GAP events related to scans.
* @param [in] event The event type for this event.
* @param [in] param Parameter data for this event.
*/
void BLEScan::handleGAPEvent(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param) {
switch(event) {
// ---------------------------
// scan_rst:
// esp_gap_search_evt_t search_evt
// esp_bd_addr_t bda
// esp_bt_dev_type_t dev_type
// esp_ble_addr_type_t ble_addr_type
// esp_ble_evt_type_t ble_evt_type
// int rssi
// uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX]
// int flag
// int num_resps
// uint8_t adv_data_len
// uint8_t scan_rsp_len
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
switch(param->scan_rst.search_evt) {
//
// ESP_GAP_SEARCH_INQ_CMPL_EVT
//
// Event that indicates that the duration allowed for the search has completed or that we have been
// asked to stop.
case ESP_GAP_SEARCH_INQ_CMPL_EVT: {
log_w("ESP_GAP_SEARCH_INQ_CMPL_EVT");
m_stopped = true;
m_semaphoreScanEnd.give();
if (m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
break;
} // ESP_GAP_SEARCH_INQ_CMPL_EVT
//
// ESP_GAP_SEARCH_INQ_RES_EVT
//
// Result that has arrived back from a Scan inquiry.
case ESP_GAP_SEARCH_INQ_RES_EVT: {
if (m_stopped) { // If we are not scanning, nothing to do with the extra results.
break;
}
// Examine our list of previously scanned addresses and, if we found this one already,
// ignore it.
BLEAddress advertisedAddress(param->scan_rst.bda);
bool found = false;
if (m_scanResults.m_vectorAdvertisedDevices.count(advertisedAddress.toString()) != 0) {
found = true;
}
if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done.
log_d("Ignoring %s, already seen it.", advertisedAddress.toString().c_str());
vTaskDelay(1); // <--- allow to switch task in case we scan infinity and dont have new devices to report, or we are blocked here
break;
}
// We now construct a model of the advertised device that we have just found for the first
// time.
// ESP_LOG_BUFFER_HEXDUMP((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len, ESP_LOG_DEBUG);
// log_w("bytes length: %d + %d, addr type: %d", param->scan_rst.adv_data_len, param->scan_rst.scan_rsp_len, param->scan_rst.ble_addr_type);
BLEAdvertisedDevice *advertisedDevice = new BLEAdvertisedDevice();
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setRSSI(param->scan_rst.rssi);
advertisedDevice->setAdFlag(param->scan_rst.flag);
advertisedDevice->parseAdvertisement((uint8_t*)param->scan_rst.ble_adv, param->scan_rst.adv_data_len + param->scan_rst.scan_rsp_len);
advertisedDevice->setScan(this);
advertisedDevice->setAddressType(param->scan_rst.ble_addr_type);
if (!found) { // If we have previously seen this device, don't record it again.
m_scanResults.m_vectorAdvertisedDevices.insert(std::pair<std::string, BLEAdvertisedDevice*>(advertisedAddress.toString(), advertisedDevice));
}
if (m_pAdvertisedDeviceCallbacks) {
m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice);
}
if(found)
delete advertisedDevice;
break;
} // ESP_GAP_SEARCH_INQ_RES_EVT
default: {
break;
}
} // switch - search_evt
break;
} // ESP_GAP_BLE_SCAN_RESULT_EVT
default: {
break;
} // default
} // End switch
} // gapEventHandler
/**
* @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
* @return N/A.
*/
void BLEScan::setActiveScan(bool active) {
if (active) {
m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE;
} else {
m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE;
}
} // setActiveScan
/**
* @brief Set the call backs to be invoked.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
* @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false.
*/
void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) {
m_wantDuplicates = wantDuplicates;
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
} // setAdvertisedDeviceCallbacks
/**
* @brief Set the interval to scan.
* @param [in] The interval in msecs.
*/
void BLEScan::setInterval(uint16_t intervalMSecs) {
m_scan_params.scan_interval = intervalMSecs / 0.625;
} // setInterval
/**
* @brief Set the window to actively scan.
* @param [in] windowMSecs How long to actively scan.
*/
void BLEScan::setWindow(uint16_t windowMSecs) {
m_scan_params.scan_window = windowMSecs / 0.625;
} // setWindow
/**
* @brief Start scanning.
* @param [in] duration The duration in seconds for which to scan.
* @param [in] scanCompleteCB A function to be called when scanning has completed.
* @param [in] are we continue scan (true) or we want to clear stored devices (false)
* @return True if scan started or false if there was an error.
*/
bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue) {
log_v(">> start(duration=%d)", duration);
m_semaphoreScanEnd.take(std::string("start"));
m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes.
// if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
// then we should not clear map or we will connect the same device few times
if(!is_continue) {
for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){
delete _dev.second;
}
m_scanResults.m_vectorAdvertisedDevices.clear();
}
esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params);
if (errRc != ESP_OK) {
log_e("esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
m_semaphoreScanEnd.give();
return false;
}
errRc = ::esp_ble_gap_start_scanning(duration);
if (errRc != ESP_OK) {
log_e("esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
m_semaphoreScanEnd.give();
return false;
}
m_stopped = false;
log_v("<< start()");
return true;
} // start
/**
* @brief Start scanning and block until scanning has been completed.
* @param [in] duration The duration in seconds for which to scan.
* @return The BLEScanResults.
*/
BLEScanResults BLEScan::start(uint32_t duration, bool is_continue) {
if(start(duration, nullptr, is_continue)) {
m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release.
}
return m_scanResults;
} // start
/**
* @brief Stop an in progress scan.
* @return N/A.
*/
void BLEScan::stop() {
log_v(">> stop()");
esp_err_t errRc = ::esp_ble_gap_stop_scanning();
m_stopped = true;
m_semaphoreScanEnd.give();
if (errRc != ESP_OK) {
log_e("esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
log_v("<< stop()");
} // stop
// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address
void BLEScan::erase(BLEAddress address) {
log_i("erase device: %s", address.toString().c_str());
BLEAdvertisedDevice *advertisedDevice = m_scanResults.m_vectorAdvertisedDevices.find(address.toString())->second;
m_scanResults.m_vectorAdvertisedDevices.erase(address.toString());
delete advertisedDevice;
}
/**
* @brief Dump the scan results to the log.
*/
void BLEScanResults::dump() {
log_v(">> Dump scan results:");
for (int i=0; i<getCount(); i++) {
log_d("- %s", getDevice(i).toString().c_str());
}
} // dump
/**
* @brief Return the count of devices found in the last scan.
* @return The number of devices found in the last scan.
*/
int BLEScanResults::getCount() {
return m_vectorAdvertisedDevices.size();
} // getCount
/**
* @brief Return the specified device at the given index.
* The index should be between 0 and getCount()-1.
* @param [in] i The index of the device.
* @return The device at the specified index.
*/
BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t i) {
uint32_t x = 0;
BLEAdvertisedDevice dev = *m_vectorAdvertisedDevices.begin()->second;
for (auto it = m_vectorAdvertisedDevices.begin(); it != m_vectorAdvertisedDevices.end(); it++) {
dev = *it->second;
if (x==i) break;
x++;
}
return dev;
}
BLEScanResults BLEScan::getResults() {
return m_scanResults;
}
void BLEScan::clearResults() {
for(auto _dev : m_scanResults.m_vectorAdvertisedDevices){
delete _dev.second;
}
m_scanResults.m_vectorAdvertisedDevices.clear();
}
#endif /* CONFIG_BT_ENABLED */

View File

@ -0,0 +1,83 @@
/*
* BLEScan.h
*
* Created on: Jul 1, 2017
* Author: kolban
*/
#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_
#define COMPONENTS_CPP_UTILS_BLESCAN_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gap_ble_api.h>
// #include <vector>
#include <string>
#include "BLEAdvertisedDevice.h"
#include "BLEClient.h"
#include "FreeRTOS.h"
class BLEAdvertisedDevice;
class BLEAdvertisedDeviceCallbacks;
class BLEClient;
class BLEScan;
/**
* @brief The result of having performed a scan.
* When a scan completes, we have a set of found devices. Each device is described
* by a BLEAdvertisedDevice object. The number of items in the set is given by
* getCount(). We can retrieve a device by calling getDevice() passing in the
* index (starting at 0) of the desired device.
*/
class BLEScanResults {
public:
void dump();
int getCount();
BLEAdvertisedDevice getDevice(uint32_t i);
private:
friend BLEScan;
std::map<std::string, BLEAdvertisedDevice*> m_vectorAdvertisedDevices;
};
/**
* @brief Perform and manage %BLE scans.
*
* Scanning is associated with a %BLE client that is attempting to locate BLE servers.
*/
class BLEScan {
public:
void setActiveScan(bool active);
void setAdvertisedDeviceCallbacks(
BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
bool wantDuplicates = false);
void setInterval(uint16_t intervalMSecs);
void setWindow(uint16_t windowMSecs);
bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults), bool is_continue = false);
BLEScanResults start(uint32_t duration, bool is_continue = false);
void stop();
void erase(BLEAddress address);
BLEScanResults getResults();
void clearResults();
private:
BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton.
friend class BLEDevice;
void handleGAPEvent(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param);
void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload);
esp_ble_scan_params_t m_scan_params;
BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
bool m_stopped = true;
FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd");
BLEScanResults m_scanResults;
bool m_wantDuplicates;
void (*m_scanCompleteCB)(BLEScanResults scanResults);
}; // BLEScan
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */

View File

@ -0,0 +1,104 @@
/*
* BLESecurity.cpp
*
* Created on: Dec 17, 2017
* Author: chegewara
*/
#include "BLESecurity.h"
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
BLESecurity::BLESecurity() {
}
BLESecurity::~BLESecurity() {
}
/*
* @brief Set requested authentication mode
*/
void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
m_authReq = auth_req;
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode
}
/**
* @brief Set our device IO capability to let end user perform authorization
* either by displaying or entering generated 6-digits pin code
*/
void BLESecurity::setCapability(esp_ble_io_cap_t iocap) {
m_iocap = iocap;
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
} // setCapability
/**
* @brief Init encryption key by server
* @param key_size is value between 7 and 16
*/
void BLESecurity::setInitEncryptionKey(uint8_t init_key) {
m_initKey = init_key;
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t));
} // setInitEncryptionKey
/**
* @brief Init encryption key by client
* @param key_size is value between 7 and 16
*/
void BLESecurity::setRespEncryptionKey(uint8_t resp_key) {
m_respKey = resp_key;
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t));
} // setRespEncryptionKey
/**
*
*
*/
void BLESecurity::setKeySize(uint8_t key_size) {
m_keySize = key_size;
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t));
} //setKeySize
/**
* @brief Debug function to display what keys are exchanged by peers
*/
char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) {
char* key_str = nullptr;
switch (key_type) {
case ESP_LE_KEY_NONE:
key_str = (char*) "ESP_LE_KEY_NONE";
break;
case ESP_LE_KEY_PENC:
key_str = (char*) "ESP_LE_KEY_PENC";
break;
case ESP_LE_KEY_PID:
key_str = (char*) "ESP_LE_KEY_PID";
break;
case ESP_LE_KEY_PCSRK:
key_str = (char*) "ESP_LE_KEY_PCSRK";
break;
case ESP_LE_KEY_PLK:
key_str = (char*) "ESP_LE_KEY_PLK";
break;
case ESP_LE_KEY_LLK:
key_str = (char*) "ESP_LE_KEY_LLK";
break;
case ESP_LE_KEY_LENC:
key_str = (char*) "ESP_LE_KEY_LENC";
break;
case ESP_LE_KEY_LID:
key_str = (char*) "ESP_LE_KEY_LID";
break;
case ESP_LE_KEY_LCSRK:
key_str = (char*) "ESP_LE_KEY_LCSRK";
break;
default:
key_str = (char*) "INVALID BLE KEY TYPE";
break;
}
return key_str;
} // esp_key_type_to_str
#endif // CONFIG_BT_ENABLED

View File

@ -0,0 +1,72 @@
/*
* BLESecurity.h
*
* Created on: Dec 17, 2017
* Author: chegewara
*/
#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_
#define COMPONENTS_CPP_UTILS_BLESECURITY_H_
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_gap_ble_api.h>
class BLESecurity {
public:
BLESecurity();
virtual ~BLESecurity();
void setAuthenticationMode(esp_ble_auth_req_t auth_req);
void setCapability(esp_ble_io_cap_t iocap);
void setInitEncryptionKey(uint8_t init_key);
void setRespEncryptionKey(uint8_t resp_key);
void setKeySize(uint8_t key_size = 16);
static char* esp_key_type_to_str(esp_ble_key_type_t key_type);
private:
esp_ble_auth_req_t m_authReq;
esp_ble_io_cap_t m_iocap;
uint8_t m_initKey;
uint8_t m_respKey;
uint8_t m_keySize;
}; // BLESecurity
/*
* @brief Callbacks to handle GAP events related to authorization
*/
class BLESecurityCallbacks {
public:
virtual ~BLESecurityCallbacks() {};
/**
* @brief Its request from peer device to input authentication pin code displayed on peer device.
* It requires that our device is capable to input 6-digits code by end user
* @return Return 6-digits integer value from input device
*/
virtual uint32_t onPassKeyRequest() = 0;
/**
* @brief Provide us 6-digits code to perform authentication.
* It requires that our device is capable to display this code to end user
* @param
*/
virtual void onPassKeyNotify(uint32_t pass_key) = 0;
/**
* @brief Here we can make decision if we want to let negotiate authorization with peer device or not
* return Return true if we accept this peer device request
*/
virtual bool onSecurityRequest() = 0 ;
/**
* Provide us information when authentication process is completed
*/
virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0;
virtual bool onConfirmPIN(uint32_t pin) = 0;
}; // BLESecurityCallbacks
#endif // CONFIG_BT_ENABLED
#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_

View File

@ -0,0 +1,420 @@
/*
* BLEServer.cpp
*
* Created on: Apr 16, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_bt.h>
#include <esp_bt_main.h>
#include "GeneralUtils.h"
#include "BLEDevice.h"
#include "BLEServer.h"
#include "BLEService.h"
#include "BLEUtils.h"
#include <string.h>
#include <string>
#include <unordered_set>
#include "esp32-hal-log.h"
/**
* @brief Construct a %BLE Server
*
* This class is not designed to be individually instantiated. Instead one should create a server by asking
* the BLEDevice class.
*/
BLEServer::BLEServer() {
m_appId = ESP_GATT_IF_NONE;
m_gatts_if = ESP_GATT_IF_NONE;
m_connectedCount = 0;
m_connId = ESP_GATT_IF_NONE;
m_pServerCallbacks = nullptr;
} // BLEServer
void BLEServer::createApp(uint16_t appId) {
m_appId = appId;
registerApp(appId);
} // createApp
/**
* @brief Create a %BLE Service.
*
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
* of a new service. Every service must have a unique UUID.
* @param [in] uuid The UUID of the new service.
* @return A reference to the new service object.
*/
BLEService* BLEServer::createService(const char* uuid) {
return createService(BLEUUID(uuid));
}
/**
* @brief Create a %BLE Service.
*
* With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
* of a new service. Every service must have a unique UUID.
* @param [in] uuid The UUID of the new service.
* @param [in] numHandles The maximum number of handles associated with this service.
* @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service.
* @return A reference to the new service object.
*/
BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) {
log_v(">> createService - %s", uuid.toString().c_str());
m_semaphoreCreateEvt.take("createService");
// Check that a service with the supplied UUID does not already exist.
if (m_serviceMap.getByUUID(uuid) != nullptr) {
log_w("<< Attempt to create a new service with uuid %s but a service with that UUID already exists.",
uuid.toString().c_str());
}
BLEService* pService = new BLEService(uuid, numHandles);
pService->m_instId = inst_id;
m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server.
pService->executeCreate(this); // Perform the API calls to actually create the service.
m_semaphoreCreateEvt.wait("createService");
log_v("<< createService");
return pService;
} // createService
/**
* @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service.
* @return A reference to the service object.
*/
BLEService* BLEServer::getServiceByUUID(const char* uuid) {
return m_serviceMap.getByUUID(uuid);
}
/**
* @brief Get a %BLE Service by its UUID
* @param [in] uuid The UUID of the new service.
* @return A reference to the service object.
*/
BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) {
return m_serviceMap.getByUUID(uuid);
}
/**
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
*
* @return An advertising object.
*/
BLEAdvertising* BLEServer::getAdvertising() {
return BLEDevice::getAdvertising();
}
uint16_t BLEServer::getConnId() {
return m_connId;
}
/**
* @brief Return the number of connected clients.
* @return The number of connected clients.
*/
uint32_t BLEServer::getConnectedCount() {
return m_connectedCount;
} // getConnectedCount
uint16_t BLEServer::getGattsIf() {
return m_gatts_if;
}
/**
* @brief Handle a GATT Server Event.
*
* @param [in] event
* @param [in] gatts_if
* @param [in] param
*
*/
void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) {
log_v(">> handleGATTServerEvent: %s",
BLEUtils::gattServerEventTypeToString(event).c_str());
switch(event) {
// ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
// add_char:
// - esp_gatt_status_t status
// - uint16_t attr_handle
// - uint16_t service_handle
// - esp_bt_uuid_t char_uuid
//
case ESP_GATTS_ADD_CHAR_EVT: {
break;
} // ESP_GATTS_ADD_CHAR_EVT
case ESP_GATTS_MTU_EVT:
updatePeerMTU(param->mtu.conn_id, param->mtu.mtu);
break;
// ESP_GATTS_CONNECT_EVT
// connect:
// - uint16_t conn_id
// - esp_bd_addr_t remote_bda
//
case ESP_GATTS_CONNECT_EVT: {
m_connId = param->connect.conn_id;
addPeerDevice((void*)this, false, m_connId);
if (m_pServerCallbacks != nullptr) {
m_pServerCallbacks->onConnect(this);
m_pServerCallbacks->onConnect(this, param);
}
m_connectedCount++; // Increment the number of connected devices count.
break;
} // ESP_GATTS_CONNECT_EVT
// ESP_GATTS_CREATE_EVT
// Called when a new service is registered as having been created.
//
// create:
// * esp_gatt_status_t status
// * uint16_t service_handle
// * esp_gatt_srvc_id_t service_id
//
case ESP_GATTS_CREATE_EVT: {
BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid
m_serviceMap.setByHandle(param->create.service_handle, pService);
m_semaphoreCreateEvt.give();
break;
} // ESP_GATTS_CREATE_EVT
// ESP_GATTS_DISCONNECT_EVT
//
// disconnect
// - uint16_t conn_id
// - esp_bd_addr_t remote_bda
// - esp_gatt_conn_reason_t reason
//
// If we receive a disconnect event then invoke the callback for disconnects (if one is present).
// we also want to start advertising again.
case ESP_GATTS_DISCONNECT_EVT: {
m_connectedCount--; // Decrement the number of connected devices count.
if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now.
m_pServerCallbacks->onDisconnect(this);
}
startAdvertising(); //- do this with some delay from the loop()
removePeerDevice(param->disconnect.conn_id, false);
break;
} // ESP_GATTS_DISCONNECT_EVT
// ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived.
//
// read:
// - uint16_t conn_id
// - uint32_t trans_id
// - esp_bd_addr_t bda
// - uint16_t handle
// - uint16_t offset
// - bool is_long
// - bool need_rsp
//
case ESP_GATTS_READ_EVT: {
break;
} // ESP_GATTS_READ_EVT
// ESP_GATTS_REG_EVT
// reg:
// - esp_gatt_status_t status
// - uint16_t app_id
//
case ESP_GATTS_REG_EVT: {
m_gatts_if = gatts_if;
m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app.
break;
} // ESP_GATTS_REG_EVT
// ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived.
//
// write:
// - uint16_t conn_id
// - uint16_t trans_id
// - esp_bd_addr_t bda
// - uint16_t handle
// - uint16_t offset
// - bool need_rsp
// - bool is_prep
// - uint16_t len
// - uint8_t* value
//
case ESP_GATTS_WRITE_EVT: {
break;
}
case ESP_GATTS_OPEN_EVT:
m_semaphoreOpenEvt.give(param->open.status);
break;
default:
break;
}
// Invoke the handler for every Service we have.
m_serviceMap.handleGATTServerEvent(event, gatts_if, param);
log_v("<< handleGATTServerEvent");
} // handleGATTServerEvent
/**
* @brief Register the app.
*
* @return N/A
*/
void BLEServer::registerApp(uint16_t m_appId) {
log_v(">> registerApp - %d", m_appId);
m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event.
::esp_ble_gatts_app_register(m_appId);
m_semaphoreRegisterAppEvt.wait("registerApp");
log_v("<< registerApp");
} // registerApp
/**
* @brief Set the server callbacks.
*
* As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client
* disconnecting. This function can be called to register a callback handler that will be invoked when these
* events are detected.
*
* @param [in] pCallbacks The callbacks to be invoked.
*/
void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) {
m_pServerCallbacks = pCallbacks;
} // setCallbacks
/*
* Remove service
*/
void BLEServer::removeService(BLEService* service) {
service->stop();
service->executeDelete();
m_serviceMap.removeService(service);
}
/**
* @brief Start advertising.
*
* Start the server advertising its existence. This is a convenience function and is equivalent to
* retrieving the advertising object and invoking start upon it.
*/
void BLEServer::startAdvertising() {
log_v(">> startAdvertising");
BLEDevice::startAdvertising();
log_v("<< startAdvertising");
} // startAdvertising
/**
* Allow to connect GATT server to peer device
* Probably can be used in ANCS for iPhone
*/
bool BLEServer::connect(BLEAddress address) {
esp_bd_addr_t addr;
memcpy(&addr, address.getNative(), 6);
// Perform the open connection request against the target BLE Server.
m_semaphoreOpenEvt.take("connect");
esp_err_t errRc = ::esp_ble_gatts_open(
getGattsIf(),
addr, // address
1 // direct connection
);
if (errRc != ESP_OK) {
log_e("esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
log_v("<< connect(), rc=%d", rc==ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
void BLEServerCallbacks::onConnect(BLEServer* pServer) {
log_d("BLEServerCallbacks", ">> onConnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onConnect()");
} // onConnect
void BLEServerCallbacks::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) {
log_d("BLEServerCallbacks", ">> onConnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onConnect()");
} // onConnect
void BLEServerCallbacks::onDisconnect(BLEServer* pServer) {
log_d("BLEServerCallbacks", ">> onDisconnect(): Default");
log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
log_d("BLEServerCallbacks", "<< onDisconnect()");
} // onDisconnect
/* multi connect support */
/* TODO do some more tweaks */
void BLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) {
// set mtu in conn_status_t
const std::map<uint16_t, conn_status_t>::iterator it = m_connectedServersMap.find(conn_id);
if (it != m_connectedServersMap.end()) {
it->second.mtu = mtu;
std::swap(m_connectedServersMap[conn_id], it->second);
}
}
std::map<uint16_t, conn_status_t> BLEServer::getPeerDevices(bool _client) {
return m_connectedServersMap;
}
uint16_t BLEServer::getPeerMTU(uint16_t conn_id) {
return m_connectedServersMap.find(conn_id)->second.mtu;
}
void BLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) {
conn_status_t status = {
.peer_device = peer,
.connected = true,
.mtu = 23
};
m_connectedServersMap.insert(std::pair<uint16_t, conn_status_t>(conn_id, status));
}
void BLEServer::removePeerDevice(uint16_t conn_id, bool _client) {
m_connectedServersMap.erase(conn_id);
}
/* multi connect support */
/**
* Update connection parameters can be called only after connection has been established
*/
void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) {
esp_ble_conn_update_params_t conn_params;
memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t));
conn_params.latency = latency;
conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms
conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms
conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms
esp_ble_gap_update_conn_params(&conn_params);
}
void BLEServer::disconnect(uint16_t connId) {
esp_ble_gatts_close(m_gatts_if, connId);
}
#endif // CONFIG_BT_ENABLED

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