3.3.7
This commit is contained in:
+503
@@ -0,0 +1,503 @@
|
||||
# Check ESP-IDF version and error out if it is not in the supported range.
|
||||
#
|
||||
# Note for arduino-esp32 developers: to bypass the version check locally,
|
||||
# set ARDUINO_SKIP_IDF_VERSION_CHECK environment variable to 1. For example:
|
||||
# export ARDUINO_SKIP_IDF_VERSION_CHECK=1
|
||||
# idf.py build
|
||||
|
||||
set(min_supported_idf_version "5.3.0")
|
||||
set(max_supported_idf_version "5.5.99")
|
||||
set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}")
|
||||
|
||||
if ("${idf_version}" AND NOT "$ENV{ARDUINO_SKIP_IDF_VERSION_CHECK}")
|
||||
if (idf_version VERSION_LESS min_supported_idf_version)
|
||||
message(FATAL_ERROR "Arduino-esp32 can be used with ESP-IDF versions "
|
||||
"between ${min_supported_idf_version} and ${max_supported_idf_version}, "
|
||||
"but an older version is detected: ${idf_version}.")
|
||||
endif()
|
||||
if (idf_version VERSION_GREATER max_supported_idf_version)
|
||||
message(FATAL_ERROR "Arduino-esp32 can be used with ESP-IDF versions "
|
||||
"between ${min_supported_idf_version} and ${max_supported_idf_version}, "
|
||||
"but a newer version is detected: ${idf_version}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CORE_SRCS
|
||||
cores/esp32/base64.cpp
|
||||
cores/esp32/cbuf.cpp
|
||||
cores/esp32/ColorFormat.c
|
||||
cores/esp32/chip-debug-report.cpp
|
||||
cores/esp32/esp32-hal-adc.c
|
||||
cores/esp32/esp32-hal-bt.c
|
||||
cores/esp32/esp32-hal-cpu.c
|
||||
cores/esp32/esp32-hal-dac.c
|
||||
cores/esp32/esp32-hal-gpio.c
|
||||
cores/esp32/esp32-hal-hosted.c
|
||||
cores/esp32/esp32-hal-i2c.c
|
||||
cores/esp32/esp32-hal-i2c-ng.c
|
||||
cores/esp32/esp32-hal-i2c-slave.c
|
||||
cores/esp32/esp32-hal-ledc.c
|
||||
cores/esp32/esp32-hal-log-wrapper.c
|
||||
cores/esp32/esp32-hal-matrix.c
|
||||
cores/esp32/esp32-hal-misc.c
|
||||
cores/esp32/esp32-hal-periman.c
|
||||
cores/esp32/esp32-hal-psram.c
|
||||
cores/esp32/esp32-hal-rgb-led.c
|
||||
cores/esp32/esp32-hal-sigmadelta.c
|
||||
cores/esp32/esp32-hal-spi.c
|
||||
cores/esp32/esp32-hal-time.c
|
||||
cores/esp32/esp32-hal-timer.c
|
||||
cores/esp32/esp32-hal-tinyusb.c
|
||||
cores/esp32/esp32-hal-touch.c
|
||||
cores/esp32/esp32-hal-touch-ng.c
|
||||
cores/esp32/esp32-hal-uart.c
|
||||
cores/esp32/esp32-hal-rmt.c
|
||||
cores/esp32/Esp.cpp
|
||||
cores/esp32/freertos_stats.cpp
|
||||
cores/esp32/FunctionalInterrupt.cpp
|
||||
cores/esp32/HardwareSerial.cpp
|
||||
cores/esp32/HashBuilder.cpp
|
||||
cores/esp32/HEXBuilder.cpp
|
||||
cores/esp32/idf_openthread_mpool_wrapper.c
|
||||
cores/esp32/IPAddress.cpp
|
||||
cores/esp32/libb64/cdecode.c
|
||||
cores/esp32/libb64/cencode.c
|
||||
cores/esp32/MacAddress.cpp
|
||||
cores/esp32/main.cpp
|
||||
cores/esp32/MD5Builder.cpp
|
||||
cores/esp32/Print.cpp
|
||||
cores/esp32/stdlib_noniso.c
|
||||
cores/esp32/Stream.cpp
|
||||
cores/esp32/StreamString.cpp
|
||||
cores/esp32/Tone.cpp
|
||||
cores/esp32/HWCDC.cpp
|
||||
cores/esp32/USB.cpp
|
||||
cores/esp32/USBCDC.cpp
|
||||
cores/esp32/USBMSC.cpp
|
||||
cores/esp32/FirmwareMSC.cpp
|
||||
cores/esp32/firmware_msc_fat.c
|
||||
cores/esp32/wiring_pulse.c
|
||||
cores/esp32/wiring_shift.c
|
||||
cores/esp32/WMath.cpp
|
||||
cores/esp32/WString.cpp
|
||||
)
|
||||
|
||||
set(ARDUINO_ALL_LIBRARIES
|
||||
ArduinoOTA
|
||||
AsyncUDP
|
||||
BLE
|
||||
BluetoothSerial
|
||||
DNSServer
|
||||
EEPROM
|
||||
ESP_I2S
|
||||
ESP_NOW
|
||||
ESP_SR
|
||||
ESP_HostedOTA
|
||||
ESPmDNS
|
||||
Ethernet
|
||||
FFat
|
||||
FS
|
||||
Hash
|
||||
HTTPClient
|
||||
HTTPUpdate
|
||||
Insights
|
||||
LittleFS
|
||||
Matter
|
||||
NetBIOS
|
||||
Network
|
||||
OpenThread
|
||||
PPP
|
||||
Preferences
|
||||
RainMaker
|
||||
SD_MMC
|
||||
SD
|
||||
SimpleBLE
|
||||
SPIFFS
|
||||
SPI
|
||||
Ticker
|
||||
Update
|
||||
USB
|
||||
WebServer
|
||||
NetworkClientSecure
|
||||
WiFi
|
||||
WiFiProv
|
||||
Wire
|
||||
Zigbee
|
||||
)
|
||||
|
||||
set(ARDUINO_LIBRARY_ArduinoOTA_SRCS libraries/ArduinoOTA/src/ArduinoOTA.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_AsyncUDP_SRCS libraries/AsyncUDP/src/AsyncUDP.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_BluetoothSerial_SRCS
|
||||
libraries/BluetoothSerial/src/BluetoothSerial.cpp
|
||||
libraries/BluetoothSerial/src/BTAddress.cpp
|
||||
libraries/BluetoothSerial/src/BTAdvertisedDeviceSet.cpp
|
||||
libraries/BluetoothSerial/src/BTScanResultsSet.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_DNSServer_SRCS libraries/DNSServer/src/DNSServer.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_EEPROM_SRCS libraries/EEPROM/src/EEPROM.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_ESP_I2S_SRCS libraries/ESP_I2S/src/ESP_I2S.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_ESP_NOW_SRCS
|
||||
libraries/ESP_NOW/src/ESP32_NOW.cpp
|
||||
libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_ESP_SR_SRCS
|
||||
libraries/ESP_SR/src/ESP_SR.cpp
|
||||
libraries/ESP_SR/src/esp32-hal-sr.c)
|
||||
|
||||
set(ARDUINO_LIBRARY_ESP_HostedOTA_SRCS
|
||||
libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_ESPmDNS_SRCS libraries/ESPmDNS/src/ESPmDNS.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Ethernet_SRCS libraries/Ethernet/src/ETH.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_FFat_SRCS libraries/FFat/src/FFat.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_FS_SRCS
|
||||
libraries/FS/src/FS.cpp
|
||||
libraries/FS/src/vfs_api.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Hash_SRCS
|
||||
libraries/Hash/src/SHA1Builder.cpp
|
||||
libraries/Hash/src/SHA2Builder.cpp
|
||||
libraries/Hash/src/SHA3Builder.cpp
|
||||
libraries/Hash/src/PBKDF2_HMACBuilder.cpp
|
||||
)
|
||||
|
||||
set(ARDUINO_LIBRARY_HTTPClient_SRCS libraries/HTTPClient/src/HTTPClient.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_HTTPUpdate_SRCS libraries/HTTPUpdate/src/HTTPUpdate.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Insights_SRCS libraries/Insights/src/Insights.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_LittleFS_SRCS libraries/LittleFS/src/LittleFS.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_NetBIOS_SRCS libraries/NetBIOS/src/NetBIOS.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_OpenThread_SRCS
|
||||
libraries/OpenThread/src/OThread.cpp
|
||||
libraries/OpenThread/src/OThreadCLI.cpp
|
||||
libraries/OpenThread/src/OThreadCLI_Util.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Matter_SRCS
|
||||
libraries/Matter/src/MatterEndpoints/MatterGenericSwitch.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterOnOffLight.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterDimmableLight.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterFan.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterHumiditySensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterContactSensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterRainSensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterPressureSensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp
|
||||
libraries/Matter/src/MatterEndpoints/MatterWindowCovering.cpp
|
||||
libraries/Matter/src/Matter.cpp
|
||||
libraries/Matter/src/MatterEndPoint.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_PPP_SRCS libraries/PPP/src/PPP.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Preferences_SRCS libraries/Preferences/src/Preferences.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_RainMaker_SRCS
|
||||
libraries/RainMaker/src/RMaker.cpp
|
||||
libraries/RainMaker/src/RMakerNode.cpp
|
||||
libraries/RainMaker/src/RMakerParam.cpp
|
||||
libraries/RainMaker/src/RMakerDevice.cpp
|
||||
libraries/RainMaker/src/RMakerType.cpp
|
||||
libraries/RainMaker/src/RMakerQR.cpp
|
||||
libraries/RainMaker/src/RMakerUtils.cpp
|
||||
libraries/RainMaker/src/AppInsights.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_SD_MMC_SRCS libraries/SD_MMC/src/SD_MMC.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_SD_SRCS
|
||||
libraries/SD/src/SD.cpp
|
||||
libraries/SD/src/sd_diskio.cpp
|
||||
libraries/SD/src/sd_diskio_crc.c)
|
||||
|
||||
set(ARDUINO_LIBRARY_SimpleBLE_SRCS libraries/SimpleBLE/src/SimpleBLE.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_SPIFFS_SRCS libraries/SPIFFS/src/SPIFFS.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_SPI_SRCS libraries/SPI/src/SPI.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Ticker_SRCS libraries/Ticker/src/Ticker.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Update_SRCS
|
||||
libraries/Update/src/Updater.cpp
|
||||
libraries/Update/src/HttpsOTAUpdate.cpp
|
||||
libraries/Update/src/Updater_Signing.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_USB_SRCS
|
||||
libraries/USB/src/USBHID.cpp
|
||||
libraries/USB/src/USBMIDI.cpp
|
||||
libraries/USB/src/USBHIDMouse.cpp
|
||||
libraries/USB/src/USBHIDKeyboard.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_da_DK.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_de_DE.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_en_US.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_es_ES.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_fr_FR.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_hu_HU.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_it_IT.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_pt_BR.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_pt_PT.cpp
|
||||
libraries/USB/src/keyboardLayout/KeyboardLayout_sv_SE.cpp
|
||||
libraries/USB/src/USBHIDGamepad.cpp
|
||||
libraries/USB/src/USBHIDConsumerControl.cpp
|
||||
libraries/USB/src/USBHIDSystemControl.cpp
|
||||
libraries/USB/src/USBHIDVendor.cpp
|
||||
libraries/USB/src/USBVendor.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_WebServer_SRCS
|
||||
libraries/WebServer/src/WebServer.cpp
|
||||
libraries/WebServer/src/Parsing.cpp
|
||||
libraries/WebServer/src/detail/mimetable.cpp
|
||||
libraries/WebServer/src/middleware/MiddlewareChain.cpp
|
||||
libraries/WebServer/src/middleware/AuthenticationMiddleware.cpp
|
||||
libraries/WebServer/src/middleware/CorsMiddleware.cpp
|
||||
libraries/WebServer/src/middleware/LoggingMiddleware.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_NetworkClientSecure_SRCS
|
||||
libraries/NetworkClientSecure/src/ssl_client.cpp
|
||||
libraries/NetworkClientSecure/src/NetworkClientSecure.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Network_SRCS
|
||||
libraries/Network/src/NetworkInterface.cpp
|
||||
libraries/Network/src/NetworkEvents.cpp
|
||||
libraries/Network/src/NetworkManager.cpp
|
||||
libraries/Network/src/NetworkClient.cpp
|
||||
libraries/Network/src/NetworkServer.cpp
|
||||
libraries/Network/src/NetworkUdp.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_WiFi_SRCS
|
||||
libraries/WiFi/src/WiFiAP.cpp
|
||||
libraries/WiFi/src/WiFi.cpp
|
||||
libraries/WiFi/src/WiFiGeneric.cpp
|
||||
libraries/WiFi/src/WiFiMulti.cpp
|
||||
libraries/WiFi/src/WiFiScan.cpp
|
||||
libraries/WiFi/src/WiFiSTA.cpp
|
||||
libraries/WiFi/src/STA.cpp
|
||||
libraries/WiFi/src/AP.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_WiFiProv_SRCS libraries/WiFiProv/src/WiFiProv.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Wire_SRCS libraries/Wire/src/Wire.cpp)
|
||||
|
||||
set(ARDUINO_LIBRARY_Zigbee_SRCS
|
||||
libraries/Zigbee/src/ZigbeeCore.cpp
|
||||
libraries/Zigbee/src/ZigbeeEP.cpp
|
||||
libraries/Zigbee/src/ZigbeeHandlers.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeLight.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeSwitch.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeThermostat.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeAnalog.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeGateway.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeBinary.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeFanControl.cpp
|
||||
libraries/Zigbee/src/ep/ZigbeeMultistate.cpp
|
||||
)
|
||||
|
||||
set(ARDUINO_LIBRARY_BLE_SRCS
|
||||
libraries/BLE/src/BLE2901.cpp
|
||||
libraries/BLE/src/BLE2902.cpp
|
||||
libraries/BLE/src/BLE2904.cpp
|
||||
libraries/BLE/src/BLEAddress.cpp
|
||||
libraries/BLE/src/BLEAdvertisedDevice.cpp
|
||||
libraries/BLE/src/BLEAdvertising.cpp
|
||||
libraries/BLE/src/BLEBeacon.cpp
|
||||
libraries/BLE/src/BLECharacteristic.cpp
|
||||
libraries/BLE/src/BLECharacteristicMap.cpp
|
||||
libraries/BLE/src/BLEClient.cpp
|
||||
libraries/BLE/src/BLEDescriptor.cpp
|
||||
libraries/BLE/src/BLEDescriptorMap.cpp
|
||||
libraries/BLE/src/BLEDevice.cpp
|
||||
libraries/BLE/src/BLEEddystoneTLM.cpp
|
||||
libraries/BLE/src/BLEEddystoneURL.cpp
|
||||
libraries/BLE/src/BLEExceptions.cpp
|
||||
libraries/BLE/src/BLEHIDDevice.cpp
|
||||
libraries/BLE/src/BLERemoteCharacteristic.cpp
|
||||
libraries/BLE/src/BLERemoteDescriptor.cpp
|
||||
libraries/BLE/src/BLERemoteService.cpp
|
||||
libraries/BLE/src/BLEScan.cpp
|
||||
libraries/BLE/src/BLESecurity.cpp
|
||||
libraries/BLE/src/BLEServer.cpp
|
||||
libraries/BLE/src/BLEService.cpp
|
||||
libraries/BLE/src/BLEServiceMap.cpp
|
||||
libraries/BLE/src/BLEUtils.cpp
|
||||
libraries/BLE/src/BLEUUID.cpp
|
||||
libraries/BLE/src/BLEValue.cpp
|
||||
libraries/BLE/src/FreeRTOS.cpp
|
||||
libraries/BLE/src/GeneralUtils.cpp
|
||||
)
|
||||
|
||||
set(ARDUINO_LIBRARIES_SRCS)
|
||||
set(ARDUINO_LIBRARIES_REQUIRES)
|
||||
set(ARDUINO_LIBRARIES_INCLUDEDIRS)
|
||||
foreach(libname IN LISTS ARDUINO_ALL_LIBRARIES)
|
||||
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_${libname})
|
||||
if(ARDUINO_LIBRARY_${libname}_SRCS)
|
||||
list(APPEND ARDUINO_LIBRARIES_SRCS ${ARDUINO_LIBRARY_${libname}_SRCS})
|
||||
endif()
|
||||
if(ARDUINO_LIBRARY_${libname}_REQUIRES)
|
||||
list(APPEND ARDUINO_LIBRARIES_REQUIRES ${ARDUINO_LIBRARY_${libname}_REQUIRES})
|
||||
endif()
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libraries/${libname}/src)
|
||||
list(APPEND ARDUINO_LIBRARIES_INCLUDEDIRS libraries/${libname}/src)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(includedirs variants/${CONFIG_ARDUINO_VARIANT}/ cores/esp32/ ${ARDUINO_LIBRARIES_INCLUDEDIRS})
|
||||
set(srcs ${CORE_SRCS} ${ARDUINO_LIBRARIES_SRCS})
|
||||
set(priv_includes cores/esp32/libb64)
|
||||
set(requires spi_flash esp_partition mbedtls wpa_supplicant esp_adc esp_eth http_parser esp_ringbuf esp_driver_gptimer esp_driver_usb_serial_jtag driver esp_http_client esp_https_ota)
|
||||
set(priv_requires fatfs nvs_flash app_update spiffs bootloader_support bt esp_hid usb esp_psram ${ARDUINO_LIBRARIES_REQUIRES})
|
||||
|
||||
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_OpenThread)
|
||||
#if(CONFIG_SOC_IEEE802154_SUPPORTED) # Does not work!
|
||||
#if(CONFIG_OPENTHREAD_ENABLED) # Does not work!
|
||||
if(IDF_TARGET STREQUAL "esp32c6" OR IDF_TARGET STREQUAL "esp32h2" OR IDF_TARGET STREQUAL "esp32c5") # Sadly only this works
|
||||
list(APPEND requires openthread)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3" OR IDF_TARGET STREQUAL "esp32p4")
|
||||
list(APPEND requires esp_driver_touch_sens)
|
||||
endif()
|
||||
|
||||
idf_component_register(INCLUDE_DIRS ${includedirs} PRIV_INCLUDE_DIRS ${priv_includes} SRCS ${srcs} REQUIRES ${requires} PRIV_REQUIRES ${priv_requires})
|
||||
|
||||
if(NOT CONFIG_FREERTOS_HZ EQUAL 1000 AND NOT "$ENV{ARDUINO_SKIP_TICK_CHECK}")
|
||||
# See delay() in cores/esp32/esp32-hal-misc.c.
|
||||
message(FATAL_ERROR "esp32-arduino requires CONFIG_FREERTOS_HZ=1000 "
|
||||
"(currently ${CONFIG_FREERTOS_HZ})")
|
||||
endif()
|
||||
|
||||
string(TOUPPER ${CONFIG_ARDUINO_VARIANT} idf_target_caps)
|
||||
string(REPLACE "-" "_" idf_target_for_macro "${idf_target_caps}")
|
||||
target_compile_options(${COMPONENT_TARGET} PUBLIC
|
||||
-DARDUINO=10812
|
||||
-DARDUINO_${idf_target_for_macro}_DEV
|
||||
-DARDUINO_ARCH_ESP32
|
||||
-DARDUINO_BOARD="${idf_target_caps}_DEV"
|
||||
-DARDUINO_VARIANT="${CONFIG_ARDUINO_VARIANT}"
|
||||
-DESP32=ESP32)
|
||||
|
||||
if(CONFIG_AUTOSTART_ARDUINO)
|
||||
# in autostart mode, arduino-esp32 contains app_main() function and needs to
|
||||
# reference setup() and loop() in the main component. If we add main
|
||||
# component to priv_requires then we create a large circular dependency
|
||||
# (arduino-esp32 -> main -> arduino-esp32) and can get linker errors, so
|
||||
# instead we add setup() and loop() to the undefined symbols list so the
|
||||
# linker will always include them.
|
||||
#
|
||||
# (As they are C++ symbol, we need to add the C++ mangled names.)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u _Z5setupv -u _Z4loopv")
|
||||
endif()
|
||||
|
||||
# Fix for WiFi/Bluetooth linker errors when CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP is not enabled
|
||||
#
|
||||
# Problem:
|
||||
# WiFi and Bluetooth binary libraries reference __wrap_esp_log_write functions.
|
||||
# When CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y, esp_diagnostics provides these wrappers.
|
||||
# When not set, no component provides them, causing "undefined reference" linker errors.
|
||||
#
|
||||
# Solution:
|
||||
# cores/esp32/esp32-hal-log-wrapper.c provides simple pass-through wrapper implementations
|
||||
# that forward calls to the real ESP-IDF logging functions without additional processing.
|
||||
#
|
||||
# Two linker flags are required:
|
||||
#
|
||||
# 1. --wrap=symbol: Sets up the wrapping/redirection mechanism
|
||||
# Effect: esp_log_write() calls → redirected to → __wrap_esp_log_write()
|
||||
# __real_esp_log_write() → created as alias → original esp_log_write()
|
||||
# Note: Applied at build level (idf_build_set_property) so it works when Arduino is a component
|
||||
#
|
||||
# 2. -u symbol: Forces inclusion of wrapper symbols from static library
|
||||
# Why needed: Static library linking is "lazy" - only pulls objects with referenced symbols.
|
||||
# Without -u, linker sees wrapper as unreferenced and drops it from the library.
|
||||
# The -u flag marks symbols as "undefined", forcing linker to search and include them.
|
||||
# Analogy: Same technique used for setup()/loop() symbols (see above)
|
||||
if(NOT CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_writev" APPEND)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __wrap_esp_log_write" "-u __wrap_esp_log_writev")
|
||||
endif()
|
||||
|
||||
# OpenThread message pool: redirect to Arduino wrapper (BOARD_HAS_PSRAM / PSRAM-aware allocation).
|
||||
# Same pattern as esp_log wrap: --wrap so calls use __wrap_* ; -u so linker keeps our wrapper .o.
|
||||
# Applied on OpenThread-capable targets (same targets that may add openthread to requires).
|
||||
if(CONFIG_OPENTHREAD_ENABLED AND CONFIG_SOC_IEEE802154_SUPPORTED)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolInit" APPEND)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolNew" APPEND)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolFree" APPEND)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolNumFreeBuffers" APPEND)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=otPlatMessagePoolDeinit" APPEND)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE
|
||||
"-u __wrap_otPlatMessagePoolInit"
|
||||
"-u __wrap_otPlatMessagePoolNew"
|
||||
"-u __wrap_otPlatMessagePoolFree"
|
||||
"-u __wrap_otPlatMessagePoolNumFreeBuffers"
|
||||
"-u __wrap_otPlatMessagePoolDeinit")
|
||||
endif()
|
||||
|
||||
# This function adds a dependency on the given component if the component is included into the build.
|
||||
function(maybe_add_component component_name)
|
||||
idf_build_get_property(components BUILD_COMPONENTS)
|
||||
if (${component_name} IN_LIST components)
|
||||
idf_component_get_property(lib_name ${component_name} COMPONENT_LIB)
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC ${lib_name})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(IDF_TARGET MATCHES "esp32s2|esp32s3|esp32p4" AND CONFIG_TINYUSB_ENABLED)
|
||||
maybe_add_component(arduino_tinyusb)
|
||||
endif()
|
||||
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_ArduinoOTA)
|
||||
maybe_add_component(esp_https_ota)
|
||||
endif()
|
||||
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_ESP_SR)
|
||||
maybe_add_component(espressif__esp_sr)
|
||||
endif()
|
||||
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_Matter)
|
||||
maybe_add_component(espressif__esp_matter)
|
||||
endif()
|
||||
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_LittleFS)
|
||||
maybe_add_component(joltwallet__littlefs)
|
||||
endif()
|
||||
if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_WiFiProv)
|
||||
maybe_add_component(espressif__network_provisioning)
|
||||
endif()
|
||||
@@ -0,0 +1,445 @@
|
||||
menu "Arduino Configuration"
|
||||
|
||||
config ARDUINO_VARIANT
|
||||
string "Arduino target variant (board)"
|
||||
default IDF_TARGET
|
||||
help
|
||||
The name of a target variant (e.g., a specific board) in the variants/
|
||||
folder, e.g. "heltec_wifi_lora_32_V2". The name is case sensitive.
|
||||
Specifying a variant name different from the target enables additional
|
||||
customization, for example the definition of GPIO pins.
|
||||
|
||||
config ENABLE_ARDUINO_DEPENDS
|
||||
bool
|
||||
select LWIP_SO_RCVBUF
|
||||
select ETHERNET
|
||||
select WIFI_ENABLED
|
||||
select ESP32_PHY_CALIBRATION_AND_DATA_STORAGE if IDF_TARGET_ESP32
|
||||
select MEMMAP_SMP
|
||||
default "y"
|
||||
|
||||
config AUTOSTART_ARDUINO
|
||||
bool "Autostart Arduino setup and loop on boot"
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will implement app_main and start Arduino.
|
||||
All you need to implement in your main.cpp is setup() and loop()
|
||||
and include Arduino.h
|
||||
If disabled, you can call initArduino() to run any preparations
|
||||
required by the framework
|
||||
|
||||
choice ARDUINO_RUNNING_CORE
|
||||
bool "Core on which Arduino's setup() and loop() are running"
|
||||
default ARDUINO_RUN_CORE0 if FREERTOS_UNICORE
|
||||
default ARDUINO_RUN_CORE1 if !FREERTOS_UNICORE
|
||||
help
|
||||
Select on which core Arduino's setup() and loop() functions run
|
||||
|
||||
config ARDUINO_RUN_CORE0
|
||||
bool "CORE 0"
|
||||
config ARDUINO_RUN_CORE1
|
||||
bool "CORE 1"
|
||||
depends on !FREERTOS_UNICORE
|
||||
config ARDUINO_RUN_NO_AFFINITY
|
||||
bool "BOTH"
|
||||
depends on !FREERTOS_UNICORE
|
||||
|
||||
endchoice
|
||||
|
||||
config ARDUINO_RUNNING_CORE
|
||||
int
|
||||
default 0 if ARDUINO_RUN_CORE0
|
||||
default 1 if ARDUINO_RUN_CORE1
|
||||
default -1 if ARDUINO_RUN_NO_AFFINITY
|
||||
|
||||
config ARDUINO_LOOP_STACK_SIZE
|
||||
int "Loop thread stack size"
|
||||
default 8192
|
||||
help
|
||||
Amount of stack available for the Arduino task.
|
||||
|
||||
choice ARDUINO_EVENT_RUNNING_CORE
|
||||
bool "Core on which Arduino's event handler is running"
|
||||
default ARDUINO_EVENT_RUN_CORE0 if FREERTOS_UNICORE
|
||||
default ARDUINO_EVENT_RUN_CORE1 if !FREERTOS_UNICORE
|
||||
help
|
||||
Select on which core Arduino's WiFi.onEvent() run
|
||||
|
||||
config ARDUINO_EVENT_RUN_CORE0
|
||||
bool "CORE 0"
|
||||
config ARDUINO_EVENT_RUN_CORE1
|
||||
bool "CORE 1"
|
||||
depends on !FREERTOS_UNICORE
|
||||
config ARDUINO_EVENT_RUN_NO_AFFINITY
|
||||
bool "BOTH"
|
||||
depends on !FREERTOS_UNICORE
|
||||
|
||||
endchoice
|
||||
|
||||
config ARDUINO_EVENT_RUNNING_CORE
|
||||
int
|
||||
default 0 if ARDUINO_EVENT_RUN_CORE0
|
||||
default 1 if ARDUINO_EVENT_RUN_CORE1
|
||||
default -1 if ARDUINO_EVENT_RUN_NO_AFFINITY
|
||||
|
||||
choice ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
|
||||
bool "Core on which Arduino's Serial Event task is running"
|
||||
default ARDUINO_SERIAL_EVENT_RUN_CORE0 if FREERTOS_UNICORE
|
||||
default ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY if !FREERTOS_UNICORE
|
||||
help
|
||||
Select on which core Arduino's Serial Event task run
|
||||
|
||||
config ARDUINO_SERIAL_EVENT_RUN_CORE0
|
||||
bool "CORE 0"
|
||||
config ARDUINO_SERIAL_EVENT_RUN_CORE1
|
||||
bool "CORE 1"
|
||||
depends on !FREERTOS_UNICORE
|
||||
config ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY
|
||||
bool "BOTH"
|
||||
depends on !FREERTOS_UNICORE
|
||||
|
||||
endchoice
|
||||
|
||||
config ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
|
||||
int
|
||||
default 0 if ARDUINO_SERIAL_EVENT_RUN_CORE0
|
||||
default 1 if ARDUINO_SERIAL_EVENT_RUN_CORE1
|
||||
default -1 if ARDUINO_SERIAL_EVENT_RUN_NO_AFFINITY
|
||||
|
||||
config ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
|
||||
int "Serial Event task stack size"
|
||||
default 2048
|
||||
help
|
||||
Amount of stack available for the Serial Event task.
|
||||
|
||||
config ARDUINO_SERIAL_EVENT_TASK_PRIORITY
|
||||
int "Priority of the Serial Event task"
|
||||
default 24
|
||||
help
|
||||
Select at what priority you want the Serial Event task to run.
|
||||
|
||||
choice ARDUINO_UDP_RUNNING_CORE
|
||||
bool "Core on which Arduino's UDP is running"
|
||||
default ARDUINO_UDP_RUN_CORE0
|
||||
help
|
||||
Select on which core Arduino's UDP run
|
||||
|
||||
config ARDUINO_UDP_RUN_CORE0
|
||||
bool "CORE 0"
|
||||
config ARDUINO_UDP_RUN_CORE1
|
||||
bool "CORE 1"
|
||||
depends on !FREERTOS_UNICORE
|
||||
config ARDUINO_UDP_RUN_NO_AFFINITY
|
||||
bool "BOTH"
|
||||
depends on !FREERTOS_UNICORE
|
||||
|
||||
endchoice
|
||||
|
||||
config ARDUINO_UDP_RUNNING_CORE
|
||||
int
|
||||
default 0 if ARDUINO_UDP_RUN_CORE0
|
||||
default 1 if ARDUINO_UDP_RUN_CORE1
|
||||
default -1 if ARDUINO_UDP_RUN_NO_AFFINITY
|
||||
|
||||
config ARDUINO_UDP_TASK_PRIORITY
|
||||
int "Priority of the UDP task"
|
||||
default 3
|
||||
help
|
||||
Select at what priority you want the UDP task to run.
|
||||
|
||||
config ARDUINO_UDP_TASK_STACK_SIZE
|
||||
int "UDP task stack size"
|
||||
default 4096
|
||||
help
|
||||
Amount of stack available for the UDP task.
|
||||
|
||||
config ARDUINO_ISR_IRAM
|
||||
bool "Run interrupts in IRAM"
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will Attach all interrupts with the IRAm flag.
|
||||
It will also make some HAL function, like, digitalRead/Write and more
|
||||
be loaded into IRAM for access inside ISRs.
|
||||
Beware that this is a very dangerous setting. Enable it only if you
|
||||
are fully aware of the consequences.
|
||||
|
||||
config DISABLE_HAL_LOCKS
|
||||
bool "Disable mutex locks for HAL"
|
||||
default "n"
|
||||
help
|
||||
Enabling this option will run all hardware abstraction without locks.
|
||||
While communication with external hardware will be faster, you need to
|
||||
make sure that there is no option to use the same bus from another thread
|
||||
or interrupt at the same time. Option is best used with Arduino enabled
|
||||
and code implemented only in setup/loop and Arduino callbacks
|
||||
|
||||
menu "Debug Log Configuration"
|
||||
choice ARDUHAL_LOG_DEFAULT_LEVEL
|
||||
bool "Default log level"
|
||||
default ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
|
||||
help
|
||||
Specify how much output to see in logs by default.
|
||||
|
||||
config ARDUHAL_LOG_DEFAULT_LEVEL_NONE
|
||||
bool "No output"
|
||||
config ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
|
||||
bool "Error"
|
||||
config ARDUHAL_LOG_DEFAULT_LEVEL_WARN
|
||||
bool "Warning"
|
||||
config ARDUHAL_LOG_DEFAULT_LEVEL_INFO
|
||||
bool "Info"
|
||||
config ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG
|
||||
bool "Debug"
|
||||
config ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE
|
||||
bool "Verbose"
|
||||
endchoice
|
||||
|
||||
config ARDUHAL_LOG_DEFAULT_LEVEL
|
||||
int
|
||||
default 0 if ARDUHAL_LOG_DEFAULT_LEVEL_NONE
|
||||
default 1 if ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
|
||||
default 2 if ARDUHAL_LOG_DEFAULT_LEVEL_WARN
|
||||
default 3 if ARDUHAL_LOG_DEFAULT_LEVEL_INFO
|
||||
default 4 if ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG
|
||||
default 5 if ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE
|
||||
|
||||
config ARDUHAL_LOG_COLORS
|
||||
bool "Use ANSI terminal colors in log output"
|
||||
default "n"
|
||||
help
|
||||
Enable ANSI terminal color codes in bootloader output.
|
||||
In order to view these, your terminal program must support ANSI color codes.
|
||||
|
||||
config ARDUHAL_ESP_LOG
|
||||
bool "Forward ESP_LOGx to Arduino log output"
|
||||
default "n"
|
||||
help
|
||||
This option will redefine the ESP_LOGx macros to Arduino's log_x macros.
|
||||
To enable for your application, add the following after your includes:
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "esp32-hal-log.h"
|
||||
#endif
|
||||
|
||||
endmenu
|
||||
|
||||
choice ARDUHAL_PARTITION_SCHEME
|
||||
bool "Used partition scheme"
|
||||
default ARDUHAL_PARTITION_SCHEME_DEFAULT
|
||||
help
|
||||
Specify which partition scheme to be used.
|
||||
|
||||
config ARDUHAL_PARTITION_SCHEME_DEFAULT
|
||||
bool "Default"
|
||||
config ARDUHAL_PARTITION_SCHEME_MINIMAL
|
||||
bool "Minimal (for 2MB FLASH)"
|
||||
config ARDUHAL_PARTITION_SCHEME_NO_OTA
|
||||
bool "No OTA (for large apps)"
|
||||
config ARDUHAL_PARTITION_SCHEME_HUGE_APP
|
||||
bool "Huge App (for very large apps)"
|
||||
config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
|
||||
bool "Minimal SPIFFS (for large apps with OTA)"
|
||||
endchoice
|
||||
|
||||
config ARDUHAL_PARTITION_SCHEME
|
||||
string
|
||||
default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT
|
||||
default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL
|
||||
default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA
|
||||
default "huge_app" if ARDUHAL_PARTITION_SCHEME_HUGE_APP
|
||||
default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
|
||||
|
||||
|
||||
config AUTOCONNECT_WIFI
|
||||
bool "Autoconnect WiFi on boot"
|
||||
default "n"
|
||||
depends on AUTOSTART_ARDUINO
|
||||
select ARDUINO_SELECTIVE_WiFi
|
||||
help
|
||||
If enabled, WiFi will connect to the last used SSID (if station was enabled),
|
||||
else connection will be started only after calling WiFi.begin(ssid, password)
|
||||
|
||||
config ARDUINO_SELECTIVE_COMPILATION
|
||||
bool "Include only specific Arduino libraries"
|
||||
default n
|
||||
|
||||
config ARDUINO_SELECTIVE_SPI
|
||||
bool "Enable SPI"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Wire
|
||||
bool "Enable Wire"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_ESP_SR
|
||||
bool "Enable ESP-SR"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_EEPROM
|
||||
bool "Enable EEPROM"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Preferences
|
||||
bool "Enable Preferences"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Ticker
|
||||
bool "Enable Ticker"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Update
|
||||
bool "Enable Update"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Zigbee
|
||||
bool "Enable Zigbee"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_FS
|
||||
bool "Enable FS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SD
|
||||
bool "Enable SD"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SD_MMC
|
||||
bool "Enable SD_MMC"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SPIFFS
|
||||
bool "Enable SPIFFS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_FFat
|
||||
bool "Enable FFat"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_LittleFS
|
||||
bool "Enable LittleFS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_FS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Network
|
||||
bool "Enable Networking"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Ethernet
|
||||
bool "Enable Ethernet"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_PPP
|
||||
bool "Enable PPP"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Hash
|
||||
bool "Enable Hash"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_ArduinoOTA
|
||||
bool "Enable ArduinoOTA"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
select ARDUINO_SELECTIVE_Hash
|
||||
select ARDUINO_SELECTIVE_ESPmDNS
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_AsyncUDP
|
||||
bool "Enable AsyncUDP"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_DNSServer
|
||||
bool "Enable DNSServer"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_ESPmDNS
|
||||
bool "Enable ESPmDNS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_HTTPClient
|
||||
bool "Enable HTTPClient"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
select ARDUINO_SELECTIVE_NetworkClientSecure
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Matter
|
||||
bool "Enable Matter"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_NetBIOS
|
||||
bool "Enable NetBIOS"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_WebServer
|
||||
bool "Enable WebServer"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
select ARDUINO_SELECTIVE_FS
|
||||
select ARDUINO_SELECTIVE_Hash
|
||||
|
||||
config ARDUINO_SELECTIVE_WiFi
|
||||
bool "Enable WiFi"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_NetworkClientSecure
|
||||
bool "Enable NetworkClientSecure"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_WiFiProv
|
||||
bool "Enable WiFiProv"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network && ARDUINO_SELECTIVE_WiFi
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_BLE
|
||||
bool "Enable BLE"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_BluetoothSerial
|
||||
bool "Enable BluetoothSerial"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_SimpleBLE
|
||||
bool "Enable SimpleBLE"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_RainMaker
|
||||
bool "Enable RainMaker"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_OpenThread
|
||||
bool "Enable OpenThread"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
config ARDUINO_SELECTIVE_Insights
|
||||
bool "Enable Insights"
|
||||
depends on ARDUINO_SELECTIVE_COMPILATION
|
||||
default y
|
||||
|
||||
endmenu
|
||||
+56399
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
Arduino.h - Main include file for the Arduino SDK
|
||||
Copyright (c) 2005-2013 Arduino Team. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef Arduino_h
|
||||
#define Arduino_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "esp_arduino_version.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp8266-compat.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
|
||||
#include "stdlib_noniso.h"
|
||||
#include "binary.h"
|
||||
#include "extra_attr.h"
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define HALF_PI 1.5707963267948966192313216916398
|
||||
#define TWO_PI 6.283185307179586476925286766559
|
||||
#define DEG_TO_RAD 0.017453292519943295769236907684886
|
||||
#define RAD_TO_DEG 57.295779513082320876798154814105
|
||||
#define EULER 2.718281828459045235360287471352
|
||||
|
||||
#define SERIAL 0x0
|
||||
#define DISPLAY 0x1
|
||||
|
||||
#define LSBFIRST 0
|
||||
#define MSBFIRST 1
|
||||
|
||||
//Interrupt Modes
|
||||
#define RISING 0x01
|
||||
#define FALLING 0x02
|
||||
#define CHANGE 0x03
|
||||
#define ONLOW 0x04
|
||||
#define ONHIGH 0x05
|
||||
#define ONLOW_WE 0x0C
|
||||
#define ONHIGH_WE 0x0D
|
||||
|
||||
#define DEFAULT 1
|
||||
#define EXTERNAL 0
|
||||
|
||||
#ifndef __STRINGIFY
|
||||
#define __STRINGIFY(a) #a
|
||||
#endif
|
||||
|
||||
// can't define max() / min() because of conflicts with C++
|
||||
#define _min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define _max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define _abs(x) ((x) > 0 ? (x) : -(x)) // abs() comes from STL
|
||||
#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
|
||||
#define _round(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x) - 0.5)) // round() comes from STL
|
||||
#define radians(deg) ((deg) * DEG_TO_RAD)
|
||||
#define degrees(rad) ((rad) * RAD_TO_DEG)
|
||||
#define sq(x) ((x) * (x))
|
||||
|
||||
// ESP32xx runs FreeRTOS... disabling interrupts can lead to issues, such as Watchdog Timeout
|
||||
#define sei() portENABLE_INTERRUPTS()
|
||||
#define cli() portDISABLE_INTERRUPTS()
|
||||
#define interrupts() sei()
|
||||
#define noInterrupts() cli()
|
||||
|
||||
#define clockCyclesPerMicrosecond() ((long int)getCpuFrequencyMhz())
|
||||
#define clockCyclesToMicroseconds(a) ((a) / clockCyclesPerMicrosecond())
|
||||
#define microsecondsToClockCycles(a) ((a) * clockCyclesPerMicrosecond())
|
||||
|
||||
#define lowByte(w) ((uint8_t)((w) & 0xff))
|
||||
#define highByte(w) ((uint8_t)((w) >> 8))
|
||||
|
||||
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
|
||||
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
|
||||
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
|
||||
#define bitToggle(value, bit) ((value) ^= (1UL << (bit)))
|
||||
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit))
|
||||
|
||||
// avr-libc defines _NOP() since 1.6.2
|
||||
#ifndef _NOP
|
||||
#define _NOP() \
|
||||
do { \
|
||||
__asm__ volatile("nop"); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define bit(b) (1UL << (b))
|
||||
#define _BV(b) (1UL << (b))
|
||||
|
||||
#define digitalPinToTimer(pin) (0)
|
||||
#define analogInPinToBit(P) (P)
|
||||
#if SOC_GPIO_PIN_COUNT <= 32
|
||||
#define digitalPinToPort(pin) (0)
|
||||
#define digitalPinToBitMask(pin) (1UL << digitalPinToGPIONumber(pin))
|
||||
#define portOutputRegister(port) ((volatile uint32_t *)GPIO_OUT_REG)
|
||||
#define portInputRegister(port) ((volatile uint32_t *)GPIO_IN_REG)
|
||||
#define portModeRegister(port) ((volatile uint32_t *)GPIO_ENABLE_REG)
|
||||
#elif SOC_GPIO_PIN_COUNT <= 64
|
||||
#define digitalPinToPort(pin) ((digitalPinToGPIONumber(pin) > 31) ? 1 : 0)
|
||||
#define digitalPinToBitMask(pin) (1UL << (digitalPinToGPIONumber(pin) & 31))
|
||||
#define portOutputRegister(port) ((volatile uint32_t *)((port) ? GPIO_OUT1_REG : GPIO_OUT_REG))
|
||||
#define portInputRegister(port) ((volatile uint32_t *)((port) ? GPIO_IN1_REG : GPIO_IN_REG))
|
||||
#define portModeRegister(port) ((volatile uint32_t *)((port) ? GPIO_ENABLE1_REG : GPIO_ENABLE_REG))
|
||||
#else
|
||||
#error SOC_GPIO_PIN_COUNT > 64 not implemented
|
||||
#endif
|
||||
|
||||
#define NOT_A_PIN -1
|
||||
#define NOT_A_PORT -1
|
||||
#define NOT_AN_INTERRUPT -1
|
||||
#define NOT_ON_TIMER 0
|
||||
|
||||
// some defines generic for all SoC moved from variants/board_name/pins_arduino.h
|
||||
#define NUM_DIGITAL_PINS SOC_GPIO_PIN_COUNT // All GPIOs
|
||||
#if SOC_ADC_PERIPH_NUM == 1
|
||||
#define NUM_ANALOG_INPUTS (SOC_ADC_CHANNEL_NUM(0)) // Depends on the SoC (ESP32C6, ESP32H2, ESP32C2, ESP32P4)
|
||||
#elif SOC_ADC_PERIPH_NUM == 2
|
||||
#define NUM_ANALOG_INPUTS (SOC_ADC_CHANNEL_NUM(0) + SOC_ADC_CHANNEL_NUM(1)) // Depends on the SoC (ESP32, ESP32S2, ESP32S3, ESP32C3)
|
||||
#endif
|
||||
#define EXTERNAL_NUM_INTERRUPTS NUM_DIGITAL_PINS // All GPIOs
|
||||
#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (analogChannelToDigitalPin(p)) : -1)
|
||||
#define digitalPinToInterrupt(p) ((((uint8_t)digitalPinToGPIONumber(p)) < NUM_DIGITAL_PINS) ? (p) : NOT_AN_INTERRUPT)
|
||||
#define digitalPinHasPWM(p) (((uint8_t)digitalPinToGPIONumber(p)) < NUM_DIGITAL_PINS)
|
||||
|
||||
typedef bool boolean;
|
||||
typedef uint8_t byte;
|
||||
typedef unsigned int word;
|
||||
|
||||
#ifdef __cplusplus
|
||||
void setup(void);
|
||||
void loop(void);
|
||||
|
||||
// The default is using Real Hardware random number generator
|
||||
// But when randomSeed() is called, it turns to Psedo random
|
||||
// generator, exactly as done in Arduino mainstream
|
||||
long random(long);
|
||||
long random(long, long);
|
||||
// Calling randomSeed() will make random()
|
||||
// using pseudo random like in Arduino
|
||||
void randomSeed(unsigned long);
|
||||
// Allow the Application to decide if the random generator
|
||||
// will use Real Hardware random generation (true - default)
|
||||
// or Pseudo random generation (false) as in Arduino MainStream
|
||||
void useRealRandomGenerator(bool useRandomHW);
|
||||
#endif
|
||||
long map(long, long, long, long, long);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void init(void);
|
||||
void initVariant(void);
|
||||
void initArduino(void);
|
||||
|
||||
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);
|
||||
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout);
|
||||
|
||||
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); // codespell:ignore shiftin
|
||||
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "WCharacter.h"
|
||||
#include "WString.h"
|
||||
#include "Stream.h"
|
||||
#include "Printable.h"
|
||||
#include "Print.h"
|
||||
#include "IPAddress.h"
|
||||
#include "Client.h"
|
||||
#include "Server.h"
|
||||
#include "Udp.h"
|
||||
#include "HardwareSerial.h"
|
||||
#include "Esp.h"
|
||||
#include "freertos_stats.h"
|
||||
|
||||
// Use float-compatible stl abs() and round(), we don't use Arduino macros to avoid issues with the C++ libraries
|
||||
using std::abs;
|
||||
using std::isinf;
|
||||
using std::isnan;
|
||||
using std::max;
|
||||
using std::min;
|
||||
using std::round;
|
||||
|
||||
uint16_t makeWord(uint16_t w);
|
||||
uint16_t makeWord(uint8_t h, uint8_t l);
|
||||
|
||||
#define word(...) makeWord(__VA_ARGS__)
|
||||
|
||||
size_t getArduinoLoopTaskStackSize(void);
|
||||
#define SET_LOOP_TASK_STACK_SIZE(sz) \
|
||||
size_t getArduinoLoopTaskStackSize() { \
|
||||
return sz; \
|
||||
}
|
||||
|
||||
#define ESP32_USB_MIDI_DEFAULT_NAME "TinyUSB MIDI"
|
||||
/**
|
||||
* @brief Set the current device name
|
||||
* 1. Name set via constructor (if any)
|
||||
* 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined)
|
||||
* 3. Default name "TinyUSB MIDI"
|
||||
* If device name is set as "", it will be ignored
|
||||
*/
|
||||
#define SET_USB_MIDI_DEVICE_NAME(name) \
|
||||
const char *getUSBMIDIDefaultDeviceName() { \
|
||||
if (!name || strlen(name) == 0) { \
|
||||
return ESP32_USB_MIDI_DEFAULT_NAME; \
|
||||
} \
|
||||
return name; \
|
||||
}
|
||||
|
||||
bool shouldPrintChipDebugReport(void);
|
||||
#define ENABLE_CHIP_DEBUG_REPORT \
|
||||
bool shouldPrintChipDebugReport(void) { \
|
||||
return true; \
|
||||
}
|
||||
|
||||
// macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) can set a time in milliseconds
|
||||
// before the sketch would start its execution. It gives the user time to open the Serial Monitor
|
||||
uint64_t getArduinoSetupWaitTime_ms(void);
|
||||
#define SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) \
|
||||
uint64_t getArduinoSetupWaitTime_ms() { \
|
||||
return (time_ms); \
|
||||
}
|
||||
|
||||
// allows user to bypass esp_spiram_test()
|
||||
bool esp_psram_extram_test(void);
|
||||
#define BYPASS_SPIRAM_TEST(bypass) \
|
||||
bool testSPIRAM(void) { \
|
||||
if (bypass) \
|
||||
return true; \
|
||||
else \
|
||||
return esp_psram_extram_test(); \
|
||||
}
|
||||
|
||||
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
|
||||
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
|
||||
|
||||
extern "C" bool getLocalTime(struct tm *info, uint32_t ms = 5000);
|
||||
extern "C" void configTime(long gmtOffset_sec, int daylightOffset_sec, const char *server1, const char *server2 = nullptr, const char *server3 = nullptr);
|
||||
extern "C" void configTzTime(const char *tz, const char *server1, const char *server2 = nullptr, const char *server3 = nullptr);
|
||||
|
||||
void setToneChannel(uint8_t channel = 0);
|
||||
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);
|
||||
void noTone(uint8_t _pin);
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
// must be applied last as it overrides some of the above
|
||||
#include "io_pin_remap.h"
|
||||
|
||||
#endif /* _ESP32_CORE_ARDUINO_H_ */
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Client.h - Base class that provides Client
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef client_h
|
||||
#define client_h
|
||||
#include "Print.h"
|
||||
#include "Stream.h"
|
||||
#include "IPAddress.h"
|
||||
|
||||
class Client : public Stream {
|
||||
public:
|
||||
virtual int connect(IPAddress ip, uint16_t port) = 0;
|
||||
virtual int connect(const char *host, uint16_t port) = 0;
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int read(uint8_t *buf, size_t size) = 0;
|
||||
virtual int peek() = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual uint8_t connected() = 0;
|
||||
virtual operator bool() = 0;
|
||||
|
||||
protected:
|
||||
uint8_t *rawIPAddress(IPAddress &addr) {
|
||||
return addr.raw_address();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2021 Project CHIP Authors
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 "ColorFormat.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards
|
||||
#define clamp(a, min, max) ((a) < (min) ? (min) : ((a) > (max) ? (max) : (a)))
|
||||
|
||||
const espHsvColor_t HSV_BLACK = {0, 0, 0};
|
||||
const espHsvColor_t HSV_WHITE = {0, 0, 254};
|
||||
const espHsvColor_t HSV_RED = {0, 254, 254};
|
||||
const espHsvColor_t HSV_YELLOW = {42, 254, 254};
|
||||
const espHsvColor_t HSV_GREEN = {84, 254, 254};
|
||||
const espHsvColor_t HSV_CYAN = {127, 254, 254};
|
||||
const espHsvColor_t HSV_BLUE = {169, 254, 254};
|
||||
const espHsvColor_t HSV_MAGENTA = {211, 254, 254};
|
||||
|
||||
const espRgbColor_t RGB_BLACK = {0, 0, 0};
|
||||
const espRgbColor_t RGB_WHITE = {255, 255, 255};
|
||||
const espRgbColor_t RGB_RED = {255, 0, 0};
|
||||
const espRgbColor_t RGB_YELLOW = {255, 255, 0};
|
||||
const espRgbColor_t RGB_GREEN = {0, 255, 0};
|
||||
const espRgbColor_t RGB_CYAN = {0, 255, 255};
|
||||
const espRgbColor_t RGB_BLUE = {0, 0, 255};
|
||||
const espRgbColor_t RGB_MAGENTA = {255, 0, 255};
|
||||
|
||||
// main color temperature values
|
||||
const espCtColor_t COOL_WHITE_COLOR_TEMPERATURE = {142};
|
||||
const espCtColor_t DAYLIGHT_WHITE_COLOR_TEMPERATURE = {181};
|
||||
const espCtColor_t WHITE_COLOR_TEMPERATURE = {250};
|
||||
const espCtColor_t SOFT_WHITE_COLOR_TEMPERATURE = {370};
|
||||
const espCtColor_t WARM_WHITE_COLOR_TEMPERATURE = {454};
|
||||
|
||||
espRgbColor_t espHsvToRgbColor(uint16_t h, uint8_t s, uint8_t v) {
|
||||
espHsvColor_t hsv = {h, s, v};
|
||||
return espHsvColorToRgbColor(hsv);
|
||||
}
|
||||
|
||||
espRgbColor_t espHsvColorToRgbColor(espHsvColor_t hsv) {
|
||||
espRgbColor_t rgb;
|
||||
|
||||
uint8_t region, p, q, t;
|
||||
uint32_t h, s, v, remainder;
|
||||
|
||||
if (hsv.s == 0) {
|
||||
rgb.r = rgb.g = rgb.b = hsv.v;
|
||||
} else {
|
||||
h = hsv.h;
|
||||
s = hsv.s;
|
||||
v = hsv.v;
|
||||
|
||||
region = h / 43;
|
||||
remainder = (h - (region * 43)) * 6;
|
||||
p = (v * (255 - s)) >> 8;
|
||||
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
|
||||
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
|
||||
switch (region) {
|
||||
case 0: rgb.r = v, rgb.g = t, rgb.b = p; break;
|
||||
case 1: rgb.r = q, rgb.g = v, rgb.b = p; break;
|
||||
case 2: rgb.r = p, rgb.g = v, rgb.b = t; break;
|
||||
case 3: rgb.r = p, rgb.g = q, rgb.b = v; break;
|
||||
case 4: rgb.r = t, rgb.g = p, rgb.b = v; break;
|
||||
case 5:
|
||||
default: rgb.r = v, rgb.g = p, rgb.b = q; break;
|
||||
}
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
|
||||
espHsvColor_t espRgbToHsvColor(uint8_t r, uint8_t g, uint8_t b) {
|
||||
espRgbColor_t rgb = {r, g, b};
|
||||
return espRgbColorToHsvColor(rgb);
|
||||
}
|
||||
|
||||
espHsvColor_t espRgbColorToHsvColor(espRgbColor_t rgb) {
|
||||
espHsvColor_t hsv;
|
||||
uint8_t rgbMin, rgbMax;
|
||||
|
||||
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
|
||||
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
|
||||
|
||||
hsv.v = rgbMax;
|
||||
if (hsv.v == 0) {
|
||||
hsv.h = 0;
|
||||
hsv.s = 0;
|
||||
return hsv;
|
||||
}
|
||||
|
||||
hsv.s = 255 * (rgbMax - rgbMin) / hsv.v;
|
||||
if (hsv.s == 0) {
|
||||
hsv.h = 0;
|
||||
return hsv;
|
||||
}
|
||||
if (rgbMax == rgb.r) {
|
||||
hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
|
||||
} else if (rgbMax == rgb.g) {
|
||||
hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
|
||||
} else {
|
||||
hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
|
||||
}
|
||||
return hsv;
|
||||
}
|
||||
|
||||
espRgbColor_t espXYColorToRgbColor(uint8_t Level, espXyColor_t xy) {
|
||||
return espXYToRgbColor(Level, xy.x, xy.y, true);
|
||||
}
|
||||
|
||||
espRgbColor_t espXYToRgbColor(uint8_t Level, uint16_t current_X, uint16_t current_Y, bool addXYZScaling) {
|
||||
// convert xyY color space to RGB
|
||||
|
||||
// https://www.easyrgb.com/en/math.php
|
||||
// https://en.wikipedia.org/wiki/SRGB
|
||||
// refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space
|
||||
|
||||
// The current_X/current_Y attribute contains the current value of the normalized chromaticity value of x/y.
|
||||
// The value of x/y shall be related to the current_X/current_Y attribute by the relationship
|
||||
// x = current_X/65536
|
||||
// y = current_Y/65536
|
||||
// z = 1-x-y
|
||||
|
||||
espRgbColor_t rgb;
|
||||
|
||||
float x, y, z;
|
||||
float X, Y, Z;
|
||||
float r, g, b;
|
||||
|
||||
x = ((float)current_X) / 65535.0f;
|
||||
y = ((float)current_Y) / 65535.0f;
|
||||
|
||||
z = 1.0f - x - y;
|
||||
|
||||
// Calculate XYZ values
|
||||
|
||||
// Y - given brightness in 0 - 1 range
|
||||
Y = ((float)Level) / 254.0f;
|
||||
X = (Y / y) * x;
|
||||
Z = (Y / y) * z;
|
||||
|
||||
// X, Y and Z input refer to a D65/2° standard illuminant.
|
||||
// sR, sG and sB (standard RGB) output range = 0 ÷ 255
|
||||
// convert XYZ to RGB - CIE XYZ to sRGB
|
||||
if (addXYZScaling) {
|
||||
X = X / 100.0f;
|
||||
Y = Y / 100.0f;
|
||||
Z = Z / 100.0f;
|
||||
}
|
||||
|
||||
r = (X * 3.2406f) - (Y * 1.5372f) - (Z * 0.4986f);
|
||||
g = -(X * 0.9689f) + (Y * 1.8758f) + (Z * 0.0415f);
|
||||
b = (X * 0.0557f) - (Y * 0.2040f) + (Z * 1.0570f);
|
||||
|
||||
// apply gamma 2.2 correction
|
||||
r = (r <= 0.0031308f ? 12.92f * r : (1.055f) * pow(r, (1.0f / 2.4f)) - 0.055f);
|
||||
g = (g <= 0.0031308f ? 12.92f * g : (1.055f) * pow(g, (1.0f / 2.4f)) - 0.055f);
|
||||
b = (b <= 0.0031308f ? 12.92f * b : (1.055f) * pow(b, (1.0f / 2.4f)) - 0.055f);
|
||||
|
||||
// Round off
|
||||
r = clamp(r, 0, 1);
|
||||
g = clamp(g, 0, 1);
|
||||
b = clamp(b, 0, 1);
|
||||
|
||||
// these rgb values are in the range of 0 to 1, convert to limit of HW specific LED
|
||||
rgb.r = (uint8_t)(r * 255);
|
||||
rgb.g = (uint8_t)(g * 255);
|
||||
rgb.b = (uint8_t)(b * 255);
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
espXyColor_t espRgbToXYColor(uint8_t r, uint8_t g, uint8_t b) {
|
||||
espRgbColor_t rgb = {r, g, b};
|
||||
return espRgbColorToXYColor(rgb);
|
||||
}
|
||||
|
||||
espXyColor_t espRgbColorToXYColor(espRgbColor_t rgb) {
|
||||
// convert RGB to xy color space
|
||||
|
||||
// https://www.easyrgb.com/en/math.php
|
||||
// https://en.wikipedia.org/wiki/SRGB
|
||||
// refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space
|
||||
|
||||
espXyColor_t xy;
|
||||
|
||||
float r, g, b;
|
||||
float X, Y, Z;
|
||||
float x, y;
|
||||
|
||||
r = ((float)rgb.r) / 255.0f;
|
||||
g = ((float)rgb.g) / 255.0f;
|
||||
b = ((float)rgb.b) / 255.0f;
|
||||
|
||||
// convert RGB to XYZ - sRGB to CIE XYZ
|
||||
r = (r <= 0.04045f ? r / 12.92f : pow((r + 0.055f) / 1.055f, 2.4f));
|
||||
g = (g <= 0.04045f ? g / 12.92f : pow((g + 0.055f) / 1.055f, 2.4f));
|
||||
b = (b <= 0.04045f ? b / 12.92f : pow((b + 0.055f) / 1.055f, 2.4f));
|
||||
|
||||
// https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d
|
||||
X = r * 0.649926f + g * 0.103455f + b * 0.197109f;
|
||||
Y = r * 0.234327f + g * 0.743075f + b * 0.022598f;
|
||||
Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f;
|
||||
|
||||
// sR, sG and sB (standard RGB) input range = 0 ÷ 255
|
||||
// X, Y and Z output refer to a D65/2° standard illuminant.
|
||||
X = r * 0.4124564f + g * 0.3575761f + b * 0.1804375f;
|
||||
Y = r * 0.2126729f + g * 0.7151522f + b * 0.0721750f;
|
||||
Z = r * 0.0193339f + g * 0.1191920f + b * 0.9503041f;
|
||||
|
||||
// Calculate xy values
|
||||
x = X / (X + Y + Z);
|
||||
y = Y / (X + Y + Z);
|
||||
|
||||
// convert to 0-65535 range
|
||||
xy.x = (uint16_t)(x * 65535);
|
||||
xy.y = (uint16_t)(y * 65535);
|
||||
return xy;
|
||||
}
|
||||
|
||||
espRgbColor_t espCTToRgbColor(uint16_t ct) {
|
||||
espCtColor_t ctColor = {ct};
|
||||
return espCTColorToRgbColor(ctColor);
|
||||
}
|
||||
|
||||
espRgbColor_t espCTColorToRgbColor(espCtColor_t ct) {
|
||||
espRgbColor_t rgb = {0, 0, 0};
|
||||
float r, g, b;
|
||||
|
||||
if (ct.ctMireds == 0) {
|
||||
return rgb;
|
||||
}
|
||||
// Algorithm credits to Tanner Helland: https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html
|
||||
|
||||
// Convert Mireds to centiKelvins. k = 1,000,000/mired
|
||||
float ctCentiKelvin = 10000 / ct.ctMireds;
|
||||
|
||||
// Red
|
||||
if (ctCentiKelvin <= 66) {
|
||||
r = 255;
|
||||
} else {
|
||||
r = 329.698727446f * pow(ctCentiKelvin - 60, -0.1332047592f);
|
||||
}
|
||||
|
||||
// Green
|
||||
if (ctCentiKelvin <= 66) {
|
||||
g = 99.4708025861f * log(ctCentiKelvin) - 161.1195681661f;
|
||||
} else {
|
||||
g = 288.1221695283f * pow(ctCentiKelvin - 60, -0.0755148492f);
|
||||
}
|
||||
|
||||
// Blue
|
||||
if (ctCentiKelvin >= 66) {
|
||||
b = 255;
|
||||
} else {
|
||||
if (ctCentiKelvin <= 19) {
|
||||
b = 0;
|
||||
} else {
|
||||
b = 138.5177312231 * log(ctCentiKelvin - 10) - 305.0447927307;
|
||||
}
|
||||
}
|
||||
rgb.r = (uint8_t)clamp(r, 0, 255);
|
||||
rgb.g = (uint8_t)clamp(g, 0, 255);
|
||||
rgb.b = (uint8_t)clamp(b, 0, 255);
|
||||
|
||||
return rgb;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2021 Project CHIP Authors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct RgbColor_t {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
struct HsvColor_t {
|
||||
uint16_t h;
|
||||
uint8_t s;
|
||||
uint8_t v;
|
||||
};
|
||||
|
||||
struct XyColor_t {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
};
|
||||
|
||||
struct CtColor_t {
|
||||
uint16_t ctMireds;
|
||||
};
|
||||
|
||||
typedef struct RgbColor_t espRgbColor_t;
|
||||
typedef struct HsvColor_t espHsvColor_t;
|
||||
typedef struct XyColor_t espXyColor_t;
|
||||
typedef struct CtColor_t espCtColor_t;
|
||||
|
||||
espRgbColor_t espXYToRgbColor(uint8_t Level, uint16_t current_X, uint16_t current_Y, bool addXYZScaling);
|
||||
espRgbColor_t espXYColorToRgb(uint8_t Level, espXyColor_t xy);
|
||||
espXyColor_t espRgbColorToXYColor(espRgbColor_t rgb);
|
||||
espXyColor_t espRgbToXYColor(uint8_t r, uint8_t g, uint8_t b);
|
||||
espRgbColor_t espHsvColorToRgbColor(espHsvColor_t hsv);
|
||||
espRgbColor_t espHsvToRgbColor(uint16_t h, uint8_t s, uint8_t v);
|
||||
espRgbColor_t espCTColorToRgbColor(espCtColor_t ct);
|
||||
espRgbColor_t espCTToRgbColor(uint16_t ct);
|
||||
espHsvColor_t espRgbColorToHsvColor(espRgbColor_t rgb);
|
||||
espHsvColor_t espRgbToHsvColor(uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
extern const espHsvColor_t HSV_BLACK, HSV_WHITE, HSV_RED, HSV_YELLOW, HSV_GREEN, HSV_CYAN, HSV_BLUE, HSV_MAGENTA;
|
||||
extern const espCtColor_t COOL_WHITE_COLOR_TEMPERATURE, DAYLIGHT_WHITE_COLOR_TEMPERATURE, WHITE_COLOR_TEMPERATURE, SOFT_WHITE_COLOR_TEMPERATURE,
|
||||
WARM_WHITE_COLOR_TEMPERATURE;
|
||||
extern const espRgbColor_t RGB_BLACK, RGB_WHITE, RGB_RED, RGB_YELLOW, RGB_GREEN, RGB_CYAN, RGB_BLUE, RGB_MAGENTA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
Esp.cpp - ESP31B-specific APIs
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Esp.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include <memory>
|
||||
#include <soc/soc.h>
|
||||
#include <esp_partition.h>
|
||||
extern "C" {
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_image_format.h"
|
||||
}
|
||||
#include <MD5Builder.h>
|
||||
|
||||
#include "soc/spi_reg.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_flash.h"
|
||||
|
||||
// Include HAL layer for flash clock access
|
||||
#include "hal/spi_flash_ll.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "soc/spi_struct.h"
|
||||
#else
|
||||
// All modern chips (S2, S3, C2, C3, C5, C6, H2, P4) use spimem
|
||||
#include "hal/spimem_flash_ll.h"
|
||||
// Try to include the newer c_struct header first, fall back to regular struct
|
||||
#if __has_include("soc/spi_mem_c_struct.h")
|
||||
#include "soc/spi_mem_c_struct.h"
|
||||
#else
|
||||
#include "soc/spi_mem_struct.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/spi_flash.h"
|
||||
#include "soc/efuse_reg.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing flash size and spi mode
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/spi_flash.h"
|
||||
#include "soc/efuse_reg.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x1000
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/spi_flash.h"
|
||||
#include "soc/efuse_reg.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#include "esp32c2/rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c2 is located at 0x0000
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#include "esp32c6/rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c6 is located at 0x0000
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#include "esp32h2/rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32h2 is located at 0x0000
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp32p4/rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32p4 is located at 0x2000
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#include "esp32c5/rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32c5 is located at 0x2000
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#include "esp32c61/rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c61 is located at 0x0000
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/spi_flash.h"
|
||||
#define ESP_FLASH_IMAGE_BASE 0x1000
|
||||
#endif
|
||||
|
||||
// REG_SPI_BASE is not defined for S3/C3 ??
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifdef REG_SPI_BASE
|
||||
#undef REG_SPI_BASE
|
||||
#endif // REG_SPI_BASE
|
||||
#define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i) > 1) ? (((i) * 0x1000) + 0x20000) : (((~(i)) & 1) * 0x1000)))
|
||||
#endif // TARGET
|
||||
|
||||
/**
|
||||
* User-defined Literals
|
||||
* usage:
|
||||
*
|
||||
* uint32_t = test = 10_MHz; // --> 10000000
|
||||
*/
|
||||
|
||||
unsigned long long operator""_kHz(unsigned long long x) {
|
||||
return x * 1000;
|
||||
}
|
||||
|
||||
unsigned long long operator""_MHz(unsigned long long x) {
|
||||
return x * 1000 * 1000;
|
||||
}
|
||||
|
||||
unsigned long long operator""_GHz(unsigned long long x) {
|
||||
return x * 1000 * 1000 * 1000;
|
||||
}
|
||||
|
||||
unsigned long long operator""_kBit(unsigned long long x) {
|
||||
return x * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator""_MBit(unsigned long long x) {
|
||||
return x * 1024 * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator""_GBit(unsigned long long x) {
|
||||
return x * 1024 * 1024 * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator""_kB(unsigned long long x) {
|
||||
return x * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator""_MB(unsigned long long x) {
|
||||
return x * 1024 * 1024;
|
||||
}
|
||||
|
||||
unsigned long long operator""_GB(unsigned long long x) {
|
||||
return x * 1024 * 1024 * 1024;
|
||||
}
|
||||
|
||||
EspClass ESP;
|
||||
|
||||
void EspClass::deepSleep(uint64_t time_us) {
|
||||
esp_deep_sleep(time_us);
|
||||
}
|
||||
|
||||
void EspClass::restart(void) {
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
uint32_t EspClass::getHeapSize(void) {
|
||||
return heap_caps_get_total_size(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreeHeap(void) {
|
||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMinFreeHeap(void) {
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMaxAllocHeap(void) {
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getPsramSize(void) {
|
||||
if (psramFound()) {
|
||||
return heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreePsram(void) {
|
||||
if (psramFound()) {
|
||||
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMinFreePsram(void) {
|
||||
if (psramFound()) {
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getMaxAllocPsram(void) {
|
||||
if (psramFound()) {
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t sketchSize(sketchSize_t response) {
|
||||
esp_image_metadata_t data;
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (!running) {
|
||||
return 0;
|
||||
}
|
||||
const esp_partition_pos_t running_pos = {
|
||||
.offset = running->address,
|
||||
.size = running->size,
|
||||
};
|
||||
data.start_addr = running_pos.offset;
|
||||
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
|
||||
if (response) {
|
||||
return running_pos.size - data.image_len;
|
||||
} else {
|
||||
return data.image_len;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EspClass::getSketchSize() {
|
||||
return sketchSize(SKETCH_SIZE_TOTAL);
|
||||
}
|
||||
|
||||
String EspClass::getSketchMD5() {
|
||||
static String result;
|
||||
if (result.length()) {
|
||||
return result;
|
||||
}
|
||||
uint32_t lengthLeft = getSketchSize();
|
||||
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
if (!running) {
|
||||
log_e("Partition could not be found");
|
||||
return String();
|
||||
}
|
||||
|
||||
const size_t bufSize = SPI_FLASH_SEC_SIZE;
|
||||
uint8_t *pb = (uint8_t *)malloc(bufSize);
|
||||
if (!pb) {
|
||||
log_e("Not enough memory to allocate buffer");
|
||||
return String();
|
||||
}
|
||||
uint32_t offset = 0;
|
||||
|
||||
MD5Builder md5;
|
||||
md5.begin();
|
||||
while (lengthLeft > 0) {
|
||||
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
|
||||
if (!ESP.flashRead(running->address + offset, (uint32_t *)pb, (readBytes + 3) & ~3)) {
|
||||
free(pb);
|
||||
log_e("Could not read buffer from flash");
|
||||
return String();
|
||||
}
|
||||
md5.add(pb, readBytes);
|
||||
lengthLeft -= readBytes;
|
||||
offset += readBytes;
|
||||
|
||||
#if CONFIG_FREERTOS_UNICORE
|
||||
delay(1); // Fix solo WDT
|
||||
#endif
|
||||
}
|
||||
free(pb);
|
||||
md5.calculate();
|
||||
result = md5.toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFreeSketchSpace() {
|
||||
const esp_partition_t *_partition = esp_ota_get_next_update_partition(NULL);
|
||||
if (!_partition) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _partition->size;
|
||||
}
|
||||
|
||||
uint16_t EspClass::getChipRevision(void) {
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
return chip_info.revision;
|
||||
}
|
||||
|
||||
const char *EspClass::getChipModel(void) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_PACKAGE);
|
||||
uint32_t pkg_ver = chip_ver & 0x7;
|
||||
switch (pkg_ver) {
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6:
|
||||
if ((getChipRevision() / 100) == 3) {
|
||||
return "ESP32-D0WDQ6-V3";
|
||||
} else {
|
||||
return "ESP32-D0WDQ6";
|
||||
}
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5:
|
||||
if ((getChipRevision() / 100) == 3) {
|
||||
return "ESP32-D0WD-V3";
|
||||
} else {
|
||||
return "ESP32-D0WD";
|
||||
}
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5: return "ESP32-D2WD";
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32U4WDH: return "ESP32-U4WDH";
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4: return "ESP32-PICO-D4";
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302: return "ESP32-PICO-V3-02";
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3: return "ESP32-D0WDR2-V3";
|
||||
default: return "Unknown";
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION);
|
||||
switch (pkg_ver) {
|
||||
case 0: return "ESP32-S2";
|
||||
case 1: return "ESP32-S2FH16";
|
||||
case 2: return "ESP32-S2FH32";
|
||||
default: return "ESP32-S2 (Unknown)";
|
||||
}
|
||||
#else
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
switch (chip_info.model) {
|
||||
case CHIP_ESP32S3: return "ESP32-S3";
|
||||
case CHIP_ESP32C3: return "ESP32-C3";
|
||||
case CHIP_ESP32C2: return "ESP32-C2";
|
||||
case CHIP_ESP32C6: return "ESP32-C6";
|
||||
case CHIP_ESP32H2: return "ESP32-H2";
|
||||
case CHIP_ESP32P4: return "ESP32-P4";
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||
case CHIP_ESP32C5: return "ESP32-C5";
|
||||
case CHIP_ESP32C61: return "ESP32-C61";
|
||||
case CHIP_ESP32H21: return "ESP32-H21";
|
||||
#endif
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t EspClass::getChipCores(void) {
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
return chip_info.cores;
|
||||
}
|
||||
|
||||
const char *EspClass::getSdkVersion(void) {
|
||||
return esp_get_idf_version();
|
||||
}
|
||||
|
||||
const char *EspClass::getCoreVersion(void) {
|
||||
return ESP_ARDUINO_VERSION_STR;
|
||||
}
|
||||
|
||||
uint32_t ESP_getFlashChipId(void) {
|
||||
uint32_t id = g_rom_flashchip.device_id;
|
||||
id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00);
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFlashChipSize(void) {
|
||||
uint32_t id = (ESP_getFlashChipId() >> 16) & 0xFF;
|
||||
return 2 << (id - 1);
|
||||
}
|
||||
|
||||
uint32_t EspClass::getFlashChipSpeed(void) {
|
||||
esp_image_header_t fhdr;
|
||||
if (esp_flash_read(esp_flash_default_chip, (void *)&fhdr, ESP_FLASH_IMAGE_BASE, sizeof(esp_image_header_t)) && fhdr.magic != ESP_IMAGE_HEADER_MAGIC) {
|
||||
return 0;
|
||||
}
|
||||
return magicFlashChipSpeed(fhdr.spi_speed);
|
||||
}
|
||||
|
||||
FlashMode_t EspClass::getFlashChipMode(void) {
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
|
||||
uint32_t spi_ctrl = REG_READ(PERIPHS_SPI_FLASH_CTRL);
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6
|
||||
uint32_t spi_ctrl = REG_READ(DR_REG_SPI0_BASE + 0x8);
|
||||
#else
|
||||
uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0));
|
||||
#endif
|
||||
/* Not all of the following constants are already defined in older versions of spi_reg.h, so do it manually for now*/
|
||||
if (spi_ctrl & BIT(24)) { //SPI_FREAD_QIO
|
||||
return (FM_QIO);
|
||||
} else if (spi_ctrl & BIT(20)) { //SPI_FREAD_QUAD
|
||||
return (FM_QOUT);
|
||||
} else if (spi_ctrl & BIT(23)) { //SPI_FREAD_DIO
|
||||
return (FM_DIO);
|
||||
} else if (spi_ctrl & BIT(14)) { // SPI_FREAD_DUAL
|
||||
return (FM_DOUT);
|
||||
} else if (spi_ctrl & BIT(13)) { //SPI_FASTRD_MODE
|
||||
return (FM_FAST_READ);
|
||||
} else {
|
||||
return (FM_SLOW_READ);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EspClass::magicFlashChipSize(uint8_t flashByte) {
|
||||
/*
|
||||
FLASH_SIZES = {
|
||||
"1MB": 0x00,
|
||||
"2MB": 0x10,
|
||||
"4MB": 0x20,
|
||||
"8MB": 0x30,
|
||||
"16MB": 0x40,
|
||||
"32MB": 0x50,
|
||||
"64MB": 0x60,
|
||||
"128MB": 0x70,
|
||||
}
|
||||
*/
|
||||
switch (flashByte & 0x0F) {
|
||||
case 0x0: return (1_MB); // 8 MBit (1MB)
|
||||
case 0x1: return (2_MB); // 16 MBit (2MB)
|
||||
case 0x2: return (4_MB); // 32 MBit (4MB)
|
||||
case 0x3: return (8_MB); // 64 MBit (8MB)
|
||||
case 0x4: return (16_MB); // 128 MBit (16MB)
|
||||
case 0x5: return (32_MB); // 256 MBit (32MB)
|
||||
case 0x6: return (64_MB); // 512 MBit (64MB)
|
||||
case 0x7: return (128_MB); // 1 GBit (128MB)
|
||||
default: // fail?
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EspClass::magicFlashChipSpeed(uint8_t flashByte) {
|
||||
#if CONFIG_IDF_TARGET_ESP32C2
|
||||
/*
|
||||
FLASH_FREQUENCY = {
|
||||
"60m": 0xF,
|
||||
"30m": 0x0,
|
||||
"20m": 0x1,
|
||||
"15m": 0x2,
|
||||
}
|
||||
*/
|
||||
switch (flashByte & 0x0F) {
|
||||
case 0xF: return (60_MHz);
|
||||
case 0x0: return (30_MHz);
|
||||
case 0x1: return (20_MHz);
|
||||
case 0x2: return (15_MHz);
|
||||
default: // fail?
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
/*
|
||||
FLASH_FREQUENCY = {
|
||||
"80m": 0x0, # workaround for wrong mspi HS div value in ROM
|
||||
"40m": 0x0,
|
||||
"20m": 0x2,
|
||||
}
|
||||
*/
|
||||
switch (flashByte & 0x0F) {
|
||||
case 0x0: return (80_MHz);
|
||||
case 0x2: return (20_MHz);
|
||||
default: // fail?
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
/*
|
||||
FLASH_FREQUENCY = {
|
||||
"80m": 0xF,
|
||||
"40m": 0x0,
|
||||
"20m": 0x2,
|
||||
}
|
||||
*/
|
||||
switch (flashByte & 0x0F) {
|
||||
case 0xF: return (80_MHz);
|
||||
case 0x0: return (40_MHz);
|
||||
case 0x2: return (20_MHz);
|
||||
default: // fail?
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
|
||||
/*
|
||||
FLASH_FREQUENCY = {
|
||||
"48m": 0xF,
|
||||
"24m": 0x0,
|
||||
"16m": 0x1,
|
||||
"12m": 0x2,
|
||||
}
|
||||
*/
|
||||
switch (flashByte & 0x0F) {
|
||||
case 0xF: return (48_MHz);
|
||||
case 0x0: return (24_MHz);
|
||||
case 0x1: return (16_MHz);
|
||||
case 0x2: return (12_MHz);
|
||||
default: // fail?
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
/*
|
||||
FLASH_FREQUENCY = {
|
||||
"80m": 0xF,
|
||||
"40m": 0x0,
|
||||
"26m": 0x1,
|
||||
"20m": 0x2,
|
||||
}
|
||||
*/
|
||||
switch (flashByte & 0x0F) {
|
||||
case 0xF: return (80_MHz);
|
||||
case 0x0: return (40_MHz);
|
||||
case 0x1: return (26_MHz);
|
||||
case 0x2: return (20_MHz);
|
||||
default: // fail?
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
FlashMode_t EspClass::magicFlashChipMode(uint8_t flashByte) {
|
||||
FlashMode_t mode = (FlashMode_t)flashByte;
|
||||
if (mode > FM_SLOW_READ) {
|
||||
mode = FM_UNKNOWN;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool EspClass::flashEraseSector(uint32_t sector) {
|
||||
return esp_flash_erase_region(esp_flash_default_chip, sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE) == ESP_OK;
|
||||
}
|
||||
|
||||
// Warning: These functions do not work with encrypted flash
|
||||
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
|
||||
return esp_flash_write(esp_flash_default_chip, (const void *)data, offset, size) == ESP_OK;
|
||||
}
|
||||
|
||||
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
|
||||
return esp_flash_read(esp_flash_default_chip, (void *)data, offset, size) == ESP_OK;
|
||||
}
|
||||
|
||||
bool EspClass::partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size) {
|
||||
return esp_partition_erase_range(partition, offset, size) == ESP_OK;
|
||||
}
|
||||
|
||||
bool EspClass::partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) {
|
||||
return esp_partition_write(partition, offset, data, size) == ESP_OK;
|
||||
}
|
||||
|
||||
bool EspClass::partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) {
|
||||
return esp_partition_read(partition, offset, data, size) == ESP_OK;
|
||||
}
|
||||
|
||||
uint64_t EspClass::getEfuseMac(void) {
|
||||
uint64_t _chipmacid = 0LL;
|
||||
esp_efuse_mac_get_default((uint8_t *)(&_chipmacid));
|
||||
return _chipmacid;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Flash Frequency Runtime Detection
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Read the source clock frequency using ESP-IDF HAL functions
|
||||
* @return Source clock frequency in MHz (80, 120, 160, or 240)
|
||||
*/
|
||||
uint8_t EspClass::getFlashSourceFrequencyMHz(void) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// ESP32: Use HAL function
|
||||
return spi_flash_ll_get_source_clock_freq_mhz(0); // host_id = 0 for SPI0
|
||||
#else
|
||||
// All modern MCUs: Use spimem HAL function
|
||||
return spimem_flash_ll_get_source_freq_mhz();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read the clock divider from hardware using HAL structures
|
||||
* Based on ESP-IDF HAL implementation:
|
||||
* - ESP32: Uses SPI1.clock (typedef in spi_flash_ll.h)
|
||||
* - All newer MCUs: Use SPIMEM1.clock (typedef in spimem_flash_ll.h)
|
||||
* @return Clock divider value (1 = no division, 2 = divide by 2, etc.)
|
||||
*/
|
||||
uint8_t EspClass::getFlashClockDivider(void) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// ESP32: Flash uses SPI1
|
||||
// See: line 52: esp-idf/components/hal/esp32/include/hal/spi_flash_ll.h
|
||||
if (SPI1.clock.clk_equ_sysclk) {
|
||||
return 1; // 1:1 clock
|
||||
}
|
||||
return SPI1.clock.clkcnt_n + 1;
|
||||
#else
|
||||
// All newer MCUs: Flash uses SPIMEM1
|
||||
// See: esp-idf/components/hal/esp32*/include/hal/spimem_flash_ll.h
|
||||
// Example S3: line 38: typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t;
|
||||
// Example C5: lines 97-99: esp-idf/components/soc/esp32c5/mp/include/soc/spi_mem_struct.h
|
||||
if (SPIMEM1.clock.clk_equ_sysclk) {
|
||||
return 1; // 1:1 clock
|
||||
}
|
||||
return SPIMEM1.clock.clkcnt_n + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the actual flash frequency in MHz
|
||||
* @return Flash frequency in MHz (80, 120, 160, or 240)
|
||||
*/
|
||||
uint32_t EspClass::getFlashFrequencyMHz(void) {
|
||||
uint8_t source = getFlashSourceFrequencyMHz();
|
||||
uint8_t divider = getFlashClockDivider();
|
||||
|
||||
if (divider == 0) {
|
||||
divider = 1; // Safety check
|
||||
}
|
||||
|
||||
return source / divider;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Esp.h - ESP31B-specific APIs
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef ESP_H
|
||||
#define ESP_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <esp_partition.h>
|
||||
#include <hal/cpu_hal.h>
|
||||
#include "esp_cpu.h"
|
||||
|
||||
/**
|
||||
* AVR macros for WDT management
|
||||
*/
|
||||
typedef enum {
|
||||
WDTO_0MS = 0, //!< WDTO_0MS
|
||||
WDTO_15MS = 15, //!< WDTO_15MS
|
||||
WDTO_30MS = 30, //!< WDTO_30MS
|
||||
WDTO_60MS = 60, //!< WDTO_60MS
|
||||
WDTO_120MS = 120, //!< WDTO_120MS
|
||||
WDTO_250MS = 250, //!< WDTO_250MS
|
||||
WDTO_500MS = 500, //!< WDTO_500MS
|
||||
WDTO_1S = 1000, //!< WDTO_1S
|
||||
WDTO_2S = 2000, //!< WDTO_2S
|
||||
WDTO_4S = 4000, //!< WDTO_4S
|
||||
WDTO_8S = 8000 //!< WDTO_8S
|
||||
} WDTO_t;
|
||||
|
||||
typedef enum {
|
||||
FM_QIO = 0x00,
|
||||
FM_QOUT = 0x01,
|
||||
FM_DIO = 0x02,
|
||||
FM_DOUT = 0x03,
|
||||
FM_FAST_READ = 0x04,
|
||||
FM_SLOW_READ = 0x05,
|
||||
FM_UNKNOWN = 0xff
|
||||
} FlashMode_t;
|
||||
|
||||
typedef enum {
|
||||
SKETCH_SIZE_TOTAL = 0,
|
||||
SKETCH_SIZE_FREE = 1
|
||||
} sketchSize_t;
|
||||
|
||||
class EspClass {
|
||||
public:
|
||||
EspClass() {}
|
||||
~EspClass() {}
|
||||
void restart();
|
||||
|
||||
//Internal RAM
|
||||
uint32_t getHeapSize(); //total heap size
|
||||
uint32_t getFreeHeap(); //available heap
|
||||
uint32_t getMinFreeHeap(); //lowest level of free heap since boot
|
||||
uint32_t getMaxAllocHeap(); //largest block of heap that can be allocated at once
|
||||
|
||||
//SPI RAM
|
||||
uint32_t getPsramSize();
|
||||
uint32_t getFreePsram();
|
||||
uint32_t getMinFreePsram();
|
||||
uint32_t getMaxAllocPsram();
|
||||
|
||||
uint16_t getChipRevision();
|
||||
const char *getChipModel();
|
||||
uint8_t getChipCores();
|
||||
uint32_t getCpuFreqMHz() {
|
||||
return getCpuFrequencyMhz();
|
||||
}
|
||||
inline uint32_t getCycleCount() __attribute__((always_inline));
|
||||
|
||||
const char *getSdkVersion(); //version of ESP-IDF
|
||||
const char *getCoreVersion(); //version of this core
|
||||
|
||||
void deepSleep(uint64_t time_us);
|
||||
|
||||
uint32_t getFlashChipSize();
|
||||
uint32_t getFlashChipSpeed();
|
||||
FlashMode_t getFlashChipMode();
|
||||
|
||||
// Flash frequency runtime detection
|
||||
uint32_t getFlashFrequencyMHz();
|
||||
uint8_t getFlashSourceFrequencyMHz();
|
||||
uint8_t getFlashClockDivider();
|
||||
|
||||
uint32_t magicFlashChipSize(uint8_t flashByte);
|
||||
uint32_t magicFlashChipSpeed(uint8_t flashByte);
|
||||
FlashMode_t magicFlashChipMode(uint8_t flashByte);
|
||||
|
||||
uint32_t getSketchSize();
|
||||
String getSketchMD5();
|
||||
uint32_t getFreeSketchSpace();
|
||||
|
||||
bool flashEraseSector(uint32_t sector);
|
||||
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
|
||||
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
|
||||
|
||||
bool partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size);
|
||||
bool partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size);
|
||||
bool partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size);
|
||||
|
||||
uint64_t getEfuseMac();
|
||||
};
|
||||
|
||||
uint32_t ARDUINO_ISR_ATTR EspClass::getCycleCount() {
|
||||
return (uint32_t)esp_cpu_get_cycle_count();
|
||||
}
|
||||
|
||||
extern EspClass ESP;
|
||||
|
||||
#endif //ESP_H
|
||||
@@ -0,0 +1,427 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "FirmwareMSC.h"
|
||||
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
|
||||
#include <cstring>
|
||||
#include "esp_partition.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "pins_arduino.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "firmware_msc_fat.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
|
||||
#ifndef USB_FW_MSC_VENDOR_ID
|
||||
#define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_PRODUCT_ID
|
||||
#define USB_FW_MSC_PRODUCT_ID "Firmware MSC" //max 16 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_PRODUCT_REVISION
|
||||
#define USB_FW_MSC_PRODUCT_REVISION "1.0" //max 4 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_VOLUME_NAME
|
||||
#define USB_FW_MSC_VOLUME_NAME "ESP32-FWMSC" //max 11 chars
|
||||
#endif
|
||||
#ifndef USB_FW_MSC_SERIAL_NUMBER
|
||||
#define USB_FW_MSC_SERIAL_NUMBER 0x00000000
|
||||
#endif
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
|
||||
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
|
||||
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
|
||||
|
||||
//General Variables
|
||||
static uint8_t *msc_ram_disk = NULL;
|
||||
static fat_boot_sector_t *msc_boot = NULL;
|
||||
static uint8_t *msc_table = NULL;
|
||||
static uint16_t msc_table_sectors = 0;
|
||||
static uint16_t msc_total_sectors = 0;
|
||||
static bool mcs_is_fat16 = false;
|
||||
|
||||
//Firmware Read
|
||||
static const esp_partition_t *msc_run_partition = NULL;
|
||||
static uint16_t fw_start_sector = 0;
|
||||
static uint16_t fw_end_sector = 0;
|
||||
static size_t fw_size = 0;
|
||||
static fat_dir_entry_t *fw_entry = NULL;
|
||||
|
||||
//Firmware Write
|
||||
typedef enum {
|
||||
MSC_UPDATE_IDLE,
|
||||
MSC_UPDATE_STARTING,
|
||||
MSC_UPDATE_RUNNING,
|
||||
MSC_UPDATE_END
|
||||
} msc_update_state_t;
|
||||
|
||||
static const esp_partition_t *msc_ota_partition = NULL;
|
||||
static msc_update_state_t msc_update_state = MSC_UPDATE_IDLE;
|
||||
static uint16_t msc_update_start_sector = 0;
|
||||
static uint32_t msc_update_bytes_written = 0;
|
||||
static fat_dir_entry_t *msc_update_entry = NULL;
|
||||
|
||||
static uint32_t get_firmware_size(const esp_partition_t *partition) {
|
||||
esp_image_metadata_t data;
|
||||
const esp_partition_pos_t running_pos = {
|
||||
.offset = partition->address,
|
||||
.size = partition->size,
|
||||
};
|
||||
data.start_addr = running_pos.offset;
|
||||
esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
|
||||
return data.image_len;
|
||||
}
|
||||
|
||||
//Get number of sectors required based on the size of the firmware and OTA partition
|
||||
static size_t msc_update_get_required_disk_sectors() {
|
||||
size_t data_sectors = 16;
|
||||
size_t total_sectors = 0;
|
||||
msc_run_partition = esp_ota_get_running_partition();
|
||||
msc_ota_partition = esp_ota_get_next_update_partition(NULL);
|
||||
if (msc_run_partition) {
|
||||
fw_size = get_firmware_size(msc_run_partition);
|
||||
data_sectors += FAT_SIZE_TO_SECTORS(fw_size);
|
||||
log_d("APP size: %u (%u sectors)", fw_size, FAT_SIZE_TO_SECTORS(fw_size));
|
||||
} else {
|
||||
log_w("APP partition not found. Reading disabled");
|
||||
}
|
||||
if (msc_ota_partition) {
|
||||
data_sectors += FAT_SIZE_TO_SECTORS(msc_ota_partition->size);
|
||||
log_d("OTA size: %u (%u sectors)", msc_ota_partition->size, FAT_SIZE_TO_SECTORS(msc_ota_partition->size));
|
||||
} else {
|
||||
log_w("OTA partition not found. Writing disabled");
|
||||
}
|
||||
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, false);
|
||||
total_sectors = data_sectors + msc_table_sectors + 2;
|
||||
if (total_sectors > 0xFF4) {
|
||||
log_d("USING FAT16");
|
||||
mcs_is_fat16 = true;
|
||||
total_sectors -= msc_table_sectors;
|
||||
msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, true);
|
||||
total_sectors += msc_table_sectors;
|
||||
} else {
|
||||
log_d("USING FAT12");
|
||||
mcs_is_fat16 = false;
|
||||
}
|
||||
log_d("FAT sector size: %u", DISK_SECTOR_SIZE);
|
||||
log_d("FAT data sectors: %u", data_sectors);
|
||||
log_d("FAT table sectors: %u", msc_table_sectors);
|
||||
log_d("FAT total sectors: %u (%uKB)", total_sectors, (total_sectors * DISK_SECTOR_SIZE) / 1024);
|
||||
return total_sectors;
|
||||
}
|
||||
|
||||
//setup the ramdisk and add the firmware download file
|
||||
static bool msc_update_setup_disk(const char *volume_label, uint32_t serial_number) {
|
||||
msc_total_sectors = msc_update_get_required_disk_sectors();
|
||||
uint8_t ram_sectors = msc_table_sectors + 2;
|
||||
msc_ram_disk = (uint8_t *)calloc(ram_sectors, DISK_SECTOR_SIZE);
|
||||
if (!msc_ram_disk) {
|
||||
log_e("Failed to allocate RAM Disk: %u bytes", ram_sectors * DISK_SECTOR_SIZE);
|
||||
return false;
|
||||
}
|
||||
fw_start_sector = ram_sectors;
|
||||
fw_end_sector = fw_start_sector;
|
||||
msc_boot = fat_add_boot_sector(msc_ram_disk, msc_total_sectors, msc_table_sectors, fat_file_system_type(mcs_is_fat16), volume_label, serial_number);
|
||||
msc_table = fat_add_table(msc_ram_disk, msc_boot, mcs_is_fat16);
|
||||
//fat_dir_entry_t * label = fat_add_label(msc_ram_disk, volume_label);
|
||||
if (msc_run_partition) {
|
||||
fw_entry = fat_add_root_file(msc_ram_disk, 0, "FIRMWARE", "BIN", fw_size, 2, mcs_is_fat16);
|
||||
fw_end_sector = FAT_SIZE_TO_SECTORS(fw_size) + fw_start_sector;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void msc_update_delete_disk() {
|
||||
fw_entry = NULL;
|
||||
fw_size = 0;
|
||||
fw_end_sector = 0;
|
||||
fw_start_sector = 0;
|
||||
msc_table = NULL;
|
||||
msc_boot = NULL;
|
||||
msc_table_sectors = 0;
|
||||
msc_total_sectors = 0;
|
||||
msc_run_partition = NULL;
|
||||
msc_ota_partition = NULL;
|
||||
msc_update_state = MSC_UPDATE_IDLE;
|
||||
msc_update_start_sector = 0;
|
||||
msc_update_bytes_written = 0;
|
||||
msc_update_entry = NULL;
|
||||
free(msc_ram_disk);
|
||||
msc_ram_disk = NULL;
|
||||
}
|
||||
|
||||
//filter out entries to only include BINs in the root folder
|
||||
static fat_dir_entry_t *msc_update_get_root_bin_entry(uint8_t index) {
|
||||
fat_dir_entry_t *entry = (fat_dir_entry_t *)(msc_ram_disk + ((msc_boot->sectors_per_alloc_table + 1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
|
||||
fat_lfn_entry_t *lfn = (fat_lfn_entry_t *)entry;
|
||||
|
||||
//empty entry
|
||||
if (entry->file_magic == 0) {
|
||||
return NULL;
|
||||
}
|
||||
//long file name
|
||||
if (lfn->attr == 0x0F && lfn->type == 0x00 && lfn->first_cluster == 0x0000) {
|
||||
return NULL;
|
||||
}
|
||||
//only files marked as archives
|
||||
if (entry->file_attr != FAT_FILE_ATTR_ARCHIVE) {
|
||||
return NULL;
|
||||
}
|
||||
//deleted
|
||||
if (entry->file_magic == 0xE5 || entry->file_magic == 0x05) {
|
||||
return NULL;
|
||||
}
|
||||
//not bins
|
||||
if (memcmp("BIN", entry->file_extension, 3)) {
|
||||
return NULL;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
//get an empty bin (the host will add an entry for file about to be written with size of zero)
|
||||
static fat_dir_entry_t *msc_update_find_new_bin() {
|
||||
for (uint8_t i = 16; i;) {
|
||||
i--;
|
||||
fat_dir_entry_t *entry = msc_update_get_root_bin_entry(i);
|
||||
if (entry && entry->file_size == 0) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//get a bin starting from particular sector
|
||||
static fat_dir_entry_t *msc_update_find_bin(uint16_t sector) {
|
||||
for (uint8_t i = 16; i;) {
|
||||
i--;
|
||||
fat_dir_entry_t *entry = msc_update_get_root_bin_entry(i);
|
||||
if (entry && entry->data_start_sector == (sector - msc_boot->sectors_per_alloc_table)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//write the new data and erase the flash blocks when necessary
|
||||
static esp_err_t msc_update_write(const esp_partition_t *partition, uint32_t offset, void *data, size_t size) {
|
||||
esp_err_t err = ESP_OK;
|
||||
if ((offset & (SPI_FLASH_SEC_SIZE - 1)) == 0) {
|
||||
err = esp_partition_erase_range(partition, offset, SPI_FLASH_SEC_SIZE);
|
||||
log_v("ERASE[0x%08X]: %s", offset, (err != ESP_OK) ? "FAIL" : "OK");
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return esp_partition_write(partition, offset, data, size);
|
||||
}
|
||||
|
||||
//called when error was encountered while updating
|
||||
static void msc_update_error() {
|
||||
log_e("UPDATE_ERROR: %u", msc_update_bytes_written);
|
||||
arduino_firmware_msc_event_data_t p;
|
||||
p.error.size = msc_update_bytes_written;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_ERROR_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
msc_update_state = MSC_UPDATE_IDLE;
|
||||
msc_update_entry = NULL;
|
||||
msc_update_bytes_written = 0;
|
||||
msc_update_start_sector = 0;
|
||||
}
|
||||
|
||||
//called when all firmware bytes have been received
|
||||
static void msc_update_end() {
|
||||
log_d("UPDATE_END: %u", msc_update_entry->file_size);
|
||||
msc_update_state = MSC_UPDATE_END;
|
||||
size_t ota_size = get_firmware_size(msc_ota_partition);
|
||||
if (ota_size != msc_update_entry->file_size) {
|
||||
log_e("OTA SIZE MISMATCH %u != %u", ota_size, msc_update_entry->file_size);
|
||||
msc_update_error();
|
||||
return;
|
||||
}
|
||||
if (!ota_size || esp_ota_set_boot_partition(msc_ota_partition) != ESP_OK) {
|
||||
log_e("ENABLING OTA PARTITION FAILED");
|
||||
msc_update_error();
|
||||
return;
|
||||
}
|
||||
arduino_firmware_msc_event_data_t p;
|
||||
p.end.size = msc_update_entry->file_size;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_END_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
static int32_t msc_write(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
|
||||
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
|
||||
if (lba < fw_start_sector) {
|
||||
//write to sectors that are in RAM
|
||||
memcpy(msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize);
|
||||
if (msc_ota_partition && lba == (fw_start_sector - 1)) {
|
||||
//monitor the root folder table
|
||||
if (msc_update_state <= MSC_UPDATE_RUNNING) {
|
||||
fat_dir_entry_t *update_entry = msc_update_find_new_bin();
|
||||
if (update_entry) {
|
||||
if (msc_update_entry) {
|
||||
log_v("REPLACING ENTRY");
|
||||
} else {
|
||||
log_v("ASSIGNING ENTRY");
|
||||
}
|
||||
if (msc_update_state <= MSC_UPDATE_STARTING) {
|
||||
msc_update_state = MSC_UPDATE_STARTING;
|
||||
msc_update_bytes_written = 0;
|
||||
msc_update_start_sector = 0;
|
||||
}
|
||||
msc_update_entry = update_entry;
|
||||
} else if (msc_update_state == MSC_UPDATE_RUNNING) {
|
||||
if (!msc_update_entry && msc_update_start_sector) {
|
||||
msc_update_entry = msc_update_find_bin(msc_update_start_sector);
|
||||
}
|
||||
if (msc_update_entry && msc_update_bytes_written >= msc_update_entry->file_size) {
|
||||
msc_update_end();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (msc_ota_partition && lba >= msc_update_start_sector) {
|
||||
//handle writes to the region where the new firmware will be uploaded
|
||||
arduino_firmware_msc_event_data_t p;
|
||||
if (msc_update_state <= MSC_UPDATE_STARTING && buffer[0] == 0xE9) {
|
||||
msc_update_state = MSC_UPDATE_RUNNING;
|
||||
msc_update_start_sector = lba;
|
||||
msc_update_bytes_written = 0;
|
||||
log_d("UPDATE_START: %u (0x%02X)", lba, lba - msc_boot->sectors_per_alloc_table);
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_START_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
if (msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK) {
|
||||
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
|
||||
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
|
||||
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
|
||||
p.write.size = bufsize;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
} else {
|
||||
msc_update_error();
|
||||
return 0;
|
||||
}
|
||||
} else if (msc_update_state == MSC_UPDATE_RUNNING) {
|
||||
if (msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written < msc_update_entry->file_size
|
||||
&& (msc_update_bytes_written + bufsize) >= msc_update_entry->file_size) {
|
||||
bufsize = msc_update_entry->file_size - msc_update_bytes_written;
|
||||
}
|
||||
if (msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK) {
|
||||
log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
|
||||
msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
|
||||
p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
|
||||
p.write.size = bufsize;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
if (msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written >= msc_update_entry->file_size) {
|
||||
msc_update_end();
|
||||
}
|
||||
} else {
|
||||
msc_update_error();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
static int32_t msc_read(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
|
||||
//log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
|
||||
if (lba < fw_start_sector) {
|
||||
memcpy(buffer, msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize);
|
||||
} else if (msc_run_partition && lba < fw_end_sector) {
|
||||
//read the currently running firmware
|
||||
if (esp_partition_read(msc_run_partition, ((lba - fw_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) != ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
memset(buffer, 0, bufsize);
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
static bool msc_start_stop(uint8_t power_condition, bool start, bool load_eject) {
|
||||
//log_d("power: %u, start: %u, eject: %u", power_condition, start, load_eject);
|
||||
arduino_firmware_msc_event_data_t p;
|
||||
p.power.power_condition = power_condition;
|
||||
p.power.start = start;
|
||||
p.power.load_eject = load_eject;
|
||||
arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_POWER_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
|
||||
return true;
|
||||
}
|
||||
|
||||
static volatile TaskHandle_t msc_task_handle = NULL;
|
||||
static void msc_task(void *pvParameters) {
|
||||
for (;;) {
|
||||
if (msc_update_state == MSC_UPDATE_END) {
|
||||
delay(100);
|
||||
esp_restart();
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
msc_task_handle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
FirmwareMSC::FirmwareMSC() : msc() {}
|
||||
|
||||
FirmwareMSC::~FirmwareMSC() {
|
||||
end();
|
||||
}
|
||||
|
||||
bool FirmwareMSC::begin() {
|
||||
if (msc_ram_disk) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!msc_update_setup_disk(USB_FW_MSC_VOLUME_NAME, USB_FW_MSC_SERIAL_NUMBER)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!msc_task_handle) {
|
||||
xTaskCreateUniversal(msc_task, "msc_disk", 1024, NULL, 2, (TaskHandle_t *)&msc_task_handle, 0);
|
||||
if (!msc_task_handle) {
|
||||
msc_update_delete_disk();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
msc.vendorID(USB_FW_MSC_VENDOR_ID);
|
||||
msc.productID(USB_FW_MSC_PRODUCT_ID);
|
||||
msc.productRevision(USB_FW_MSC_PRODUCT_REVISION);
|
||||
msc.onStartStop(msc_start_stop);
|
||||
msc.onRead(msc_read);
|
||||
msc.onWrite(msc_write);
|
||||
msc.mediaPresent(true);
|
||||
msc.begin(msc_boot->fat12_sector_num, DISK_SECTOR_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FirmwareMSC::end() {
|
||||
msc.end();
|
||||
if (msc_task_handle) {
|
||||
vTaskDelete(msc_task_handle);
|
||||
msc_task_handle = NULL;
|
||||
}
|
||||
msc_update_delete_disk();
|
||||
}
|
||||
|
||||
void FirmwareMSC::onEvent(esp_event_handler_t callback) {
|
||||
onEvent(ARDUINO_FIRMWARE_MSC_ANY_EVENT, callback);
|
||||
}
|
||||
void FirmwareMSC::onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback) {
|
||||
arduino_usb_event_handler_register_with(ARDUINO_FIRMWARE_MSC_EVENTS, event, callback, this);
|
||||
}
|
||||
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
FirmwareMSC MSC_Update;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_USB_MSC_ENABLED */
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include "USBMSC.h"
|
||||
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
|
||||
#include "esp_event.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
ARDUINO_FIRMWARE_MSC_ANY_EVENT = ESP_EVENT_ANY_ID,
|
||||
ARDUINO_FIRMWARE_MSC_START_EVENT = 0,
|
||||
ARDUINO_FIRMWARE_MSC_WRITE_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_END_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_ERROR_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_POWER_EVENT,
|
||||
ARDUINO_FIRMWARE_MSC_MAX_EVENT,
|
||||
} arduino_firmware_msc_event_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
size_t offset;
|
||||
size_t size;
|
||||
} write;
|
||||
struct {
|
||||
uint8_t power_condition;
|
||||
bool start;
|
||||
bool load_eject;
|
||||
} power;
|
||||
struct {
|
||||
size_t size;
|
||||
} end;
|
||||
struct {
|
||||
size_t size;
|
||||
} error;
|
||||
} arduino_firmware_msc_event_data_t;
|
||||
|
||||
class FirmwareMSC {
|
||||
private:
|
||||
USBMSC msc;
|
||||
|
||||
public:
|
||||
FirmwareMSC();
|
||||
~FirmwareMSC();
|
||||
bool begin();
|
||||
void end();
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
void onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback);
|
||||
};
|
||||
|
||||
#if ARDUINO_USB_MSC_ON_BOOT
|
||||
extern FirmwareMSC MSC_Update;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* FunctionalInterrupt.cpp
|
||||
*
|
||||
* Created on: 8 jul. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
|
||||
#include "FunctionalInterrupt.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void *);
|
||||
|
||||
extern "C" {
|
||||
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void *arg, int intr_type, bool functional);
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR interruptFunctional(void *arg) {
|
||||
InterruptArgStructure *localArg = (InterruptArgStructure *)arg;
|
||||
if (localArg->interruptFunction) {
|
||||
localArg->interruptFunction();
|
||||
}
|
||||
}
|
||||
|
||||
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode) {
|
||||
// use the local interrupt routine which takes the ArgStructure as argument
|
||||
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void cleanupFunctional(void *arg) {
|
||||
delete (InterruptArgStructure *)arg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* FunctionalInterrupt.h
|
||||
*
|
||||
* Created on: 8 jul. 2018
|
||||
* Author: Herman
|
||||
*/
|
||||
|
||||
#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||
#define CORE_CORE_FUNCTIONALINTERRUPT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
|
||||
struct InterruptArgStructure {
|
||||
std::function<void(void)> interruptFunction;
|
||||
};
|
||||
|
||||
// The extra set of parentheses here prevents macros defined
|
||||
// in io_pin_remap.h from applying to this declaration.
|
||||
void(attachInterrupt)(uint8_t pin, std::function<void(void)> intRoutine, int mode);
|
||||
|
||||
#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp32 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "HEXBuilder.h"
|
||||
#include <ctype.h>
|
||||
|
||||
static uint8_t hex_char_to_byte(uint8_t c) {
|
||||
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa))
|
||||
: (c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA))
|
||||
: (c >= '0' && c <= '9') ? (c - (uint8_t)'0')
|
||||
: 0x10; // unknown char is 16
|
||||
}
|
||||
|
||||
bool HEXBuilder::isHexString(const char *str, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (isxdigit(str[i]) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HEXBuilder::isHexString(String str) {
|
||||
return isHexString(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
size_t HEXBuilder::hex2bytes(unsigned char *out, size_t maxlen, String &in) {
|
||||
return hex2bytes(out, maxlen, in.c_str());
|
||||
}
|
||||
|
||||
size_t HEXBuilder::hex2bytes(unsigned char *out, size_t maxlen, const char *in) {
|
||||
size_t len = 0;
|
||||
for (; *in; in++) {
|
||||
uint8_t c = hex_char_to_byte(*in);
|
||||
// Silently skip anything unknown.
|
||||
if (c > 15) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len & 1) {
|
||||
if (len / 2 < maxlen) {
|
||||
out[len / 2] |= c;
|
||||
}
|
||||
} else {
|
||||
if (len / 2 < maxlen) {
|
||||
out[len / 2] = c << 4;
|
||||
}
|
||||
}
|
||||
len++;
|
||||
}
|
||||
return (len + 1) / 2;
|
||||
}
|
||||
|
||||
size_t HEXBuilder::bytes2hex(char *out, size_t maxlen, const unsigned char *in, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (i * 2 + 1 < maxlen) {
|
||||
sprintf(out + (i * 2), "%02x", in[i]);
|
||||
}
|
||||
}
|
||||
return len * 2 + 1;
|
||||
}
|
||||
|
||||
String HEXBuilder::bytes2hex(const unsigned char *in, size_t len) {
|
||||
size_t maxlen = len * 2 + 1;
|
||||
char *out = (char *)malloc(maxlen);
|
||||
if (!out) {
|
||||
return String();
|
||||
}
|
||||
bytes2hex(out, maxlen, in, len);
|
||||
String ret = String(out);
|
||||
free(out);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp32 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef HEXBuilder_h
|
||||
#define HEXBuilder_h
|
||||
|
||||
#include <WString.h>
|
||||
#include <Stream.h>
|
||||
|
||||
// Basic hex/byte conversion class to be used by hash builders
|
||||
|
||||
class HEXBuilder {
|
||||
public:
|
||||
static size_t hex2bytes(unsigned char *out, size_t maxlen, String &in);
|
||||
static size_t hex2bytes(unsigned char *out, size_t maxlen, const char *in);
|
||||
|
||||
static String bytes2hex(const unsigned char *in, size_t len);
|
||||
static size_t bytes2hex(char *out, size_t maxlen, const unsigned char *in, size_t len);
|
||||
|
||||
static bool isHexString(const char *str, size_t len);
|
||||
static bool isHexString(String str);
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,622 @@
|
||||
// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "USB.h"
|
||||
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
||||
|
||||
#include "Arduino.h" // defines ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE and ARDUINO_SERIAL_EVENT_TASK_PRIORITY
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "HWCDC.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "soc/usb_serial_jtag_struct.h"
|
||||
#pragma GCC diagnostic ignored "-Wvolatile"
|
||||
#include "hal/usb_serial_jtag_ll.h"
|
||||
#pragma GCC diagnostic warning "-Wvolatile"
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ARDUINO_HW_CDC_EVENTS);
|
||||
|
||||
static RingbufHandle_t tx_ring_buf = NULL;
|
||||
static QueueHandle_t rx_queue = NULL;
|
||||
static uint8_t rx_data_buf[64] = {0};
|
||||
static intr_handle_t intr_handle = NULL;
|
||||
static SemaphoreHandle_t tx_lock = NULL;
|
||||
static volatile bool connected = false;
|
||||
|
||||
// SOF in ISR causes problems for uploading firmware
|
||||
//static volatile unsigned long lastSOF_ms;
|
||||
//static volatile uint8_t SOF_TIMEOUT;
|
||||
|
||||
// timeout has no effect when USB CDC is unplugged
|
||||
static uint32_t tx_timeout_ms = 100;
|
||||
|
||||
static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL;
|
||||
|
||||
static esp_err_t
|
||||
arduino_hw_cdc_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, BaseType_t *task_unblocked) {
|
||||
if (arduino_hw_cdc_event_loop_handle == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_event_isr_post_to(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_data, event_data_size, task_unblocked);
|
||||
}
|
||||
|
||||
static esp_err_t
|
||||
arduino_hw_cdc_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg) {
|
||||
if (!arduino_hw_cdc_event_loop_handle) {
|
||||
esp_event_loop_args_t event_task_args = {
|
||||
.queue_size = 5,
|
||||
.task_name = "arduino_hw_cdc_events",
|
||||
.task_priority = ARDUINO_SERIAL_EVENT_TASK_PRIORITY,
|
||||
.task_stack_size = ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE,
|
||||
.task_core_id = tskNO_AFFINITY
|
||||
};
|
||||
if (esp_event_loop_create(&event_task_args, &arduino_hw_cdc_event_loop_handle) != ESP_OK) {
|
||||
log_e("esp_event_loop_create failed");
|
||||
}
|
||||
}
|
||||
if (arduino_hw_cdc_event_loop_handle == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_event_handler_register_with(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_handler, event_handler_arg);
|
||||
}
|
||||
|
||||
static void hw_cdc_isr_handler(void *arg) {
|
||||
portBASE_TYPE xTaskWoken = 0;
|
||||
uint32_t usbjtag_intr_status = 0;
|
||||
arduino_hw_cdc_event_data_t event = {0};
|
||||
usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask();
|
||||
|
||||
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) {
|
||||
// Interrupt tells us the host picked up the data we sent.
|
||||
if (!HWCDC::isPlugged()) {
|
||||
connected = false;
|
||||
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
// USB is unplugged, nothing to be done here
|
||||
return;
|
||||
} else {
|
||||
connected = true;
|
||||
}
|
||||
if (tx_ring_buf != NULL && usb_serial_jtag_ll_txfifo_writable() == 1) {
|
||||
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
|
||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
size_t queued_size = 0;
|
||||
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64);
|
||||
// If the hardware fifo is available, write in it. Otherwise, do nothing.
|
||||
if (queued_buff != NULL) { //Although tx_queued_bytes may be larger than 0. We may have interrupt before xRingbufferSend() was called.
|
||||
//Copy the queued buffer into the TX FIFO
|
||||
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size);
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken);
|
||||
if (connected) {
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
//send event?
|
||||
//ets_printf("TX:%u\n", queued_size);
|
||||
event.tx.len = queued_size;
|
||||
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_TX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
|
||||
}
|
||||
} else {
|
||||
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT) {
|
||||
// read rx buffer(max length is 64), and send available data to ringbuffer.
|
||||
// Ensure the rx buffer size is larger than RX_MAX_SIZE.
|
||||
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
|
||||
uint32_t rx_fifo_len = usb_serial_jtag_ll_read_rxfifo(rx_data_buf, 64);
|
||||
uint32_t i = 0;
|
||||
for (i = 0; i < rx_fifo_len; i++) {
|
||||
if (rx_queue == NULL || !xQueueSendFromISR(rx_queue, rx_data_buf + i, &xTaskWoken)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
event.rx.len = i;
|
||||
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_RX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
|
||||
connected = true;
|
||||
}
|
||||
|
||||
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
|
||||
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
|
||||
arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_BUS_RESET_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken);
|
||||
connected = false;
|
||||
}
|
||||
|
||||
// SOF ISR is causing esptool to be unable to upload firmware to the board
|
||||
// if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SOF) {
|
||||
// usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SOF);
|
||||
// lastSOF_ms = millis();
|
||||
// }
|
||||
|
||||
if (xTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
// Moved to header file as inline function. Kept just as future reference.
|
||||
//inline bool HWCDC::isPlugged(void) {
|
||||
// SOF ISR is causing esptool to be unable to upload firmware to the board
|
||||
// Timer test for SOF seems to work when uploading firmware
|
||||
// return usb_serial_jtag_is_connected();//(lastSOF_ms + SOF_TIMEOUT) >= millis();
|
||||
//}
|
||||
|
||||
bool HWCDC::isCDC_Connected() {
|
||||
static bool running = false;
|
||||
|
||||
// USB may be unplugged
|
||||
if (!isPlugged()) {
|
||||
connected = false;
|
||||
running = false;
|
||||
// SOF in ISR causes problems for uploading firmware
|
||||
//SOF_TIMEOUT = 5; // SOF timeout when unplugged
|
||||
return false;
|
||||
}
|
||||
//else {
|
||||
// SOF_TIMEOUT = 50; // SOF timeout when plugged
|
||||
//}
|
||||
|
||||
if (connected) {
|
||||
running = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (running == false && !connected) { // enables it only once!
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
|
||||
// this will feed CDC TX FIFO to trigger IN_EMPTY
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
running = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void flushTXBuffer(const uint8_t *buffer, size_t size) {
|
||||
if (!tx_ring_buf) {
|
||||
return;
|
||||
}
|
||||
UBaseType_t uxItemsWaiting = 0;
|
||||
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
|
||||
size_t freeSpace = xRingbufferGetCurFreeSize(tx_ring_buf);
|
||||
size_t ringbufferLength = freeSpace + uxItemsWaiting;
|
||||
|
||||
if (buffer == NULL) {
|
||||
// just flush the whole ring buffer and exit - used by HWCDC::flush()
|
||||
size_t queued_size = 0;
|
||||
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, ringbufferLength);
|
||||
if (queued_size && queued_buff != NULL) {
|
||||
vRingbufferReturnItem(tx_ring_buf, (void *)queued_buff);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (size == 0) {
|
||||
return; // nothing to do
|
||||
}
|
||||
if (freeSpace >= size) {
|
||||
// there is enough space, just add the data to the ring buffer
|
||||
if (xRingbufferSend(tx_ring_buf, (void *)buffer, size, 0) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// how many byte should be flushed to make space for the new data
|
||||
size_t to_flush = size - freeSpace;
|
||||
if (to_flush > ringbufferLength) {
|
||||
to_flush = ringbufferLength;
|
||||
}
|
||||
size_t queued_size = 0;
|
||||
uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, to_flush);
|
||||
if (queued_size && queued_buff != NULL) {
|
||||
vRingbufferReturnItem(tx_ring_buf, (void *)queued_buff);
|
||||
}
|
||||
// now add the new data that fits into the ring buffer
|
||||
uint8_t *bptr = (uint8_t *)buffer;
|
||||
if (size >= ringbufferLength) {
|
||||
size = ringbufferLength;
|
||||
bptr = (uint8_t *)buffer + (size - ringbufferLength);
|
||||
}
|
||||
if (xRingbufferSend(tx_ring_buf, (void *)bptr, size, 0) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// flushes CDC FIFO
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
}
|
||||
|
||||
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
|
||||
if (tx_ring_buf == NULL) {
|
||||
return;
|
||||
}
|
||||
if (!HWCDC::isConnected()) {
|
||||
// just pop/push RingBuffer and apply FIFO policy
|
||||
flushTXBuffer((const uint8_t *)&c, 1);
|
||||
return;
|
||||
}
|
||||
if (xPortInIsrContext()) {
|
||||
xRingbufferSendFromISR(tx_ring_buf, (void *)(&c), 1, NULL);
|
||||
} else {
|
||||
xRingbufferSend(tx_ring_buf, (void *)(&c), 1, tx_timeout_ms / portTICK_PERIOD_MS);
|
||||
}
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
|
||||
HWCDC::HWCDC() {
|
||||
// SOF in ISR causes problems for uploading firmware
|
||||
// lastSOF_ms = 0;
|
||||
// SOF_TIMEOUT = 5;
|
||||
}
|
||||
|
||||
HWCDC::~HWCDC() {
|
||||
end();
|
||||
}
|
||||
|
||||
// It should return <true> just when USB is plugged and CDC is connected.
|
||||
HWCDC::operator bool() const {
|
||||
return HWCDC::isCDC_Connected();
|
||||
}
|
||||
|
||||
void HWCDC::onEvent(esp_event_handler_t callback) {
|
||||
onEvent(ARDUINO_HW_CDC_ANY_EVENT, callback);
|
||||
}
|
||||
|
||||
void HWCDC::onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback) {
|
||||
arduino_hw_cdc_event_handler_register_with(ARDUINO_HW_CDC_EVENTS, event, callback, this);
|
||||
}
|
||||
|
||||
bool HWCDC::deinit(void *busptr) {
|
||||
// avoid any recursion issue with Peripheral Manager perimanSetPinBus() call
|
||||
static bool running = false;
|
||||
if (running) {
|
||||
return true;
|
||||
}
|
||||
running = true;
|
||||
// Setting USB D+ D- pins
|
||||
bool retCode = true;
|
||||
retCode &= perimanClearPinBus(USB_INT_PHY0_DM_GPIO_NUM);
|
||||
retCode &= perimanClearPinBus(USB_INT_PHY0_DP_GPIO_NUM);
|
||||
if (retCode) {
|
||||
// Force the host to re-enumerate (BUS_RESET)
|
||||
pinMode(USB_INT_PHY0_DM_GPIO_NUM, OUTPUT_OPEN_DRAIN);
|
||||
pinMode(USB_INT_PHY0_DP_GPIO_NUM, OUTPUT_OPEN_DRAIN);
|
||||
digitalWrite(USB_INT_PHY0_DM_GPIO_NUM, LOW);
|
||||
digitalWrite(USB_INT_PHY0_DP_GPIO_NUM, LOW);
|
||||
}
|
||||
// release the flag
|
||||
running = false;
|
||||
return retCode;
|
||||
}
|
||||
|
||||
void HWCDC::begin(unsigned long baud) {
|
||||
if (tx_lock == NULL) {
|
||||
tx_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
//RX Buffer default has 256 bytes if not preset
|
||||
if (rx_queue == NULL) {
|
||||
if (!setRxBufferSize(256)) {
|
||||
log_e("HW CDC RX Buffer error");
|
||||
}
|
||||
}
|
||||
//TX Buffer default has 256 bytes if not preset
|
||||
if (tx_ring_buf == NULL) {
|
||||
if (!setTxBufferSize(256)) {
|
||||
log_e("HW CDC TX Buffer error");
|
||||
}
|
||||
}
|
||||
|
||||
// the HW Serial pins needs to be first deinited in order to allow `if(Serial)` to work :-(
|
||||
// But this is also causing terminal to hang, so they are disabled
|
||||
// deinit(NULL);
|
||||
// delay(10); // USB Host has to enumerate it again
|
||||
|
||||
// Peripheral Manager setting for USB D+ D- pins
|
||||
uint8_t pin = USB_INT_PHY0_DM_GPIO_NUM;
|
||||
if (perimanGetBusDeinit(ESP32_BUS_TYPE_USB_DM) == NULL) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DM, HWCDC::deinit);
|
||||
}
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_USB_DM, (void *)this, -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
pin = USB_INT_PHY0_DP_GPIO_NUM;
|
||||
if (perimanGetBusDeinit(ESP32_BUS_TYPE_USB_DP) == NULL) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DP, HWCDC::deinit);
|
||||
}
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_USB_DP, (void *)this, -1, -1)) {
|
||||
goto err;
|
||||
}
|
||||
// Configure PHY
|
||||
// USB_Serial_JTAG use internal PHY
|
||||
USB_SERIAL_JTAG.conf0.phy_sel = 0;
|
||||
// Disable software control USB D+ D- pullup pulldown (Device FS: dp_pullup = 1)
|
||||
USB_SERIAL_JTAG.conf0.pad_pull_override = 0;
|
||||
// Enable USB D+ pullup
|
||||
USB_SERIAL_JTAG.conf0.dp_pullup = 1;
|
||||
// Enable USB pad function
|
||||
USB_SERIAL_JTAG.conf0.usb_pad_enable = 1;
|
||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET);
|
||||
// SOF ISR is causing esptool to be unable to upload firmware to the board
|
||||
// usb_serial_jtag_ll_ena_intr_mask(
|
||||
// USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY | USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT | USB_SERIAL_JTAG_INTR_BUS_RESET | USB_SERIAL_JTAG_INTR_SOF
|
||||
// );
|
||||
if (!intr_handle && esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_isr_handler, NULL, &intr_handle) != ESP_OK) {
|
||||
isr_log_e("HW USB CDC failed to init interrupts");
|
||||
end();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
err:
|
||||
log_e("Serial JTAG Pin %u can't be set into Peripheral Manager.", pin);
|
||||
end();
|
||||
}
|
||||
|
||||
void HWCDC::end() {
|
||||
//Disable/clear/free tx/rx interrupt.
|
||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
|
||||
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
|
||||
esp_intr_free(intr_handle);
|
||||
intr_handle = NULL;
|
||||
if (tx_lock != NULL) {
|
||||
vSemaphoreDelete(tx_lock);
|
||||
tx_lock = NULL;
|
||||
}
|
||||
setRxBufferSize(0);
|
||||
setTxBufferSize(0);
|
||||
if (arduino_hw_cdc_event_loop_handle) {
|
||||
esp_event_loop_delete(arduino_hw_cdc_event_loop_handle);
|
||||
arduino_hw_cdc_event_loop_handle = NULL;
|
||||
}
|
||||
HWCDC::deinit(this);
|
||||
setDebugOutput(false);
|
||||
connected = false;
|
||||
}
|
||||
|
||||
void HWCDC::setTxTimeoutMs(uint32_t timeout) {
|
||||
tx_timeout_ms = timeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* WRITING
|
||||
*/
|
||||
|
||||
size_t HWCDC::setTxBufferSize(size_t tx_queue_len) {
|
||||
if (tx_ring_buf) {
|
||||
vRingbufferDelete(tx_ring_buf);
|
||||
tx_ring_buf = NULL;
|
||||
}
|
||||
if (!tx_queue_len) {
|
||||
return 0;
|
||||
}
|
||||
tx_ring_buf = xRingbufferCreate(tx_queue_len, RINGBUF_TYPE_BYTEBUF);
|
||||
if (!tx_ring_buf) {
|
||||
return 0;
|
||||
}
|
||||
return tx_queue_len;
|
||||
}
|
||||
|
||||
int HWCDC::availableForWrite(void) {
|
||||
if (tx_ring_buf == NULL || tx_lock == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
|
||||
return 0;
|
||||
}
|
||||
size_t a = xRingbufferGetCurFreeSize(tx_ring_buf);
|
||||
xSemaphoreGive(tx_lock);
|
||||
return a;
|
||||
}
|
||||
|
||||
size_t HWCDC::write(const uint8_t *buffer, size_t size) {
|
||||
if (buffer == NULL || size == 0 || tx_ring_buf == NULL || tx_lock == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
|
||||
return 0;
|
||||
}
|
||||
if (!isCDC_Connected()) {
|
||||
// just pop/push RingBuffer and apply FIFO policy
|
||||
flushTXBuffer(buffer, size);
|
||||
} else {
|
||||
size_t space = xRingbufferGetCurFreeSize(tx_ring_buf);
|
||||
size_t to_send = size, so_far = 0;
|
||||
|
||||
if (space > size) {
|
||||
space = size;
|
||||
}
|
||||
// Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR.
|
||||
if (space > 0 && xRingbufferSend(tx_ring_buf, (void *)(buffer), space, 0) != pdTRUE) {
|
||||
size = 0;
|
||||
} else {
|
||||
to_send -= space;
|
||||
so_far += space;
|
||||
// Now trigger the ISR to read data from the ring buffer.
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
if (connected) {
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
// tracks CDC transmission progress to avoid hanging if CDC is unplugged while still sending data
|
||||
size_t last_toSend = to_send;
|
||||
uint32_t tries = tx_timeout_ms; // waits 1ms per sending data attempt, in case CDC is unplugged
|
||||
while (connected && to_send) {
|
||||
space = xRingbufferGetCurFreeSize(tx_ring_buf);
|
||||
if (space > to_send) {
|
||||
space = to_send;
|
||||
}
|
||||
// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
|
||||
if (xRingbufferSend(tx_ring_buf, (void *)(buffer + so_far), space, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE) {
|
||||
size = so_far;
|
||||
log_w("write failed due to ring buffer full - timeout");
|
||||
break;
|
||||
}
|
||||
so_far += space;
|
||||
to_send -= space;
|
||||
// Now trigger the ISR to read data from the ring buffer.
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
if (connected) {
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
if (last_toSend == to_send) {
|
||||
// no progress in sending data... USB CDC is probably unplugged
|
||||
tries--;
|
||||
delay(1);
|
||||
} else {
|
||||
last_toSend = to_send;
|
||||
tries = tx_timeout_ms; // reset the timeout
|
||||
}
|
||||
if (tries == 0) { // CDC isn't connected anymore...
|
||||
size = so_far;
|
||||
log_w("write failed due to waiting USB Host - timeout");
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// CDC was disconnected while sending data ==> flush the TX buffer keeping the last data
|
||||
if (to_send && !usb_serial_jtag_ll_txfifo_writable()) {
|
||||
connected = false;
|
||||
flushTXBuffer(buffer + so_far, to_send);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(tx_lock);
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t HWCDC::write(uint8_t c) {
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
void HWCDC::flush(void) {
|
||||
if (tx_ring_buf == NULL || tx_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
|
||||
return;
|
||||
}
|
||||
if (!isCDC_Connected()) {
|
||||
flushTXBuffer(NULL, 0);
|
||||
} else {
|
||||
UBaseType_t uxItemsWaiting = 0;
|
||||
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
|
||||
if (uxItemsWaiting) {
|
||||
// Now trigger the ISR to read data from the ring buffer.
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
if (connected) {
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
}
|
||||
uint32_t tries = tx_timeout_ms; // waits 1ms per ISR sending data attempt, in case CDC is unplugged
|
||||
while (connected && tries && uxItemsWaiting) {
|
||||
delay(1);
|
||||
UBaseType_t lastUxItemsWaiting = uxItemsWaiting;
|
||||
vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting);
|
||||
if (lastUxItemsWaiting == uxItemsWaiting) {
|
||||
tries--;
|
||||
}
|
||||
if (connected) {
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
|
||||
}
|
||||
}
|
||||
if (tries == 0) { // CDC isn't connected anymore...
|
||||
connected = false;
|
||||
flushTXBuffer(NULL, 0); // flushes all TX Buffer
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(tx_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* READING
|
||||
*/
|
||||
|
||||
size_t HWCDC::setRxBufferSize(size_t rx_queue_len) {
|
||||
if (rx_queue) {
|
||||
vQueueDelete(rx_queue);
|
||||
rx_queue = NULL;
|
||||
}
|
||||
if (!rx_queue_len) {
|
||||
return 0;
|
||||
}
|
||||
rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
|
||||
if (!rx_queue) {
|
||||
return 0;
|
||||
}
|
||||
return rx_queue_len;
|
||||
}
|
||||
|
||||
int HWCDC::available(void) {
|
||||
if (rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return uxQueueMessagesWaiting(rx_queue);
|
||||
}
|
||||
|
||||
int HWCDC::peek(void) {
|
||||
if (rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t c;
|
||||
if (xQueuePeek(rx_queue, &c, 0)) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int HWCDC::read(void) {
|
||||
if (rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t c = 0;
|
||||
if (xQueueReceive(rx_queue, &c, 0)) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t HWCDC::read(uint8_t *buffer, size_t size) {
|
||||
if (rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t c = 0;
|
||||
size_t count = 0;
|
||||
while (count < size && xQueueReceive(rx_queue, &c, 0)) {
|
||||
buffer[count++] = c;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* DEBUG
|
||||
*/
|
||||
|
||||
void HWCDC::setDebugOutput(bool en) {
|
||||
if (en) {
|
||||
uartSetDebug(NULL);
|
||||
ets_install_putc2((void (*)(char)) & cdc0_write_char);
|
||||
} else {
|
||||
ets_install_putc2(NULL);
|
||||
}
|
||||
ets_install_putc1(NULL); // closes UART log output
|
||||
}
|
||||
|
||||
#if ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Hardware JTAG CDC selected
|
||||
// USBSerial is always available to be used
|
||||
HWCDC HWCDCSerial;
|
||||
#endif
|
||||
|
||||
#endif /* SOC_USB_SERIAL_JTAG_SUPPORTED */
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "esp_event.h"
|
||||
#include "Stream.h"
|
||||
#include "driver/usb_serial_jtag.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
ARDUINO_HW_CDC_ANY_EVENT = ESP_EVENT_ANY_ID,
|
||||
ARDUINO_HW_CDC_CONNECTED_EVENT = 0,
|
||||
ARDUINO_HW_CDC_BUS_RESET_EVENT,
|
||||
ARDUINO_HW_CDC_RX_EVENT,
|
||||
ARDUINO_HW_CDC_TX_EVENT,
|
||||
ARDUINO_HW_CDC_MAX_EVENT,
|
||||
} arduino_hw_cdc_event_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
size_t len;
|
||||
} rx;
|
||||
struct {
|
||||
size_t len;
|
||||
} tx;
|
||||
} arduino_hw_cdc_event_data_t;
|
||||
|
||||
class HWCDC : public Stream {
|
||||
private:
|
||||
static bool deinit(void *busptr);
|
||||
static bool isCDC_Connected();
|
||||
|
||||
public:
|
||||
HWCDC();
|
||||
~HWCDC();
|
||||
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
void onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback);
|
||||
|
||||
size_t setRxBufferSize(size_t);
|
||||
size_t setTxBufferSize(size_t);
|
||||
void setTxTimeoutMs(uint32_t timeout);
|
||||
void begin(unsigned long baud = 0);
|
||||
void end();
|
||||
|
||||
int available(void);
|
||||
int availableForWrite(void);
|
||||
int peek(void);
|
||||
int read(void);
|
||||
size_t read(uint8_t *buffer, size_t size);
|
||||
size_t write(uint8_t);
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
void flush(void);
|
||||
|
||||
inline static bool isPlugged(void) {
|
||||
// SOF ISR is causing esptool to be unable to upload firmware to the board
|
||||
// Using IDF 5.1 helper function because it is based on Timer check instead of ISR
|
||||
return usb_serial_jtag_is_connected();
|
||||
}
|
||||
|
||||
inline static bool isConnected(void) {
|
||||
return isCDC_Connected();
|
||||
}
|
||||
|
||||
inline size_t read(char *buffer, size_t size) {
|
||||
return read((uint8_t *)buffer, size);
|
||||
}
|
||||
inline size_t write(const char *buffer, size_t size) {
|
||||
return write((uint8_t *)buffer, size);
|
||||
}
|
||||
inline size_t write(const char *s) {
|
||||
return write((uint8_t *)s, strlen(s));
|
||||
}
|
||||
inline size_t write(unsigned long n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(long n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(unsigned int n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(int n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
operator bool() const;
|
||||
void setDebugOutput(bool);
|
||||
uint32_t baudRate() {
|
||||
return 115200;
|
||||
}
|
||||
};
|
||||
#if ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Hardware JTAG CDC selected
|
||||
#ifndef HWCDC_SERIAL_IS_DEFINED
|
||||
#define HWCDC_SERIAL_IS_DEFINED 1
|
||||
#endif
|
||||
// HWCDCSerial is always available to be used
|
||||
extern HWCDC HWCDCSerial;
|
||||
#endif
|
||||
|
||||
#endif /* SOC_USB_SERIAL_JTAG_SUPPORTED */
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Stream.h"
|
||||
#include <functional>
|
||||
|
||||
class HardwareI2C : public Stream {
|
||||
public:
|
||||
virtual bool begin() = 0;
|
||||
virtual bool begin(uint8_t address) = 0;
|
||||
virtual bool end() = 0;
|
||||
|
||||
virtual bool setClock(uint32_t freq) = 0;
|
||||
|
||||
virtual void beginTransmission(uint8_t address) = 0;
|
||||
virtual uint8_t endTransmission(bool stopBit) = 0;
|
||||
virtual uint8_t endTransmission(void) = 0;
|
||||
|
||||
virtual size_t requestFrom(uint8_t address, size_t len, bool stopBit) = 0;
|
||||
virtual size_t requestFrom(uint8_t address, size_t len) = 0;
|
||||
|
||||
// Update base class to use std::function
|
||||
virtual void onReceive(const std::function<void(int)> &) = 0;
|
||||
virtual void onRequest(const std::function<void()> &) = 0;
|
||||
};
|
||||
@@ -0,0 +1,696 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctime>
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "io_pin_remap.h"
|
||||
#include "HardwareSerial.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#if (SOC_UART_LP_NUM >= 1)
|
||||
#define UART_HW_FIFO_LEN(uart_num) ((uart_num < SOC_UART_HP_NUM) ? SOC_UART_FIFO_LEN : SOC_LP_UART_FIFO_LEN)
|
||||
#else
|
||||
#define UART_HW_FIFO_LEN(uart_num) SOC_UART_FIFO_LEN
|
||||
#endif
|
||||
|
||||
void serialEvent(void) __attribute__((weak));
|
||||
|
||||
#if SOC_UART_NUM > 1
|
||||
void serialEvent1(void) __attribute__((weak));
|
||||
#endif /* SOC_UART_NUM > 1 */
|
||||
|
||||
#if SOC_UART_NUM > 2
|
||||
void serialEvent2(void) __attribute__((weak));
|
||||
#endif /* SOC_UART_NUM > 2 */
|
||||
|
||||
#if SOC_UART_NUM > 3
|
||||
void serialEvent3(void) __attribute__((weak));
|
||||
#endif /* SOC_UART_NUM > 3 */
|
||||
|
||||
#if SOC_UART_NUM > 4
|
||||
void serialEvent4(void) __attribute__((weak));
|
||||
#endif /* SOC_UART_NUM > 4 */
|
||||
|
||||
#if SOC_UART_NUM > 5
|
||||
void serialEvent5(void) __attribute__((weak));
|
||||
#endif /* SOC_UART_NUM > 5 */
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
// There is always Seria0 for UART0
|
||||
HardwareSerial Serial0(0);
|
||||
#if SOC_UART_NUM > 1
|
||||
HardwareSerial Serial1(1);
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
HardwareSerial Serial2(2);
|
||||
#endif
|
||||
#if SOC_UART_NUM > 3
|
||||
HardwareSerial Serial3(3);
|
||||
#endif
|
||||
#if SOC_UART_NUM > 4
|
||||
HardwareSerial Serial4(4);
|
||||
#endif
|
||||
#if (SOC_UART_NUM > 5)
|
||||
HardwareSerial Serial5(5);
|
||||
#endif
|
||||
#if HWCDC_SERIAL_IS_DEFINED == 1 // Hardware JTAG CDC Event
|
||||
extern void HWCDCSerialEvent(void) __attribute__((weak));
|
||||
#endif
|
||||
|
||||
// C-callable helper used by HAL when pins are detached and the high-level
|
||||
// HardwareSerial instance must be finalized.
|
||||
extern "C" void hal_uart_notify_pins_detached(int uart_num) {
|
||||
log_d("hal_uart_notify_pins_detached: Notifying HardwareSerial for UART%d", uart_num);
|
||||
switch (uart_num) {
|
||||
case 0: Serial0.end(); break;
|
||||
#if SOC_UART_NUM > 1
|
||||
case 1: Serial1.end(); break;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
case 2: Serial2.end(); break;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 3
|
||||
case 3: Serial3.end(); break;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 4
|
||||
case 4: Serial4.end(); break;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 5
|
||||
case 5: Serial5.end(); break;
|
||||
#endif
|
||||
default: log_e("hal_uart_notify_pins_detached: UART%d not handled!", uart_num); break;
|
||||
}
|
||||
}
|
||||
|
||||
#if USB_SERIAL_IS_DEFINED == 1 // Native USB CDC Event
|
||||
// Used by Hardware Serial for USB CDC events
|
||||
extern void USBSerialEvent(void) __attribute__((weak));
|
||||
#endif
|
||||
|
||||
void serialEventRun(void) {
|
||||
#if HWCDC_SERIAL_IS_DEFINED == 1 // Hardware JTAG CDC Event
|
||||
if (HWCDCSerialEvent && HWCDCSerial.available()) {
|
||||
HWCDCSerialEvent();
|
||||
}
|
||||
#endif
|
||||
#if USB_SERIAL_IS_DEFINED == 1 // Native USB CDC Event
|
||||
if (USBSerialEvent && USBSerial.available()) {
|
||||
USBSerialEvent();
|
||||
}
|
||||
#endif
|
||||
// UART0 is default serialEvent()
|
||||
if (serialEvent && Serial0.available()) {
|
||||
serialEvent();
|
||||
}
|
||||
#if SOC_UART_NUM > 1
|
||||
if (serialEvent1 && Serial1.available()) {
|
||||
serialEvent1();
|
||||
}
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
if (serialEvent2 && Serial2.available()) {
|
||||
serialEvent2();
|
||||
}
|
||||
#endif
|
||||
#if SOC_UART_NUM > 3
|
||||
if (serialEvent3 && Serial3.available()) {
|
||||
serialEvent3();
|
||||
}
|
||||
#endif
|
||||
#if SOC_UART_NUM > 4
|
||||
if (serialEvent4 && Serial4.available()) {
|
||||
serialEvent4();
|
||||
}
|
||||
#endif
|
||||
#if SOC_UART_NUM > 5
|
||||
if (serialEvent5 && Serial5.available()) {
|
||||
serialEvent5();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
#define HSERIAL_MUTEX_LOCK() \
|
||||
do { \
|
||||
} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS)
|
||||
#define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock)
|
||||
#else
|
||||
#define HSERIAL_MUTEX_LOCK()
|
||||
#define HSERIAL_MUTEX_UNLOCK()
|
||||
#endif
|
||||
|
||||
HardwareSerial::HardwareSerial(uint8_t uart_nr)
|
||||
: _uart_nr(uart_nr), _uart(NULL), _rxBufferSize(256), _txBufferSize(0), _onReceiveCB(NULL), _onReceiveErrorCB(NULL), _onReceiveTimeout(false), _rxTimeout(1),
|
||||
_rxFIFOFull(0), _eventTask(NULL)
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
,
|
||||
_lock(NULL)
|
||||
#endif
|
||||
{
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (_lock == NULL) {
|
||||
_lock = xSemaphoreCreateMutex();
|
||||
if (_lock == NULL) {
|
||||
log_e("xSemaphoreCreateMutex failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HardwareSerial::~HardwareSerial() {
|
||||
end(); // explicit Full UART termination
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (_lock != NULL) {
|
||||
vSemaphoreDelete(_lock);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HardwareSerial::_createEventTask(void *args) {
|
||||
// Creating UART event Task
|
||||
xTaskCreateUniversal(
|
||||
_uartEventTask, "uart_event_task", ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, this, ARDUINO_SERIAL_EVENT_TASK_PRIORITY, &_eventTask,
|
||||
ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
|
||||
);
|
||||
if (_eventTask == NULL) {
|
||||
log_e(" -- UART%d Event Task not Created!", _uart_nr);
|
||||
}
|
||||
}
|
||||
|
||||
void HardwareSerial::_destroyEventTask(void) {
|
||||
if (_eventTask != NULL) {
|
||||
vTaskDelete(_eventTask);
|
||||
_eventTask = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void HardwareSerial::onReceiveError(OnReceiveErrorCb function) {
|
||||
HSERIAL_MUTEX_LOCK();
|
||||
// function may be NULL to cancel onReceive() from its respective task
|
||||
_onReceiveErrorCB = function;
|
||||
// this can be called after Serial.begin(), therefore it shall create the event task
|
||||
if (function != NULL && _uart != NULL && _eventTask == NULL) {
|
||||
_createEventTask(this);
|
||||
}
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout) {
|
||||
HSERIAL_MUTEX_LOCK();
|
||||
// function may be NULL to cancel onReceive() from its respective task
|
||||
_onReceiveCB = function;
|
||||
|
||||
// setting the callback to NULL will just disable it
|
||||
if (_onReceiveCB != NULL) {
|
||||
// When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes
|
||||
_onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false;
|
||||
|
||||
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
|
||||
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
|
||||
// Not valid for the LP UART
|
||||
if (_onReceiveTimeout && _uart_nr < SOC_UART_HP_NUM) {
|
||||
uartSetRxFIFOFull(_uart, 120);
|
||||
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes.");
|
||||
}
|
||||
|
||||
// this method can be called after Serial.begin(), therefore it shall create the event task
|
||||
if (_uart != NULL && _eventTask == NULL) {
|
||||
_createEventTask(this); // Create event task
|
||||
}
|
||||
}
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
// This function allow the user to define how many bytes will trigger an Interrupt that will copy RX FIFO to the internal RX Ringbuffer
|
||||
// ISR will also move data from FIFO to RX Ringbuffer after a RX Timeout defined in HardwareSerial::setRxTimeout(uint8_t symbols_timeout)
|
||||
// A low value of FIFO Full bytes will consume more CPU time within the ISR
|
||||
// A high value of FIFO Full bytes will make the application wait longer to have byte available for the Stkech in a streaming scenario
|
||||
// Both RX FIFO Full and RX Timeout may affect when onReceive() will be called
|
||||
bool HardwareSerial::setRxFIFOFull(uint8_t fifoBytes) {
|
||||
HSERIAL_MUTEX_LOCK();
|
||||
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
|
||||
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
|
||||
// Not valid for the LP UART
|
||||
if (_onReceiveCB != NULL && _onReceiveTimeout && _uart_nr < SOC_UART_HP_NUM) {
|
||||
fifoBytes = 120;
|
||||
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes.");
|
||||
}
|
||||
bool retCode = uartSetRxFIFOFull(_uart, fifoBytes); // Set new timeout
|
||||
if (fifoBytes > 0 && fifoBytes < UART_HW_FIFO_LEN(_uart_nr) - 1) {
|
||||
_rxFIFOFull = fifoBytes;
|
||||
}
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// timeout is calculates in time to receive UART symbols at the UART baudrate.
|
||||
// the estimation is about 11 bits per symbol (SERIAL_8N1)
|
||||
bool HardwareSerial::setRxTimeout(uint8_t symbols_timeout) {
|
||||
HSERIAL_MUTEX_LOCK();
|
||||
|
||||
// Zero disables timeout, thus, onReceive callback will only be called when RX FIFO reaches 120 bytes
|
||||
// Any non-zero value will activate onReceive callback based on UART baudrate with about 11 bits per symbol
|
||||
_rxTimeout = symbols_timeout;
|
||||
if (!symbols_timeout) {
|
||||
_onReceiveTimeout = false; // only when RX timeout is disabled, we also must disable this flag
|
||||
}
|
||||
|
||||
bool retCode = uartSetRxTimeout(_uart, _rxTimeout); // Set new timeout
|
||||
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
return retCode;
|
||||
}
|
||||
|
||||
void HardwareSerial::eventQueueReset() {
|
||||
QueueHandle_t uartEventQueue = NULL;
|
||||
if (_uart == NULL) {
|
||||
return;
|
||||
}
|
||||
uartGetEventQueue(_uart, &uartEventQueue);
|
||||
if (uartEventQueue != NULL) {
|
||||
xQueueReset(uartEventQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void HardwareSerial::_uartEventTask(void *args) {
|
||||
HardwareSerial *uart = (HardwareSerial *)args;
|
||||
uart_event_t event;
|
||||
QueueHandle_t uartEventQueue = NULL;
|
||||
uartGetEventQueue(uart->_uart, &uartEventQueue);
|
||||
if (uartEventQueue != NULL) {
|
||||
for (;;) {
|
||||
//Waiting for UART event.
|
||||
if (xQueueReceive(uartEventQueue, (void *)&event, (TickType_t)portMAX_DELAY)) {
|
||||
hardwareSerial_error_t currentErr = UART_NO_ERROR;
|
||||
switch (event.type) {
|
||||
case UART_DATA:
|
||||
if (uart->_onReceiveCB && uart->available() > 0 && ((uart->_onReceiveTimeout && event.timeout_flag) || !uart->_onReceiveTimeout)) {
|
||||
uart->_onReceiveCB();
|
||||
}
|
||||
break;
|
||||
case UART_FIFO_OVF:
|
||||
log_w("UART%d FIFO Overflow. Consider adding Hardware Flow Control to your Application.", uart->_uart_nr);
|
||||
currentErr = UART_FIFO_OVF_ERROR;
|
||||
break;
|
||||
case UART_BUFFER_FULL:
|
||||
log_w("UART%d Buffer Full. Consider increasing your buffer size of your Application.", uart->_uart_nr);
|
||||
currentErr = UART_BUFFER_FULL_ERROR;
|
||||
break;
|
||||
case UART_BREAK:
|
||||
log_v("UART%d RX break.", uart->_uart_nr);
|
||||
currentErr = UART_BREAK_ERROR;
|
||||
break;
|
||||
case UART_PARITY_ERR:
|
||||
log_v("UART%d parity error.", uart->_uart_nr);
|
||||
currentErr = UART_PARITY_ERROR;
|
||||
break;
|
||||
case UART_FRAME_ERR:
|
||||
log_v("UART%d frame error.", uart->_uart_nr);
|
||||
currentErr = UART_FRAME_ERROR;
|
||||
break;
|
||||
default: log_v("UART%d unknown event type %d.", uart->_uart_nr, event.type); break;
|
||||
}
|
||||
if (currentErr != UART_NO_ERROR) {
|
||||
if (uart->_onReceiveErrorCB) {
|
||||
uart->_onReceiveErrorCB(currentErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd) {
|
||||
if (_uart_nr >= SOC_UART_NUM) {
|
||||
log_e("Serial number is invalid, please use a number from 0 to %u", SOC_UART_NUM - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (_lock == NULL) {
|
||||
log_e("MUTEX Lock failed. Can't begin.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// map logical pins to GPIO numbers
|
||||
rxPin = digitalPinToGPIONumber(rxPin);
|
||||
txPin = digitalPinToGPIONumber(txPin);
|
||||
int8_t _rxPin = uart_get_RxPin(_uart_nr);
|
||||
int8_t _txPin = uart_get_TxPin(_uart_nr);
|
||||
|
||||
rxPin = rxPin < 0 ? _rxPin : rxPin;
|
||||
txPin = txPin < 0 ? _txPin : txPin;
|
||||
|
||||
HSERIAL_MUTEX_LOCK();
|
||||
// First Time or after end() --> set default Pins
|
||||
if (!uartIsDriverInstalled(_uart)) {
|
||||
// get previously used RX/TX pins, if any.
|
||||
int8_t _rxPin = uart_get_RxPin(_uart_nr);
|
||||
int8_t _txPin = uart_get_TxPin(_uart_nr);
|
||||
switch (_uart_nr) {
|
||||
case UART_NUM_0:
|
||||
if (rxPin < 0 && txPin < 0) {
|
||||
// do not change RX0/TX0 if it has already been set before
|
||||
rxPin = _rxPin < 0 ? (int8_t)SOC_RX0 : _rxPin;
|
||||
txPin = _txPin < 0 ? (int8_t)SOC_TX0 : _txPin;
|
||||
}
|
||||
break;
|
||||
#if SOC_UART_HP_NUM > 1
|
||||
case UART_NUM_1:
|
||||
if (rxPin < 0 && txPin < 0) {
|
||||
// do not change RX1/TX1 if it has already been set before
|
||||
rxPin = _rxPin < 0 ? (int8_t)RX1 : _rxPin;
|
||||
txPin = _txPin < 0 ? (int8_t)TX1 : _txPin;
|
||||
}
|
||||
break;
|
||||
#endif // UART_NUM_1
|
||||
#if SOC_UART_HP_NUM > 2
|
||||
case UART_NUM_2:
|
||||
if (rxPin < 0 && txPin < 0) {
|
||||
// do not change RX2/TX2 if it has already been set before
|
||||
#ifdef RX2
|
||||
rxPin = _rxPin < 0 ? (int8_t)RX2 : _rxPin;
|
||||
#endif
|
||||
#ifdef TX2
|
||||
txPin = _txPin < 0 ? (int8_t)TX2 : _txPin;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#endif // UART_NUM_2
|
||||
#if SOC_UART_HP_NUM > 3
|
||||
case UART_NUM_3:
|
||||
if (rxPin < 0 && txPin < 0) {
|
||||
// do not change RX3/TX3 if it has already been set before
|
||||
#ifdef RX3
|
||||
rxPin = _rxPin < 0 ? (int8_t)RX3 : _rxPin;
|
||||
#endif
|
||||
#ifdef TX3
|
||||
txPin = _txPin < 0 ? (int8_t)TX3 : _txPin;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#endif // UART_NUM_3
|
||||
#if SOC_UART_HP_NUM > 4
|
||||
case UART_NUM_4:
|
||||
if (rxPin < 0 && txPin < 0) {
|
||||
// do not change RX4/TX4 if it has already been set before
|
||||
#ifdef RX4
|
||||
rxPin = _rxPin < 0 ? (int8_t)RX4 : _rxPin;
|
||||
#endif
|
||||
#ifdef TX4
|
||||
txPin = _txPin < 0 ? (int8_t)TX4 : _txPin;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#endif // UART_NUM_4
|
||||
#if (SOC_UART_LP_NUM >= 1)
|
||||
case LP_UART_NUM_0:
|
||||
if (rxPin < 0 && txPin < 0) {
|
||||
// do not change RX0_LP/TX0_LP if it has already been set before
|
||||
#ifdef LP_RX0
|
||||
rxPin = _rxPin < 0 ? (int8_t)LP_RX0 : _rxPin;
|
||||
#endif
|
||||
#ifdef LP_TX0
|
||||
txPin = _txPin < 0 ? (int8_t)LP_TX0 : _txPin;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
#endif // LP_UART_NUM_0
|
||||
}
|
||||
}
|
||||
|
||||
// if no RX/TX pins are defined, it will not start the UART driver
|
||||
if (rxPin < 0 && txPin < 0) {
|
||||
log_e("No RX/TX pins defined. Please set RX/TX pins.");
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
// IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified.
|
||||
// it will detach previous UART attached pins
|
||||
|
||||
// indicates that uartbegin() has to initialize a new IDF driver
|
||||
if (_testUartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd)) {
|
||||
_destroyEventTask(); // when IDF uart driver must be restarted, _eventTask must finish too
|
||||
}
|
||||
|
||||
// IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified.
|
||||
// it will detach previous UART attached pins
|
||||
_uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd);
|
||||
if (_uart == NULL) {
|
||||
log_e("UART driver failed to start. Please check the logs.");
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
return;
|
||||
}
|
||||
if (!baud) {
|
||||
// using baud rate as zero, forces it to try to detect the current baud rate in place
|
||||
uartStartDetectBaudrate(_uart);
|
||||
time_t startMillis = millis();
|
||||
unsigned long detectedBaudRate = 0;
|
||||
while (millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) {
|
||||
yield();
|
||||
}
|
||||
|
||||
if (detectedBaudRate) {
|
||||
delay(100); // Give some time...
|
||||
_uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd);
|
||||
if (_uart == NULL) {
|
||||
log_e("UART driver failed to start. Please check the logs.");
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible");
|
||||
_uart = NULL;
|
||||
}
|
||||
}
|
||||
// create a task to deal with Serial Events when, for example, calling begin() twice to change the baudrate,
|
||||
// or when setting the callback before calling begin()
|
||||
if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) {
|
||||
_createEventTask(this);
|
||||
}
|
||||
|
||||
// Set UART RX timeout
|
||||
uartSetRxTimeout(_uart, _rxTimeout);
|
||||
|
||||
// Set UART FIFO Full depending on the baud rate.
|
||||
// Lower baud rates will force to emulate byte-by-byte reading
|
||||
// Higher baud rates will keep IDF default of 120 bytes for FIFO FULL Interrupt
|
||||
// It can also be changed by the application at any time
|
||||
if (!_rxFIFOFull) { // it has not being changed before calling begin()
|
||||
// set a default FIFO Full value for the IDF driver
|
||||
uint8_t fifoFull = 1;
|
||||
// if baud rate is higher than 57600 or onReceive() is set, it will set FIFO Full to 120 bytes, except for LP UART
|
||||
if (_uart_nr < SOC_UART_HP_NUM && (baud > 57600 || (_onReceiveCB != NULL && _onReceiveTimeout))) {
|
||||
fifoFull = 120;
|
||||
}
|
||||
uartSetRxFIFOFull(_uart, fifoFull);
|
||||
_rxFIFOFull = fifoFull;
|
||||
}
|
||||
|
||||
HSERIAL_MUTEX_UNLOCK();
|
||||
}
|
||||
|
||||
void HardwareSerial::updateBaudRate(unsigned long baud) {
|
||||
uartSetBaudRate(_uart, baud);
|
||||
}
|
||||
|
||||
void HardwareSerial::end() {
|
||||
// default Serial.end() will completely disable HardwareSerial,
|
||||
// including any tasks or debug message channel (log_x()) - but not for IDF log messages!
|
||||
_onReceiveCB = NULL;
|
||||
_onReceiveErrorCB = NULL;
|
||||
_rxFIFOFull = 0;
|
||||
uartEnd(_uart_nr); // fully detach all pins and delete the UART driver
|
||||
_destroyEventTask(); // when IDF uart driver is deleted, _eventTask must finish too
|
||||
_uart = NULL;
|
||||
}
|
||||
|
||||
void HardwareSerial::setDebugOutput(bool en) {
|
||||
if (_uart == 0) {
|
||||
return;
|
||||
}
|
||||
#if (SOC_UART_LP_NUM >= 1)
|
||||
if (_uart_nr >= SOC_UART_HP_NUM) {
|
||||
log_e("LP UART does not support Debug Output.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (en) {
|
||||
uartSetDebug(_uart);
|
||||
} else {
|
||||
if (uartGetDebug() == _uart_nr) {
|
||||
uartSetDebug(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int HardwareSerial::available(void) {
|
||||
return uartAvailable(_uart);
|
||||
}
|
||||
int HardwareSerial::availableForWrite(void) {
|
||||
return uartAvailableForWrite(_uart);
|
||||
}
|
||||
|
||||
int HardwareSerial::peek(void) {
|
||||
if (available()) {
|
||||
return uartPeek(_uart);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int HardwareSerial::read(void) {
|
||||
uint8_t c = 0;
|
||||
if (uartReadBytes(_uart, &c, 1, 0) == 1) {
|
||||
return c;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// read characters into buffer
|
||||
// terminates if size characters have been read, or no further are pending
|
||||
// returns the number of characters placed in the buffer
|
||||
// the buffer is NOT null terminated.
|
||||
size_t HardwareSerial::read(uint8_t *buffer, size_t size) {
|
||||
return uartReadBytes(_uart, buffer, size, 0);
|
||||
}
|
||||
|
||||
// Overrides Stream::readBytes() to be faster using IDF
|
||||
size_t HardwareSerial::readBytes(uint8_t *buffer, size_t length) {
|
||||
return uartReadBytes(_uart, buffer, length, (uint32_t)getTimeout());
|
||||
}
|
||||
|
||||
void HardwareSerial::flush(void) {
|
||||
uartFlush(_uart);
|
||||
}
|
||||
|
||||
void HardwareSerial::flush(bool txOnly) {
|
||||
uartFlushTxOnly(_uart, txOnly);
|
||||
}
|
||||
|
||||
size_t HardwareSerial::write(uint8_t c) {
|
||||
uartWrite(_uart, c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t HardwareSerial::write(const uint8_t *buffer, size_t size) {
|
||||
uartWriteBuf(_uart, buffer, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
uint32_t HardwareSerial::baudRate() {
|
||||
return uartGetBaudRate(_uart);
|
||||
}
|
||||
HardwareSerial::operator bool() const {
|
||||
return uartIsDriverInstalled(_uart);
|
||||
}
|
||||
|
||||
bool HardwareSerial::setRxInvert(bool invert) {
|
||||
return uartSetRxInvert(_uart, invert);
|
||||
}
|
||||
|
||||
bool HardwareSerial::setTxInvert(bool invert) {
|
||||
return uartSetTxInvert(_uart, invert);
|
||||
}
|
||||
|
||||
bool HardwareSerial::setCtsInvert(bool invert) {
|
||||
return uartSetCtsInvert(_uart, invert);
|
||||
}
|
||||
|
||||
bool HardwareSerial::setRtsInvert(bool invert) {
|
||||
return uartSetRtsInvert(_uart, invert);
|
||||
}
|
||||
|
||||
// negative Pin value will keep it unmodified
|
||||
// can be called after or before begin()
|
||||
bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) {
|
||||
// map logical pins to GPIO numbers
|
||||
rxPin = digitalPinToGPIONumber(rxPin);
|
||||
txPin = digitalPinToGPIONumber(txPin);
|
||||
ctsPin = digitalPinToGPIONumber(ctsPin);
|
||||
rtsPin = digitalPinToGPIONumber(rtsPin);
|
||||
|
||||
// uartSetPins() checks if pins are valid and, if necessary, detaches the previous ones
|
||||
return uartSetPins(_uart_nr, rxPin, txPin, ctsPin, rtsPin);
|
||||
}
|
||||
|
||||
// Enables or disables Hardware Flow Control using RTS and/or CTS pins
|
||||
// must use setAllPins() in order to set RTS/CTS pins
|
||||
// SerialHwFlowCtrl = UART_HW_FLOWCTRL_DISABLE, UART_HW_FLOWCTRL_RTS,
|
||||
// UART_HW_FLOWCTRL_CTS, UART_HW_FLOWCTRL_CTS_RTS
|
||||
bool HardwareSerial::setHwFlowCtrlMode(SerialHwFlowCtrl mode, uint8_t threshold) {
|
||||
return uartSetHwFlowCtrlMode(_uart, mode, threshold);
|
||||
}
|
||||
|
||||
// Sets the uart mode in the esp32 uart for use with RS485 modes
|
||||
// HwFlowCtrl must be disabled and RTS pin set
|
||||
// SerialMode = UART_MODE_UART, UART_MODE_RS485_HALF_DUPLEX, UART_MODE_IRDA,
|
||||
// or testing mode: UART_MODE_RS485_COLLISION_DETECT, UART_MODE_RS485_APP_CTRL
|
||||
bool HardwareSerial::setMode(SerialMode mode) {
|
||||
return uartSetMode(_uart, mode);
|
||||
}
|
||||
|
||||
// Sets the UART Clock Source based on the compatible SoC options
|
||||
// This method must be called before starting UART using begin(), otherwise it won't have any effect.
|
||||
// Clock Source Options are:
|
||||
// UART_CLK_SRC_DEFAULT :: any SoC - it will set whatever IDF defines as the default UART Clock Source
|
||||
// UART_CLK_SRC_APB :: ESP32, ESP32-S2, ESP32-C3 and ESP32-S3
|
||||
// UART_CLK_SRC_PLL :: ESP32-C2, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2 and ESP32-P4
|
||||
// UART_CLK_SRC_XTAL :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
|
||||
// UART_CLK_SRC_RTC :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
|
||||
// UART_CLK_SRC_REF_TICK :: ESP32 and ESP32-S2
|
||||
// Note: CLK_SRC_PLL Freq depends on the SoC - ESP32-C2 has 40MHz, ESP32-H2 has 48MHz and ESP32-C5, C6, C61 and P4 has 80MHz
|
||||
// Note: ESP32-C6, C61, ESP32-P4 and ESP32-C5 have LP UART that will use only RTC_FAST or XTAL/2 as Clock Source
|
||||
bool HardwareSerial::setClockSource(SerialClkSrc clkSrc) {
|
||||
if (_uart) {
|
||||
log_e("No Clock Source change was done. This function must be called before beginning UART%d.", _uart_nr);
|
||||
return false;
|
||||
}
|
||||
return uartSetClockSource(_uart_nr, (uart_sclk_t)clkSrc);
|
||||
}
|
||||
// minimum total RX Buffer size is the UART FIFO space (128 bytes for most SoC) + 1. IDF imposition.
|
||||
// LP UART has FIFO of 16 bytes
|
||||
size_t HardwareSerial::setRxBufferSize(size_t new_size) {
|
||||
|
||||
if (_uart) {
|
||||
log_e("RX Buffer can't be resized when Serial is already running. Set it before calling begin().");
|
||||
return 0;
|
||||
}
|
||||
uint8_t FIFOLen = UART_HW_FIFO_LEN(_uart_nr);
|
||||
// Valid value is higher than the FIFO length
|
||||
if (new_size <= FIFOLen) {
|
||||
new_size = FIFOLen + 1;
|
||||
log_w("RX Buffer set to minimum value: %d.", new_size);
|
||||
}
|
||||
|
||||
_rxBufferSize = new_size;
|
||||
return _rxBufferSize;
|
||||
}
|
||||
|
||||
// minimum total TX Buffer size is the UART FIFO space (128 bytes for most SoC) + 1.
|
||||
// LP UART has FIFO of 16 bytes
|
||||
size_t HardwareSerial::setTxBufferSize(size_t new_size) {
|
||||
|
||||
if (_uart) {
|
||||
log_e("TX Buffer can't be resized when Serial is already running. Set it before calling begin().");
|
||||
return 0;
|
||||
}
|
||||
uint8_t FIFOLen = UART_HW_FIFO_LEN(_uart_nr);
|
||||
// Valid values are zero or higher than the FIFO length
|
||||
if (new_size > 0 && new_size <= FIFOLen) {
|
||||
new_size = FIFOLen + 1;
|
||||
log_w("TX Buffer set to minimum value: %d.", new_size);
|
||||
}
|
||||
// if new_size is higher than SOC_UART_FIFO_LEN, TX Ringbuffer will be active and it will be used to report back "availableToWrite()"
|
||||
_txBufferSize = new_size;
|
||||
return new_size;
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
/*
|
||||
HardwareSerial.h - Hardware serial library for Wiring
|
||||
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified 28 September 2010 by Mark Sproul
|
||||
Modified 14 August 2012 by Alarus
|
||||
Modified 3 December 2013 by Matthijs Kooijman
|
||||
Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support)
|
||||
Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266)
|
||||
Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266)
|
||||
Modified 13 October 2018 by Jeroen Döll (add baudrate detection)
|
||||
Baudrate detection example usage (detection on Serial1):
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(100);
|
||||
Serial.println();
|
||||
|
||||
Serial1.begin(0, SERIAL_8N1, -1, -1, true, 11000UL); // Passing 0 for baudrate to detect it, the last parameter is a timeout in ms
|
||||
|
||||
unsigned long detectedBaudRate = Serial1.baudRate();
|
||||
if(detectedBaudRate) {
|
||||
Serial.printf("Detected baudrate is %lu\n", detectedBaudRate);
|
||||
} else {
|
||||
Serial.println("No baudrate detected, Serial1 will not work!");
|
||||
}
|
||||
}
|
||||
|
||||
Pay attention: the baudrate returned by baudRate() may be rounded, eg 115200 returns 115201
|
||||
*/
|
||||
|
||||
#ifndef HardwareSerial_h
|
||||
#define HardwareSerial_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <functional>
|
||||
#include "Stream.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "HWCDC.h"
|
||||
#include "USBCDC.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
enum SerialConfig {
|
||||
SERIAL_5N1 = 0x8000010,
|
||||
SERIAL_6N1 = 0x8000014,
|
||||
SERIAL_7N1 = 0x8000018,
|
||||
SERIAL_8N1 = 0x800001c,
|
||||
SERIAL_5N2 = 0x8000030,
|
||||
SERIAL_6N2 = 0x8000034,
|
||||
SERIAL_7N2 = 0x8000038,
|
||||
SERIAL_8N2 = 0x800003c,
|
||||
SERIAL_5E1 = 0x8000012,
|
||||
SERIAL_6E1 = 0x8000016,
|
||||
SERIAL_7E1 = 0x800001a,
|
||||
SERIAL_8E1 = 0x800001e,
|
||||
SERIAL_5E2 = 0x8000032,
|
||||
SERIAL_6E2 = 0x8000036,
|
||||
SERIAL_7E2 = 0x800003a,
|
||||
SERIAL_8E2 = 0x800003e,
|
||||
SERIAL_5O1 = 0x8000013,
|
||||
SERIAL_6O1 = 0x8000017,
|
||||
SERIAL_7O1 = 0x800001b,
|
||||
SERIAL_8O1 = 0x800001f,
|
||||
SERIAL_5O2 = 0x8000033,
|
||||
SERIAL_6O2 = 0x8000037,
|
||||
SERIAL_7O2 = 0x800003b,
|
||||
SERIAL_8O2 = 0x800003f
|
||||
};
|
||||
|
||||
typedef uart_mode_t SerialMode;
|
||||
typedef uart_hw_flowcontrol_t SerialHwFlowCtrl;
|
||||
|
||||
typedef enum {
|
||||
UART_NO_ERROR,
|
||||
UART_BREAK_ERROR,
|
||||
UART_BUFFER_FULL_ERROR,
|
||||
UART_FIFO_OVF_ERROR,
|
||||
UART_FRAME_ERROR,
|
||||
UART_PARITY_ERROR
|
||||
} hardwareSerial_error_t;
|
||||
|
||||
typedef enum {
|
||||
UART_CLK_SRC_DEFAULT = UART_SCLK_DEFAULT,
|
||||
#if SOC_UART_SUPPORT_APB_CLK
|
||||
UART_CLK_SRC_APB = UART_SCLK_APB,
|
||||
#endif
|
||||
#if SOC_UART_SUPPORT_PLL_F40M_CLK
|
||||
UART_CLK_SRC_PLL = UART_SCLK_PLL_F40M,
|
||||
#elif SOC_UART_SUPPORT_PLL_F80M_CLK
|
||||
UART_CLK_SRC_PLL = UART_SCLK_PLL_F80M,
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
UART_CLK_SRC_PLL = UART_SCLK_PLL_F48M,
|
||||
#endif
|
||||
#if SOC_UART_SUPPORT_XTAL_CLK
|
||||
UART_CLK_SRC_XTAL = UART_SCLK_XTAL,
|
||||
#endif
|
||||
#if SOC_UART_SUPPORT_RTC_CLK
|
||||
UART_CLK_SRC_RTC = UART_SCLK_RTC,
|
||||
#endif
|
||||
#if SOC_UART_SUPPORT_REF_TICK
|
||||
UART_CLK_SRC_REF_TICK = UART_SCLK_REF_TICK,
|
||||
#endif
|
||||
} SerialClkSrc;
|
||||
|
||||
#ifndef ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
|
||||
#ifndef CONFIG_ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
|
||||
#define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE 2048
|
||||
#else
|
||||
#define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE CONFIG_ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINO_SERIAL_EVENT_TASK_PRIORITY
|
||||
#ifndef CONFIG_ARDUINO_SERIAL_EVENT_TASK_PRIORITY
|
||||
#define ARDUINO_SERIAL_EVENT_TASK_PRIORITY (configMAX_PRIORITIES - 1)
|
||||
#else
|
||||
#define ARDUINO_SERIAL_EVENT_TASK_PRIORITY CONFIG_ARDUINO_SERIAL_EVENT_TASK_PRIORITY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
|
||||
#ifndef CONFIG_ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
|
||||
#define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE -1
|
||||
#else
|
||||
#define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE CONFIG_ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// UART0 pins are defined by default by the bootloader.
|
||||
// The definitions for SOC_* should not be changed unless the bootloader pins
|
||||
// have changed and you know what you are doing.
|
||||
|
||||
#ifndef SOC_RX0
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define SOC_RX0 (gpio_num_t)3
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
#define SOC_RX0 (gpio_num_t)44
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#define SOC_RX0 (gpio_num_t)19
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define SOC_RX0 (gpio_num_t)20
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#define SOC_RX0 (gpio_num_t)17
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#define SOC_RX0 (gpio_num_t)23
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define SOC_RX0 (gpio_num_t)38
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#define SOC_RX0 (gpio_num_t)12
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#define SOC_RX0 (gpio_num_t)10
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SOC_TX0
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define SOC_TX0 (gpio_num_t)1
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
#define SOC_TX0 (gpio_num_t)43
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#define SOC_TX0 (gpio_num_t)20
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define SOC_TX0 (gpio_num_t)21
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#define SOC_TX0 (gpio_num_t)16
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#define SOC_TX0 (gpio_num_t)24
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define SOC_TX0 (gpio_num_t)37
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
|
||||
#define SOC_TX0 (gpio_num_t)11
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Default pins for UART1 are arbitrary, and defined here for convenience.
|
||||
|
||||
#if SOC_UART_HP_NUM > 1
|
||||
#ifndef RX1
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define RX1 (gpio_num_t)26
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define RX1 (gpio_num_t)4
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#define RX1 (gpio_num_t)10
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define RX1 (gpio_num_t)18
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define RX1 (gpio_num_t)15
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#define RX1 (gpio_num_t)4
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#define RX1 (gpio_num_t)0
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define RX1 (gpio_num_t)11
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#define RX1 (gpio_num_t)4
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#define RX1 (gpio_num_t)8
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef TX1
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define TX1 (gpio_num_t)27
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define TX1 (gpio_num_t)5
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#define TX1 (gpio_num_t)18
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define TX1 (gpio_num_t)19
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define TX1 (gpio_num_t)16
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#define TX1 (gpio_num_t)5
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#define TX1 (gpio_num_t)1
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define TX1 (gpio_num_t)10
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#define TX1 (gpio_num_t)5
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#define TX1 (gpio_num_t)29
|
||||
#endif
|
||||
#endif
|
||||
#endif /* SOC_UART_HP_NUM > 1 */
|
||||
|
||||
// Default pins for UART2 are arbitrary, and defined here for convenience.
|
||||
|
||||
#if SOC_UART_HP_NUM > 2
|
||||
#ifndef RX2
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define RX2 (gpio_num_t)4
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define RX2 (gpio_num_t)19
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef TX2
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define TX2 (gpio_num_t)25
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define TX2 (gpio_num_t)20
|
||||
#endif
|
||||
#endif
|
||||
#endif /* SOC_UART_HP_NUM > 2 */
|
||||
|
||||
#if SOC_UART_LP_NUM >= 1
|
||||
#ifndef LP_RX0
|
||||
#define LP_RX0 (gpio_num_t) LP_U0RXD_GPIO_NUM
|
||||
#endif
|
||||
|
||||
#ifndef LP_TX0
|
||||
#define LP_TX0 (gpio_num_t) LP_U0TXD_GPIO_NUM
|
||||
#endif
|
||||
#endif /* SOC_UART_LP_NUM >= 1 */
|
||||
|
||||
typedef std::function<void(void)> OnReceiveCb;
|
||||
typedef std::function<void(hardwareSerial_error_t)> OnReceiveErrorCb;
|
||||
|
||||
class HardwareSerial : public Stream {
|
||||
public:
|
||||
HardwareSerial(uint8_t uart_nr);
|
||||
~HardwareSerial();
|
||||
|
||||
// setRxTimeout sets the timeout after which onReceive callback will be called (after receiving data, it waits for this time of UART rx inactivity to call the callback fnc)
|
||||
// param symbols_timeout defines a timeout threshold in uart symbol periods. Setting 0 symbol timeout disables the callback call by timeout.
|
||||
// Maximum timeout setting is calculacted automatically by IDF. If set above the maximum, it is ignored and an error is printed on Serial0 (check console).
|
||||
// Examples: Maximum for 11 bits symbol is 92 (SERIAL_8N2, SERIAL_8E1, SERIAL_8O1, etc), Maximum for 10 bits symbol is 101 (SERIAL_8N1).
|
||||
// For example symbols_timeout=1 defines a timeout equal to transmission time of one symbol (~11 bit) on current baudrate.
|
||||
// For a baudrate of 9600, SERIAL_8N1 (10 bit symbol) and symbols_timeout = 3, the timeout would be 3 / (9600 / 10) = 3.125 ms
|
||||
bool setRxTimeout(uint8_t symbols_timeout);
|
||||
|
||||
// setRxFIFOFull(uint8_t fifoBytes) will set the number of bytes that will trigger UART_INTR_RXFIFO_FULL interrupt and fill up RxRingBuffer
|
||||
// This affects some functions such as Serial::available() and Serial.read() because, in a UART flow of receiving data, Serial internal
|
||||
// RxRingBuffer will be filled only after these number of bytes arrive or a RX Timeout happens.
|
||||
// This parameter can be set to 1 in order to receive byte by byte, but it will also consume more CPU time as the ISR will be activates often.
|
||||
bool setRxFIFOFull(uint8_t fifoBytes);
|
||||
|
||||
// onReceive will setup a callback that will be called whenever an UART interruption occurs (UART_INTR_RXFIFO_FULL or UART_INTR_RXFIFO_TOUT)
|
||||
// UART_INTR_RXFIFO_FULL interrupt triggers at UART_FULL_THRESH_DEFAULT bytes received (defined as 120 bytes by default in IDF)
|
||||
// UART_INTR_RXFIFO_TOUT interrupt triggers at UART_TOUT_THRESH_DEFAULT symbols passed without any reception (defined as 10 symbols by default in IDF)
|
||||
// onlyOnTimeout parameter will define how onReceive will behave:
|
||||
// Default: true -- The callback will only be called when RX Timeout happens.
|
||||
// Whole stream of bytes will be ready for being read on the callback function at once.
|
||||
// This option may lead to Rx Overflow depending on the Rx Buffer Size and number of bytes received in the streaming
|
||||
// false -- The callback will be called when FIFO reaches 120 bytes and also on RX Timeout.
|
||||
// The stream of incommig bytes will be "split" into blocks of 120 bytes on each callback.
|
||||
// This option avoid any sort of Rx Overflow, but leaves the UART packet reassembling work to the Application.
|
||||
void onReceive(OnReceiveCb function, bool onlyOnTimeout = false);
|
||||
|
||||
// onReceive will be called on error events (see hardwareSerial_error_t)
|
||||
void onReceiveError(OnReceiveErrorCb function);
|
||||
|
||||
// eventQueueReset clears all events in the queue (the events that trigger onReceive and onReceiveError) - maybe useful in some use cases
|
||||
void eventQueueReset();
|
||||
|
||||
// When pins are changed, it will detach the previous ones
|
||||
// if pin is negative, it won't be set/changed and will be kept as is
|
||||
// timeout_ms is used in baudrate detection (ESP32, ESP32S2 only)
|
||||
// invert will invert RX/TX polarity
|
||||
// rxfifo_full_thrhd if the UART Flow Control Threshold in the UART FIFO (max 127)
|
||||
void begin(
|
||||
unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1, bool invert = false, unsigned long timeout_ms = 20000UL,
|
||||
uint8_t rxfifo_full_thrhd = 120
|
||||
);
|
||||
void end(void);
|
||||
void updateBaudRate(unsigned long baud);
|
||||
int available(void);
|
||||
int availableForWrite(void);
|
||||
int peek(void);
|
||||
int read(void);
|
||||
size_t read(uint8_t *buffer, size_t size);
|
||||
inline size_t read(char *buffer, size_t size) {
|
||||
return read((uint8_t *)buffer, size);
|
||||
}
|
||||
// Overrides Stream::readBytes() to be faster using IDF
|
||||
size_t readBytes(uint8_t *buffer, size_t length);
|
||||
size_t readBytes(char *buffer, size_t length) {
|
||||
return readBytes((uint8_t *)buffer, length);
|
||||
}
|
||||
void flush(void);
|
||||
void flush(bool txOnly);
|
||||
size_t write(uint8_t);
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
inline size_t write(const char *buffer, size_t size) {
|
||||
return write((uint8_t *)buffer, size);
|
||||
}
|
||||
inline size_t write(const char *s) {
|
||||
return write((uint8_t *)s, strlen(s));
|
||||
}
|
||||
inline size_t write(unsigned long n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(long n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(unsigned int n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(int n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
uint32_t baudRate();
|
||||
operator bool() const;
|
||||
|
||||
void setDebugOutput(bool);
|
||||
|
||||
// functions used to enable or disable UART pins signal inversion
|
||||
// returns the requested operation success status
|
||||
bool setRxInvert(bool);
|
||||
bool setTxInvert(bool);
|
||||
bool setCtsInvert(bool);
|
||||
bool setRtsInvert(bool);
|
||||
|
||||
// Negative Pin Number will keep it unmodified, thus this function can set individual pins
|
||||
// setPins() can be called after or before begin()
|
||||
// When pins are changed, it will detach the previous ones
|
||||
bool setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin = -1, int8_t rtsPin = -1);
|
||||
// Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before)
|
||||
// UART_HW_FLOWCTRL_DISABLE = 0x0 disable hardware flow control
|
||||
// UART_HW_FLOWCTRL_RTS = 0x1 enable RX hardware flow control (rts)
|
||||
// UART_HW_FLOWCTRL_CTS = 0x2 enable TX hardware flow control (cts)
|
||||
// UART_HW_FLOWCTRL_CTS_RTS = 0x3 enable hardware flow control
|
||||
bool setHwFlowCtrlMode(SerialHwFlowCtrl mode = UART_HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); // 64 is half FIFO Length
|
||||
// Used to set RS485 modes such as UART_MODE_RS485_HALF_DUPLEX for Auto RTS function on ESP32
|
||||
// UART_MODE_UART = 0x00 mode: regular UART mode
|
||||
// UART_MODE_RS485_HALF_DUPLEX = 0x01 mode: half duplex RS485 UART mode control by RTS pin
|
||||
// UART_MODE_IRDA = 0x02 mode: IRDA UART mode
|
||||
// UART_MODE_RS485_COLLISION_DETECT = 0x03 mode: RS485 collision detection UART mode (used for test purposes)
|
||||
// UART_MODE_RS485_APP_CTRL = 0x04 mode: application control RS485 UART mode (used for test purposes)
|
||||
bool setMode(SerialMode mode);
|
||||
// Used to set the UART clock source mode. It must be set before calling begin(), otherwise it won't have any effect.
|
||||
// Not all clock source are available to every SoC. The compatible option are listed here:
|
||||
// UART_CLK_SRC_DEFAULT :: any SoC - it will set whatever IDF defines as the default UART Clock Source
|
||||
// UART_CLK_SRC_APB :: ESP32, ESP32-S2, ESP32-C3 and ESP32-S3
|
||||
// UART_CLK_SRC_PLL :: ESP32-C2, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2 and ESP32-P4
|
||||
// UART_CLK_SRC_XTAL :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
|
||||
// UART_CLK_SRC_RTC :: ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3 and ESP32-P4
|
||||
// UART_CLK_SRC_REF_TICK :: ESP32 and ESP32-S2
|
||||
// Note: CLK_SRC_PLL Freq depends on the SoC - ESP32-C2 has 40MHz, ESP32-H2 has 48MHz and ESP32-C5, C6, C61 and P4 has 80MHz
|
||||
// Note: ESP32-C6, C61, ESP32-P4 and ESP32-C5 have LP UART that will use only RTC_FAST or XTAL/2 as Clock Source
|
||||
bool setClockSource(SerialClkSrc clkSrc);
|
||||
size_t setRxBufferSize(size_t new_size);
|
||||
size_t setTxBufferSize(size_t new_size);
|
||||
|
||||
protected:
|
||||
uint8_t _uart_nr;
|
||||
uart_t *_uart;
|
||||
size_t _rxBufferSize;
|
||||
size_t _txBufferSize;
|
||||
OnReceiveCb _onReceiveCB;
|
||||
OnReceiveErrorCb _onReceiveErrorCB;
|
||||
// _onReceive and _rxTimeout have be consistent when timeout is disabled
|
||||
bool _onReceiveTimeout;
|
||||
uint8_t _rxTimeout, _rxFIFOFull;
|
||||
TaskHandle_t _eventTask;
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
SemaphoreHandle_t _lock;
|
||||
#endif
|
||||
|
||||
void _createEventTask(void *args);
|
||||
void _destroyEventTask(void);
|
||||
static void _uartEventTask(void *args);
|
||||
};
|
||||
|
||||
extern void serialEventRun(void) __attribute__((weak));
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
#ifndef ARDUINO_USB_CDC_ON_BOOT
|
||||
#define ARDUINO_USB_CDC_ON_BOOT 0
|
||||
#endif
|
||||
#if ARDUINO_USB_CDC_ON_BOOT //Serial used from Native_USB_CDC | HW_CDC_JTAG
|
||||
#if ARDUINO_USB_MODE // Hardware CDC mode
|
||||
// Arduino Serial is the HW JTAG CDC device
|
||||
#define Serial HWCDCSerial
|
||||
#else // !ARDUINO_USB_MODE -- Native USB Mode
|
||||
// Arduino Serial is the Native USB CDC device
|
||||
#define Serial USBSerial
|
||||
#endif // ARDUINO_USB_MODE
|
||||
#else // !ARDUINO_USB_CDC_ON_BOOT -- Serial is used from UART0
|
||||
// if not using CDC on Boot, Arduino Serial is the UART0 device
|
||||
#define Serial Serial0
|
||||
#endif // ARDUINO_USB_CDC_ON_BOOT
|
||||
// There is always Seria0 for UART0
|
||||
extern HardwareSerial Serial0;
|
||||
#if SOC_UART_NUM > 1
|
||||
extern HardwareSerial Serial1;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 2
|
||||
extern HardwareSerial Serial2;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 3
|
||||
extern HardwareSerial Serial3;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 4
|
||||
extern HardwareSerial Serial4;
|
||||
#endif
|
||||
#if SOC_UART_NUM > 5
|
||||
extern HardwareSerial Serial5;
|
||||
#endif
|
||||
#endif //!defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
|
||||
|
||||
#endif // HardwareSerial_h
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2025 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 "HashBuilder.h"
|
||||
|
||||
void HashBuilder::add(const char *data) {
|
||||
add((const uint8_t *)data, strlen(data));
|
||||
}
|
||||
|
||||
void HashBuilder::add(String data) {
|
||||
add(data.c_str());
|
||||
}
|
||||
|
||||
void HashBuilder::addHexString(const char *data) {
|
||||
size_t len = strlen(data);
|
||||
uint8_t *tmp = (uint8_t *)malloc(len / 2);
|
||||
if (tmp == NULL) {
|
||||
return;
|
||||
}
|
||||
hex2bytes(tmp, len / 2, data);
|
||||
add(tmp, len / 2);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
void HashBuilder::addHexString(String data) {
|
||||
addHexString(data.c_str());
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2024 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 HashBuilder_h
|
||||
#define HashBuilder_h
|
||||
|
||||
#include <WString.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#include "HEXBuilder.h"
|
||||
|
||||
/* Try to prevent most compilers from optimizing out clearing of memory that
|
||||
* becomes unaccessible after this function is called. This is mostly the case
|
||||
* for clearing local stack variables at the end of a function. This is not
|
||||
* exactly perfect, i.e., someone could come up with a compiler that figures out
|
||||
* the pointer is pointing to memset and then end up optimizing the call out, so
|
||||
* try go a bit further by storing the first octet (now zero) to make this even
|
||||
* a bit more difficult to optimize out. Once memset_s() is available, that
|
||||
* could be used here instead. */
|
||||
static void *(*const volatile memset_func)(void *, int, size_t) = memset;
|
||||
static uint8_t forced_memzero_val;
|
||||
|
||||
static inline void forced_memzero(void *ptr, size_t len) {
|
||||
memset_func(ptr, 0, len);
|
||||
if (len) {
|
||||
forced_memzero_val = ((uint8_t *)ptr)[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Base class for hash builders
|
||||
|
||||
class HashBuilder : public HEXBuilder {
|
||||
public:
|
||||
virtual ~HashBuilder() {}
|
||||
virtual void begin() = 0;
|
||||
|
||||
virtual void add(const uint8_t *data, size_t len) = 0;
|
||||
void add(const char *data);
|
||||
void add(String data);
|
||||
|
||||
void addHexString(const char *data);
|
||||
void addHexString(String data);
|
||||
|
||||
virtual bool addStream(Stream &stream, const size_t maxLen) = 0;
|
||||
virtual void calculate() = 0;
|
||||
virtual void getBytes(uint8_t *output) = 0;
|
||||
virtual void getChars(char *output) = 0;
|
||||
virtual String toString() = 0;
|
||||
virtual size_t getHashSize() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
IPAddress.cpp - Base class that provides IPAddress
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "IPAddress.h"
|
||||
#include "Print.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "StreamString.h"
|
||||
|
||||
#ifndef CONFIG_LWIP_IPV6
|
||||
#define IP6_NO_ZONE 0
|
||||
#endif
|
||||
|
||||
IPAddress::IPAddress() : IPAddress(IPv4) {}
|
||||
|
||||
IPAddress::IPAddress(IPType ip_type) {
|
||||
_type = ip_type;
|
||||
_zone = IP6_NO_ZONE;
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
|
||||
_type = IPv4;
|
||||
_zone = IP6_NO_ZONE;
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
_address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet;
|
||||
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet;
|
||||
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 2] = third_octet;
|
||||
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(
|
||||
uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12,
|
||||
uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16, uint8_t z
|
||||
) {
|
||||
_type = IPv6;
|
||||
_address.bytes[0] = o1;
|
||||
_address.bytes[1] = o2;
|
||||
_address.bytes[2] = o3;
|
||||
_address.bytes[3] = o4;
|
||||
_address.bytes[4] = o5;
|
||||
_address.bytes[5] = o6;
|
||||
_address.bytes[6] = o7;
|
||||
_address.bytes[7] = o8;
|
||||
_address.bytes[8] = o9;
|
||||
_address.bytes[9] = o10;
|
||||
_address.bytes[10] = o11;
|
||||
_address.bytes[11] = o12;
|
||||
_address.bytes[12] = o13;
|
||||
_address.bytes[13] = o14;
|
||||
_address.bytes[14] = o15;
|
||||
_address.bytes[15] = o16;
|
||||
_zone = z;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(uint32_t address) {
|
||||
// IPv4 only
|
||||
_type = IPv4;
|
||||
_zone = IP6_NO_ZONE;
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
|
||||
|
||||
// NOTE on conversion/comparison and uint32_t:
|
||||
// These conversions are host platform dependent.
|
||||
// There is a defined integer representation of IPv4 addresses,
|
||||
// based on network byte order (will be the value on big endian systems),
|
||||
// e.g. http://2398766798 is the same as http://142.250.70.206,
|
||||
// However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE,
|
||||
// in that order, will form the integer (uint32_t) 3460758158 .
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
|
||||
|
||||
IPAddress::IPAddress(IPType ip_type, const uint8_t *address, uint8_t z) {
|
||||
_type = ip_type;
|
||||
if (ip_type == IPv4) {
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
|
||||
_zone = 0;
|
||||
} else {
|
||||
memcpy(_address.bytes, address, sizeof(_address.bytes));
|
||||
_zone = z;
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const char *address) {
|
||||
fromString(address);
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const IPAddress &address) {
|
||||
*this = address;
|
||||
}
|
||||
|
||||
String IPAddress::toString(bool includeZone) const {
|
||||
StreamString s;
|
||||
printTo(s, includeZone);
|
||||
return String(s);
|
||||
}
|
||||
|
||||
bool IPAddress::fromString(const char *address) {
|
||||
if (!fromString4(address)) {
|
||||
return fromString6(address);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPAddress::fromString4(const char *address) {
|
||||
// TODO: add support for "a", "a.b", "a.b.c" formats
|
||||
|
||||
int16_t acc = -1; // Accumulator
|
||||
uint8_t dots = 0;
|
||||
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
while (*address) {
|
||||
char c = *address++;
|
||||
if (c >= '0' && c <= '9') {
|
||||
acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0');
|
||||
if (acc > 255) {
|
||||
// Value out of [0..255] range
|
||||
return false;
|
||||
}
|
||||
} else if (c == '.') {
|
||||
if (dots == 3) {
|
||||
// Too many dots (there must be 3 dots)
|
||||
return false;
|
||||
}
|
||||
if (acc < 0) {
|
||||
/* No value between dots, e.g. '1..' */
|
||||
return false;
|
||||
}
|
||||
_address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc;
|
||||
acc = -1;
|
||||
} else {
|
||||
// Invalid char
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dots != 3) {
|
||||
// Too few dots (there must be 3 dots)
|
||||
return false;
|
||||
}
|
||||
if (acc < 0) {
|
||||
/* No value between dots, e.g. '1..' */
|
||||
return false;
|
||||
}
|
||||
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc;
|
||||
_type = IPv4;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPAddress::fromString6(const char *address) {
|
||||
uint32_t acc = 0; // Accumulator
|
||||
int colons = 0, double_colons = -1;
|
||||
|
||||
while (*address) {
|
||||
char c = tolower(*address++);
|
||||
if (isalnum(c) && c <= 'f') {
|
||||
if (c >= 'a') {
|
||||
c -= 'a' - '0' - 10;
|
||||
}
|
||||
acc = acc * 16 + (c - '0');
|
||||
if (acc > 0xffff) {
|
||||
// Value out of range
|
||||
return false;
|
||||
}
|
||||
} else if (c == ':') {
|
||||
if (*address == ':') {
|
||||
if (double_colons >= 0) {
|
||||
// :: allowed once
|
||||
return false;
|
||||
}
|
||||
if (*address != '\0' && *(address + 1) == ':') {
|
||||
// ::: not allowed
|
||||
return false;
|
||||
}
|
||||
// remember location
|
||||
double_colons = colons + !!acc;
|
||||
address++;
|
||||
} else if (*address == '\0') {
|
||||
// can't end with a single colon
|
||||
return false;
|
||||
}
|
||||
if (colons == 7) {
|
||||
// too many separators
|
||||
return false;
|
||||
}
|
||||
_address.bytes[colons * 2] = acc >> 8;
|
||||
_address.bytes[colons * 2 + 1] = acc & 0xff;
|
||||
colons++;
|
||||
acc = 0;
|
||||
} else if (c == '%') {
|
||||
// netif_index_to_name crashes on latest esp-idf
|
||||
// _zone = netif_name_to_index(address);
|
||||
// in the interim, we parse the suffix as a zone number
|
||||
while ((*address != '\0') && (!isdigit(*address))) { // skip all non-digit after '%'
|
||||
address++;
|
||||
}
|
||||
_zone = atol(address) + 1; // increase by one by convention, so we can have zone '0'
|
||||
while (*address != '\0') {
|
||||
address++;
|
||||
}
|
||||
} else {
|
||||
// Invalid char
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (double_colons == -1 && colons != 7) {
|
||||
// Too few separators
|
||||
return false;
|
||||
}
|
||||
if (double_colons > -1 && colons > 6) {
|
||||
// Too many segments (double colon must be at least one zero field)
|
||||
return false;
|
||||
}
|
||||
_address.bytes[colons * 2] = acc >> 8;
|
||||
_address.bytes[colons * 2 + 1] = acc & 0xff;
|
||||
colons++;
|
||||
|
||||
if (double_colons != -1) {
|
||||
for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--) {
|
||||
_address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i];
|
||||
}
|
||||
for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++) {
|
||||
_address.bytes[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_type = IPv6;
|
||||
return true;
|
||||
}
|
||||
|
||||
IPAddress &IPAddress::operator=(const uint8_t *address) {
|
||||
// IPv4 only conversion from byte pointer
|
||||
_type = IPv4;
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress &IPAddress::operator=(const char *address) {
|
||||
fromString(address);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress &IPAddress::operator=(uint32_t address) {
|
||||
// IPv4 conversion
|
||||
// See note on conversion/comparison and uint32_t
|
||||
_type = IPv4;
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress &IPAddress::operator=(const IPAddress &address) {
|
||||
_type = address.type();
|
||||
_zone = address.zone();
|
||||
memcpy(_address.bytes, address._address.bytes, sizeof(_address.bytes));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const IPAddress &addr) const {
|
||||
return (addr._type == _type) && (_type == IPType::IPv4 ? addr._address.dword[IPADDRESS_V4_DWORD_INDEX] == _address.dword[IPADDRESS_V4_DWORD_INDEX] : memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const uint8_t *addr) const {
|
||||
// IPv4 only comparison to byte pointer
|
||||
// Can't support IPv6 as we know our type, but not the length of the pointer
|
||||
return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
|
||||
}
|
||||
|
||||
uint8_t IPAddress::operator[](int index) const {
|
||||
if (_type == IPv4) {
|
||||
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
|
||||
}
|
||||
return _address.bytes[index];
|
||||
}
|
||||
|
||||
uint8_t &IPAddress::operator[](int index) {
|
||||
if (_type == IPv4) {
|
||||
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
|
||||
}
|
||||
return _address.bytes[index];
|
||||
}
|
||||
|
||||
size_t IPAddress::printTo(Print &p) const {
|
||||
return printTo(p, false);
|
||||
}
|
||||
|
||||
size_t IPAddress::printTo(Print &p, bool includeZone) const {
|
||||
size_t n = 0;
|
||||
|
||||
if (_type == IPv6) {
|
||||
// IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
|
||||
int8_t longest_start = -1;
|
||||
int8_t longest_length = 1;
|
||||
int8_t current_start = -1;
|
||||
int8_t current_length = 0;
|
||||
for (int8_t f = 0; f < 8; f++) {
|
||||
if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
|
||||
if (current_start == -1) {
|
||||
current_start = f;
|
||||
current_length = 1;
|
||||
} else {
|
||||
current_length++;
|
||||
}
|
||||
if (current_length > longest_length) {
|
||||
longest_start = current_start;
|
||||
longest_length = current_length;
|
||||
}
|
||||
} else {
|
||||
current_start = -1;
|
||||
}
|
||||
}
|
||||
for (int f = 0; f < 8; f++) {
|
||||
if (f < longest_start || f >= longest_start + longest_length) {
|
||||
uint8_t c1 = _address.bytes[f * 2] >> 4;
|
||||
uint8_t c2 = _address.bytes[f * 2] & 0xf;
|
||||
uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
|
||||
uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
|
||||
if (c1 > 0) {
|
||||
n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
|
||||
}
|
||||
if (c1 > 0 || c2 > 0) {
|
||||
n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
|
||||
}
|
||||
if (c1 > 0 || c2 > 0 || c3 > 0) {
|
||||
n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
|
||||
}
|
||||
n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
|
||||
if (f < 7) {
|
||||
n += p.print(':');
|
||||
}
|
||||
} else if (f == longest_start) {
|
||||
if (longest_start == 0) {
|
||||
n += p.print(':');
|
||||
}
|
||||
n += p.print(':');
|
||||
}
|
||||
}
|
||||
// add a zone if zone-id is non-zero (causes exception on recent IDF builds)
|
||||
// if (_zone > 0 && includeZone) {
|
||||
// n += p.print('%');
|
||||
// char if_name[NETIF_NAMESIZE];
|
||||
// netif_index_to_name(_zone, if_name);
|
||||
// n += p.print(if_name);
|
||||
// }
|
||||
// In the interim, we just output the index number
|
||||
if (_zone > 0 && includeZone) {
|
||||
n += p.print('%');
|
||||
// look for the interface name
|
||||
for (netif *intf = netif_list; intf != nullptr; intf = intf->next) {
|
||||
if (_zone - 1 == intf->num) {
|
||||
n += p.print(intf->name[0]);
|
||||
n += p.print(intf->name[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
n += p.print(_zone - 1);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// IPv4
|
||||
for (int i = 0; i < 3; i++) {
|
||||
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
|
||||
n += p.print('.');
|
||||
}
|
||||
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
|
||||
return n;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const ip_addr_t *addr) {
|
||||
from_ip_addr_t(addr);
|
||||
}
|
||||
|
||||
void IPAddress::to_ip_addr_t(ip_addr_t *addr) const {
|
||||
#if CONFIG_LWIP_IPV6
|
||||
if (_type == IPv6) {
|
||||
addr->type = IPADDR_TYPE_V6;
|
||||
addr->u_addr.ip6.addr[0] = _address.dword[0];
|
||||
addr->u_addr.ip6.addr[1] = _address.dword[1];
|
||||
addr->u_addr.ip6.addr[2] = _address.dword[2];
|
||||
addr->u_addr.ip6.addr[3] = _address.dword[3];
|
||||
#if LWIP_IPV6_SCOPES
|
||||
addr->u_addr.ip6.zone = _zone;
|
||||
#endif /* LWIP_IPV6_SCOPES */
|
||||
} else {
|
||||
addr->type = IPADDR_TYPE_V4;
|
||||
addr->u_addr.ip4.addr = _address.dword[IPADDRESS_V4_DWORD_INDEX];
|
||||
}
|
||||
#else
|
||||
addr->addr = _address.dword[IPADDRESS_V4_DWORD_INDEX];
|
||||
#endif
|
||||
}
|
||||
|
||||
IPAddress &IPAddress::from_ip_addr_t(const ip_addr_t *addr) {
|
||||
#if CONFIG_LWIP_IPV6
|
||||
if (addr->type == IPADDR_TYPE_V6) {
|
||||
_type = IPv6;
|
||||
_address.dword[0] = addr->u_addr.ip6.addr[0];
|
||||
_address.dword[1] = addr->u_addr.ip6.addr[1];
|
||||
_address.dword[2] = addr->u_addr.ip6.addr[2];
|
||||
_address.dword[3] = addr->u_addr.ip6.addr[3];
|
||||
#if LWIP_IPV6_SCOPES
|
||||
_zone = addr->u_addr.ip6.zone;
|
||||
#endif /* LWIP_IPV6_SCOPES */
|
||||
} else {
|
||||
#endif
|
||||
_type = IPv4;
|
||||
memset(_address.bytes, 0, sizeof(_address.bytes));
|
||||
#if CONFIG_LWIP_IPV6
|
||||
_address.dword[IPADDRESS_V4_DWORD_INDEX] = addr->u_addr.ip4.addr;
|
||||
#else
|
||||
_address.dword[IPADDRESS_V4_DWORD_INDEX] = addr->addr;
|
||||
#endif
|
||||
#if CONFIG_LWIP_IPV6
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
esp_ip6_addr_type_t IPAddress::addr_type() const {
|
||||
if (_type != IPv6) {
|
||||
return ESP_IP6_ADDR_IS_UNKNOWN;
|
||||
}
|
||||
ip_addr_t addr;
|
||||
to_ip_addr_t(&addr);
|
||||
return esp_netif_ip6_get_addr_type((esp_ip6_addr_t *)(&(addr.u_addr.ip6)));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_LWIP_IPV6
|
||||
const IPAddress IN6ADDR_ANY(IPv6);
|
||||
#endif
|
||||
const IPAddress INADDR_NONE(0, 0, 0, 0);
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
IPAddress.h - Base class that provides IPAddress
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Printable.h"
|
||||
#include "WString.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define IPADDRESS_V4_BYTES_INDEX 12
|
||||
#define IPADDRESS_V4_DWORD_INDEX 3
|
||||
|
||||
// A class to make it easier to handle and pass around IP addresses
|
||||
|
||||
enum IPType {
|
||||
IPv4,
|
||||
IPv6
|
||||
};
|
||||
|
||||
class IPAddress : public Printable {
|
||||
private:
|
||||
union {
|
||||
uint8_t bytes[16];
|
||||
uint32_t dword[4];
|
||||
} _address;
|
||||
IPType _type;
|
||||
uint8_t _zone;
|
||||
|
||||
// Access the raw byte array containing the address. Because this returns a pointer
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t *raw_address() {
|
||||
return _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
|
||||
// Default IPv4
|
||||
IPAddress();
|
||||
IPAddress(IPType ip_type);
|
||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||
IPAddress(
|
||||
uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12,
|
||||
uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16, uint8_t z = 0
|
||||
);
|
||||
// IPv4; see implementation note
|
||||
IPAddress(uint32_t address);
|
||||
// Default IPv4
|
||||
IPAddress(const uint8_t *address);
|
||||
IPAddress(IPType ip_type, const uint8_t *address, uint8_t z = 0);
|
||||
// If IPv4 fails tries IPv6 see fromString function
|
||||
IPAddress(const char *address);
|
||||
IPAddress(const IPAddress &address);
|
||||
|
||||
bool fromString(const char *address);
|
||||
bool fromString(const String &address) {
|
||||
return fromString(address.c_str());
|
||||
}
|
||||
|
||||
// Overloaded cast operator to allow IPAddress objects to be used where a uint32_t is expected
|
||||
// NOTE: IPv4 only; see implementation note
|
||||
operator uint32_t() const {
|
||||
return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0;
|
||||
};
|
||||
|
||||
bool operator==(const IPAddress &addr) const;
|
||||
bool operator!=(const IPAddress &addr) const {
|
||||
return !(*this == addr);
|
||||
};
|
||||
|
||||
// NOTE: IPv4 only; we don't know the length of the pointer
|
||||
bool operator==(const uint8_t *addr) const;
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const;
|
||||
uint8_t &operator[](int index);
|
||||
|
||||
// Overloaded copy operators to allow initialization of IPAddress objects from other types
|
||||
// NOTE: IPv4 only
|
||||
IPAddress &operator=(const uint8_t *address);
|
||||
// NOTE: IPv4 only; see implementation note
|
||||
IPAddress &operator=(uint32_t address);
|
||||
// If IPv4 fails tries IPv6 see fromString function
|
||||
IPAddress &operator=(const char *address);
|
||||
IPAddress &operator=(const IPAddress &address);
|
||||
|
||||
virtual size_t printTo(Print &p) const;
|
||||
String toString(bool includeZone = false) const;
|
||||
|
||||
IPType type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
// Espresif LwIP conversions
|
||||
IPAddress(const ip_addr_t *addr);
|
||||
void to_ip_addr_t(ip_addr_t *addr) const;
|
||||
IPAddress &from_ip_addr_t(const ip_addr_t *addr);
|
||||
#if CONFIG_LWIP_IPV6
|
||||
esp_ip6_addr_type_t addr_type() const;
|
||||
#endif
|
||||
uint8_t zone() const {
|
||||
return (type() == IPv6) ? _zone : 0;
|
||||
}
|
||||
size_t printTo(Print &p, bool includeZone) const;
|
||||
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
friend class EthernetClass;
|
||||
friend class DhcpClass;
|
||||
friend class DNSClient;
|
||||
|
||||
protected:
|
||||
bool fromString4(const char *address);
|
||||
bool fromString6(const char *address);
|
||||
};
|
||||
|
||||
extern const IPAddress IN6ADDR_ANY;
|
||||
extern const IPAddress INADDR_NONE;
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright (c) 2015 Hristo Gochkov. 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
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "HEXBuilder.h"
|
||||
#include "MD5Builder.h"
|
||||
|
||||
void MD5Builder::begin(void) {
|
||||
memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN);
|
||||
esp_rom_md5_init(&_ctx);
|
||||
}
|
||||
|
||||
void MD5Builder::add(const uint8_t *data, size_t len) {
|
||||
esp_rom_md5_update(&_ctx, data, len);
|
||||
}
|
||||
|
||||
bool MD5Builder::addStream(Stream &stream, const size_t maxLen) {
|
||||
const int buf_size = 512;
|
||||
int maxLengthLeft = maxLen;
|
||||
uint8_t *buf = (uint8_t *)malloc(buf_size);
|
||||
|
||||
if (!buf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int bytesAvailable = stream.available();
|
||||
while ((bytesAvailable > 0) && (maxLengthLeft > 0)) {
|
||||
|
||||
// determine number of bytes to read
|
||||
int readBytes = bytesAvailable;
|
||||
if (readBytes > maxLengthLeft) {
|
||||
readBytes = maxLengthLeft; // read only until max_len
|
||||
}
|
||||
if (readBytes > buf_size) {
|
||||
readBytes = buf_size; // not read more the buffer can handle
|
||||
}
|
||||
|
||||
// read data and check if we got something
|
||||
int numBytesRead = stream.readBytes(buf, readBytes);
|
||||
if (numBytesRead < 1) {
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update MD5 with buffer payload
|
||||
esp_rom_md5_update(&_ctx, buf, numBytesRead);
|
||||
|
||||
// update available number of bytes
|
||||
maxLengthLeft -= numBytesRead;
|
||||
bytesAvailable = stream.available();
|
||||
}
|
||||
free(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MD5Builder::calculate(void) {
|
||||
esp_rom_md5_final(_buf, &_ctx);
|
||||
}
|
||||
|
||||
void MD5Builder::getBytes(uint8_t *output) {
|
||||
memcpy(output, _buf, ESP_ROM_MD5_DIGEST_LEN);
|
||||
}
|
||||
|
||||
void MD5Builder::getChars(char *output) {
|
||||
bytes2hex(output, ESP_ROM_MD5_DIGEST_LEN * 2 + 1, _buf, ESP_ROM_MD5_DIGEST_LEN);
|
||||
}
|
||||
|
||||
String MD5Builder::toString(void) {
|
||||
char out[(ESP_ROM_MD5_DIGEST_LEN * 2) + 1];
|
||||
getChars(out);
|
||||
return String(out);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
|
||||
This file is part of the esp32 core for Arduino environment.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified 10 Jan 2024 by Lucas Saavedra Vaz (Use abstract class HashBuilder)
|
||||
*/
|
||||
#ifndef MD5Builder_h
|
||||
#define MD5Builder_h
|
||||
|
||||
#include <WString.h>
|
||||
#include <Stream.h>
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_rom_md5.h"
|
||||
|
||||
#include "HashBuilder.h"
|
||||
|
||||
class MD5Builder : public HashBuilder {
|
||||
private:
|
||||
md5_context_t _ctx;
|
||||
uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN];
|
||||
|
||||
public:
|
||||
using HashBuilder::add;
|
||||
|
||||
void begin(void) override;
|
||||
void add(const uint8_t *data, size_t len) override;
|
||||
bool addStream(Stream &stream, const size_t maxLen) override;
|
||||
void calculate(void) override;
|
||||
void getBytes(uint8_t *output) override;
|
||||
void getChars(char *output) override;
|
||||
String toString(void) override;
|
||||
size_t getHashSize() const override {
|
||||
return ESP_ROM_MD5_DIGEST_LEN;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,246 @@
|
||||
#include <MacAddress.h>
|
||||
#include <stdio.h>
|
||||
#include <Print.h>
|
||||
|
||||
//Default constructor, blank mac address.
|
||||
MacAddress::MacAddress() : MacAddress(MAC6) {}
|
||||
|
||||
MacAddress::MacAddress(MACType mac_type) {
|
||||
_type = mac_type;
|
||||
memset(_mac.bytes, 0, sizeof(_mac.bytes));
|
||||
}
|
||||
MacAddress::MacAddress(MACType mac_type, uint64_t mac) {
|
||||
_type = mac_type;
|
||||
_mac.val = mac;
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(MACType mac_type, const uint8_t *macbytearray) {
|
||||
_type = mac_type;
|
||||
memset(_mac.bytes, 0, sizeof(_mac.bytes));
|
||||
if (_type == MAC6) {
|
||||
memcpy(_mac.bytes, macbytearray, 6);
|
||||
} else {
|
||||
memcpy(_mac.bytes, macbytearray, 8);
|
||||
}
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(const char *macstr) {
|
||||
fromString(macstr);
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(const String &macstr) {
|
||||
fromString(macstr.c_str());
|
||||
}
|
||||
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
MacAddress::MacAddress(std::initializer_list<uint8_t> list) {
|
||||
size_t size = list.size();
|
||||
memset(_mac.bytes, 0, sizeof(_mac.bytes));
|
||||
if (size == 6) {
|
||||
_type = MAC6;
|
||||
} else if (size == 8) {
|
||||
_type = MAC8;
|
||||
} else {
|
||||
// Default to MAC6 and keep the rest of the bytes as 0
|
||||
_type = MAC6;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(_mac.bytes, list.begin(), size);
|
||||
}
|
||||
#endif
|
||||
|
||||
MacAddress::MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6) {
|
||||
_type = MAC6;
|
||||
memset(_mac.bytes, 0, sizeof(_mac.bytes));
|
||||
_mac.bytes[0] = b1;
|
||||
_mac.bytes[1] = b2;
|
||||
_mac.bytes[2] = b3;
|
||||
_mac.bytes[3] = b4;
|
||||
_mac.bytes[4] = b5;
|
||||
_mac.bytes[5] = b6;
|
||||
}
|
||||
|
||||
MacAddress::MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, uint8_t b8) {
|
||||
_type = MAC8;
|
||||
_mac.bytes[0] = b1;
|
||||
_mac.bytes[1] = b2;
|
||||
_mac.bytes[2] = b3;
|
||||
_mac.bytes[3] = b4;
|
||||
_mac.bytes[4] = b5;
|
||||
_mac.bytes[5] = b6;
|
||||
_mac.bytes[6] = b7;
|
||||
_mac.bytes[7] = b8;
|
||||
}
|
||||
|
||||
//Parse user entered string into MAC address
|
||||
bool MacAddress::fromString(const char *buf) {
|
||||
if (strlen(buf) == 17) {
|
||||
return fromString6(buf);
|
||||
} else if (strlen(buf) == 23) {
|
||||
return fromString8(buf);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Parse user entered string into MAC address
|
||||
bool MacAddress::fromString6(const char *buf) {
|
||||
char cs[18]; // 17 + 1 for null terminator
|
||||
char *token;
|
||||
char *next; //Unused but required
|
||||
int i;
|
||||
|
||||
strncpy(cs, buf, sizeof(cs) - 1); //strtok modifies the buffer: copy to working buffer.
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
token = strtok((i == 0) ? cs : NULL, ":"); //Find first or next token
|
||||
if (!token) { //No more tokens found
|
||||
return false;
|
||||
}
|
||||
_mac.bytes[i] = strtol(token, &next, 16);
|
||||
}
|
||||
_type = MAC6;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacAddress::fromString8(const char *buf) {
|
||||
char cs[24]; // 23 + 1 for null terminator
|
||||
char *token;
|
||||
char *next; //Unused but required
|
||||
int i;
|
||||
|
||||
strncpy(cs, buf, sizeof(cs) - 1); //strtok modifies the buffer: copy to working buffer.
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
token = strtok((i == 0) ? cs : NULL, ":"); //Find first or next token
|
||||
if (!token) { //No more tokens found
|
||||
return false;
|
||||
}
|
||||
_mac.bytes[i] = strtol(token, &next, 16);
|
||||
}
|
||||
_type = MAC8;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Copy MAC into byte array
|
||||
void MacAddress::toBytes(uint8_t *buf) {
|
||||
if (_type == MAC6) {
|
||||
memcpy(buf, _mac.bytes, 6);
|
||||
} else {
|
||||
memcpy(buf, _mac.bytes, sizeof(_mac.bytes));
|
||||
}
|
||||
}
|
||||
|
||||
//Print MAC address into a C string.
|
||||
//MAC: Buffer must be at least 18 chars
|
||||
int MacAddress::toString(char *buf) {
|
||||
if (_type == MAC6) {
|
||||
return sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5]);
|
||||
} else {
|
||||
return sprintf(
|
||||
buf, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5], _mac.bytes[6],
|
||||
_mac.bytes[7]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String MacAddress::toString() const {
|
||||
uint8_t bytes = (_type == MAC6) ? 18 : 24;
|
||||
char buf[bytes];
|
||||
if (_type == MAC6) {
|
||||
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5]);
|
||||
} else {
|
||||
snprintf(
|
||||
buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", _mac.bytes[0], _mac.bytes[1], _mac.bytes[2], _mac.bytes[3], _mac.bytes[4], _mac.bytes[5],
|
||||
_mac.bytes[6], _mac.bytes[7]
|
||||
);
|
||||
}
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
uint64_t MacAddress::Value() {
|
||||
return _mac.val;
|
||||
}
|
||||
|
||||
//Allow getting individual octets of the address. e.g. uint8_t b0 = ma[0];
|
||||
uint8_t MacAddress::operator[](int index) const {
|
||||
index = EnforceIndexBounds(index);
|
||||
return _mac.bytes[index];
|
||||
}
|
||||
|
||||
//Allow setting individual octets of the address. e.g. ma[2] = 255;
|
||||
uint8_t &MacAddress::operator[](int index) {
|
||||
index = EnforceIndexBounds(index);
|
||||
return _mac.bytes[index];
|
||||
}
|
||||
|
||||
//Overloaded copy operator: init MacAddress object from byte array
|
||||
MacAddress &MacAddress::operator=(const uint8_t *macbytearray) {
|
||||
// 6-bytes MacAddress only
|
||||
_type = MAC6;
|
||||
memset(_mac.bytes, 0, sizeof(_mac.bytes));
|
||||
memcpy(_mac.bytes, macbytearray, 6);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//Overloaded copy operator: init MacAddress object from uint64_t
|
||||
MacAddress &MacAddress::operator=(uint64_t macval) {
|
||||
// 6-bytes MacAddress only
|
||||
_type = MAC6;
|
||||
_mac.val = macval;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//Compare class to byte array
|
||||
bool MacAddress::operator==(const uint8_t *macbytearray) const {
|
||||
return !memcmp(_mac.bytes, macbytearray, 6);
|
||||
}
|
||||
|
||||
//Allow comparing value of two classes
|
||||
bool MacAddress::operator==(const MacAddress &mac2) const {
|
||||
return _mac.val == mac2._mac.val;
|
||||
}
|
||||
|
||||
//Type converter object to uint64_t [same as .Value()]
|
||||
MacAddress::operator uint64_t() const {
|
||||
return _mac.val;
|
||||
}
|
||||
|
||||
//Type converter object to read only pointer to mac bytes. e.g. const uint8_t *ip_8 = ma;
|
||||
MacAddress::operator const uint8_t *() const {
|
||||
return _mac.bytes;
|
||||
}
|
||||
|
||||
//Type converter object to read only pointer to mac value. e.g. const uint32_t *ip_64 = ma;
|
||||
MacAddress::operator const uint64_t *() const {
|
||||
return &_mac.val;
|
||||
}
|
||||
|
||||
size_t MacAddress::printTo(Print &p) const {
|
||||
uint8_t bytes = (_type == MAC6) ? 6 : 8;
|
||||
size_t n = 0;
|
||||
for (int i = 0; i < bytes; i++) {
|
||||
if (i) {
|
||||
n += p.print(':');
|
||||
}
|
||||
n += p.printf("%02X", _mac.bytes[i]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
//Bounds checking
|
||||
int MacAddress::EnforceIndexBounds(int i) const {
|
||||
if (i < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (_type == MAC6) {
|
||||
if (i >= 6) {
|
||||
return 5;
|
||||
}
|
||||
} else {
|
||||
if (i >= 8) {
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MacAddress.h - class to make it easier to handle BSSID and MAC addresses.
|
||||
//
|
||||
// Copyright 2022 David McCurley
|
||||
// Modified by Espressif Systems 2024
|
||||
//
|
||||
// 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 MacAddress_h
|
||||
#define MacAddress_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <WString.h>
|
||||
#include <Printable.h>
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
#include <initializer_list>
|
||||
#endif
|
||||
|
||||
enum MACType {
|
||||
MAC6,
|
||||
MAC8
|
||||
};
|
||||
|
||||
// A class to make it easier to handle and pass around MAC addresses, supporting both 6-byte and 8-byte MAC addresses.
|
||||
class MacAddress : public Printable {
|
||||
private:
|
||||
union {
|
||||
uint8_t bytes[8];
|
||||
uint64_t val;
|
||||
} _mac;
|
||||
MACType _type;
|
||||
|
||||
public:
|
||||
//Default MAC6
|
||||
MacAddress();
|
||||
|
||||
MacAddress(MACType mac_type);
|
||||
MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6);
|
||||
MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, uint8_t b8);
|
||||
|
||||
MacAddress(MACType mac_type, uint64_t mac);
|
||||
MacAddress(MACType mac_type, const uint8_t *macbytearray);
|
||||
|
||||
//Default MAC6
|
||||
MacAddress(uint64_t mac) : MacAddress(MAC6, mac) {}
|
||||
MacAddress(const uint8_t *macbytearray) : MacAddress(MAC6, macbytearray) {}
|
||||
|
||||
MacAddress(const char *macstr);
|
||||
MacAddress(const String &macstr);
|
||||
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
// Initializer list constructor for {0xAA, 0xBB, ...} syntax
|
||||
// This has higher priority than String conversion, preventing ambiguity
|
||||
MacAddress(std::initializer_list<uint8_t> list);
|
||||
#endif
|
||||
|
||||
virtual ~MacAddress() {}
|
||||
|
||||
bool fromString(const char *buf);
|
||||
bool fromString(const String &macstr) {
|
||||
return fromString(macstr.c_str());
|
||||
}
|
||||
|
||||
void toBytes(uint8_t *buf);
|
||||
int toString(char *buf);
|
||||
String toString() const;
|
||||
uint64_t Value();
|
||||
|
||||
uint8_t operator[](int index) const;
|
||||
uint8_t &operator[](int index);
|
||||
|
||||
//MAC6 only
|
||||
MacAddress &operator=(const uint8_t *macbytearray);
|
||||
MacAddress &operator=(uint64_t macval);
|
||||
|
||||
bool operator==(const uint8_t *macbytearray) const;
|
||||
bool operator==(const MacAddress &mac2) const;
|
||||
operator uint64_t() const;
|
||||
operator const uint8_t *() const;
|
||||
operator const uint64_t *() const;
|
||||
|
||||
virtual size_t printTo(Print &p) const;
|
||||
|
||||
// future use in Arduino Networking
|
||||
/*
|
||||
friend class EthernetClass;
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
friend class DhcpClass;
|
||||
friend class DNSClient;
|
||||
*/
|
||||
|
||||
protected:
|
||||
bool fromString6(const char *buf);
|
||||
bool fromString8(const char *buf);
|
||||
|
||||
private:
|
||||
int EnforceIndexBounds(int i) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
Print.cpp - Base class that provides print() and println()
|
||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified 23 November 2006 by David A. Mellis
|
||||
Modified December 2014 by Ivan Grokhotkov
|
||||
Modified May 2015 by Michael C. Miller - ESP31B progmem support
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "Print.h"
|
||||
extern "C" {
|
||||
#include "time.h"
|
||||
}
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
/* default implementation: may be overridden */
|
||||
size_t Print::write(const uint8_t *buffer, size_t size) {
|
||||
size_t n = 0;
|
||||
while (size--) {
|
||||
n += write(*buffer++);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::vprintf(const char *format, va_list arg) {
|
||||
char loc_buf[64];
|
||||
char *temp = loc_buf;
|
||||
va_list copy;
|
||||
va_copy(copy, arg);
|
||||
int len = vsnprintf(temp, sizeof(loc_buf), format, copy);
|
||||
va_end(copy);
|
||||
if (len < 0) {
|
||||
va_end(arg);
|
||||
return 0;
|
||||
}
|
||||
if (len >= (int)sizeof(loc_buf)) { // comparison of same sign type for the compiler
|
||||
temp = (char *)malloc(len + 1);
|
||||
if (temp == NULL) {
|
||||
va_end(arg);
|
||||
return 0;
|
||||
}
|
||||
len = vsnprintf(temp, len + 1, format, arg);
|
||||
}
|
||||
va_end(arg);
|
||||
len = write((uint8_t *)temp, len);
|
||||
if (temp != loc_buf) {
|
||||
free(temp);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Print::printf(const __FlashStringHelper *ifsh, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, ifsh);
|
||||
const char *format = (reinterpret_cast<const char *>(ifsh));
|
||||
size_t ret = vprintf(format, arg);
|
||||
va_end(arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t Print::printf(const char *format, ...) {
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
size_t ret = vprintf(format, arg);
|
||||
va_end(arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t Print::print(const String &s) {
|
||||
return write(s.c_str(), s.length());
|
||||
}
|
||||
|
||||
size_t Print::print(const char str[]) {
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::print(char c) {
|
||||
return write(c);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned char b, int base) {
|
||||
return print((unsigned long)b, base);
|
||||
}
|
||||
|
||||
size_t Print::print(int n, int base) {
|
||||
return print((long)n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned int n, int base) {
|
||||
return print((unsigned long)n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(long n, int base) {
|
||||
int t = 0;
|
||||
if (base == 10 && n < 0) {
|
||||
t = print('-');
|
||||
n = -n;
|
||||
}
|
||||
return printNumber(static_cast<unsigned long>(n), base) + t;
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned long n, int base) {
|
||||
if (base == 0) {
|
||||
return write(n);
|
||||
} else {
|
||||
return printNumber(n, base);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Print::print(long long n, int base) {
|
||||
int t = 0;
|
||||
if (base == 10 && n < 0) {
|
||||
t = print('-');
|
||||
n = -n;
|
||||
}
|
||||
return printNumber(static_cast<unsigned long long>(n), base) + t;
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned long long n, int base) {
|
||||
if (base == 0) {
|
||||
return write(n);
|
||||
} else {
|
||||
return printNumber(n, base);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Print::print(double n, int digits) {
|
||||
return printFloat(n, digits);
|
||||
}
|
||||
|
||||
size_t Print::print(const Printable &x) {
|
||||
return x.printTo(*this);
|
||||
}
|
||||
|
||||
size_t Print::print(struct tm *timeinfo, const char *format) {
|
||||
const char *f = format;
|
||||
if (!f) {
|
||||
f = "%c";
|
||||
}
|
||||
char buf[64];
|
||||
size_t written = strftime(buf, 64, f, timeinfo);
|
||||
if (written == 0) {
|
||||
return written;
|
||||
}
|
||||
return print(buf);
|
||||
}
|
||||
|
||||
size_t Print::println(void) {
|
||||
return print("\r\n");
|
||||
}
|
||||
|
||||
size_t Print::println(const String &s) {
|
||||
size_t n = print(s);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(const char c[]) {
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(char c) {
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned char b, int base) {
|
||||
size_t n = print(b, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(int num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned int num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(long num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned long num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(long long num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned long long num, int base) {
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(double num, int digits) {
|
||||
size_t n = print(num, digits);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(const Printable &x) {
|
||||
size_t n = print(x);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(struct tm *timeinfo, const char *format) {
|
||||
size_t n = print(timeinfo, format);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
// Private Methods /////////////////////////////////////////////////////////////
|
||||
|
||||
size_t Print::printNumber(unsigned long n, uint8_t base) {
|
||||
char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||
char *str = &buf[sizeof(buf) - 1];
|
||||
|
||||
*str = '\0';
|
||||
|
||||
// prevent crash if called with base == 1
|
||||
if (base < 2) {
|
||||
base = 10;
|
||||
}
|
||||
|
||||
do {
|
||||
char c = n % base;
|
||||
n /= base;
|
||||
|
||||
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while (n);
|
||||
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::printNumber(unsigned long long n, uint8_t base) {
|
||||
char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||
char *str = &buf[sizeof(buf) - 1];
|
||||
|
||||
*str = '\0';
|
||||
|
||||
// prevent crash if called with base == 1
|
||||
if (base < 2) {
|
||||
base = 10;
|
||||
}
|
||||
|
||||
do {
|
||||
auto m = n;
|
||||
n /= base;
|
||||
char c = m - base * n;
|
||||
|
||||
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while (n);
|
||||
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::printFloat(double number, uint8_t digits) {
|
||||
size_t n = 0;
|
||||
|
||||
if (isnan(number)) {
|
||||
return print("nan");
|
||||
}
|
||||
if (isinf(number)) {
|
||||
return print("inf");
|
||||
}
|
||||
if (number > 4294967040.0) {
|
||||
return print("ovf"); // constant determined empirically
|
||||
}
|
||||
if (number < -4294967040.0) {
|
||||
return print("ovf"); // constant determined empirically
|
||||
}
|
||||
|
||||
// Handle negative numbers
|
||||
if (number < 0.0) {
|
||||
n += print('-');
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for (uint8_t i = 0; i < digits; ++i) {
|
||||
rounding /= 10.0;
|
||||
}
|
||||
|
||||
number += rounding;
|
||||
|
||||
// Extract the integer part of the number and print it
|
||||
unsigned long int_part = (unsigned long)number;
|
||||
double remainder = number - (double)int_part;
|
||||
n += print(int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if (digits > 0) {
|
||||
n += print(".");
|
||||
}
|
||||
|
||||
// Extract digits from the remainder one at a time
|
||||
while (digits-- > 0) {
|
||||
remainder *= 10.0;
|
||||
int toPrint = int(remainder);
|
||||
n += print(toPrint);
|
||||
remainder -= toPrint;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Print.h - Base class that provides print() and println()
|
||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef Print_h
|
||||
#define Print_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "WString.h"
|
||||
#include "Printable.h"
|
||||
|
||||
#define DEC 10
|
||||
#define HEX 16
|
||||
#define OCT 8
|
||||
#define BIN 2
|
||||
|
||||
class Print {
|
||||
private:
|
||||
int write_error;
|
||||
size_t printNumber(unsigned long, uint8_t);
|
||||
size_t printNumber(unsigned long long, uint8_t);
|
||||
size_t printFloat(double, uint8_t);
|
||||
|
||||
protected:
|
||||
void setWriteError(int err = 1) {
|
||||
write_error = err;
|
||||
}
|
||||
|
||||
public:
|
||||
Print() : write_error(0) {}
|
||||
virtual ~Print() {}
|
||||
int getWriteError() {
|
||||
return write_error;
|
||||
}
|
||||
void clearWriteError() {
|
||||
setWriteError(0);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
size_t write(const char *str) {
|
||||
if (str == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return write((const uint8_t *)str, strlen(str));
|
||||
}
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
size_t write(const char *buffer, size_t size) {
|
||||
return write((const uint8_t *)buffer, size);
|
||||
}
|
||||
|
||||
size_t vprintf(const char *format, va_list arg);
|
||||
|
||||
size_t printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
|
||||
size_t printf(const __FlashStringHelper *ifsh, ...);
|
||||
|
||||
// add availableForWrite to make compatible with Arduino Print.h
|
||||
// default to zero, meaning "a single write may block"
|
||||
// should be overridden by subclasses with buffering
|
||||
virtual int availableForWrite() {
|
||||
return 0;
|
||||
}
|
||||
size_t print(const __FlashStringHelper *ifsh) {
|
||||
return print(reinterpret_cast<const char *>(ifsh));
|
||||
}
|
||||
size_t print(const String &);
|
||||
size_t print(const char[]);
|
||||
size_t print(char);
|
||||
size_t print(unsigned char, int = DEC);
|
||||
size_t print(int, int = DEC);
|
||||
size_t print(unsigned int, int = DEC);
|
||||
size_t print(long, int = DEC);
|
||||
size_t print(unsigned long, int = DEC);
|
||||
size_t print(long long, int = DEC);
|
||||
size_t print(unsigned long long, int = DEC);
|
||||
size_t print(double, int = 2);
|
||||
size_t print(const Printable &);
|
||||
size_t print(struct tm *timeinfo, const char *format = NULL);
|
||||
|
||||
size_t println(const __FlashStringHelper *ifsh) {
|
||||
return println(reinterpret_cast<const char *>(ifsh));
|
||||
}
|
||||
size_t println(const String &s);
|
||||
size_t println(const char[]);
|
||||
size_t println(char);
|
||||
size_t println(unsigned char, int = DEC);
|
||||
size_t println(int, int = DEC);
|
||||
size_t println(unsigned int, int = DEC);
|
||||
size_t println(long, int = DEC);
|
||||
size_t println(unsigned long, int = DEC);
|
||||
size_t println(long long, int = DEC);
|
||||
size_t println(unsigned long long, int = DEC);
|
||||
size_t println(double, int = 2);
|
||||
size_t println(const Printable &);
|
||||
size_t println(struct tm *timeinfo, const char *format = NULL);
|
||||
size_t println(void);
|
||||
|
||||
virtual void flush() { /* Empty implementation for backward compatibility */ }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Printable.h - Interface class that allows printing of complex types
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef Printable_h
|
||||
#define Printable_h
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
class Print;
|
||||
|
||||
/** The Printable class provides a way for new classes to allow themselves to be printed.
|
||||
By deriving from Printable and implementing the printTo method, it will then be possible
|
||||
for users to print out instances of this class by passing them into the usual
|
||||
Print::print and Print::println methods.
|
||||
*/
|
||||
|
||||
class Printable {
|
||||
public:
|
||||
virtual ~Printable() {}
|
||||
virtual size_t printTo(Print &p) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Server.h - Base class that provides Server
|
||||
Copyright (c) 2011 Adrian McEwen. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef server_h
|
||||
#define server_h
|
||||
|
||||
#include "Print.h"
|
||||
|
||||
class Server : public Print {
|
||||
public:
|
||||
virtual void begin() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
Stream.cpp - adds parsing methods to Stream class
|
||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Created July 2011
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
|
||||
findMulti/findUntil routines written by Jim Leonard/Xuth
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
|
||||
|
||||
// private method to read stream with timeout
|
||||
int Stream::timedRead() {
|
||||
int c;
|
||||
_startMillis = millis();
|
||||
do {
|
||||
c = read();
|
||||
if (c >= 0) {
|
||||
return c;
|
||||
}
|
||||
} while (millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// private method to peek stream with timeout
|
||||
int Stream::timedPeek() {
|
||||
int c;
|
||||
_startMillis = millis();
|
||||
do {
|
||||
c = peek();
|
||||
if (c >= 0) {
|
||||
return c;
|
||||
}
|
||||
} while (millis() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// returns peek of the next digit in the stream or -1 if timeout
|
||||
// discards non-numeric characters
|
||||
int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal) {
|
||||
int c;
|
||||
while (1) {
|
||||
c = timedPeek();
|
||||
|
||||
if (c < 0 || c == '-' || (c >= '0' && c <= '9') || (detectDecimal && c == '.')) {
|
||||
return c;
|
||||
}
|
||||
|
||||
switch (lookahead) {
|
||||
case SKIP_NONE: return -1; // Fail code.
|
||||
case SKIP_WHITESPACE:
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n': break;
|
||||
default: return -1; // Fail code.
|
||||
}
|
||||
case SKIP_ALL: break;
|
||||
}
|
||||
read(); // discard non-numeric
|
||||
}
|
||||
}
|
||||
|
||||
// Public Methods
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait
|
||||
{
|
||||
_timeout = timeout;
|
||||
}
|
||||
|
||||
// find returns true if the target string is found
|
||||
bool Stream::find(const char *target) {
|
||||
return findUntil(target, strlen(target), NULL, 0);
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of given length is found
|
||||
// returns true if target string is found, false if timed out
|
||||
bool Stream::find(const char *target, size_t length) {
|
||||
return findUntil(target, length, NULL, 0);
|
||||
}
|
||||
|
||||
// as find but search ends if the terminator string is found
|
||||
bool Stream::findUntil(const char *target, const char *terminator) {
|
||||
return findUntil(target, strlen(target), terminator, strlen(terminator));
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of the given length is found
|
||||
// search terminated if the terminator string is found
|
||||
// returns true if target string is found, false if terminated or timed out
|
||||
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) {
|
||||
if (terminator == NULL) {
|
||||
MultiTarget t[1] = {{target, targetLen, 0}};
|
||||
return findMulti(t, 1) == 0;
|
||||
} else {
|
||||
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
|
||||
return findMulti(t, 2) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// returns the first valid (long) integer value from the current position.
|
||||
// lookahead determines how parseInt looks ahead in the stream.
|
||||
// See LookaheadMode enumeration at the top of the file.
|
||||
// Lookahead is terminated by the first character that is not a valid part of an integer.
|
||||
// Once parsing commences, 'ignore' will be skipped in the stream.
|
||||
long Stream::parseInt(LookaheadMode lookahead, char ignore) {
|
||||
bool isNegative = false;
|
||||
long value = 0;
|
||||
int c;
|
||||
|
||||
c = peekNextDigit(lookahead, false);
|
||||
// ignore non numeric leading characters
|
||||
if (c < 0) {
|
||||
return 0; // zero returned if timeout
|
||||
}
|
||||
|
||||
do {
|
||||
if ((char)c == ignore)
|
||||
; // ignore this character
|
||||
else if (c == '-') {
|
||||
isNegative = true;
|
||||
} else if (c >= '0' && c <= '9') { // is c a digit?
|
||||
value = value * 10 + c - '0';
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while ((c >= '0' && c <= '9') || (char)c == ignore);
|
||||
|
||||
if (isNegative) {
|
||||
value = -value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// as parseInt but returns a floating point value
|
||||
float Stream::parseFloat(LookaheadMode lookahead, char ignore) {
|
||||
bool isNegative = false;
|
||||
bool isFraction = false;
|
||||
double value = 0.0;
|
||||
int c;
|
||||
double fraction = 1.0;
|
||||
|
||||
c = peekNextDigit(lookahead, true);
|
||||
// ignore non numeric leading characters
|
||||
if (c < 0) {
|
||||
return 0; // zero returned if timeout
|
||||
}
|
||||
|
||||
do {
|
||||
if ((char)c == ignore)
|
||||
; // ignore
|
||||
else if (c == '-') {
|
||||
isNegative = true;
|
||||
} else if (c == '.') {
|
||||
isFraction = true;
|
||||
} else if (c >= '0' && c <= '9') { // is c a digit?
|
||||
if (isFraction) {
|
||||
fraction *= 0.1;
|
||||
value = value + fraction * (c - '0');
|
||||
} else {
|
||||
value = value * 10 + c - '0';
|
||||
}
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while ((c >= '0' && c <= '9') || (c == '.' && !isFraction) || (char)c == ignore);
|
||||
|
||||
if (isNegative) {
|
||||
value = -value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// read characters from stream into buffer
|
||||
// terminates if length characters have been read, or timeout (see setTimeout)
|
||||
// returns the number of characters placed in the buffer
|
||||
// the buffer is NOT null terminated.
|
||||
//
|
||||
size_t Stream::readBytes(char *buffer, size_t length) {
|
||||
size_t count = 0;
|
||||
while (count < length) {
|
||||
int c = timedRead();
|
||||
if (c < 0) {
|
||||
break;
|
||||
}
|
||||
*buffer++ = (char)c;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// as readBytes with terminator character
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) {
|
||||
size_t index = 0;
|
||||
while (index < length) {
|
||||
int c = timedRead();
|
||||
if (c < 0 || (char)c == terminator) {
|
||||
break;
|
||||
}
|
||||
*buffer++ = (char)c;
|
||||
index++;
|
||||
}
|
||||
return index; // return number of characters, not including null terminator
|
||||
}
|
||||
|
||||
String Stream::readString() {
|
||||
String ret;
|
||||
int c = timedRead();
|
||||
while (c >= 0) {
|
||||
ret += (char)c;
|
||||
c = timedRead();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String Stream::readStringUntil(char terminator) {
|
||||
String ret;
|
||||
int c = timedRead();
|
||||
while (c >= 0 && (char)c != terminator) {
|
||||
ret += (char)c;
|
||||
c = timedRead();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Stream::findMulti(struct Stream::MultiTarget *targets, int tCount) {
|
||||
// any zero length target string automatically matches and would make
|
||||
// a mess of the rest of the algorithm.
|
||||
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
|
||||
if (t->len <= 0) {
|
||||
return t - targets;
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int c = timedRead();
|
||||
if (c < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
|
||||
// the simple case is if we match, deal with that first.
|
||||
if ((char)c == t->str[t->index]) {
|
||||
if (++t->index == t->len) {
|
||||
return t - targets;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// if not we need to walk back and see if we could have matched further
|
||||
// down the stream (ie '1112' doesn't match the first position in '11112'
|
||||
// but it will match the second position so we can't just reset the current
|
||||
// index to 0 when we find a mismatch.
|
||||
if (t->index == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int origIndex = t->index;
|
||||
do {
|
||||
--t->index;
|
||||
// first check if current char works against the new current index
|
||||
if ((char)c != t->str[t->index]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it's the only char then we're good, nothing more to check
|
||||
if (t->index == 0) {
|
||||
t->index++;
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise we need to check the rest of the found string
|
||||
int diff = origIndex - t->index;
|
||||
size_t i;
|
||||
for (i = 0; i < t->index; ++i) {
|
||||
if (t->str[i] != t->str[i + diff]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we successfully got through the previous loop then our current
|
||||
// index is good.
|
||||
if (i == t->index) {
|
||||
t->index++;
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise we just try the next index
|
||||
} while (t->index);
|
||||
}
|
||||
}
|
||||
// unreachable
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Stream.h - base class for character-based streams.
|
||||
Copyright (c) 2010 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
|
||||
// compatibility macros for testing
|
||||
/*
|
||||
#define getInt() parseInt()
|
||||
#define getInt(ignore) parseInt(ignore)
|
||||
#define getFloat() parseFloat()
|
||||
#define getFloat(ignore) parseFloat(ignore)
|
||||
#define getString( pre_string, post_string, buffer, length)
|
||||
readBytesBetween( pre_string, terminator, buffer, length)
|
||||
*/
|
||||
|
||||
// This enumeration provides the lookahead options for parseInt(), parseFloat()
|
||||
// The rules set out here are used until either the first valid character is found
|
||||
// or a time out occurs due to lack of input.
|
||||
enum LookaheadMode {
|
||||
SKIP_ALL, // All invalid characters are ignored.
|
||||
SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid.
|
||||
SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped.
|
||||
};
|
||||
|
||||
#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field
|
||||
|
||||
class Stream : public Print {
|
||||
protected:
|
||||
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
|
||||
unsigned long _startMillis; // used for timeout measurement
|
||||
int timedRead(); // private method to read stream with timeout
|
||||
int timedPeek(); // private method to peek stream with timeout
|
||||
int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout
|
||||
|
||||
public:
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int peek() = 0;
|
||||
|
||||
Stream() {
|
||||
_timeout = 1000;
|
||||
}
|
||||
|
||||
// parsing methods
|
||||
|
||||
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||
unsigned long getTimeout(void) {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
bool find(const char *target); // reads data from the stream until the target string is found
|
||||
bool find(const uint8_t *target) {
|
||||
return find((const char *)target);
|
||||
}
|
||||
// returns true if target string is found, false if timed out (see setTimeout)
|
||||
|
||||
bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found
|
||||
bool find(const uint8_t *target, size_t length) {
|
||||
return find((const char *)target, length);
|
||||
}
|
||||
// returns true if target string is found, false if timed out
|
||||
|
||||
bool find(char target) {
|
||||
return find(&target, 1);
|
||||
}
|
||||
|
||||
bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found
|
||||
bool findUntil(const uint8_t *target, const char *terminator) {
|
||||
return findUntil((const char *)target, terminator);
|
||||
}
|
||||
|
||||
bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found
|
||||
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) {
|
||||
return findUntil((const char *)target, targetLen, terminate, termLen);
|
||||
}
|
||||
|
||||
long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
|
||||
// returns the first valid (long) integer value from the current position.
|
||||
// lookahead determines how parseInt looks ahead in the stream.
|
||||
// See LookaheadMode enumeration at the top of the file.
|
||||
// Lookahead is terminated by the first character that is not a valid part of an integer.
|
||||
// Once parsing commences, 'ignore' will be skipped in the stream.
|
||||
|
||||
float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
|
||||
// float version of parseInt
|
||||
|
||||
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
|
||||
virtual size_t readBytes(uint8_t *buffer, size_t length) {
|
||||
return readBytes((char *)buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read or timeout (see setTimeout)
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character
|
||||
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length) {
|
||||
return readBytesUntil(terminator, (char *)buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
// Arduino String functions to be added here
|
||||
virtual String readString();
|
||||
String readStringUntil(char terminator);
|
||||
|
||||
protected:
|
||||
long parseInt(char ignore) {
|
||||
return parseInt(SKIP_ALL, ignore);
|
||||
}
|
||||
float parseFloat(char ignore) {
|
||||
return parseFloat(SKIP_ALL, ignore);
|
||||
}
|
||||
// These overload exists for compatibility with any class that has derived
|
||||
// Stream and used parseFloat/Int with a custom ignore character. To keep
|
||||
// the public API simple, these overload remains protected.
|
||||
|
||||
struct MultiTarget {
|
||||
const char *str; // string you're searching for
|
||||
size_t len; // length of string you're searching for
|
||||
size_t index; // index used by the search routine.
|
||||
};
|
||||
|
||||
// This allows you to search for an arbitrary number of strings.
|
||||
// Returns index of the target that is found first or -1 if timeout occurs.
|
||||
int findMulti(struct MultiTarget *targets, int tCount);
|
||||
};
|
||||
|
||||
#undef NO_IGNORE_CHAR
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
StreamString.cpp
|
||||
|
||||
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
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "StreamString.h"
|
||||
|
||||
size_t StreamString::write(const uint8_t *data, size_t size) {
|
||||
if (size && data) {
|
||||
const unsigned int newlen = length() + size;
|
||||
if (reserve(newlen + 1)) {
|
||||
memcpy((void *)(wbuffer() + len()), (const void *)data, size);
|
||||
setLen(newlen);
|
||||
*(wbuffer() + newlen) = 0x00; // add null for string end
|
||||
return size;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t StreamString::write(uint8_t data) {
|
||||
return concat((char)data);
|
||||
}
|
||||
|
||||
int StreamString::available() {
|
||||
return length();
|
||||
}
|
||||
|
||||
int StreamString::read() {
|
||||
if (length()) {
|
||||
char c = charAt(0);
|
||||
remove(0, 1);
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int StreamString::peek() {
|
||||
if (length()) {
|
||||
char c = charAt(0);
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void StreamString::flush() {}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
StreamString.h
|
||||
|
||||
Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef STREAMSTRING_H_
|
||||
#define STREAMSTRING_H_
|
||||
#include "Stream.h"
|
||||
#include "WString.h"
|
||||
|
||||
class StreamString : public Stream, public String {
|
||||
public:
|
||||
size_t write(const uint8_t *buffer, size_t size) override;
|
||||
size_t write(uint8_t data) override;
|
||||
|
||||
int available() override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
};
|
||||
|
||||
#endif /* STREAMSTRING_H_ */
|
||||
@@ -0,0 +1,152 @@
|
||||
#include <Arduino.h>
|
||||
#include "esp32-hal-ledc.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#if SOC_LEDC_SUPPORTED
|
||||
static TaskHandle_t _tone_task = NULL;
|
||||
static QueueHandle_t _tone_queue = NULL;
|
||||
static int8_t _pin = -1;
|
||||
static uint8_t _channel = 255;
|
||||
|
||||
typedef enum {
|
||||
TONE_START,
|
||||
TONE_END
|
||||
} tone_cmd_t;
|
||||
|
||||
typedef struct {
|
||||
tone_cmd_t tone_cmd;
|
||||
uint8_t pin;
|
||||
unsigned int frequency;
|
||||
unsigned long duration;
|
||||
} tone_msg_t;
|
||||
|
||||
#ifdef SOC_LEDC_SUPPORT_HS_MODE
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1)
|
||||
#else
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
|
||||
#endif
|
||||
|
||||
static void tone_task(void *) {
|
||||
tone_msg_t tone_msg;
|
||||
while (1) {
|
||||
xQueueReceive(_tone_queue, &tone_msg, portMAX_DELAY);
|
||||
switch (tone_msg.tone_cmd) {
|
||||
case TONE_START:
|
||||
log_d("Task received from queue TONE_START: pin=%d, frequency=%u Hz, duration=%lu ms", tone_msg.pin, tone_msg.frequency, tone_msg.duration);
|
||||
|
||||
if (_pin == -1) {
|
||||
bool ret = true;
|
||||
if (_channel == 255) {
|
||||
ret = ledcAttach(tone_msg.pin, tone_msg.frequency, 10);
|
||||
} else {
|
||||
ret = ledcAttachChannel(tone_msg.pin, tone_msg.frequency, 10, _channel);
|
||||
}
|
||||
if (!ret) {
|
||||
log_e("Tone start failed");
|
||||
break;
|
||||
}
|
||||
_pin = tone_msg.pin;
|
||||
}
|
||||
ledcWriteTone(tone_msg.pin, tone_msg.frequency);
|
||||
|
||||
if (tone_msg.duration) {
|
||||
delay(tone_msg.duration);
|
||||
ledcWriteTone(tone_msg.pin, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case TONE_END:
|
||||
log_d("Task received from queue TONE_END: pin=%d", tone_msg.pin);
|
||||
ledcWriteTone(tone_msg.pin, 0);
|
||||
ledcDetach(tone_msg.pin);
|
||||
_pin = -1;
|
||||
break;
|
||||
|
||||
default:; // do nothing
|
||||
} // switch
|
||||
} // infinite loop
|
||||
}
|
||||
|
||||
static int tone_init() {
|
||||
if (_tone_queue == NULL) {
|
||||
log_v("Creating tone queue");
|
||||
_tone_queue = xQueueCreate(128, sizeof(tone_msg_t));
|
||||
if (_tone_queue == NULL) {
|
||||
log_e("Could not create tone queue");
|
||||
return 0; // ERR
|
||||
}
|
||||
log_v("Tone queue created");
|
||||
}
|
||||
|
||||
if (_tone_task == NULL) {
|
||||
log_v("Creating tone task");
|
||||
xTaskCreate(
|
||||
tone_task, // Function to implement the task
|
||||
"toneTask", // Name of the task
|
||||
3500, // Stack size in words
|
||||
NULL, // Task input parameter
|
||||
10, // Priority of the task must be higher than Arduino task
|
||||
&_tone_task // Task handle.
|
||||
);
|
||||
if (_tone_task == NULL) {
|
||||
log_e("Could not create tone task");
|
||||
return 0; // ERR
|
||||
}
|
||||
log_v("Tone task created");
|
||||
}
|
||||
return 1; // OK
|
||||
}
|
||||
|
||||
void noTone(uint8_t pin) {
|
||||
log_d("noTone was called");
|
||||
if (_pin == pin) {
|
||||
if (tone_init()) {
|
||||
tone_msg_t tone_msg = {
|
||||
.tone_cmd = TONE_END,
|
||||
.pin = pin,
|
||||
.frequency = 0, // Ignored
|
||||
.duration = 0, // Ignored
|
||||
};
|
||||
xQueueReset(_tone_queue); // clear queue
|
||||
xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
|
||||
}
|
||||
} else {
|
||||
log_e("Tone is not running on given pin %d", pin);
|
||||
}
|
||||
}
|
||||
|
||||
// parameters:
|
||||
// pin - pin number which will output the signal
|
||||
// frequency - PWM frequency in Hz
|
||||
// duration - time in ms - how long will the signal be outputted.
|
||||
// If not provided, or 0 you must manually call noTone to end output
|
||||
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
log_d("pin=%d, frequency=%u Hz, duration=%lu ms", pin, frequency, duration);
|
||||
if (_pin == -1 || _pin == pin) {
|
||||
if (tone_init()) {
|
||||
tone_msg_t tone_msg = {
|
||||
.tone_cmd = TONE_START,
|
||||
.pin = pin,
|
||||
.frequency = frequency,
|
||||
.duration = duration,
|
||||
};
|
||||
xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log_e("Tone is still running on pin %d, call noTone(%d) first!", _pin, _pin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void setToneChannel(uint8_t channel) {
|
||||
if (channel >= LEDC_CHANNELS) {
|
||||
log_e("Channel %u is not available (maximum %u)!", channel, LEDC_CHANNELS);
|
||||
return;
|
||||
}
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
#endif /* SOC_LEDC_SUPPORTED */
|
||||
@@ -0,0 +1,358 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "USB.h"
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#if CONFIG_TINYUSB_ENABLED
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-tinyusb.h"
|
||||
#include "common/tusb_common.h"
|
||||
#include "StreamString.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "esp_mac.h"
|
||||
|
||||
#ifndef USB_VID
|
||||
#define USB_VID USB_ESPRESSIF_VID
|
||||
#endif
|
||||
#ifndef USB_PID
|
||||
#define USB_PID 0x0002
|
||||
#endif
|
||||
#ifndef USB_MANUFACTURER
|
||||
#define USB_MANUFACTURER "Espressif Systems"
|
||||
#endif
|
||||
#ifndef USB_PRODUCT
|
||||
#define USB_PRODUCT ARDUINO_BOARD
|
||||
#endif
|
||||
#ifndef USB_SERIAL
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
#define USB_SERIAL "__MAC__"
|
||||
#else
|
||||
#define USB_SERIAL "0"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef USB_WEBUSB_ENABLED
|
||||
#define USB_WEBUSB_ENABLED false
|
||||
#endif
|
||||
#ifndef USB_WEBUSB_URL
|
||||
#define USB_WEBUSB_URL "https://docs.espressif.com/projects/arduino-esp32/en/latest/_static/webusb.html"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_DFU
|
||||
__attribute__((weak)) uint16_t load_dfu_ota_descriptor(uint8_t *dst, uint8_t *itf) {
|
||||
return 0;
|
||||
}
|
||||
#elif CFG_TUD_DFU_RUNTIME
|
||||
static uint16_t load_dfu_descriptor(uint8_t *dst, uint8_t *itf) {
|
||||
#define DFU_ATTRS (DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_CAN_UPLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB DFU_RT");
|
||||
uint8_t descriptor[TUD_DFU_RT_DESC_LEN] = {// Interface number, string index, attributes, detach timeout, transfer size */
|
||||
TUD_DFU_RT_DESCRIPTOR(*itf, str_index, DFU_ATTRS, 700, 64)
|
||||
};
|
||||
*itf += 1;
|
||||
memcpy(dst, descriptor, TUD_DFU_RT_DESC_LEN);
|
||||
return TUD_DFU_RT_DESC_LEN;
|
||||
}
|
||||
#endif /* CFG_TUD_DFU_RUNTIME */
|
||||
|
||||
#if CFG_TUD_DFU_RUNTIME
|
||||
// Invoked on DFU_DETACH request to reboot to the bootloader
|
||||
void tud_dfu_runtime_reboot_to_dfu_cb(void) {
|
||||
usb_persist_restart(RESTART_BOOTLOADER_DFU);
|
||||
}
|
||||
#endif /* CFG_TUD_DFU_RUNTIME */
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_EVENTS);
|
||||
|
||||
static esp_event_loop_handle_t arduino_usb_event_loop_handle = NULL;
|
||||
|
||||
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait) {
|
||||
if (arduino_usb_event_loop_handle == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_event_post_to(arduino_usb_event_loop_handle, event_base, event_id, event_data, event_data_size, ticks_to_wait);
|
||||
}
|
||||
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg) {
|
||||
if (arduino_usb_event_loop_handle == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_event_handler_register_with(arduino_usb_event_loop_handle, event_base, event_id, event_handler, event_handler_arg);
|
||||
}
|
||||
|
||||
static bool tinyusb_device_mounted = false;
|
||||
static bool tinyusb_device_suspended = false;
|
||||
|
||||
// Invoked when device is mounted (configured)
|
||||
void tud_mount_cb(void) {
|
||||
tinyusb_device_mounted = true;
|
||||
arduino_usb_event_data_t p;
|
||||
p.suspend.remote_wakeup_en = 0;
|
||||
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_STARTED_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void) {
|
||||
tinyusb_device_mounted = false;
|
||||
arduino_usb_event_data_t p;
|
||||
p.suspend.remote_wakeup_en = 0;
|
||||
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
// Invoked when usb bus is suspended
|
||||
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
|
||||
void tud_suspend_cb(bool remote_wakeup_en) {
|
||||
tinyusb_device_suspended = true;
|
||||
arduino_usb_event_data_t p;
|
||||
p.suspend.remote_wakeup_en = remote_wakeup_en;
|
||||
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_SUSPEND_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
// Invoked when usb bus is resumed
|
||||
void tud_resume_cb(void) {
|
||||
tinyusb_device_suspended = false;
|
||||
arduino_usb_event_data_t p;
|
||||
p.suspend.remote_wakeup_en = 0;
|
||||
arduino_usb_event_post(ARDUINO_USB_EVENTS, ARDUINO_USB_RESUME_EVENT, &p, sizeof(arduino_usb_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
ESPUSB::ESPUSB(size_t task_stack_size, uint8_t event_task_priority)
|
||||
: vid(USB_VID), pid(USB_PID), product_name(USB_PRODUCT), manufacturer_name(USB_MANUFACTURER), serial_number(USB_SERIAL), fw_version(0x0100),
|
||||
usb_version(0x0200) // at least 2.1 or 3.x for BOS & webUSB
|
||||
,
|
||||
usb_class(TUSB_CLASS_MISC), usb_subclass(MISC_SUBCLASS_COMMON), usb_protocol(MISC_PROTOCOL_IAD), usb_attributes(TUSB_DESC_CONFIG_ATT_SELF_POWERED),
|
||||
usb_power_ma(500), webusb_enabled(USB_WEBUSB_ENABLED), webusb_url(USB_WEBUSB_URL), _started(false), _task_stack_size(task_stack_size),
|
||||
_event_task_priority(event_task_priority) {
|
||||
if (!arduino_usb_event_loop_handle) {
|
||||
esp_event_loop_args_t event_task_args = {
|
||||
.queue_size = 5,
|
||||
.task_name = "arduino_usb_events",
|
||||
.task_priority = _event_task_priority,
|
||||
.task_stack_size = _task_stack_size,
|
||||
.task_core_id = tskNO_AFFINITY
|
||||
};
|
||||
if (esp_event_loop_create(&event_task_args, &arduino_usb_event_loop_handle) != ESP_OK) {
|
||||
log_e("esp_event_loop_create failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ESPUSB::~ESPUSB() {
|
||||
if (arduino_usb_event_loop_handle) {
|
||||
esp_event_loop_delete(arduino_usb_event_loop_handle);
|
||||
arduino_usb_event_loop_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool ESPUSB::begin() {
|
||||
if (!_started) {
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
if (serial_number == "__MAC__") {
|
||||
StreamString s;
|
||||
uint8_t m[6];
|
||||
esp_efuse_mac_get_default(m);
|
||||
s.printf("%02X%02X%02X%02X%02X%02X", m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
serial_number = s;
|
||||
}
|
||||
#endif
|
||||
tinyusb_device_config_t tinyusb_device_config = {
|
||||
.vid = vid,
|
||||
.pid = pid,
|
||||
.product_name = product_name.c_str(),
|
||||
.manufacturer_name = manufacturer_name.c_str(),
|
||||
.serial_number = serial_number.c_str(),
|
||||
.fw_version = fw_version,
|
||||
.usb_version = usb_version,
|
||||
.usb_class = usb_class,
|
||||
.usb_subclass = usb_subclass,
|
||||
.usb_protocol = usb_protocol,
|
||||
.usb_attributes = usb_attributes,
|
||||
.usb_power_ma = usb_power_ma,
|
||||
.webusb_enabled = webusb_enabled,
|
||||
.webusb_url = webusb_url.c_str()
|
||||
};
|
||||
_started = tinyusb_init(&tinyusb_device_config) == ESP_OK;
|
||||
}
|
||||
return _started;
|
||||
}
|
||||
|
||||
void ESPUSB::onEvent(esp_event_handler_t callback) {
|
||||
onEvent(ARDUINO_USB_ANY_EVENT, callback);
|
||||
}
|
||||
void ESPUSB::onEvent(arduino_usb_event_t event, esp_event_handler_t callback) {
|
||||
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, event, callback, this);
|
||||
}
|
||||
|
||||
ESPUSB::operator bool() const {
|
||||
return _started && tinyusb_device_mounted;
|
||||
}
|
||||
|
||||
bool ESPUSB::enableDFU() {
|
||||
#if CFG_TUD_DFU
|
||||
return tinyusb_enable_interface(USB_INTERFACE_DFU, TUD_DFU_DESC_LEN(1), load_dfu_ota_descriptor) == ESP_OK;
|
||||
#elif CFG_TUD_DFU_RUNTIME
|
||||
return tinyusb_enable_interface(USB_INTERFACE_DFU, TUD_DFU_RT_DESC_LEN, load_dfu_descriptor) == ESP_OK;
|
||||
#endif /* CFG_TUD_DFU_RUNTIME */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ESPUSB::VID(uint16_t v) {
|
||||
if (!_started) {
|
||||
vid = v;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint16_t ESPUSB::VID(void) {
|
||||
return vid;
|
||||
}
|
||||
|
||||
bool ESPUSB::PID(uint16_t p) {
|
||||
if (!_started) {
|
||||
pid = p;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint16_t ESPUSB::PID(void) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
bool ESPUSB::firmwareVersion(uint16_t version) {
|
||||
if (!_started) {
|
||||
fw_version = version;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint16_t ESPUSB::firmwareVersion(void) {
|
||||
return fw_version;
|
||||
}
|
||||
|
||||
bool ESPUSB::usbVersion(uint16_t version) {
|
||||
if (!_started) {
|
||||
usb_version = version;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint16_t ESPUSB::usbVersion(void) {
|
||||
return usb_version;
|
||||
}
|
||||
|
||||
bool ESPUSB::usbPower(uint16_t mA) {
|
||||
if (!_started) {
|
||||
usb_power_ma = mA;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint16_t ESPUSB::usbPower(void) {
|
||||
return usb_power_ma;
|
||||
}
|
||||
|
||||
bool ESPUSB::usbClass(uint8_t _class) {
|
||||
if (!_started) {
|
||||
usb_class = _class;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint8_t ESPUSB::usbClass(void) {
|
||||
return usb_class;
|
||||
}
|
||||
|
||||
bool ESPUSB::usbSubClass(uint8_t subClass) {
|
||||
if (!_started) {
|
||||
usb_subclass = subClass;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint8_t ESPUSB::usbSubClass(void) {
|
||||
return usb_subclass;
|
||||
}
|
||||
|
||||
bool ESPUSB::usbProtocol(uint8_t protocol) {
|
||||
if (!_started) {
|
||||
usb_protocol = protocol;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint8_t ESPUSB::usbProtocol(void) {
|
||||
return usb_protocol;
|
||||
}
|
||||
|
||||
bool ESPUSB::usbAttributes(uint8_t attr) {
|
||||
if (!_started) {
|
||||
usb_attributes = attr;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
uint8_t ESPUSB::usbAttributes(void) {
|
||||
return usb_attributes;
|
||||
}
|
||||
|
||||
bool ESPUSB::webUSB(bool enabled) {
|
||||
if (!_started) {
|
||||
webusb_enabled = enabled;
|
||||
if (enabled && usb_version < 0x0210) {
|
||||
usb_version = 0x0210;
|
||||
}
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
bool ESPUSB::webUSB(void) {
|
||||
return webusb_enabled;
|
||||
}
|
||||
|
||||
bool ESPUSB::productName(const char *name) {
|
||||
if (!_started) {
|
||||
product_name = name;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
const char *ESPUSB::productName(void) {
|
||||
return product_name.c_str();
|
||||
}
|
||||
|
||||
bool ESPUSB::manufacturerName(const char *name) {
|
||||
if (!_started) {
|
||||
manufacturer_name = name;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
const char *ESPUSB::manufacturerName(void) {
|
||||
return manufacturer_name.c_str();
|
||||
}
|
||||
|
||||
bool ESPUSB::serialNumber(const char *name) {
|
||||
if (!_started) {
|
||||
serial_number = name;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
const char *ESPUSB::serialNumber(void) {
|
||||
return serial_number.c_str();
|
||||
}
|
||||
|
||||
bool ESPUSB::webUSBURL(const char *name) {
|
||||
if (!_started) {
|
||||
webusb_url = name;
|
||||
}
|
||||
return !_started;
|
||||
}
|
||||
const char *ESPUSB::webUSBURL(void) {
|
||||
return webusb_url.c_str();
|
||||
}
|
||||
|
||||
ESPUSB USB;
|
||||
|
||||
#endif /* CONFIG_TINYUSB_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_TINYUSB_ENABLED
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "USBCDC.h"
|
||||
#include "Arduino.h" // defines ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE and ARDUINO_SERIAL_EVENT_TASK_PRIORITY
|
||||
|
||||
#define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT | ARDUINO_USB_MSC_ON_BOOT | ARDUINO_USB_DFU_ON_BOOT)
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
ARDUINO_USB_ANY_EVENT = ESP_EVENT_ANY_ID,
|
||||
ARDUINO_USB_STARTED_EVENT = 0,
|
||||
ARDUINO_USB_STOPPED_EVENT,
|
||||
ARDUINO_USB_SUSPEND_EVENT,
|
||||
ARDUINO_USB_RESUME_EVENT,
|
||||
ARDUINO_USB_MAX_EVENT,
|
||||
} arduino_usb_event_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
bool remote_wakeup_en;
|
||||
} suspend;
|
||||
} arduino_usb_event_data_t;
|
||||
|
||||
class ESPUSB {
|
||||
public:
|
||||
ESPUSB(size_t event_task_stack_size = ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, uint8_t event_task_priority = ARDUINO_SERIAL_EVENT_TASK_PRIORITY);
|
||||
~ESPUSB();
|
||||
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
void onEvent(arduino_usb_event_t event, esp_event_handler_t callback);
|
||||
|
||||
bool VID(uint16_t v);
|
||||
uint16_t VID(void);
|
||||
|
||||
bool PID(uint16_t p);
|
||||
uint16_t PID(void);
|
||||
|
||||
bool firmwareVersion(uint16_t version);
|
||||
uint16_t firmwareVersion(void);
|
||||
|
||||
bool usbVersion(uint16_t version);
|
||||
uint16_t usbVersion(void);
|
||||
|
||||
bool usbPower(uint16_t mA);
|
||||
uint16_t usbPower(void);
|
||||
|
||||
bool usbClass(uint8_t _class);
|
||||
uint8_t usbClass(void);
|
||||
|
||||
bool usbSubClass(uint8_t subClass);
|
||||
uint8_t usbSubClass(void);
|
||||
|
||||
bool usbProtocol(uint8_t protocol);
|
||||
uint8_t usbProtocol(void);
|
||||
|
||||
bool usbAttributes(uint8_t attr);
|
||||
uint8_t usbAttributes(void);
|
||||
|
||||
bool webUSB(bool enabled);
|
||||
bool webUSB(void);
|
||||
|
||||
bool productName(const char *name);
|
||||
const char *productName(void);
|
||||
|
||||
bool manufacturerName(const char *name);
|
||||
const char *manufacturerName(void);
|
||||
|
||||
bool serialNumber(const char *name);
|
||||
const char *serialNumber(void);
|
||||
|
||||
bool webUSBURL(const char *name);
|
||||
const char *webUSBURL(void);
|
||||
|
||||
bool enableDFU();
|
||||
bool begin();
|
||||
operator bool() const;
|
||||
|
||||
private:
|
||||
uint16_t vid;
|
||||
uint16_t pid;
|
||||
String product_name;
|
||||
String manufacturer_name;
|
||||
String serial_number;
|
||||
uint16_t fw_version;
|
||||
uint16_t usb_version;
|
||||
uint8_t usb_class;
|
||||
uint8_t usb_subclass;
|
||||
uint8_t usb_protocol;
|
||||
uint8_t usb_attributes;
|
||||
uint16_t usb_power_ma;
|
||||
bool webusb_enabled;
|
||||
String webusb_url;
|
||||
|
||||
bool _started;
|
||||
size_t _task_stack_size;
|
||||
uint8_t _event_task_priority;
|
||||
};
|
||||
|
||||
extern ESPUSB USB;
|
||||
|
||||
#endif /* CONFIG_TINYUSB_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,477 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "USBCDC.h"
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#include "USB.h"
|
||||
#if CONFIG_TINYUSB_CDC_ENABLED
|
||||
|
||||
#include "esp32-hal-tinyusb.h"
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_CDC_EVENTS);
|
||||
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
|
||||
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
|
||||
|
||||
USBCDC *devices[CFG_TUD_CDC];
|
||||
|
||||
static uint16_t load_cdc_descriptor(uint8_t *dst, uint8_t *itf) {
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC");
|
||||
uint8_t descriptor[TUD_CDC_DESC_LEN] = {// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(*itf, str_index, 0x85, CFG_TUD_ENDOINT_SIZE, 0x03, 0x84, CFG_TUD_ENDOINT_SIZE)
|
||||
};
|
||||
*itf += 2;
|
||||
memcpy(dst, descriptor, TUD_CDC_DESC_LEN);
|
||||
return TUD_CDC_DESC_LEN;
|
||||
}
|
||||
|
||||
static uint16_t load_cdc_descriptor2(uint8_t *dst, uint8_t *itf) {
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC2");
|
||||
uint8_t ep_ntfy = tinyusb_get_free_in_endpoint();
|
||||
TU_VERIFY(ep_ntfy != 0);
|
||||
uint8_t ep_in = tinyusb_get_free_in_endpoint();
|
||||
TU_VERIFY(ep_in != 0);
|
||||
uint8_t ep_out = tinyusb_get_free_out_endpoint();
|
||||
TU_VERIFY(ep_out != 0);
|
||||
uint8_t descriptor[TUD_CDC_DESC_LEN] = {
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(*itf, str_index, (uint8_t)(0x80 | ep_ntfy), CFG_TUD_ENDOINT_SIZE, ep_out, (uint8_t)(0x80 | ep_in), CFG_TUD_ENDOINT_SIZE)
|
||||
};
|
||||
*itf += 2;
|
||||
memcpy(dst, descriptor, TUD_CDC_DESC_LEN);
|
||||
return TUD_CDC_DESC_LEN;
|
||||
}
|
||||
|
||||
// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
|
||||
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
|
||||
//log_v("ITF: %u, DTR: %u, RTS: %u", itf, dtr, rts);
|
||||
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
|
||||
devices[itf]->_onLineState(dtr, rts);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when line coding is change via SET_LINE_CODING
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding) {
|
||||
//log_v("ITF: %u, BITRATE: %lu, STOP_BITS: %u, PARITY: %u, DATA_BITS: %u", itf, p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
|
||||
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
|
||||
devices[itf]->_onLineCoding(p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when received new data
|
||||
void tud_cdc_rx_cb(uint8_t itf) {
|
||||
//log_v("ITF: %u", itf);
|
||||
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
|
||||
devices[itf]->_onRX();
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when received send break
|
||||
void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) {
|
||||
//log_v("itf: %u, duration_ms: %u", itf, duration_ms);
|
||||
}
|
||||
|
||||
// Invoked when space becomes available in TX buffer
|
||||
void tud_cdc_tx_complete_cb(uint8_t itf) {
|
||||
if (itf < CFG_TUD_CDC && devices[itf] != NULL) {
|
||||
devices[itf]->_onTX();
|
||||
}
|
||||
}
|
||||
|
||||
static void ARDUINO_ISR_ATTR cdc0_write_char(char c) {
|
||||
if (CFG_TUD_CDC && devices[0] != NULL) {
|
||||
tud_cdc_n_write_char(0, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_unplugged_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
||||
((USBCDC *)arg)->_onUnplugged();
|
||||
}
|
||||
|
||||
USBCDC::USBCDC(uint8_t itfn)
|
||||
: itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false), rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_lock(NULL),
|
||||
tx_timeout_ms(250) {
|
||||
if (itf < CFG_TUD_CDC) {
|
||||
if (itf == 0) {
|
||||
tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
|
||||
} else {
|
||||
tinyusb_enable_interface(USB_INTERFACE_CDC2, TUD_CDC_DESC_LEN, load_cdc_descriptor2);
|
||||
}
|
||||
arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
|
||||
} else {
|
||||
log_e("Maximum of %u CDC devices are supported", CFG_TUD_CDC);
|
||||
}
|
||||
}
|
||||
|
||||
USBCDC::~USBCDC() {
|
||||
end();
|
||||
}
|
||||
|
||||
void USBCDC::onEvent(esp_event_handler_t callback) {
|
||||
onEvent(ARDUINO_USB_CDC_ANY_EVENT, callback);
|
||||
}
|
||||
void USBCDC::onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback) {
|
||||
arduino_usb_event_handler_register_with(ARDUINO_USB_CDC_EVENTS, event, callback, this);
|
||||
}
|
||||
|
||||
size_t USBCDC::setRxBufferSize(size_t rx_queue_len) {
|
||||
size_t currentQueueSize = rx_queue ? uxQueueSpacesAvailable(rx_queue) + uxQueueMessagesWaiting(rx_queue) : 0;
|
||||
|
||||
if (rx_queue_len != currentQueueSize) {
|
||||
QueueHandle_t new_rx_queue = NULL;
|
||||
if (rx_queue_len) {
|
||||
new_rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
|
||||
if (!new_rx_queue) {
|
||||
log_e("CDC Queue creation failed.");
|
||||
return 0;
|
||||
}
|
||||
if (rx_queue) {
|
||||
size_t copySize = uxQueueMessagesWaiting(rx_queue);
|
||||
if (copySize > 0) {
|
||||
for (size_t i = 0; i < copySize; i++) {
|
||||
uint8_t ch = 0;
|
||||
xQueueReceive(rx_queue, &ch, 0);
|
||||
if (!xQueueSend(new_rx_queue, &ch, 0)) {
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
p.rx_overflow.dropped_bytes = copySize - i;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_RX_OVERFLOW_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
log_e("CDC RX Overflow.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vQueueDelete(rx_queue);
|
||||
}
|
||||
rx_queue = new_rx_queue;
|
||||
return rx_queue_len;
|
||||
} else {
|
||||
if (rx_queue) {
|
||||
vQueueDelete(rx_queue);
|
||||
rx_queue = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rx_queue_len;
|
||||
}
|
||||
|
||||
void USBCDC::begin(unsigned long baud) {
|
||||
if (itf >= CFG_TUD_CDC) {
|
||||
return;
|
||||
}
|
||||
if (tx_lock == NULL) {
|
||||
tx_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
// if rx_queue was set before begin(), keep it
|
||||
if (!rx_queue) {
|
||||
setRxBufferSize(256); //default if not preset
|
||||
}
|
||||
devices[itf] = this;
|
||||
}
|
||||
|
||||
void USBCDC::end() {
|
||||
if (itf >= CFG_TUD_CDC) {
|
||||
return;
|
||||
}
|
||||
connected = false;
|
||||
devices[itf] = NULL;
|
||||
setRxBufferSize(0);
|
||||
if (tx_lock != NULL) {
|
||||
vSemaphoreDelete(tx_lock);
|
||||
tx_lock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void USBCDC::setTxTimeoutMs(uint32_t timeout) {
|
||||
tx_timeout_ms = timeout;
|
||||
}
|
||||
|
||||
void USBCDC::_onUnplugged(void) {
|
||||
if (connected) {
|
||||
connected = false;
|
||||
dtr = false;
|
||||
rts = false;
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
CDC_LINE_IDLE,
|
||||
CDC_LINE_1,
|
||||
CDC_LINE_2,
|
||||
CDC_LINE_3
|
||||
};
|
||||
void USBCDC::_onLineState(bool _dtr, bool _rts) {
|
||||
static uint8_t lineState = CDC_LINE_IDLE;
|
||||
|
||||
if (dtr == _dtr && rts == _rts) {
|
||||
return; // Skip duplicate events
|
||||
}
|
||||
|
||||
dtr = _dtr;
|
||||
rts = _rts;
|
||||
|
||||
if (reboot_enable) {
|
||||
if (!dtr && rts) {
|
||||
if (lineState == CDC_LINE_IDLE) {
|
||||
lineState++;
|
||||
if (connected) {
|
||||
connected = false;
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
// } else if(lineState == CDC_LINE_2){//esptool.js
|
||||
// lineState++;
|
||||
} else {
|
||||
lineState = CDC_LINE_IDLE;
|
||||
}
|
||||
} else if (dtr && rts) {
|
||||
if (lineState == CDC_LINE_1) {
|
||||
lineState++;
|
||||
} else {
|
||||
lineState = CDC_LINE_IDLE;
|
||||
}
|
||||
} else if (dtr && !rts) {
|
||||
if (lineState == CDC_LINE_2) {
|
||||
lineState++;
|
||||
// } else if(lineState == CDC_LINE_IDLE){//esptool.js
|
||||
// lineState++;
|
||||
} else {
|
||||
lineState = CDC_LINE_IDLE;
|
||||
}
|
||||
} else if (!dtr && !rts) {
|
||||
if (lineState == CDC_LINE_3) {
|
||||
usb_persist_restart(RESTART_BOOTLOADER);
|
||||
} else {
|
||||
lineState = CDC_LINE_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lineState == CDC_LINE_IDLE) {
|
||||
if (dtr && rts && !connected) {
|
||||
connected = true;
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_CONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
} else if (!dtr && connected) {
|
||||
connected = false;
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
arduino_usb_cdc_event_data_t l;
|
||||
l.line_state.dtr = dtr;
|
||||
l.line_state.rts = rts;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_LINE_STATE_EVENT, &l, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void USBCDC::_onLineCoding(uint32_t _bit_rate, uint8_t _stop_bits, uint8_t _parity, uint8_t _data_bits) {
|
||||
if (bit_rate != _bit_rate || data_bits != _data_bits || stop_bits != _stop_bits || parity != _parity) {
|
||||
// ArduinoIDE sends LineCoding with 1200bps baud to reset the device
|
||||
if (reboot_enable && _bit_rate == 1200) {
|
||||
usb_persist_restart(RESTART_BOOTLOADER);
|
||||
} else {
|
||||
bit_rate = _bit_rate;
|
||||
data_bits = _data_bits;
|
||||
stop_bits = _stop_bits;
|
||||
parity = _parity;
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
p.line_coding.bit_rate = bit_rate;
|
||||
p.line_coding.data_bits = data_bits;
|
||||
p.line_coding.stop_bits = stop_bits;
|
||||
p.line_coding.parity = parity;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_LINE_CODING_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void USBCDC::_onRX() {
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
|
||||
uint32_t count = tud_cdc_n_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (rx_queue == NULL || !xQueueSend(rx_queue, buf + i, 10)) {
|
||||
p.rx_overflow.dropped_bytes = count - i;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_RX_OVERFLOW_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
log_e("CDC RX Overflow.");
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count) {
|
||||
p.rx.len = count;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_RX_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void USBCDC::_onTX() {
|
||||
arduino_usb_cdc_event_data_t p;
|
||||
arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_TX_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
void USBCDC::enableReboot(bool enable) {
|
||||
reboot_enable = enable;
|
||||
}
|
||||
bool USBCDC::rebootEnabled(void) {
|
||||
return reboot_enable;
|
||||
}
|
||||
|
||||
int USBCDC::available(void) {
|
||||
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return uxQueueMessagesWaiting(rx_queue);
|
||||
}
|
||||
|
||||
int USBCDC::peek(void) {
|
||||
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t c;
|
||||
if (xQueuePeek(rx_queue, &c, 0)) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int USBCDC::read(void) {
|
||||
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t c = 0;
|
||||
if (xQueueReceive(rx_queue, &c, 0)) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t USBCDC::read(uint8_t *buffer, size_t size) {
|
||||
if (itf >= CFG_TUD_CDC || rx_queue == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t c = 0;
|
||||
size_t count = 0;
|
||||
while (count < size && xQueueReceive(rx_queue, &c, 0)) {
|
||||
buffer[count++] = c;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void USBCDC::flush(void) {
|
||||
if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
|
||||
return;
|
||||
}
|
||||
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
|
||||
return;
|
||||
}
|
||||
tud_cdc_n_write_flush(itf);
|
||||
xSemaphoreGive(tx_lock);
|
||||
}
|
||||
|
||||
int USBCDC::availableForWrite(void) {
|
||||
if (itf >= CFG_TUD_CDC || tx_lock == NULL || !tud_cdc_n_connected(itf)) {
|
||||
return 0;
|
||||
}
|
||||
if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
|
||||
return 0;
|
||||
}
|
||||
size_t a = tud_cdc_n_write_available(itf);
|
||||
xSemaphoreGive(tx_lock);
|
||||
return a;
|
||||
}
|
||||
|
||||
size_t USBCDC::write(const uint8_t *buffer, size_t size) {
|
||||
if (itf >= CFG_TUD_CDC || tx_lock == NULL || buffer == NULL || size == 0 || !tud_cdc_n_connected(itf)) {
|
||||
return 0;
|
||||
}
|
||||
if (xPortInIsrContext()) {
|
||||
BaseType_t taskWoken = false;
|
||||
if (xSemaphoreTakeFromISR(tx_lock, &taskWoken) != pdPASS) {
|
||||
return 0;
|
||||
}
|
||||
} else if (xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS) {
|
||||
return 0;
|
||||
}
|
||||
size_t to_send = size, so_far = 0;
|
||||
// writeTimeout will prevent that TinyUSB failure locks the while(to_send) loop
|
||||
uint32_t writeTimeout = millis() + tx_timeout_ms;
|
||||
while (to_send) {
|
||||
if (!tud_cdc_n_connected(itf) || (int32_t)(millis() - writeTimeout) >= 0) {
|
||||
log_e("USB is disconnected or CDC writing has timed out.");
|
||||
size = so_far;
|
||||
break;
|
||||
}
|
||||
size_t space = tud_cdc_n_write_available(itf);
|
||||
if (!space) {
|
||||
tud_cdc_n_write_flush(itf);
|
||||
continue;
|
||||
}
|
||||
if (space > to_send) {
|
||||
space = to_send;
|
||||
}
|
||||
size_t sent = tud_cdc_n_write(itf, buffer + so_far, space);
|
||||
if (sent) {
|
||||
so_far += sent;
|
||||
to_send -= sent;
|
||||
tud_cdc_n_write_flush(itf);
|
||||
} else {
|
||||
size = so_far;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (xPortInIsrContext()) {
|
||||
BaseType_t taskWoken = false;
|
||||
xSemaphoreGiveFromISR(tx_lock, &taskWoken);
|
||||
} else {
|
||||
xSemaphoreGive(tx_lock);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t USBCDC::write(uint8_t c) {
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
uint32_t USBCDC::baudRate() {
|
||||
return bit_rate;
|
||||
}
|
||||
|
||||
void USBCDC::setDebugOutput(bool en) {
|
||||
if (itf) {
|
||||
return;
|
||||
}
|
||||
if (en) {
|
||||
uartSetDebug(NULL);
|
||||
ets_install_putc2((void (*)(char)) & cdc0_write_char);
|
||||
} else {
|
||||
ets_install_putc2(NULL);
|
||||
}
|
||||
ets_install_putc1(NULL); // closes UART log output
|
||||
}
|
||||
|
||||
USBCDC::operator bool() const {
|
||||
if (itf >= CFG_TUD_CDC) {
|
||||
return false;
|
||||
}
|
||||
return connected;
|
||||
}
|
||||
|
||||
#if !ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Native USB CDC selected
|
||||
// USBSerial is always available to be used
|
||||
USBCDC USBSerial(0);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_TINYUSB_CDC_ENABLED
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "esp_event.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "Stream.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS);
|
||||
|
||||
typedef enum {
|
||||
ARDUINO_USB_CDC_ANY_EVENT = ESP_EVENT_ANY_ID,
|
||||
ARDUINO_USB_CDC_CONNECTED_EVENT = 0,
|
||||
ARDUINO_USB_CDC_DISCONNECTED_EVENT,
|
||||
ARDUINO_USB_CDC_LINE_STATE_EVENT,
|
||||
ARDUINO_USB_CDC_LINE_CODING_EVENT,
|
||||
ARDUINO_USB_CDC_RX_EVENT,
|
||||
ARDUINO_USB_CDC_TX_EVENT,
|
||||
ARDUINO_USB_CDC_RX_OVERFLOW_EVENT,
|
||||
ARDUINO_USB_CDC_MAX_EVENT,
|
||||
} arduino_usb_cdc_event_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
bool dtr;
|
||||
bool rts;
|
||||
} line_state;
|
||||
struct {
|
||||
uint32_t bit_rate;
|
||||
uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
|
||||
uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
|
||||
uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16
|
||||
} line_coding;
|
||||
struct {
|
||||
size_t len;
|
||||
} rx;
|
||||
struct {
|
||||
size_t dropped_bytes;
|
||||
} rx_overflow;
|
||||
} arduino_usb_cdc_event_data_t;
|
||||
|
||||
class USBCDC : public Stream {
|
||||
public:
|
||||
USBCDC(uint8_t itf = 0);
|
||||
~USBCDC();
|
||||
|
||||
void onEvent(esp_event_handler_t callback);
|
||||
void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
|
||||
|
||||
size_t setRxBufferSize(size_t size);
|
||||
void setTxTimeoutMs(uint32_t timeout);
|
||||
void begin(unsigned long baud = 0);
|
||||
void end();
|
||||
|
||||
int available(void);
|
||||
int availableForWrite(void);
|
||||
int peek(void);
|
||||
int read(void);
|
||||
size_t read(uint8_t *buffer, size_t size);
|
||||
size_t write(uint8_t);
|
||||
size_t write(const uint8_t *buffer, size_t size);
|
||||
void flush(void);
|
||||
|
||||
inline size_t read(char *buffer, size_t size) {
|
||||
return read((uint8_t *)buffer, size);
|
||||
}
|
||||
inline size_t write(const char *buffer, size_t size) {
|
||||
return write((uint8_t *)buffer, size);
|
||||
}
|
||||
inline size_t write(const char *s) {
|
||||
return write((uint8_t *)s, strlen(s));
|
||||
}
|
||||
inline size_t write(unsigned long n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(long n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(unsigned int n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
inline size_t write(int n) {
|
||||
return write((uint8_t)n);
|
||||
}
|
||||
uint32_t baudRate();
|
||||
void setDebugOutput(bool);
|
||||
operator bool() const;
|
||||
|
||||
void enableReboot(bool enable);
|
||||
bool rebootEnabled(void);
|
||||
|
||||
//internal methods
|
||||
void _onDFU(void);
|
||||
void _onLineState(bool _dtr, bool _rts);
|
||||
void _onLineCoding(uint32_t _bit_rate, uint8_t _stop_bits, uint8_t _parity, uint8_t _data_bits);
|
||||
void _onRX(void);
|
||||
void _onTX(void);
|
||||
void _onUnplugged(void);
|
||||
|
||||
protected:
|
||||
uint8_t itf;
|
||||
uint32_t bit_rate;
|
||||
uint8_t stop_bits; ///< 0: 1 stop bit - 1: 1.5 stop bits - 2: 2 stop bits
|
||||
uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space
|
||||
uint8_t data_bits; ///< can be 5, 6, 7, 8 or 16
|
||||
bool dtr;
|
||||
bool rts;
|
||||
bool connected;
|
||||
bool reboot_enable;
|
||||
QueueHandle_t rx_queue;
|
||||
SemaphoreHandle_t tx_lock;
|
||||
uint32_t tx_timeout_ms;
|
||||
};
|
||||
|
||||
#if !ARDUINO_USB_MODE && ARDUINO_USB_CDC_ON_BOOT // Native USB CDC selected
|
||||
#ifndef USB_SERIAL_IS_DEFINED
|
||||
#define USB_SERIAL_IS_DEFINED 1
|
||||
#endif
|
||||
// USBSerial is always available to be used
|
||||
extern USBCDC USBSerial;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_TINYUSB_CDC_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,267 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "USBMSC.h"
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
|
||||
#include "esp32-hal-tinyusb.h"
|
||||
|
||||
extern "C" uint16_t tusb_msc_load_descriptor(uint8_t *dst, uint8_t *itf) {
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC");
|
||||
uint8_t ep_num = tinyusb_get_free_duplex_endpoint();
|
||||
TU_VERIFY(ep_num != 0);
|
||||
uint8_t descriptor[TUD_MSC_DESC_LEN] = {// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), CFG_TUD_ENDOINT_SIZE)
|
||||
};
|
||||
*itf += 1;
|
||||
memcpy(dst, descriptor, TUD_MSC_DESC_LEN);
|
||||
return TUD_MSC_DESC_LEN;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool media_present;
|
||||
bool is_writable;
|
||||
uint8_t vendor_id[8];
|
||||
uint8_t product_id[16];
|
||||
uint8_t product_rev[4];
|
||||
uint16_t block_size;
|
||||
uint32_t block_count;
|
||||
bool (*start_stop)(uint8_t power_condition, bool start, bool load_eject);
|
||||
int32_t (*read)(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize);
|
||||
int32_t (*write)(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize);
|
||||
} msc_lun_t;
|
||||
|
||||
static const uint8_t MSC_MAX_LUN = 3;
|
||||
static uint8_t MSC_ACTIVE_LUN = 0;
|
||||
static msc_lun_t msc_luns[MSC_MAX_LUN];
|
||||
|
||||
static void cplstr(void *dst, const void *src, size_t max_len) {
|
||||
if (!src || !dst || !max_len) {
|
||||
return;
|
||||
}
|
||||
size_t l = strlen((const char *)src);
|
||||
if (l > max_len) {
|
||||
l = max_len;
|
||||
}
|
||||
memcpy(dst, src, l);
|
||||
}
|
||||
|
||||
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
|
||||
uint8_t tud_msc_get_maxlun_cb(void) {
|
||||
log_v("%u", MSC_ACTIVE_LUN);
|
||||
return MSC_ACTIVE_LUN;
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
|
||||
log_v("[%u]", lun);
|
||||
cplstr(vendor_id, msc_luns[lun].vendor_id, 8);
|
||||
cplstr(product_id, msc_luns[lun].product_id, 16);
|
||||
cplstr(product_rev, msc_luns[lun].product_rev, 4);
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun) {
|
||||
log_v("[%u]: %u", lun, msc_luns[lun].media_present);
|
||||
return msc_luns[lun].media_present; // RAM disk is always ready
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
|
||||
log_v("[%u]", lun);
|
||||
if (!msc_luns[lun].media_present) {
|
||||
*block_count = 0;
|
||||
*block_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
*block_count = msc_luns[lun].block_count;
|
||||
*block_size = msc_luns[lun].block_size;
|
||||
}
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
|
||||
log_v("[%u] power: %u, start: %u, eject: %u", lun, power_condition, start, load_eject);
|
||||
if (msc_luns[lun].start_stop) {
|
||||
return msc_luns[lun].start_stop(power_condition, start, load_eject);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback invoked when received READ10 command.
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
|
||||
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
|
||||
if (!msc_luns[lun].media_present) {
|
||||
return 0;
|
||||
}
|
||||
if (msc_luns[lun].read) {
|
||||
return msc_luns[lun].read(lba, offset, buffer, bufsize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Callback invoked when received WRITE10 command.
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
|
||||
log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
|
||||
if (!msc_luns[lun].media_present) {
|
||||
return 0;
|
||||
}
|
||||
if (msc_luns[lun].write) {
|
||||
return msc_luns[lun].write(lba, offset, buffer, bufsize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Callback invoked when received an SCSI command not in built-in list below
|
||||
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
|
||||
// - READ10 and WRITE10 has their own callbacks
|
||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
|
||||
// read10 & write10 has their own callback and MUST not be handled here
|
||||
log_v("[%u] cmd: %u, bufsize: %u", lun, scsi_cmd[0], bufsize);
|
||||
|
||||
void const *response = NULL;
|
||||
uint16_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
|
||||
if (!msc_luns[lun].media_present) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (scsi_cmd[0]) {
|
||||
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
||||
// Host is about to read/write etc ... better not to disconnect disk
|
||||
resplen = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Set Sense = Invalid Command Operation
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
resplen = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if (resplen > bufsize) {
|
||||
resplen = bufsize;
|
||||
}
|
||||
|
||||
if (response && (resplen > 0)) {
|
||||
if (in_xfer) {
|
||||
memcpy(buffer, response, resplen);
|
||||
} else {
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
return resplen;
|
||||
}
|
||||
|
||||
bool tud_msc_is_writable_cb(uint8_t lun) {
|
||||
log_v("[%u]: %u", lun, msc_luns[lun].is_writable);
|
||||
return msc_luns[lun].is_writable; // RAM disk is always ready
|
||||
}
|
||||
|
||||
USBMSC::USBMSC() {
|
||||
if (MSC_ACTIVE_LUN < MSC_MAX_LUN) {
|
||||
_lun = MSC_ACTIVE_LUN;
|
||||
MSC_ACTIVE_LUN++;
|
||||
msc_luns[_lun].media_present = false;
|
||||
msc_luns[_lun].is_writable = true;
|
||||
msc_luns[_lun].vendor_id[0] = 0;
|
||||
msc_luns[_lun].product_id[0] = 0;
|
||||
msc_luns[_lun].product_rev[0] = 0;
|
||||
msc_luns[_lun].block_size = 0;
|
||||
msc_luns[_lun].block_count = 0;
|
||||
msc_luns[_lun].start_stop = NULL;
|
||||
msc_luns[_lun].read = NULL;
|
||||
msc_luns[_lun].write = NULL;
|
||||
}
|
||||
if (_lun == 0) {
|
||||
tinyusb_enable_interface(USB_INTERFACE_MSC, TUD_MSC_DESC_LEN, tusb_msc_load_descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
USBMSC::~USBMSC() {
|
||||
end();
|
||||
}
|
||||
|
||||
bool USBMSC::begin(uint32_t block_count, uint16_t block_size) {
|
||||
msc_luns[_lun].block_size = block_size;
|
||||
msc_luns[_lun].block_count = block_count;
|
||||
if (!msc_luns[_lun].block_size || !msc_luns[_lun].block_count || !msc_luns[_lun].read || !msc_luns[_lun].write) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void USBMSC::end() {
|
||||
msc_luns[_lun].media_present = false;
|
||||
msc_luns[_lun].is_writable = false;
|
||||
msc_luns[_lun].vendor_id[0] = 0;
|
||||
msc_luns[_lun].product_id[0] = 0;
|
||||
msc_luns[_lun].product_rev[0] = 0;
|
||||
msc_luns[_lun].block_size = 0;
|
||||
msc_luns[_lun].block_count = 0;
|
||||
msc_luns[_lun].start_stop = NULL;
|
||||
msc_luns[_lun].read = NULL;
|
||||
msc_luns[_lun].write = NULL;
|
||||
}
|
||||
|
||||
void USBMSC::vendorID(const char *vid) {
|
||||
cplstr(msc_luns[_lun].vendor_id, vid, 8);
|
||||
}
|
||||
|
||||
void USBMSC::productID(const char *pid) {
|
||||
cplstr(msc_luns[_lun].product_id, pid, 16);
|
||||
}
|
||||
|
||||
void USBMSC::productRevision(const char *rev) {
|
||||
cplstr(msc_luns[_lun].product_rev, rev, 4);
|
||||
}
|
||||
|
||||
void USBMSC::onStartStop(msc_start_stop_cb cb) {
|
||||
msc_luns[_lun].start_stop = cb;
|
||||
}
|
||||
|
||||
void USBMSC::onRead(msc_read_cb cb) {
|
||||
msc_luns[_lun].read = cb;
|
||||
}
|
||||
|
||||
void USBMSC::onWrite(msc_write_cb cb) {
|
||||
msc_luns[_lun].write = cb;
|
||||
}
|
||||
|
||||
void USBMSC::isWritable(bool is_writable) {
|
||||
msc_luns[_lun].is_writable = is_writable;
|
||||
}
|
||||
|
||||
void USBMSC::mediaPresent(bool media_present) {
|
||||
msc_luns[_lun].media_present = media_present;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
typedef bool (*msc_start_stop_cb)(uint8_t power_condition, bool start, bool load_eject);
|
||||
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
typedef int32_t (*msc_read_cb)(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize);
|
||||
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
typedef int32_t (*msc_write_cb)(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize);
|
||||
|
||||
class USBMSC {
|
||||
public:
|
||||
USBMSC();
|
||||
~USBMSC();
|
||||
bool begin(uint32_t block_count, uint16_t block_size);
|
||||
void end();
|
||||
void vendorID(const char *vid); //max 8 chars
|
||||
void productID(const char *pid); //max 16 chars
|
||||
void productRevision(const char *ver); //max 4 chars
|
||||
void mediaPresent(bool media_present);
|
||||
void isWritable(bool is_writable);
|
||||
void onStartStop(msc_start_stop_cb cb);
|
||||
void onRead(msc_read_cb cb);
|
||||
void onWrite(msc_write_cb cb);
|
||||
|
||||
private:
|
||||
uint8_t _lun;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_TINYUSB_MSC_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Udp.cpp: Library to send/receive UDP packets.
|
||||
*
|
||||
* NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)
|
||||
* 1) UDP does not guarantee the order in which assembled UDP packets are received. This
|
||||
* might not happen often in practice, but in larger network topologies, a UDP
|
||||
* packet can be received out of sequence.
|
||||
* 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being
|
||||
* aware of it. Again, this may not be a concern in practice on small local networks.
|
||||
* For more information, see http://www.cafeaulait.org/course/week12/35.html
|
||||
*
|
||||
* MIT License:
|
||||
* Copyright (c) 2008 Bjoern Hartmann
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* bjoern@cs.stanford.edu 12/30/2008
|
||||
*/
|
||||
|
||||
#ifndef udp_h
|
||||
#define udp_h
|
||||
|
||||
#include <Stream.h>
|
||||
#include <IPAddress.h>
|
||||
|
||||
class UDP : public Stream {
|
||||
|
||||
public:
|
||||
virtual uint8_t begin(uint16_t) = 0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
|
||||
virtual uint8_t beginMulticast(IPAddress, uint16_t) {
|
||||
return 0;
|
||||
} // initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 on failure
|
||||
virtual void stop() = 0; // Finish with the UDP socket
|
||||
|
||||
// Sending UDP packets
|
||||
|
||||
// Start building up a packet to send to the remote host specific in ip and port
|
||||
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
|
||||
virtual int beginPacket(IPAddress ip, uint16_t port) = 0;
|
||||
// Start building up a packet to send to the remote host specific in host and port
|
||||
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
|
||||
virtual int beginPacket(const char *host, uint16_t port) = 0;
|
||||
// Finish off this packet and send it
|
||||
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
||||
virtual int endPacket() = 0;
|
||||
// Write a single byte into the packet
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
// Write size bytes from buffer into the packet
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
|
||||
|
||||
// Start processing the next available incoming packet
|
||||
// Returns the size of the packet in bytes, or 0 if no packets are available
|
||||
virtual int parsePacket() = 0;
|
||||
// Number of bytes remaining in the current packet
|
||||
virtual int available() = 0;
|
||||
// Read a single byte from the current packet
|
||||
virtual int read() = 0;
|
||||
// Read up to len bytes from the current packet and place them into buffer
|
||||
// Returns the number of bytes read, or 0 if none are available
|
||||
virtual int read(unsigned char *buffer, size_t len) = 0;
|
||||
// Read up to len characters from the current packet and place them into buffer
|
||||
// Returns the number of characters read, or 0 if none are available
|
||||
virtual int read(char *buffer, size_t len) = 0;
|
||||
// Return the next byte from the current packet without moving on to the next byte
|
||||
virtual int peek() = 0;
|
||||
virtual void flush() = 0; // Finish reading the current packet
|
||||
|
||||
// Return the IP address of the host who sent the current incoming packet
|
||||
virtual IPAddress remoteIP() = 0;
|
||||
// Return the port of the host who sent the current incoming packet
|
||||
virtual uint16_t remotePort() = 0;
|
||||
|
||||
protected:
|
||||
uint8_t *rawIPAddress(IPAddress &addr) {
|
||||
return addr.raw_address();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
WCharacter.h - Character utility functions for Wiring & Arduino
|
||||
Copyright (c) 2010 Hernando Barragan. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef Character_h
|
||||
#define Character_h
|
||||
|
||||
#include <ctype.h>
|
||||
#define isascii(__c) ((unsigned)(__c) <= 0177)
|
||||
#define toascii(__c) ((__c) & 0177)
|
||||
|
||||
// WCharacter.h prototypes
|
||||
inline boolean isAlphaNumeric(int c) __attribute__((always_inline));
|
||||
inline boolean isAlpha(int c) __attribute__((always_inline));
|
||||
inline boolean isAscii(int c) __attribute__((always_inline));
|
||||
inline boolean isWhitespace(int c) __attribute__((always_inline));
|
||||
inline boolean isControl(int c) __attribute__((always_inline));
|
||||
inline boolean isDigit(int c) __attribute__((always_inline));
|
||||
inline boolean isGraph(int c) __attribute__((always_inline));
|
||||
inline boolean isLowerCase(int c) __attribute__((always_inline));
|
||||
inline boolean isPrintable(int c) __attribute__((always_inline));
|
||||
inline boolean isPunct(int c) __attribute__((always_inline));
|
||||
inline boolean isSpace(int c) __attribute__((always_inline));
|
||||
inline boolean isUpperCase(int c) __attribute__((always_inline));
|
||||
inline boolean isHexadecimalDigit(int c) __attribute__((always_inline));
|
||||
inline int toAscii(int c) __attribute__((always_inline));
|
||||
inline int toLowerCase(int c) __attribute__((always_inline));
|
||||
inline int toUpperCase(int c) __attribute__((always_inline));
|
||||
|
||||
// Checks for an alphanumeric character.
|
||||
// It is equivalent to (isalpha(c) || isdigit(c)).
|
||||
inline boolean isAlphaNumeric(int c) {
|
||||
return (isalnum(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for an alphabetic character.
|
||||
// It is equivalent to (isupper(c) || islower(c)).
|
||||
inline boolean isAlpha(int c) {
|
||||
return (isalpha(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks whether c is a 7-bit unsigned char value
|
||||
// that fits into the ASCII character set.
|
||||
inline boolean isAscii(int c) {
|
||||
return (isascii(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a blank character, that is, a space or a tab.
|
||||
inline boolean isWhitespace(int c) {
|
||||
return (isblank(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a control character.
|
||||
inline boolean isControl(int c) {
|
||||
return (iscntrl(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a digit (0 through 9).
|
||||
inline boolean isDigit(int c) {
|
||||
return (isdigit(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for any printable character except space.
|
||||
inline boolean isGraph(int c) {
|
||||
return (isgraph(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a lower-case character.
|
||||
inline boolean isLowerCase(int c) {
|
||||
return (islower(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for any printable character including space.
|
||||
inline boolean isPrintable(int c) {
|
||||
return (isprint(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for any printable character which is not a space
|
||||
// or an alphanumeric character.
|
||||
inline boolean isPunct(int c) {
|
||||
return (ispunct(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for white-space characters. For the avr-libc library,
|
||||
// these are: space, formfeed ('\f'), newline ('\n'), carriage
|
||||
// return ('\r'), horizontal tab ('\t'), and vertical tab ('\v').
|
||||
inline boolean isSpace(int c) {
|
||||
return (isspace(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for an uppercase letter.
|
||||
inline boolean isUpperCase(int c) {
|
||||
return (isupper(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Checks for a hexadecimal digits, i.e. one of 0 1 2 3 4 5 6 7
|
||||
// 8 9 a b c d e f A B C D E F.
|
||||
inline boolean isHexadecimalDigit(int c) {
|
||||
return (isxdigit(c) == 0 ? false : true);
|
||||
}
|
||||
|
||||
// Converts c to a 7-bit unsigned char value that fits into the
|
||||
// ASCII character set, by clearing the high-order bits.
|
||||
inline int toAscii(int c) {
|
||||
return toascii(c);
|
||||
}
|
||||
|
||||
// Warning:
|
||||
// Many people will be unhappy if you use this function.
|
||||
// This function will convert accented letters into random
|
||||
// characters.
|
||||
|
||||
// Converts the letter c to lower case, if possible.
|
||||
inline int toLowerCase(int c) {
|
||||
return tolower(c);
|
||||
}
|
||||
|
||||
// Converts the letter c to upper case, if possible.
|
||||
inline int toUpperCase(int c) {
|
||||
return toupper(c);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
Part of the Wiring project - http://wiring.org.co
|
||||
Copyright (c) 2004-06 Hernando Barragan
|
||||
Modified 13 August 2006, David A. Mellis for Arduino - http://www.arduino.cc/
|
||||
|
||||
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., 59 Temple Place, Suite 330,
|
||||
Boston, MA 02111-1307 USA
|
||||
|
||||
$Id$
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <stdlib.h>
|
||||
#include "esp_system.h"
|
||||
}
|
||||
#include "esp32-hal-log.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
// Allows the user to choose between Real Hardware
|
||||
// or Software Pseudo random generators for the
|
||||
// Arduino random() functions
|
||||
static bool s_useRandomHW = true;
|
||||
void useRealRandomGenerator(bool useRandomHW) {
|
||||
s_useRandomHW = useRandomHW;
|
||||
}
|
||||
|
||||
// Calling randomSeed() will force the
|
||||
// Pseudo Random generator like in
|
||||
// Arduino mainstream API
|
||||
void randomSeed(unsigned long seed) {
|
||||
if (seed != 0) {
|
||||
srand(seed);
|
||||
s_useRandomHW = false;
|
||||
}
|
||||
}
|
||||
|
||||
long random(long howsmall, long howbig);
|
||||
long random(long howbig) {
|
||||
if (howbig == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (howbig < 0) {
|
||||
return (random(0, -howbig));
|
||||
}
|
||||
// if randomSeed was called, fall back to software PRNG
|
||||
uint32_t val = (s_useRandomHW) ? esp_random() : rand();
|
||||
return val % howbig;
|
||||
}
|
||||
|
||||
long random(long howsmall, long howbig) {
|
||||
if (howsmall >= howbig) {
|
||||
return howsmall;
|
||||
}
|
||||
long diff = howbig - howsmall;
|
||||
return random(diff) + howsmall;
|
||||
}
|
||||
|
||||
long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
||||
const long run = in_max - in_min;
|
||||
if (run == 0) {
|
||||
log_e("map(): Invalid input range, min == max");
|
||||
return -1; // AVR returns -1, SAM returns 0
|
||||
}
|
||||
const long rise = out_max - out_min;
|
||||
const long delta = x - in_min;
|
||||
return (delta * rise) / run + out_min;
|
||||
}
|
||||
|
||||
uint16_t makeWord(uint16_t w) {
|
||||
return w;
|
||||
}
|
||||
|
||||
uint16_t makeWord(uint8_t h, uint8_t l) {
|
||||
return (h << 8) | l;
|
||||
}
|
||||
@@ -0,0 +1,934 @@
|
||||
/*
|
||||
WString.cpp - String library for Wiring & Arduino
|
||||
...mostly rewritten by Paul Stoffregen...
|
||||
Copyright (c) 2009-10 Hernando Barragan. All rights reserved.
|
||||
Copyright 2011, Paul Stoffregen, paul@pjrc.com
|
||||
Modified by Ivan Grokhotkov, 2014 - esp8266 support
|
||||
Modified by Michael C. Miller, 2015 - esp8266 progmem support
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "WString.h"
|
||||
#include "stdlib_noniso.h"
|
||||
#include "esp32-hal-log.h"
|
||||
|
||||
/*********************************************/
|
||||
/* Constructors */
|
||||
/*********************************************/
|
||||
|
||||
String::String(const char *cstr) {
|
||||
init();
|
||||
if (cstr) {
|
||||
copy(cstr, strlen(cstr));
|
||||
}
|
||||
}
|
||||
|
||||
String::String(const char *cstr, unsigned int length) {
|
||||
init();
|
||||
if (cstr) {
|
||||
copy(cstr, length);
|
||||
}
|
||||
}
|
||||
|
||||
String::String(const String &value) {
|
||||
init();
|
||||
*this = value;
|
||||
}
|
||||
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
String::String(String &&rval) {
|
||||
init();
|
||||
move(rval);
|
||||
}
|
||||
|
||||
String::String(StringSumHelper &&rval) {
|
||||
init();
|
||||
move(rval);
|
||||
}
|
||||
|
||||
String::String(std::initializer_list<char> list) {
|
||||
init();
|
||||
if (list.size() > 0) {
|
||||
copy(list.begin(), list.size());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
String::String(char c) {
|
||||
init();
|
||||
char buf[] = {c, '\0'};
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::String(unsigned char value, unsigned char base) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned char)];
|
||||
utoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::String(int value, unsigned char base) {
|
||||
init();
|
||||
char buf[2 + 8 * sizeof(int)];
|
||||
itoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::String(unsigned int value, unsigned char base) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned int)];
|
||||
utoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::String(long value, unsigned char base) {
|
||||
init();
|
||||
char buf[2 + 8 * sizeof(long)];
|
||||
ltoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::String(unsigned long value, unsigned char base) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned long)];
|
||||
ultoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::String(float value, unsigned int decimalPlaces) {
|
||||
init();
|
||||
char *buf = (char *)malloc(decimalPlaces + 42);
|
||||
if (buf) {
|
||||
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
|
||||
free(buf);
|
||||
} else {
|
||||
*this = "nan";
|
||||
log_e("No enough memory for the operation.");
|
||||
}
|
||||
}
|
||||
|
||||
String::String(double value, unsigned int decimalPlaces) {
|
||||
init();
|
||||
char *buf = (char *)malloc(decimalPlaces + 312);
|
||||
if (buf) {
|
||||
*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
|
||||
free(buf);
|
||||
} else {
|
||||
*this = "nan";
|
||||
log_e("No enough memory for the operation.");
|
||||
}
|
||||
}
|
||||
|
||||
String::String(long long value, unsigned char base) {
|
||||
init();
|
||||
char buf[2 + 8 * sizeof(long long)];
|
||||
lltoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::String(unsigned long long value, unsigned char base) {
|
||||
init();
|
||||
char buf[1 + 8 * sizeof(unsigned long long)];
|
||||
ulltoa(value, buf, base);
|
||||
*this = buf;
|
||||
}
|
||||
|
||||
String::~String() {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Memory Management */
|
||||
/*********************************************/
|
||||
|
||||
inline void String::init(void) {
|
||||
setSSO(false);
|
||||
setBuffer(nullptr);
|
||||
setCapacity(0);
|
||||
setLen(0);
|
||||
}
|
||||
|
||||
void String::invalidate(void) {
|
||||
if (!isSSO() && wbuffer()) {
|
||||
free(wbuffer());
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
bool String::reserve(unsigned int size) {
|
||||
if (buffer() && capacity() >= size) {
|
||||
return true;
|
||||
}
|
||||
if (changeBuffer(size)) {
|
||||
if (len() == 0) {
|
||||
wbuffer()[0] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool String::changeBuffer(unsigned int maxStrLen) {
|
||||
// Can we use SSO here to avoid allocation?
|
||||
if (maxStrLen < sizeof(sso.buff) - 1) {
|
||||
if (isSSO() || !buffer()) {
|
||||
// Already using SSO, nothing to do
|
||||
size_t oldLen = len();
|
||||
setSSO(true);
|
||||
setLen(oldLen);
|
||||
} else { // if bufptr && !isSSO()
|
||||
// Using bufptr, need to shrink into sso.buff
|
||||
char temp[sizeof(sso.buff)];
|
||||
memcpy(temp, buffer(), maxStrLen);
|
||||
free(wbuffer());
|
||||
size_t oldLen = len();
|
||||
setSSO(true);
|
||||
memcpy(wbuffer(), temp, maxStrLen);
|
||||
setLen(oldLen);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Fallthrough to normal allocator
|
||||
size_t newSize = (maxStrLen + 16) & (~0xf);
|
||||
// Make sure we can fit newsize in the buffer
|
||||
if (newSize > CAPACITY_MAX) {
|
||||
return false;
|
||||
}
|
||||
size_t oldLen = len();
|
||||
char *newbuffer = (char *)realloc(isSSO() ? nullptr : wbuffer(), newSize);
|
||||
if (newbuffer) {
|
||||
size_t oldSize = capacity() + 1; // include NULL.
|
||||
if (isSSO()) {
|
||||
// Copy the SSO buffer into allocated space
|
||||
memmove(newbuffer, sso.buff, sizeof(sso.buff));
|
||||
}
|
||||
if (newSize > oldSize) {
|
||||
memset(newbuffer + oldSize, 0, newSize - oldSize);
|
||||
}
|
||||
setSSO(false);
|
||||
setCapacity(newSize - 1);
|
||||
setBuffer(newbuffer);
|
||||
setLen(oldLen); // Needed in case of SSO where len() never existed
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Copy and Move */
|
||||
/*********************************************/
|
||||
|
||||
String &String::copy(const char *cstr, unsigned int length) {
|
||||
if (cstr == nullptr || !reserve(length)) {
|
||||
invalidate();
|
||||
return *this;
|
||||
}
|
||||
memmove(wbuffer(), cstr, length);
|
||||
setLen(length);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
void String::move(String &rhs) {
|
||||
if (buffer()) {
|
||||
if (capacity() >= rhs.len()) {
|
||||
// Use case: When 'reserve()' was called and the first
|
||||
// assignment/append is the return value of a function.
|
||||
if (rhs.len() && rhs.buffer()) {
|
||||
memmove(wbuffer(), rhs.buffer(), rhs.length());
|
||||
}
|
||||
setLen(rhs.len());
|
||||
rhs.invalidate();
|
||||
return;
|
||||
}
|
||||
if (!isSSO()) {
|
||||
free(wbuffer());
|
||||
setBuffer(nullptr);
|
||||
}
|
||||
}
|
||||
if (rhs.isSSO()) {
|
||||
setSSO(true);
|
||||
memmove(sso.buff, rhs.sso.buff, sizeof(sso.buff));
|
||||
} else {
|
||||
setSSO(false);
|
||||
setBuffer(rhs.wbuffer());
|
||||
}
|
||||
setCapacity(rhs.capacity());
|
||||
setLen(rhs.len());
|
||||
rhs.init();
|
||||
}
|
||||
#endif
|
||||
|
||||
String &String::operator=(const String &rhs) {
|
||||
if (this == &rhs) {
|
||||
return *this;
|
||||
}
|
||||
return copy(rhs.buffer(), rhs.len());
|
||||
}
|
||||
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
String &String::operator=(String &&rval) {
|
||||
if (this != &rval) {
|
||||
move(rval);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator=(StringSumHelper &&rval) {
|
||||
if (this != &rval) {
|
||||
move(rval);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
String &String::operator=(const char *cstr) {
|
||||
const uint32_t length = cstr ? strlen(cstr) : 0u;
|
||||
return copy(cstr, length);
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* concat */
|
||||
/*********************************************/
|
||||
|
||||
bool String::concat(const String &s) {
|
||||
// Special case if we're concatting ourself (s += s;) since we may end up
|
||||
// realloc'ing the buffer and moving s.buffer in the method called
|
||||
if (&s == this) {
|
||||
if (s.len() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (!s.buffer()) {
|
||||
return false;
|
||||
}
|
||||
unsigned int newlen = 2 * len();
|
||||
if (!reserve(newlen)) {
|
||||
return false;
|
||||
}
|
||||
memmove(wbuffer() + len(), buffer(), len());
|
||||
setLen(newlen);
|
||||
return true;
|
||||
}
|
||||
return concat(s.buffer(), s.len());
|
||||
}
|
||||
|
||||
bool String::concat(const char *cstr, unsigned int length) {
|
||||
unsigned int newlen = len() + length;
|
||||
if (!cstr) {
|
||||
return false;
|
||||
}
|
||||
if (length == 0) {
|
||||
return true;
|
||||
}
|
||||
if (!reserve(newlen)) {
|
||||
return false;
|
||||
}
|
||||
if (cstr >= wbuffer() && cstr < wbuffer() + len()) {
|
||||
// compatible with SSO in ram #6155 (case "x += x.c_str()")
|
||||
memmove(wbuffer() + len(), cstr, length);
|
||||
} else {
|
||||
// compatible with source in flash #6367
|
||||
memcpy_P(wbuffer() + len(), cstr, length);
|
||||
}
|
||||
setLen(newlen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool String::concat(const char *cstr) {
|
||||
if (!cstr) {
|
||||
return false;
|
||||
}
|
||||
return concat(cstr, strlen(cstr));
|
||||
}
|
||||
|
||||
bool String::concat(char c) {
|
||||
char buf[] = {c, '\0'};
|
||||
return concat(buf, 1);
|
||||
}
|
||||
|
||||
bool String::concat(unsigned char num) {
|
||||
char buf[1 + 3 * sizeof(unsigned char)];
|
||||
utoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
}
|
||||
|
||||
bool String::concat(int num) {
|
||||
char buf[2 + 3 * sizeof(int)];
|
||||
itoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
}
|
||||
|
||||
bool String::concat(unsigned int num) {
|
||||
char buf[1 + 3 * sizeof(unsigned int)];
|
||||
utoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
}
|
||||
|
||||
bool String::concat(long num) {
|
||||
char buf[2 + 3 * sizeof(long)];
|
||||
ltoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
}
|
||||
|
||||
bool String::concat(unsigned long num) {
|
||||
char buf[1 + 3 * sizeof(unsigned long)];
|
||||
ultoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
}
|
||||
|
||||
bool String::concat(long long num) {
|
||||
char buf[2 + 3 * sizeof(long long)];
|
||||
lltoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
}
|
||||
|
||||
bool String::concat(unsigned long long num) {
|
||||
char buf[1 + 3 * sizeof(unsigned long long)];
|
||||
ulltoa(num, buf, 10);
|
||||
return concat(buf, strlen(buf));
|
||||
}
|
||||
|
||||
bool String::concat(float num) {
|
||||
char buf[20];
|
||||
char *string = dtostrf(num, 4, 2, buf);
|
||||
return concat(string, strlen(string));
|
||||
}
|
||||
|
||||
bool String::concat(double num) {
|
||||
char buf[20];
|
||||
char *string = dtostrf(num, 4, 2, buf);
|
||||
return concat(string, strlen(string));
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Concatenate */
|
||||
/*********************************************/
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, const String &rhs) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(rhs.buffer(), rhs.len())) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, const char *cstr) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!cstr || !a.concat(cstr, strlen(cstr))) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, char c) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(c)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned char num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, int num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned int num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, long num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, float num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, double num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, long long num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long long num) {
|
||||
StringSumHelper &a = const_cast<StringSumHelper &>(lhs);
|
||||
if (!a.concat(num)) {
|
||||
a.invalidate();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Comparison */
|
||||
/*********************************************/
|
||||
|
||||
int String::compareTo(const String &s) const {
|
||||
if (!buffer() || !s.buffer()) {
|
||||
if (s.buffer() && s.len() > 0) {
|
||||
return 0 - *(unsigned char *)s.buffer();
|
||||
}
|
||||
if (buffer() && len() > 0) {
|
||||
return *(unsigned char *)buffer();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return strcmp(buffer(), s.buffer());
|
||||
}
|
||||
|
||||
bool String::equals(const String &s2) const {
|
||||
return (len() == s2.len() && compareTo(s2) == 0);
|
||||
}
|
||||
|
||||
bool String::equals(const char *cstr) const {
|
||||
if (len() == 0) {
|
||||
return (cstr == NULL || *cstr == 0);
|
||||
}
|
||||
if (cstr == NULL) {
|
||||
return buffer()[0] == 0;
|
||||
}
|
||||
return strcmp(buffer(), cstr) == 0;
|
||||
}
|
||||
|
||||
bool String::operator<(const String &rhs) const {
|
||||
return compareTo(rhs) < 0;
|
||||
}
|
||||
|
||||
bool String::operator>(const String &rhs) const {
|
||||
return compareTo(rhs) > 0;
|
||||
}
|
||||
|
||||
bool String::operator<=(const String &rhs) const {
|
||||
return compareTo(rhs) <= 0;
|
||||
}
|
||||
|
||||
bool String::operator>=(const String &rhs) const {
|
||||
return compareTo(rhs) >= 0;
|
||||
}
|
||||
|
||||
bool String::equalsIgnoreCase(const String &s2) const {
|
||||
if (this == &s2) {
|
||||
return true;
|
||||
}
|
||||
if (len() != s2.len()) {
|
||||
return false;
|
||||
}
|
||||
if (len() == 0) {
|
||||
return true;
|
||||
}
|
||||
const char *p1 = buffer();
|
||||
const char *p2 = s2.buffer();
|
||||
while (*p1) {
|
||||
if (tolower(*p1++) != tolower(*p2++)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char String::equalsConstantTime(const String &s2) const {
|
||||
// To avoid possible time-based attacks present function
|
||||
// compares given strings in a constant time.
|
||||
if (len() != s2.len()) {
|
||||
return 0;
|
||||
}
|
||||
//at this point lengths are the same
|
||||
if (len() == 0) {
|
||||
return 1;
|
||||
}
|
||||
//at this point lengths are the same and non-zero
|
||||
const char *p1 = buffer();
|
||||
const char *p2 = s2.buffer();
|
||||
unsigned int equalchars = 0;
|
||||
unsigned int diffchars = 0;
|
||||
while (*p1) {
|
||||
if (*p1 == *p2) {
|
||||
++equalchars;
|
||||
} else {
|
||||
++diffchars;
|
||||
}
|
||||
++p1;
|
||||
++p2;
|
||||
}
|
||||
//the following should force a constant time eval of the condition without a compiler "logical shortcut"
|
||||
unsigned char equalcond = (equalchars == len());
|
||||
unsigned char diffcond = (diffchars == 0);
|
||||
return (equalcond & diffcond); //bitwise AND
|
||||
}
|
||||
|
||||
bool String::startsWith(const String &s2) const {
|
||||
if (len() < s2.len()) {
|
||||
return false;
|
||||
}
|
||||
return startsWith(s2, 0);
|
||||
}
|
||||
|
||||
bool String::startsWith(const String &s2, unsigned int offset) const {
|
||||
if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer()) {
|
||||
return false;
|
||||
}
|
||||
return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0;
|
||||
}
|
||||
|
||||
bool String::endsWith(const String &s2) const {
|
||||
if (len() < s2.len() || !buffer() || !s2.buffer()) {
|
||||
return false;
|
||||
}
|
||||
return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Character Access */
|
||||
/*********************************************/
|
||||
|
||||
char String::charAt(unsigned int loc) const {
|
||||
return operator[](loc);
|
||||
}
|
||||
|
||||
void String::setCharAt(unsigned int loc, char c) {
|
||||
if (loc < len()) {
|
||||
wbuffer()[loc] = c;
|
||||
}
|
||||
}
|
||||
|
||||
char &String::operator[](unsigned int index) {
|
||||
static char dummy_writable_char;
|
||||
if (index >= len() || !buffer()) {
|
||||
dummy_writable_char = 0;
|
||||
return dummy_writable_char;
|
||||
}
|
||||
return wbuffer()[index];
|
||||
}
|
||||
|
||||
char String::operator[](unsigned int index) const {
|
||||
if (index >= len() || !buffer()) {
|
||||
return 0;
|
||||
}
|
||||
return buffer()[index];
|
||||
}
|
||||
|
||||
void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const {
|
||||
if (!bufsize || !buf) {
|
||||
return;
|
||||
}
|
||||
if (index >= len()) {
|
||||
buf[0] = 0;
|
||||
return;
|
||||
}
|
||||
unsigned int n = bufsize - 1;
|
||||
if (n > len() - index) {
|
||||
n = len() - index;
|
||||
}
|
||||
strncpy((char *)buf, buffer() + index, n);
|
||||
buf[n] = 0;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Search */
|
||||
/*********************************************/
|
||||
|
||||
int String::indexOf(char c) const {
|
||||
return indexOf(c, 0);
|
||||
}
|
||||
|
||||
int String::indexOf(char ch, unsigned int fromIndex) const {
|
||||
if (fromIndex >= len()) {
|
||||
return -1;
|
||||
}
|
||||
const char *temp = strchr(buffer() + fromIndex, ch);
|
||||
if (temp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return temp - buffer();
|
||||
}
|
||||
|
||||
int String::indexOf(const String &s2) const {
|
||||
return indexOf(s2, 0);
|
||||
}
|
||||
|
||||
int String::indexOf(const String &s2, unsigned int fromIndex) const {
|
||||
if (fromIndex >= len()) {
|
||||
return -1;
|
||||
}
|
||||
const char *found = strstr(buffer() + fromIndex, s2.buffer());
|
||||
if (found == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return found - buffer();
|
||||
}
|
||||
|
||||
int String::lastIndexOf(char theChar) const {
|
||||
return lastIndexOf(theChar, len() - 1);
|
||||
}
|
||||
|
||||
int String::lastIndexOf(char ch, unsigned int fromIndex) const {
|
||||
if (fromIndex >= len()) {
|
||||
return -1;
|
||||
}
|
||||
char tempchar = buffer()[fromIndex + 1];
|
||||
wbuffer()[fromIndex + 1] = '\0';
|
||||
char *temp = strrchr(wbuffer(), ch);
|
||||
wbuffer()[fromIndex + 1] = tempchar;
|
||||
if (temp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
const int rv = temp - buffer();
|
||||
if (rv >= len()) {
|
||||
return -1;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int String::lastIndexOf(const String &s2) const {
|
||||
return lastIndexOf(s2, len() - s2.len());
|
||||
}
|
||||
|
||||
int String::lastIndexOf(const String &s2, unsigned int fromIndex) const {
|
||||
if (s2.len() == 0 || len() == 0 || s2.len() > len()) {
|
||||
return -1;
|
||||
}
|
||||
if (fromIndex >= len()) {
|
||||
fromIndex = len() - 1;
|
||||
}
|
||||
int found = -1;
|
||||
for (char *p = wbuffer(); p <= wbuffer() + fromIndex; p++) {
|
||||
p = strstr(p, s2.buffer());
|
||||
if (!p) {
|
||||
break;
|
||||
}
|
||||
if ((unsigned int)(p - wbuffer()) <= fromIndex) {
|
||||
found = p - buffer();
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
String String::substring(unsigned int left, unsigned int right) const {
|
||||
if (left > right) {
|
||||
unsigned int temp = right;
|
||||
right = left;
|
||||
left = temp;
|
||||
}
|
||||
String out;
|
||||
if (left >= len()) {
|
||||
return out;
|
||||
}
|
||||
if (right > len()) {
|
||||
right = len();
|
||||
}
|
||||
out.copy(buffer() + left, right - left);
|
||||
return out;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Modification */
|
||||
/*********************************************/
|
||||
|
||||
void String::replace(char find, char replace) {
|
||||
if (!buffer()) {
|
||||
return;
|
||||
}
|
||||
for (char *p = wbuffer(); *p; p++) {
|
||||
if (*p == find) {
|
||||
*p = replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void String::replace(const String &find, const String &replace) {
|
||||
if (len() == 0 || find.len() == 0) {
|
||||
return;
|
||||
}
|
||||
int diff = replace.len() - find.len();
|
||||
char *readFrom = wbuffer();
|
||||
char *foundAt;
|
||||
if (diff == 0) {
|
||||
while ((foundAt = strstr(readFrom, find.buffer())) != NULL) {
|
||||
memmove(foundAt, replace.buffer(), replace.len());
|
||||
readFrom = foundAt + replace.len();
|
||||
}
|
||||
} else if (diff < 0) {
|
||||
char *writeTo = wbuffer();
|
||||
unsigned int l = len();
|
||||
while ((foundAt = strstr(readFrom, find.buffer())) != NULL) {
|
||||
unsigned int n = foundAt - readFrom;
|
||||
memmove(writeTo, readFrom, n);
|
||||
writeTo += n;
|
||||
memmove(writeTo, replace.buffer(), replace.len());
|
||||
writeTo += replace.len();
|
||||
readFrom = foundAt + find.len();
|
||||
l += diff;
|
||||
}
|
||||
memmove(writeTo, readFrom, strlen(readFrom) + 1);
|
||||
setLen(l);
|
||||
} else {
|
||||
unsigned int size = len(); // compute size needed for result
|
||||
while ((foundAt = strstr(readFrom, find.buffer())) != NULL) {
|
||||
readFrom = foundAt + find.len();
|
||||
size += diff;
|
||||
}
|
||||
if (size == len()) {
|
||||
return;
|
||||
}
|
||||
if (size > capacity() && !changeBuffer(size)) {
|
||||
log_w("String.Replace() Insufficient space to replace string");
|
||||
return;
|
||||
}
|
||||
int index = len() - 1;
|
||||
while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) {
|
||||
readFrom = wbuffer() + index + find.len();
|
||||
memmove(readFrom + diff, readFrom, len() - (readFrom - buffer()));
|
||||
int newLen = len() + diff;
|
||||
memmove(wbuffer() + index, replace.buffer(), replace.len());
|
||||
setLen(newLen);
|
||||
wbuffer()[newLen] = 0;
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void String::remove(unsigned int index) {
|
||||
// Pass the biggest integer as the count. The remove method
|
||||
// below will take care of truncating it at the end of the
|
||||
// string.
|
||||
remove(index, (unsigned int)-1);
|
||||
}
|
||||
|
||||
void String::remove(unsigned int index, unsigned int count) {
|
||||
if (index >= len()) {
|
||||
return;
|
||||
}
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
if (count > len() - index) {
|
||||
count = len() - index;
|
||||
}
|
||||
char *writeTo = wbuffer() + index;
|
||||
unsigned int newlen = len() - count;
|
||||
memmove(writeTo, wbuffer() + index + count, newlen - index);
|
||||
setLen(newlen);
|
||||
wbuffer()[newlen] = 0;
|
||||
}
|
||||
|
||||
void String::toLowerCase(void) {
|
||||
if (!buffer()) {
|
||||
return;
|
||||
}
|
||||
for (char *p = wbuffer(); *p; p++) {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
}
|
||||
|
||||
void String::toUpperCase(void) {
|
||||
if (!buffer()) {
|
||||
return;
|
||||
}
|
||||
for (char *p = wbuffer(); *p; p++) {
|
||||
*p = toupper(*p);
|
||||
}
|
||||
}
|
||||
|
||||
void String::trim(void) {
|
||||
if (!buffer() || len() == 0) {
|
||||
return;
|
||||
}
|
||||
char *begin = wbuffer();
|
||||
while (isspace(*begin)) {
|
||||
begin++;
|
||||
}
|
||||
char *end = wbuffer() + len() - 1;
|
||||
while (isspace(*end) && end >= begin) {
|
||||
end--;
|
||||
}
|
||||
unsigned int newlen = end + 1 - begin;
|
||||
if (begin > buffer()) {
|
||||
memmove(wbuffer(), begin, newlen);
|
||||
}
|
||||
setLen(newlen);
|
||||
wbuffer()[newlen] = 0;
|
||||
}
|
||||
|
||||
/*********************************************/
|
||||
/* Parsing / Conversion */
|
||||
/*********************************************/
|
||||
|
||||
long String::toInt(void) const {
|
||||
if (buffer()) {
|
||||
return atol(buffer());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float String::toFloat(void) const {
|
||||
if (buffer()) {
|
||||
return static_cast<float>(atof(buffer()));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double String::toDouble(void) const {
|
||||
if (buffer()) {
|
||||
return atof(buffer());
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// global empty string to allow returning const String& with nothing
|
||||
|
||||
const String emptyString;
|
||||
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
WString.h - String library for Wiring & Arduino
|
||||
...mostly rewritten by Paul Stoffregen...
|
||||
Copyright (c) 2009-10 Hernando Barragan. All right reserved.
|
||||
Copyright 2011, Paul Stoffregen, paul@pjrc.com
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef String_class_h
|
||||
#define String_class_h
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <pgmspace.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
#include <initializer_list>
|
||||
#endif
|
||||
|
||||
// A pure abstract class forward used as a means to proide a unique pointer type
|
||||
// but really is never defined.
|
||||
class __FlashStringHelper;
|
||||
#define FPSTR(str_pointer) (reinterpret_cast<const __FlashStringHelper *>(str_pointer))
|
||||
#define F(string_literal) (FPSTR(PSTR(string_literal)))
|
||||
|
||||
// An inherited class for holding the result of a concatenation. These
|
||||
// result objects are assumed to be writable by subsequent concatenations.
|
||||
class StringSumHelper;
|
||||
|
||||
// The string class
|
||||
class String {
|
||||
// use a function pointer to allow for "if (s)" without the
|
||||
// complications of an operator bool(). for more information, see:
|
||||
// http://www.artima.com/cppsource/safebool.html
|
||||
typedef void (String::*StringIfHelperType)() 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 char *cstr, unsigned int length);
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
String(const uint8_t *cstr, unsigned int length) : String(reinterpret_cast<const char *>(cstr), length) {}
|
||||
String(std::initializer_list<char> list);
|
||||
#endif
|
||||
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 int decimalPlaces = 2);
|
||||
explicit String(double, unsigned int decimalPlaces = 2);
|
||||
explicit String(long long, unsigned char base = 10);
|
||||
explicit String(unsigned long long, unsigned char base = 10);
|
||||
~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)
|
||||
bool 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) {
|
||||
return *this = reinterpret_cast<const char *>(str);
|
||||
}
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
String &operator=(String &&rval);
|
||||
String &operator=(StringSumHelper &&rval);
|
||||
#endif
|
||||
|
||||
// concatenate (works w/ built-in types, same as assignment)
|
||||
|
||||
// returns true on success, false on failure (in which case, the string
|
||||
// is left unchanged). if the argument is null or invalid, the
|
||||
// concatenation is considered unsuccessful.
|
||||
bool concat(const String &str);
|
||||
bool concat(const char *cstr);
|
||||
bool concat(const char *cstr, unsigned int length);
|
||||
bool concat(const uint8_t *cstr, unsigned int length) {
|
||||
return concat(reinterpret_cast<const char *>(cstr), length);
|
||||
}
|
||||
bool concat(char c);
|
||||
bool concat(unsigned char c);
|
||||
bool concat(int num);
|
||||
bool concat(unsigned int num);
|
||||
bool concat(long num);
|
||||
bool concat(unsigned long num);
|
||||
bool concat(float num);
|
||||
bool concat(double num);
|
||||
bool concat(long long num);
|
||||
bool concat(unsigned long long num);
|
||||
bool concat(const __FlashStringHelper *str) {
|
||||
return concat(reinterpret_cast<const char *>(str));
|
||||
}
|
||||
|
||||
// if there's not enough memory for the concatenated value, the string
|
||||
// will be left unchanged (but this isn't signaled in any way)
|
||||
String &operator+=(const String &rhs) {
|
||||
concat(rhs);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(const char *cstr) {
|
||||
concat(cstr);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(char c) {
|
||||
concat(c);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(unsigned char num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(int num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(unsigned int num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(long num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(unsigned long num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(float num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(double num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(long long num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(unsigned long long num) {
|
||||
concat(num);
|
||||
return (*this);
|
||||
}
|
||||
String &operator+=(const __FlashStringHelper *str) {
|
||||
return *this += reinterpret_cast<const char *>(str);
|
||||
}
|
||||
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, const String &rhs);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, const char *cstr);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, char c);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, unsigned char num);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, 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, unsigned long num);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, float num);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, double num);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, long long num);
|
||||
friend StringSumHelper &operator+(const StringSumHelper &lhs, unsigned long long num);
|
||||
|
||||
// comparison (only works w/ Strings and "strings")
|
||||
operator StringIfHelperType() const {
|
||||
return buffer() ? &String::StringIfHelper : 0;
|
||||
}
|
||||
int compareTo(const String &s) const;
|
||||
bool equals(const String &s) const;
|
||||
bool equals(const char *cstr) const;
|
||||
bool operator==(const String &rhs) const {
|
||||
return equals(rhs);
|
||||
}
|
||||
bool operator==(const char *cstr) const {
|
||||
return equals(cstr);
|
||||
}
|
||||
bool operator!=(const String &rhs) const {
|
||||
return !equals(rhs);
|
||||
}
|
||||
bool operator!=(const char *cstr) const {
|
||||
return !equals(cstr);
|
||||
}
|
||||
bool operator<(const String &rhs) const;
|
||||
bool operator>(const String &rhs) const;
|
||||
bool operator<=(const String &rhs) const;
|
||||
bool operator>=(const String &rhs) const;
|
||||
bool equalsIgnoreCase(const String &s) const;
|
||||
unsigned char equalsConstantTime(const String &s) const;
|
||||
bool startsWith(const String &prefix) const;
|
||||
bool startsWith(const char *prefix) const {
|
||||
return this->startsWith(String(prefix));
|
||||
}
|
||||
bool startsWith(const __FlashStringHelper *prefix) const {
|
||||
return this->startsWith(reinterpret_cast<const char *>(prefix));
|
||||
}
|
||||
bool startsWith(const String &prefix, unsigned int offset) const;
|
||||
bool endsWith(const String &suffix) const;
|
||||
bool endsWith(const char *suffix) const {
|
||||
return this->endsWith(String(suffix));
|
||||
}
|
||||
bool endsWith(const __FlashStringHelper *suffix) const {
|
||||
return this->endsWith(reinterpret_cast<const char *>(suffix));
|
||||
}
|
||||
|
||||
// character access
|
||||
char charAt(unsigned int index) const;
|
||||
void setCharAt(unsigned int index, char c);
|
||||
char operator[](unsigned int index) const;
|
||||
char &operator[](unsigned int index);
|
||||
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 {
|
||||
getBytes((unsigned char *)buf, bufsize, index);
|
||||
}
|
||||
const char *c_str() const {
|
||||
return buffer();
|
||||
}
|
||||
char *begin() {
|
||||
return wbuffer();
|
||||
}
|
||||
char *end() {
|
||||
return wbuffer() + length();
|
||||
}
|
||||
const char *begin() const {
|
||||
return c_str();
|
||||
}
|
||||
const char *end() const {
|
||||
return c_str() + length();
|
||||
}
|
||||
|
||||
// search
|
||||
int indexOf(char ch) const;
|
||||
int indexOf(char ch, unsigned int fromIndex) const;
|
||||
int indexOf(const String &str) const;
|
||||
int indexOf(const String &str, unsigned int fromIndex) const;
|
||||
int lastIndexOf(char ch) const;
|
||||
int lastIndexOf(char ch, unsigned int fromIndex) const;
|
||||
int lastIndexOf(const String &str) const;
|
||||
int lastIndexOf(const String &str, unsigned int fromIndex) const;
|
||||
String substring(unsigned int beginIndex) const {
|
||||
return substring(beginIndex, len());
|
||||
}
|
||||
String substring(unsigned int beginIndex, unsigned int endIndex) const;
|
||||
|
||||
// modification
|
||||
void replace(char find, char replace);
|
||||
void replace(const String &find, const String &replace);
|
||||
void replace(const char *find, const String &replace) {
|
||||
this->replace(String(find), replace);
|
||||
}
|
||||
void replace(const __FlashStringHelper *find, const String &replace) {
|
||||
this->replace(reinterpret_cast<const char *>(find), replace);
|
||||
}
|
||||
void replace(const char *find, const char *replace) {
|
||||
this->replace(String(find), String(replace));
|
||||
}
|
||||
void replace(const __FlashStringHelper *find, const char *replace) {
|
||||
this->replace(reinterpret_cast<const char *>(find), String(replace));
|
||||
}
|
||||
void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) {
|
||||
this->replace(reinterpret_cast<const char *>(find), reinterpret_cast<const char *>(replace));
|
||||
}
|
||||
void remove(unsigned int index);
|
||||
void remove(unsigned int index, unsigned int count);
|
||||
void toLowerCase(void);
|
||||
void toUpperCase(void);
|
||||
void trim(void);
|
||||
|
||||
// parsing/conversion
|
||||
long toInt(void) const;
|
||||
float toFloat(void) const;
|
||||
double toDouble(void) const;
|
||||
|
||||
protected:
|
||||
// Contains the string info when we're not in SSO mode
|
||||
struct _ptr {
|
||||
char *buff;
|
||||
uint32_t cap;
|
||||
uint32_t len;
|
||||
};
|
||||
// This allows strings up up to 11 (10 + \0 termination) without any extra space.
|
||||
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
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
enum {
|
||||
CAPACITY_MAX = 3145728
|
||||
};
|
||||
#else
|
||||
enum {
|
||||
CAPACITY_MAX = 65535
|
||||
};
|
||||
#endif
|
||||
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;
|
||||
sso.buff[len] = 0;
|
||||
} else {
|
||||
ptr.len = len;
|
||||
if (ptr.buff) {
|
||||
ptr.buff[len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 reinterpret_cast<const char *>(isSSO() ? sso.buff : ptr.buff);
|
||||
}
|
||||
inline char *wbuffer() const {
|
||||
return isSSO() ? const_cast<char *>(sso.buff) : ptr.buff;
|
||||
} // Writable version of buffer
|
||||
|
||||
protected:
|
||||
void init(void);
|
||||
void invalidate(void);
|
||||
bool changeBuffer(unsigned int maxStrLen);
|
||||
|
||||
// copy and move
|
||||
String ©(const char *cstr, unsigned int length);
|
||||
String ©(const __FlashStringHelper *pstr, unsigned int length) {
|
||||
return copy(reinterpret_cast<const char *>(pstr), length);
|
||||
}
|
||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__
|
||||
void move(String &rhs);
|
||||
#endif
|
||||
};
|
||||
|
||||
class StringSumHelper : public String {
|
||||
public:
|
||||
StringSumHelper(const String &s) : String(s) {}
|
||||
StringSumHelper(const char *p) : String(p) {}
|
||||
StringSumHelper(char c) : String(c) {}
|
||||
StringSumHelper(unsigned char num) : String(num) {}
|
||||
StringSumHelper(int num) : String(num) {}
|
||||
StringSumHelper(unsigned int num) : String(num) {}
|
||||
StringSumHelper(long num) : String(num) {}
|
||||
StringSumHelper(unsigned long num) : String(num) {}
|
||||
StringSumHelper(float num) : String(num) {}
|
||||
StringSumHelper(double num) : String(num) {}
|
||||
StringSumHelper(long long num) : String(num) {}
|
||||
StringSumHelper(unsigned long long num) : String(num) {}
|
||||
};
|
||||
|
||||
inline StringSumHelper &operator+(const StringSumHelper &lhs, const __FlashStringHelper *rhs) {
|
||||
return lhs + reinterpret_cast<const char *>(rhs);
|
||||
}
|
||||
|
||||
extern const String emptyString;
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // String_class_h
|
||||
@@ -0,0 +1 @@
|
||||
#include "lwip/apps/sntp.h"
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* base64.cpp
|
||||
*
|
||||
* Created on: 09.12.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the ESP31B core for Arduino.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
extern "C" {
|
||||
#include "libb64/cdecode.h"
|
||||
#include "libb64/cencode.h"
|
||||
}
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
* convert input data to base64
|
||||
* @param data const uint8_t *
|
||||
* @param length size_t
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(const uint8_t *data, size_t length) {
|
||||
size_t size = base64_encode_expected_len(length) + 1;
|
||||
char *buffer = (char *)malloc(size);
|
||||
if (buffer) {
|
||||
base64_encodestate _state;
|
||||
base64_init_encodestate(&_state);
|
||||
int len = base64_encode_block((const char *)&data[0], length, &buffer[0], &_state);
|
||||
len = base64_encode_blockend((buffer + len), &_state);
|
||||
|
||||
String base64 = String(buffer);
|
||||
free(buffer);
|
||||
return base64;
|
||||
}
|
||||
return String("-FAIL-");
|
||||
}
|
||||
|
||||
/**
|
||||
* convert input data to base64
|
||||
* @param text const String&
|
||||
* @return String
|
||||
*/
|
||||
String base64::encode(const String &text) {
|
||||
return base64::encode((uint8_t *)text.c_str(), text.length());
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef CORE_BASE64_H_
|
||||
#define CORE_BASE64_H_
|
||||
|
||||
class base64 {
|
||||
public:
|
||||
static String encode(const uint8_t *data, size_t length);
|
||||
static String encode(const String &text);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif /* CORE_BASE64_H_ */
|
||||
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
binary.h - Definitions for binary constants
|
||||
Deprecated -- use 0b binary literals instead
|
||||
Copyright (c) 2006 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef Binary_h
|
||||
#define Binary_h
|
||||
|
||||
/* If supported, 0b binary literals are preferable to these constants.
|
||||
* In that case, warn the user about these being deprecated (if possible). */
|
||||
#if __cplusplus >= 201402L
|
||||
/* C++14 introduces binary literals; C++11 introduces [[deprecated()]] */
|
||||
#define DEPRECATED(x) [[deprecated("use " #x " instead")]]
|
||||
#elif __GNUC__ >= 6
|
||||
/* GCC 4.3 supports binary literals; GCC 6 supports __deprecated__ on enums*/
|
||||
#define DEPRECATED(x) __attribute__((__deprecated__("use " #x " instead")))
|
||||
#else
|
||||
/* binary literals not supported, or "deprecated" warning not displayable */
|
||||
#define DEPRECATED(x)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
B0 DEPRECATED(0b0) = 0,
|
||||
B00 DEPRECATED(0b00) = 0,
|
||||
B000 DEPRECATED(0b000) = 0,
|
||||
B0000 DEPRECATED(0b0000) = 0,
|
||||
B00000 DEPRECATED(0b00000) = 0,
|
||||
B000000 DEPRECATED(0b000000) = 0,
|
||||
B0000000 DEPRECATED(0b0000000) = 0,
|
||||
B00000000 DEPRECATED(0b00000000) = 0,
|
||||
B1 DEPRECATED(0b1) = 1,
|
||||
B01 DEPRECATED(0b01) = 1,
|
||||
B001 DEPRECATED(0b001) = 1,
|
||||
B0001 DEPRECATED(0b0001) = 1,
|
||||
B00001 DEPRECATED(0b00001) = 1,
|
||||
B000001 DEPRECATED(0b000001) = 1,
|
||||
B0000001 DEPRECATED(0b0000001) = 1,
|
||||
B00000001 DEPRECATED(0b00000001) = 1,
|
||||
B10 DEPRECATED(0b10) = 2,
|
||||
B010 DEPRECATED(0b010) = 2,
|
||||
B0010 DEPRECATED(0b0010) = 2,
|
||||
B00010 DEPRECATED(0b00010) = 2,
|
||||
B000010 DEPRECATED(0b000010) = 2,
|
||||
B0000010 DEPRECATED(0b0000010) = 2,
|
||||
B00000010 DEPRECATED(0b00000010) = 2,
|
||||
B11 DEPRECATED(0b11) = 3,
|
||||
B011 DEPRECATED(0b011) = 3,
|
||||
B0011 DEPRECATED(0b0011) = 3,
|
||||
B00011 DEPRECATED(0b00011) = 3,
|
||||
B000011 DEPRECATED(0b000011) = 3,
|
||||
B0000011 DEPRECATED(0b0000011) = 3,
|
||||
B00000011 DEPRECATED(0b00000011) = 3,
|
||||
B100 DEPRECATED(0b100) = 4,
|
||||
B0100 DEPRECATED(0b0100) = 4,
|
||||
B00100 DEPRECATED(0b00100) = 4,
|
||||
B000100 DEPRECATED(0b000100) = 4,
|
||||
B0000100 DEPRECATED(0b0000100) = 4,
|
||||
B00000100 DEPRECATED(0b00000100) = 4,
|
||||
B101 DEPRECATED(0b101) = 5,
|
||||
B0101 DEPRECATED(0b0101) = 5,
|
||||
B00101 DEPRECATED(0b00101) = 5,
|
||||
B000101 DEPRECATED(0b000101) = 5,
|
||||
B0000101 DEPRECATED(0b0000101) = 5,
|
||||
B00000101 DEPRECATED(0b00000101) = 5,
|
||||
B110 DEPRECATED(0b110) = 6,
|
||||
B0110 DEPRECATED(0b0110) = 6,
|
||||
B00110 DEPRECATED(0b00110) = 6,
|
||||
B000110 DEPRECATED(0b000110) = 6,
|
||||
B0000110 DEPRECATED(0b0000110) = 6,
|
||||
B00000110 DEPRECATED(0b00000110) = 6,
|
||||
B111 DEPRECATED(0b111) = 7,
|
||||
B0111 DEPRECATED(0b0111) = 7,
|
||||
B00111 DEPRECATED(0b00111) = 7,
|
||||
B000111 DEPRECATED(0b000111) = 7,
|
||||
B0000111 DEPRECATED(0b0000111) = 7,
|
||||
B00000111 DEPRECATED(0b00000111) = 7,
|
||||
B1000 DEPRECATED(0b1000) = 8,
|
||||
B01000 DEPRECATED(0b01000) = 8,
|
||||
B001000 DEPRECATED(0b001000) = 8,
|
||||
B0001000 DEPRECATED(0b0001000) = 8,
|
||||
B00001000 DEPRECATED(0b00001000) = 8,
|
||||
B1001 DEPRECATED(0b1001) = 9,
|
||||
B01001 DEPRECATED(0b01001) = 9,
|
||||
B001001 DEPRECATED(0b001001) = 9,
|
||||
B0001001 DEPRECATED(0b0001001) = 9,
|
||||
B00001001 DEPRECATED(0b00001001) = 9,
|
||||
B1010 DEPRECATED(0b1010) = 10,
|
||||
B01010 DEPRECATED(0b01010) = 10,
|
||||
B001010 DEPRECATED(0b001010) = 10,
|
||||
B0001010 DEPRECATED(0b0001010) = 10,
|
||||
B00001010 DEPRECATED(0b00001010) = 10,
|
||||
B1011 DEPRECATED(0b1011) = 11,
|
||||
B01011 DEPRECATED(0b01011) = 11,
|
||||
B001011 DEPRECATED(0b001011) = 11,
|
||||
B0001011 DEPRECATED(0b0001011) = 11,
|
||||
B00001011 DEPRECATED(0b00001011) = 11,
|
||||
B1100 DEPRECATED(0b1100) = 12,
|
||||
B01100 DEPRECATED(0b01100) = 12,
|
||||
B001100 DEPRECATED(0b001100) = 12,
|
||||
B0001100 DEPRECATED(0b0001100) = 12,
|
||||
B00001100 DEPRECATED(0b00001100) = 12,
|
||||
B1101 DEPRECATED(0b1101) = 13,
|
||||
B01101 DEPRECATED(0b01101) = 13,
|
||||
B001101 DEPRECATED(0b001101) = 13,
|
||||
B0001101 DEPRECATED(0b0001101) = 13,
|
||||
B00001101 DEPRECATED(0b00001101) = 13,
|
||||
B1110 DEPRECATED(0b1110) = 14,
|
||||
B01110 DEPRECATED(0b01110) = 14,
|
||||
B001110 DEPRECATED(0b001110) = 14,
|
||||
B0001110 DEPRECATED(0b0001110) = 14,
|
||||
B00001110 DEPRECATED(0b00001110) = 14,
|
||||
B1111 DEPRECATED(0b1111) = 15,
|
||||
B01111 DEPRECATED(0b01111) = 15,
|
||||
B001111 DEPRECATED(0b001111) = 15,
|
||||
B0001111 DEPRECATED(0b0001111) = 15,
|
||||
B00001111 DEPRECATED(0b00001111) = 15,
|
||||
B10000 DEPRECATED(0b10000) = 16,
|
||||
B010000 DEPRECATED(0b010000) = 16,
|
||||
B0010000 DEPRECATED(0b0010000) = 16,
|
||||
B00010000 DEPRECATED(0b00010000) = 16,
|
||||
B10001 DEPRECATED(0b10001) = 17,
|
||||
B010001 DEPRECATED(0b010001) = 17,
|
||||
B0010001 DEPRECATED(0b0010001) = 17,
|
||||
B00010001 DEPRECATED(0b00010001) = 17,
|
||||
B10010 DEPRECATED(0b10010) = 18,
|
||||
B010010 DEPRECATED(0b010010) = 18,
|
||||
B0010010 DEPRECATED(0b0010010) = 18,
|
||||
B00010010 DEPRECATED(0b00010010) = 18,
|
||||
B10011 DEPRECATED(0b10011) = 19,
|
||||
B010011 DEPRECATED(0b010011) = 19,
|
||||
B0010011 DEPRECATED(0b0010011) = 19,
|
||||
B00010011 DEPRECATED(0b00010011) = 19,
|
||||
B10100 DEPRECATED(0b10100) = 20,
|
||||
B010100 DEPRECATED(0b010100) = 20,
|
||||
B0010100 DEPRECATED(0b0010100) = 20,
|
||||
B00010100 DEPRECATED(0b00010100) = 20,
|
||||
B10101 DEPRECATED(0b10101) = 21,
|
||||
B010101 DEPRECATED(0b010101) = 21,
|
||||
B0010101 DEPRECATED(0b0010101) = 21,
|
||||
B00010101 DEPRECATED(0b00010101) = 21,
|
||||
B10110 DEPRECATED(0b10110) = 22,
|
||||
B010110 DEPRECATED(0b010110) = 22,
|
||||
B0010110 DEPRECATED(0b0010110) = 22,
|
||||
B00010110 DEPRECATED(0b00010110) = 22,
|
||||
B10111 DEPRECATED(0b10111) = 23,
|
||||
B010111 DEPRECATED(0b010111) = 23,
|
||||
B0010111 DEPRECATED(0b0010111) = 23,
|
||||
B00010111 DEPRECATED(0b00010111) = 23,
|
||||
B11000 DEPRECATED(0b11000) = 24,
|
||||
B011000 DEPRECATED(0b011000) = 24,
|
||||
B0011000 DEPRECATED(0b0011000) = 24,
|
||||
B00011000 DEPRECATED(0b00011000) = 24,
|
||||
B11001 DEPRECATED(0b11001) = 25,
|
||||
B011001 DEPRECATED(0b011001) = 25,
|
||||
B0011001 DEPRECATED(0b0011001) = 25,
|
||||
B00011001 DEPRECATED(0b00011001) = 25,
|
||||
B11010 DEPRECATED(0b11010) = 26,
|
||||
B011010 DEPRECATED(0b011010) = 26,
|
||||
B0011010 DEPRECATED(0b0011010) = 26,
|
||||
B00011010 DEPRECATED(0b00011010) = 26,
|
||||
B11011 DEPRECATED(0b11011) = 27,
|
||||
B011011 DEPRECATED(0b011011) = 27,
|
||||
B0011011 DEPRECATED(0b0011011) = 27,
|
||||
B00011011 DEPRECATED(0b00011011) = 27,
|
||||
B11100 DEPRECATED(0b11100) = 28,
|
||||
B011100 DEPRECATED(0b011100) = 28,
|
||||
B0011100 DEPRECATED(0b0011100) = 28,
|
||||
B00011100 DEPRECATED(0b00011100) = 28,
|
||||
B11101 DEPRECATED(0b11101) = 29,
|
||||
B011101 DEPRECATED(0b011101) = 29,
|
||||
B0011101 DEPRECATED(0b0011101) = 29,
|
||||
B00011101 DEPRECATED(0b00011101) = 29,
|
||||
B11110 DEPRECATED(0b11110) = 30,
|
||||
B011110 DEPRECATED(0b011110) = 30,
|
||||
B0011110 DEPRECATED(0b0011110) = 30,
|
||||
B00011110 DEPRECATED(0b00011110) = 30,
|
||||
B11111 DEPRECATED(0b11111) = 31,
|
||||
B011111 DEPRECATED(0b011111) = 31,
|
||||
B0011111 DEPRECATED(0b0011111) = 31,
|
||||
B00011111 DEPRECATED(0b00011111) = 31,
|
||||
B100000 DEPRECATED(0b100000) = 32,
|
||||
B0100000 DEPRECATED(0b0100000) = 32,
|
||||
B00100000 DEPRECATED(0b00100000) = 32,
|
||||
B100001 DEPRECATED(0b100001) = 33,
|
||||
B0100001 DEPRECATED(0b0100001) = 33,
|
||||
B00100001 DEPRECATED(0b00100001) = 33,
|
||||
B100010 DEPRECATED(0b100010) = 34,
|
||||
B0100010 DEPRECATED(0b0100010) = 34,
|
||||
B00100010 DEPRECATED(0b00100010) = 34,
|
||||
B100011 DEPRECATED(0b100011) = 35,
|
||||
B0100011 DEPRECATED(0b0100011) = 35,
|
||||
B00100011 DEPRECATED(0b00100011) = 35,
|
||||
B100100 DEPRECATED(0b100100) = 36,
|
||||
B0100100 DEPRECATED(0b0100100) = 36,
|
||||
B00100100 DEPRECATED(0b00100100) = 36,
|
||||
B100101 DEPRECATED(0b100101) = 37,
|
||||
B0100101 DEPRECATED(0b0100101) = 37,
|
||||
B00100101 DEPRECATED(0b00100101) = 37,
|
||||
B100110 DEPRECATED(0b100110) = 38,
|
||||
B0100110 DEPRECATED(0b0100110) = 38,
|
||||
B00100110 DEPRECATED(0b00100110) = 38,
|
||||
B100111 DEPRECATED(0b100111) = 39,
|
||||
B0100111 DEPRECATED(0b0100111) = 39,
|
||||
B00100111 DEPRECATED(0b00100111) = 39,
|
||||
B101000 DEPRECATED(0b101000) = 40,
|
||||
B0101000 DEPRECATED(0b0101000) = 40,
|
||||
B00101000 DEPRECATED(0b00101000) = 40,
|
||||
B101001 DEPRECATED(0b101001) = 41,
|
||||
B0101001 DEPRECATED(0b0101001) = 41,
|
||||
B00101001 DEPRECATED(0b00101001) = 41,
|
||||
B101010 DEPRECATED(0b101010) = 42,
|
||||
B0101010 DEPRECATED(0b0101010) = 42,
|
||||
B00101010 DEPRECATED(0b00101010) = 42,
|
||||
B101011 DEPRECATED(0b101011) = 43,
|
||||
B0101011 DEPRECATED(0b0101011) = 43,
|
||||
B00101011 DEPRECATED(0b00101011) = 43,
|
||||
B101100 DEPRECATED(0b101100) = 44,
|
||||
B0101100 DEPRECATED(0b0101100) = 44,
|
||||
B00101100 DEPRECATED(0b00101100) = 44,
|
||||
B101101 DEPRECATED(0b101101) = 45,
|
||||
B0101101 DEPRECATED(0b0101101) = 45,
|
||||
B00101101 DEPRECATED(0b00101101) = 45,
|
||||
B101110 DEPRECATED(0b101110) = 46,
|
||||
B0101110 DEPRECATED(0b0101110) = 46,
|
||||
B00101110 DEPRECATED(0b00101110) = 46,
|
||||
B101111 DEPRECATED(0b101111) = 47,
|
||||
B0101111 DEPRECATED(0b0101111) = 47,
|
||||
B00101111 DEPRECATED(0b00101111) = 47,
|
||||
B110000 DEPRECATED(0b110000) = 48,
|
||||
B0110000 DEPRECATED(0b0110000) = 48,
|
||||
B00110000 DEPRECATED(0b00110000) = 48,
|
||||
B110001 DEPRECATED(0b110001) = 49,
|
||||
B0110001 DEPRECATED(0b0110001) = 49,
|
||||
B00110001 DEPRECATED(0b00110001) = 49,
|
||||
B110010 DEPRECATED(0b110010) = 50,
|
||||
B0110010 DEPRECATED(0b0110010) = 50,
|
||||
B00110010 DEPRECATED(0b00110010) = 50,
|
||||
B110011 DEPRECATED(0b110011) = 51,
|
||||
B0110011 DEPRECATED(0b0110011) = 51,
|
||||
B00110011 DEPRECATED(0b00110011) = 51,
|
||||
B110100 DEPRECATED(0b110100) = 52,
|
||||
B0110100 DEPRECATED(0b0110100) = 52,
|
||||
B00110100 DEPRECATED(0b00110100) = 52,
|
||||
B110101 DEPRECATED(0b110101) = 53,
|
||||
B0110101 DEPRECATED(0b0110101) = 53,
|
||||
B00110101 DEPRECATED(0b00110101) = 53,
|
||||
B110110 DEPRECATED(0b110110) = 54,
|
||||
B0110110 DEPRECATED(0b0110110) = 54,
|
||||
B00110110 DEPRECATED(0b00110110) = 54,
|
||||
B110111 DEPRECATED(0b110111) = 55,
|
||||
B0110111 DEPRECATED(0b0110111) = 55,
|
||||
B00110111 DEPRECATED(0b00110111) = 55,
|
||||
B111000 DEPRECATED(0b111000) = 56,
|
||||
B0111000 DEPRECATED(0b0111000) = 56,
|
||||
B00111000 DEPRECATED(0b00111000) = 56,
|
||||
B111001 DEPRECATED(0b111001) = 57,
|
||||
B0111001 DEPRECATED(0b0111001) = 57,
|
||||
B00111001 DEPRECATED(0b00111001) = 57,
|
||||
B111010 DEPRECATED(0b111010) = 58,
|
||||
B0111010 DEPRECATED(0b0111010) = 58,
|
||||
B00111010 DEPRECATED(0b00111010) = 58,
|
||||
B111011 DEPRECATED(0b111011) = 59,
|
||||
B0111011 DEPRECATED(0b0111011) = 59,
|
||||
B00111011 DEPRECATED(0b00111011) = 59,
|
||||
B111100 DEPRECATED(0b111100) = 60,
|
||||
B0111100 DEPRECATED(0b0111100) = 60,
|
||||
B00111100 DEPRECATED(0b00111100) = 60,
|
||||
B111101 DEPRECATED(0b111101) = 61,
|
||||
B0111101 DEPRECATED(0b0111101) = 61,
|
||||
B00111101 DEPRECATED(0b00111101) = 61,
|
||||
B111110 DEPRECATED(0b111110) = 62,
|
||||
B0111110 DEPRECATED(0b0111110) = 62,
|
||||
B00111110 DEPRECATED(0b00111110) = 62,
|
||||
B111111 DEPRECATED(0b111111) = 63,
|
||||
B0111111 DEPRECATED(0b0111111) = 63,
|
||||
B00111111 DEPRECATED(0b00111111) = 63,
|
||||
B1000000 DEPRECATED(0b1000000) = 64,
|
||||
B01000000 DEPRECATED(0b01000000) = 64,
|
||||
B1000001 DEPRECATED(0b1000001) = 65,
|
||||
B01000001 DEPRECATED(0b01000001) = 65,
|
||||
B1000010 DEPRECATED(0b1000010) = 66,
|
||||
B01000010 DEPRECATED(0b01000010) = 66,
|
||||
B1000011 DEPRECATED(0b1000011) = 67,
|
||||
B01000011 DEPRECATED(0b01000011) = 67,
|
||||
B1000100 DEPRECATED(0b1000100) = 68,
|
||||
B01000100 DEPRECATED(0b01000100) = 68,
|
||||
B1000101 DEPRECATED(0b1000101) = 69,
|
||||
B01000101 DEPRECATED(0b01000101) = 69,
|
||||
B1000110 DEPRECATED(0b1000110) = 70,
|
||||
B01000110 DEPRECATED(0b01000110) = 70,
|
||||
B1000111 DEPRECATED(0b1000111) = 71,
|
||||
B01000111 DEPRECATED(0b01000111) = 71,
|
||||
B1001000 DEPRECATED(0b1001000) = 72,
|
||||
B01001000 DEPRECATED(0b01001000) = 72,
|
||||
B1001001 DEPRECATED(0b1001001) = 73,
|
||||
B01001001 DEPRECATED(0b01001001) = 73,
|
||||
B1001010 DEPRECATED(0b1001010) = 74,
|
||||
B01001010 DEPRECATED(0b01001010) = 74,
|
||||
B1001011 DEPRECATED(0b1001011) = 75,
|
||||
B01001011 DEPRECATED(0b01001011) = 75,
|
||||
B1001100 DEPRECATED(0b1001100) = 76,
|
||||
B01001100 DEPRECATED(0b01001100) = 76,
|
||||
B1001101 DEPRECATED(0b1001101) = 77,
|
||||
B01001101 DEPRECATED(0b01001101) = 77,
|
||||
B1001110 DEPRECATED(0b1001110) = 78,
|
||||
B01001110 DEPRECATED(0b01001110) = 78,
|
||||
B1001111 DEPRECATED(0b1001111) = 79,
|
||||
B01001111 DEPRECATED(0b01001111) = 79,
|
||||
B1010000 DEPRECATED(0b1010000) = 80,
|
||||
B01010000 DEPRECATED(0b01010000) = 80,
|
||||
B1010001 DEPRECATED(0b1010001) = 81,
|
||||
B01010001 DEPRECATED(0b01010001) = 81,
|
||||
B1010010 DEPRECATED(0b1010010) = 82,
|
||||
B01010010 DEPRECATED(0b01010010) = 82,
|
||||
B1010011 DEPRECATED(0b1010011) = 83,
|
||||
B01010011 DEPRECATED(0b01010011) = 83,
|
||||
B1010100 DEPRECATED(0b1010100) = 84,
|
||||
B01010100 DEPRECATED(0b01010100) = 84,
|
||||
B1010101 DEPRECATED(0b1010101) = 85,
|
||||
B01010101 DEPRECATED(0b01010101) = 85,
|
||||
B1010110 DEPRECATED(0b1010110) = 86,
|
||||
B01010110 DEPRECATED(0b01010110) = 86,
|
||||
B1010111 DEPRECATED(0b1010111) = 87,
|
||||
B01010111 DEPRECATED(0b01010111) = 87,
|
||||
B1011000 DEPRECATED(0b1011000) = 88,
|
||||
B01011000 DEPRECATED(0b01011000) = 88,
|
||||
B1011001 DEPRECATED(0b1011001) = 89,
|
||||
B01011001 DEPRECATED(0b01011001) = 89,
|
||||
B1011010 DEPRECATED(0b1011010) = 90,
|
||||
B01011010 DEPRECATED(0b01011010) = 90,
|
||||
B1011011 DEPRECATED(0b1011011) = 91,
|
||||
B01011011 DEPRECATED(0b01011011) = 91,
|
||||
B1011100 DEPRECATED(0b1011100) = 92,
|
||||
B01011100 DEPRECATED(0b01011100) = 92,
|
||||
B1011101 DEPRECATED(0b1011101) = 93,
|
||||
B01011101 DEPRECATED(0b01011101) = 93,
|
||||
B1011110 DEPRECATED(0b1011110) = 94,
|
||||
B01011110 DEPRECATED(0b01011110) = 94,
|
||||
B1011111 DEPRECATED(0b1011111) = 95,
|
||||
B01011111 DEPRECATED(0b01011111) = 95,
|
||||
B1100000 DEPRECATED(0b1100000) = 96,
|
||||
B01100000 DEPRECATED(0b01100000) = 96,
|
||||
B1100001 DEPRECATED(0b1100001) = 97,
|
||||
B01100001 DEPRECATED(0b01100001) = 97,
|
||||
B1100010 DEPRECATED(0b1100010) = 98,
|
||||
B01100010 DEPRECATED(0b01100010) = 98,
|
||||
B1100011 DEPRECATED(0b1100011) = 99,
|
||||
B01100011 DEPRECATED(0b01100011) = 99,
|
||||
B1100100 DEPRECATED(0b1100100) = 100,
|
||||
B01100100 DEPRECATED(0b01100100) = 100,
|
||||
B1100101 DEPRECATED(0b1100101) = 101,
|
||||
B01100101 DEPRECATED(0b01100101) = 101,
|
||||
B1100110 DEPRECATED(0b1100110) = 102,
|
||||
B01100110 DEPRECATED(0b01100110) = 102,
|
||||
B1100111 DEPRECATED(0b1100111) = 103,
|
||||
B01100111 DEPRECATED(0b01100111) = 103,
|
||||
B1101000 DEPRECATED(0b1101000) = 104,
|
||||
B01101000 DEPRECATED(0b01101000) = 104,
|
||||
B1101001 DEPRECATED(0b1101001) = 105,
|
||||
B01101001 DEPRECATED(0b01101001) = 105,
|
||||
B1101010 DEPRECATED(0b1101010) = 106,
|
||||
B01101010 DEPRECATED(0b01101010) = 106,
|
||||
B1101011 DEPRECATED(0b1101011) = 107,
|
||||
B01101011 DEPRECATED(0b01101011) = 107,
|
||||
B1101100 DEPRECATED(0b1101100) = 108,
|
||||
B01101100 DEPRECATED(0b01101100) = 108,
|
||||
B1101101 DEPRECATED(0b1101101) = 109,
|
||||
B01101101 DEPRECATED(0b01101101) = 109,
|
||||
B1101110 DEPRECATED(0b1101110) = 110,
|
||||
B01101110 DEPRECATED(0b01101110) = 110,
|
||||
B1101111 DEPRECATED(0b1101111) = 111,
|
||||
B01101111 DEPRECATED(0b01101111) = 111,
|
||||
B1110000 DEPRECATED(0b1110000) = 112,
|
||||
B01110000 DEPRECATED(0b01110000) = 112,
|
||||
B1110001 DEPRECATED(0b1110001) = 113,
|
||||
B01110001 DEPRECATED(0b01110001) = 113,
|
||||
B1110010 DEPRECATED(0b1110010) = 114,
|
||||
B01110010 DEPRECATED(0b01110010) = 114,
|
||||
B1110011 DEPRECATED(0b1110011) = 115,
|
||||
B01110011 DEPRECATED(0b01110011) = 115,
|
||||
B1110100 DEPRECATED(0b1110100) = 116,
|
||||
B01110100 DEPRECATED(0b01110100) = 116,
|
||||
B1110101 DEPRECATED(0b1110101) = 117,
|
||||
B01110101 DEPRECATED(0b01110101) = 117,
|
||||
B1110110 DEPRECATED(0b1110110) = 118,
|
||||
B01110110 DEPRECATED(0b01110110) = 118,
|
||||
B1110111 DEPRECATED(0b1110111) = 119,
|
||||
B01110111 DEPRECATED(0b01110111) = 119,
|
||||
B1111000 DEPRECATED(0b1111000) = 120,
|
||||
B01111000 DEPRECATED(0b01111000) = 120,
|
||||
B1111001 DEPRECATED(0b1111001) = 121,
|
||||
B01111001 DEPRECATED(0b01111001) = 121,
|
||||
B1111010 DEPRECATED(0b1111010) = 122,
|
||||
B01111010 DEPRECATED(0b01111010) = 122,
|
||||
B1111011 DEPRECATED(0b1111011) = 123,
|
||||
B01111011 DEPRECATED(0b01111011) = 123,
|
||||
B1111100 DEPRECATED(0b1111100) = 124,
|
||||
B01111100 DEPRECATED(0b01111100) = 124,
|
||||
B1111101 DEPRECATED(0b1111101) = 125,
|
||||
B01111101 DEPRECATED(0b01111101) = 125,
|
||||
B1111110 DEPRECATED(0b1111110) = 126,
|
||||
B01111110 DEPRECATED(0b01111110) = 126,
|
||||
B1111111 DEPRECATED(0b1111111) = 127,
|
||||
B01111111 DEPRECATED(0b01111111) = 127,
|
||||
B10000000 DEPRECATED(0b10000000) = 128,
|
||||
B10000001 DEPRECATED(0b10000001) = 129,
|
||||
B10000010 DEPRECATED(0b10000010) = 130,
|
||||
B10000011 DEPRECATED(0b10000011) = 131,
|
||||
B10000100 DEPRECATED(0b10000100) = 132,
|
||||
B10000101 DEPRECATED(0b10000101) = 133,
|
||||
B10000110 DEPRECATED(0b10000110) = 134,
|
||||
B10000111 DEPRECATED(0b10000111) = 135,
|
||||
B10001000 DEPRECATED(0b10001000) = 136,
|
||||
B10001001 DEPRECATED(0b10001001) = 137,
|
||||
B10001010 DEPRECATED(0b10001010) = 138,
|
||||
B10001011 DEPRECATED(0b10001011) = 139,
|
||||
B10001100 DEPRECATED(0b10001100) = 140,
|
||||
B10001101 DEPRECATED(0b10001101) = 141,
|
||||
B10001110 DEPRECATED(0b10001110) = 142,
|
||||
B10001111 DEPRECATED(0b10001111) = 143,
|
||||
B10010000 DEPRECATED(0b10010000) = 144,
|
||||
B10010001 DEPRECATED(0b10010001) = 145,
|
||||
B10010010 DEPRECATED(0b10010010) = 146,
|
||||
B10010011 DEPRECATED(0b10010011) = 147,
|
||||
B10010100 DEPRECATED(0b10010100) = 148,
|
||||
B10010101 DEPRECATED(0b10010101) = 149,
|
||||
B10010110 DEPRECATED(0b10010110) = 150,
|
||||
B10010111 DEPRECATED(0b10010111) = 151,
|
||||
B10011000 DEPRECATED(0b10011000) = 152,
|
||||
B10011001 DEPRECATED(0b10011001) = 153,
|
||||
B10011010 DEPRECATED(0b10011010) = 154,
|
||||
B10011011 DEPRECATED(0b10011011) = 155,
|
||||
B10011100 DEPRECATED(0b10011100) = 156,
|
||||
B10011101 DEPRECATED(0b10011101) = 157,
|
||||
B10011110 DEPRECATED(0b10011110) = 158,
|
||||
B10011111 DEPRECATED(0b10011111) = 159,
|
||||
B10100000 DEPRECATED(0b10100000) = 160,
|
||||
B10100001 DEPRECATED(0b10100001) = 161,
|
||||
B10100010 DEPRECATED(0b10100010) = 162,
|
||||
B10100011 DEPRECATED(0b10100011) = 163,
|
||||
B10100100 DEPRECATED(0b10100100) = 164,
|
||||
B10100101 DEPRECATED(0b10100101) = 165,
|
||||
B10100110 DEPRECATED(0b10100110) = 166,
|
||||
B10100111 DEPRECATED(0b10100111) = 167,
|
||||
B10101000 DEPRECATED(0b10101000) = 168,
|
||||
B10101001 DEPRECATED(0b10101001) = 169,
|
||||
B10101010 DEPRECATED(0b10101010) = 170,
|
||||
B10101011 DEPRECATED(0b10101011) = 171,
|
||||
B10101100 DEPRECATED(0b10101100) = 172,
|
||||
B10101101 DEPRECATED(0b10101101) = 173,
|
||||
B10101110 DEPRECATED(0b10101110) = 174,
|
||||
B10101111 DEPRECATED(0b10101111) = 175,
|
||||
B10110000 DEPRECATED(0b10110000) = 176,
|
||||
B10110001 DEPRECATED(0b10110001) = 177,
|
||||
B10110010 DEPRECATED(0b10110010) = 178,
|
||||
B10110011 DEPRECATED(0b10110011) = 179,
|
||||
B10110100 DEPRECATED(0b10110100) = 180,
|
||||
B10110101 DEPRECATED(0b10110101) = 181,
|
||||
B10110110 DEPRECATED(0b10110110) = 182,
|
||||
B10110111 DEPRECATED(0b10110111) = 183,
|
||||
B10111000 DEPRECATED(0b10111000) = 184,
|
||||
B10111001 DEPRECATED(0b10111001) = 185,
|
||||
B10111010 DEPRECATED(0b10111010) = 186,
|
||||
B10111011 DEPRECATED(0b10111011) = 187,
|
||||
B10111100 DEPRECATED(0b10111100) = 188,
|
||||
B10111101 DEPRECATED(0b10111101) = 189,
|
||||
B10111110 DEPRECATED(0b10111110) = 190,
|
||||
B10111111 DEPRECATED(0b10111111) = 191,
|
||||
B11000000 DEPRECATED(0b11000000) = 192,
|
||||
B11000001 DEPRECATED(0b11000001) = 193,
|
||||
B11000010 DEPRECATED(0b11000010) = 194,
|
||||
B11000011 DEPRECATED(0b11000011) = 195,
|
||||
B11000100 DEPRECATED(0b11000100) = 196,
|
||||
B11000101 DEPRECATED(0b11000101) = 197,
|
||||
B11000110 DEPRECATED(0b11000110) = 198,
|
||||
B11000111 DEPRECATED(0b11000111) = 199,
|
||||
B11001000 DEPRECATED(0b11001000) = 200,
|
||||
B11001001 DEPRECATED(0b11001001) = 201,
|
||||
B11001010 DEPRECATED(0b11001010) = 202,
|
||||
B11001011 DEPRECATED(0b11001011) = 203,
|
||||
B11001100 DEPRECATED(0b11001100) = 204,
|
||||
B11001101 DEPRECATED(0b11001101) = 205,
|
||||
B11001110 DEPRECATED(0b11001110) = 206,
|
||||
B11001111 DEPRECATED(0b11001111) = 207,
|
||||
B11010000 DEPRECATED(0b11010000) = 208,
|
||||
B11010001 DEPRECATED(0b11010001) = 209,
|
||||
B11010010 DEPRECATED(0b11010010) = 210,
|
||||
B11010011 DEPRECATED(0b11010011) = 211,
|
||||
B11010100 DEPRECATED(0b11010100) = 212,
|
||||
B11010101 DEPRECATED(0b11010101) = 213,
|
||||
B11010110 DEPRECATED(0b11010110) = 214,
|
||||
B11010111 DEPRECATED(0b11010111) = 215,
|
||||
B11011000 DEPRECATED(0b11011000) = 216,
|
||||
B11011001 DEPRECATED(0b11011001) = 217,
|
||||
B11011010 DEPRECATED(0b11011010) = 218,
|
||||
B11011011 DEPRECATED(0b11011011) = 219,
|
||||
B11011100 DEPRECATED(0b11011100) = 220,
|
||||
B11011101 DEPRECATED(0b11011101) = 221,
|
||||
B11011110 DEPRECATED(0b11011110) = 222,
|
||||
B11011111 DEPRECATED(0b11011111) = 223,
|
||||
B11100000 DEPRECATED(0b11100000) = 224,
|
||||
B11100001 DEPRECATED(0b11100001) = 225,
|
||||
B11100010 DEPRECATED(0b11100010) = 226,
|
||||
B11100011 DEPRECATED(0b11100011) = 227,
|
||||
B11100100 DEPRECATED(0b11100100) = 228,
|
||||
B11100101 DEPRECATED(0b11100101) = 229,
|
||||
B11100110 DEPRECATED(0b11100110) = 230,
|
||||
B11100111 DEPRECATED(0b11100111) = 231,
|
||||
B11101000 DEPRECATED(0b11101000) = 232,
|
||||
B11101001 DEPRECATED(0b11101001) = 233,
|
||||
B11101010 DEPRECATED(0b11101010) = 234,
|
||||
B11101011 DEPRECATED(0b11101011) = 235,
|
||||
B11101100 DEPRECATED(0b11101100) = 236,
|
||||
B11101101 DEPRECATED(0b11101101) = 237,
|
||||
B11101110 DEPRECATED(0b11101110) = 238,
|
||||
B11101111 DEPRECATED(0b11101111) = 239,
|
||||
B11110000 DEPRECATED(0b11110000) = 240,
|
||||
B11110001 DEPRECATED(0b11110001) = 241,
|
||||
B11110010 DEPRECATED(0b11110010) = 242,
|
||||
B11110011 DEPRECATED(0b11110011) = 243,
|
||||
B11110100 DEPRECATED(0b11110100) = 244,
|
||||
B11110101 DEPRECATED(0b11110101) = 245,
|
||||
B11110110 DEPRECATED(0b11110110) = 246,
|
||||
B11110111 DEPRECATED(0b11110111) = 247,
|
||||
B11111000 DEPRECATED(0b11111000) = 248,
|
||||
B11111001 DEPRECATED(0b11111001) = 249,
|
||||
B11111010 DEPRECATED(0b11111010) = 250,
|
||||
B11111011 DEPRECATED(0b11111011) = 251,
|
||||
B11111100 DEPRECATED(0b11111100) = 252,
|
||||
B11111101 DEPRECATED(0b11111101) = 253,
|
||||
B11111110 DEPRECATED(0b11111110) = 254,
|
||||
B11111111 DEPRECATED(0b11111111) = 255
|
||||
};
|
||||
|
||||
#undef DEPRECATED
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
cbuf.cpp - Circular buffer implementation
|
||||
Copyright (c) 2014 Ivan Grokhotkov. 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
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "cbuf.h"
|
||||
#include "esp32-hal-log.h"
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
#define CBUF_MUTEX_CREATE()
|
||||
#define CBUF_MUTEX_LOCK()
|
||||
#define CBUF_MUTEX_UNLOCK()
|
||||
#define CBUF_MUTEX_DELETE()
|
||||
#else
|
||||
#define CBUF_MUTEX_CREATE() \
|
||||
if (_lock == NULL) { \
|
||||
_lock = xSemaphoreCreateMutex(); \
|
||||
if (_lock == NULL) { \
|
||||
log_e("failed to create mutex"); \
|
||||
} \
|
||||
}
|
||||
#define CBUF_MUTEX_LOCK() \
|
||||
if (_lock != NULL) { \
|
||||
xSemaphoreTakeRecursive(_lock, portMAX_DELAY); \
|
||||
}
|
||||
#define CBUF_MUTEX_UNLOCK() \
|
||||
if (_lock != NULL) { \
|
||||
xSemaphoreGiveRecursive(_lock); \
|
||||
}
|
||||
#define CBUF_MUTEX_DELETE() \
|
||||
if (_lock != NULL) { \
|
||||
SemaphoreHandle_t l = _lock; \
|
||||
_lock = NULL; \
|
||||
vSemaphoreDelete(l); \
|
||||
}
|
||||
#endif
|
||||
|
||||
cbuf::cbuf(size_t size) : next(NULL), has_peek(false), peek_byte(0), _buf(xRingbufferCreate(size, RINGBUF_TYPE_BYTEBUF)) {
|
||||
if (_buf == NULL) {
|
||||
log_e("failed to allocate ring buffer");
|
||||
}
|
||||
CBUF_MUTEX_CREATE();
|
||||
}
|
||||
|
||||
cbuf::~cbuf() {
|
||||
CBUF_MUTEX_LOCK();
|
||||
if (_buf != NULL) {
|
||||
RingbufHandle_t b = _buf;
|
||||
_buf = NULL;
|
||||
vRingbufferDelete(b);
|
||||
}
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
CBUF_MUTEX_DELETE();
|
||||
}
|
||||
|
||||
size_t cbuf::resizeAdd(size_t addSize) {
|
||||
return resize(size() + addSize);
|
||||
}
|
||||
|
||||
size_t cbuf::resize(size_t newSize) {
|
||||
CBUF_MUTEX_LOCK();
|
||||
size_t _size = size();
|
||||
if (newSize == _size) {
|
||||
return _size;
|
||||
}
|
||||
|
||||
// not lose any data
|
||||
// if data can be lost use remove or flush before resize
|
||||
size_t bytes_available = available();
|
||||
if (newSize < bytes_available) {
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
log_e("new size is less than the currently available data size");
|
||||
return _size;
|
||||
}
|
||||
|
||||
RingbufHandle_t newbuf = xRingbufferCreate(newSize, RINGBUF_TYPE_BYTEBUF);
|
||||
if (newbuf == NULL) {
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
log_e("failed to allocate new ring buffer");
|
||||
return _size;
|
||||
}
|
||||
|
||||
if (_buf != NULL) {
|
||||
if (bytes_available) {
|
||||
char *old_data = (char *)malloc(bytes_available);
|
||||
if (old_data == NULL) {
|
||||
vRingbufferDelete(newbuf);
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
log_e("failed to allocate temporary buffer");
|
||||
return _size;
|
||||
}
|
||||
bytes_available = read(old_data, bytes_available);
|
||||
if (!bytes_available) {
|
||||
free(old_data);
|
||||
vRingbufferDelete(newbuf);
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
log_e("failed to read previous data");
|
||||
return _size;
|
||||
}
|
||||
if (xRingbufferSend(newbuf, (void *)old_data, bytes_available, 0) != pdTRUE) {
|
||||
write(old_data, bytes_available);
|
||||
free(old_data);
|
||||
vRingbufferDelete(newbuf);
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
log_e("failed to restore previous data");
|
||||
return _size;
|
||||
}
|
||||
free(old_data);
|
||||
}
|
||||
|
||||
RingbufHandle_t b = _buf;
|
||||
_buf = newbuf;
|
||||
vRingbufferDelete(b);
|
||||
} else {
|
||||
_buf = newbuf;
|
||||
}
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
return newSize;
|
||||
}
|
||||
|
||||
size_t cbuf::available() const {
|
||||
size_t available = 0;
|
||||
if (_buf != NULL) {
|
||||
vRingbufferGetInfo(_buf, NULL, NULL, NULL, NULL, (UBaseType_t *)&available);
|
||||
}
|
||||
if (has_peek) {
|
||||
available++;
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
size_t cbuf::size() {
|
||||
size_t _size = 0;
|
||||
if (_buf != NULL) {
|
||||
_size = xRingbufferGetMaxItemSize(_buf);
|
||||
}
|
||||
return _size;
|
||||
}
|
||||
|
||||
size_t cbuf::room() const {
|
||||
size_t _room = 0;
|
||||
if (_buf != NULL) {
|
||||
_room = xRingbufferGetCurFreeSize(_buf);
|
||||
}
|
||||
return _room;
|
||||
}
|
||||
|
||||
bool cbuf::empty() const {
|
||||
return available() == 0;
|
||||
}
|
||||
|
||||
bool cbuf::full() const {
|
||||
return room() == 0;
|
||||
}
|
||||
|
||||
int cbuf::peek() {
|
||||
if (!available()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int c;
|
||||
|
||||
CBUF_MUTEX_LOCK();
|
||||
if (has_peek) {
|
||||
c = peek_byte;
|
||||
} else {
|
||||
c = read();
|
||||
if (c >= 0) {
|
||||
has_peek = true;
|
||||
peek_byte = c;
|
||||
}
|
||||
}
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
return c;
|
||||
}
|
||||
|
||||
int cbuf::read() {
|
||||
char result = 0;
|
||||
if (!read(&result, 1)) {
|
||||
return -1;
|
||||
}
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
size_t cbuf::read(char *dst, size_t size) {
|
||||
CBUF_MUTEX_LOCK();
|
||||
size_t bytes_available = available();
|
||||
if (!bytes_available || !size) {
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (has_peek) {
|
||||
if (dst != NULL) {
|
||||
*dst++ = peek_byte;
|
||||
}
|
||||
size--;
|
||||
}
|
||||
|
||||
size_t size_read = 0;
|
||||
if (size) {
|
||||
size_t received_size = 0;
|
||||
size_t size_to_read = (size < bytes_available) ? size : bytes_available;
|
||||
uint8_t *received_buff = (uint8_t *)xRingbufferReceiveUpTo(_buf, &received_size, 0, size_to_read);
|
||||
if (received_buff != NULL) {
|
||||
if (dst != NULL) {
|
||||
memcpy(dst, received_buff, received_size);
|
||||
}
|
||||
vRingbufferReturnItem(_buf, received_buff);
|
||||
size_read = received_size;
|
||||
size_to_read -= received_size;
|
||||
// wrap around data
|
||||
if (size_to_read) {
|
||||
received_size = 0;
|
||||
received_buff = (uint8_t *)xRingbufferReceiveUpTo(_buf, &received_size, 0, size_to_read);
|
||||
if (received_buff != NULL) {
|
||||
if (dst != NULL) {
|
||||
memcpy(dst + size_read, received_buff, received_size);
|
||||
}
|
||||
vRingbufferReturnItem(_buf, received_buff);
|
||||
size_read += received_size;
|
||||
} else {
|
||||
log_e("failed to read wrap around data from ring buffer");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_e("failed to read from ring buffer");
|
||||
}
|
||||
}
|
||||
|
||||
if (has_peek) {
|
||||
has_peek = false;
|
||||
size_read++;
|
||||
}
|
||||
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
return size_read;
|
||||
}
|
||||
|
||||
size_t cbuf::write(char c) {
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
size_t cbuf::write(const char *src, size_t size) {
|
||||
CBUF_MUTEX_LOCK();
|
||||
size_t bytes_available = room();
|
||||
if (!bytes_available || !size) {
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
size_t size_to_write = (size < bytes_available) ? size : bytes_available;
|
||||
if (xRingbufferSend(_buf, (void *)src, size_to_write, 0) != pdTRUE) {
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
log_e("failed to write to ring buffer");
|
||||
return 0;
|
||||
}
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
return size_to_write;
|
||||
}
|
||||
|
||||
void cbuf::flush() {
|
||||
read(NULL, available());
|
||||
}
|
||||
|
||||
size_t cbuf::remove(size_t size) {
|
||||
CBUF_MUTEX_LOCK();
|
||||
size_t bytes_available = available();
|
||||
if (bytes_available && size) {
|
||||
size_t size_to_remove = (size < bytes_available) ? size : bytes_available;
|
||||
bytes_available -= read(NULL, size_to_remove);
|
||||
}
|
||||
CBUF_MUTEX_UNLOCK();
|
||||
return bytes_available;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
cbuf.h - Circular buffer implementation
|
||||
Copyright (c) 2014 Ivan Grokhotkov. 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
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
class cbuf {
|
||||
public:
|
||||
cbuf(size_t size);
|
||||
~cbuf();
|
||||
|
||||
size_t resizeAdd(size_t addSize);
|
||||
size_t resize(size_t newSize);
|
||||
|
||||
size_t available() const;
|
||||
size_t size();
|
||||
size_t room() const;
|
||||
bool empty() const;
|
||||
bool full() const;
|
||||
|
||||
int peek();
|
||||
|
||||
int read();
|
||||
size_t read(char *dst, size_t size);
|
||||
|
||||
size_t write(char c);
|
||||
size_t write(const char *src, size_t size);
|
||||
|
||||
void flush();
|
||||
size_t remove(size_t size);
|
||||
|
||||
cbuf *next;
|
||||
bool has_peek;
|
||||
uint8_t peek_byte;
|
||||
|
||||
protected:
|
||||
RingbufHandle_t _buf = NULL;
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
SemaphoreHandle_t _lock = NULL;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,338 @@
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_arduino_version.h"
|
||||
#include "esp_rom_spiflash.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_app_format.h"
|
||||
#include "soc/efuse_reg.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/spi_reg.h"
|
||||
#include "soc/soc.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/spi_flash.h"
|
||||
#endif
|
||||
#include "esp_bit_defs.h"
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "chip-debug-report.h"
|
||||
|
||||
#define chip_report_printf log_printf
|
||||
|
||||
#define printMemCapsInfo(caps) _printMemCapsInfo(MALLOC_CAP_##caps, #caps)
|
||||
#define b2kb(b) ((float)b / 1024.0)
|
||||
#define b2mb(b) ((float)b / (1024.0 * 1024.0))
|
||||
static void _printMemCapsInfo(uint32_t caps, const char *caps_str) {
|
||||
multi_heap_info_t info;
|
||||
size_t total = heap_caps_get_total_size(caps);
|
||||
heap_caps_get_info(&info, caps);
|
||||
chip_report_printf("%s Memory Info:\n", caps_str);
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
chip_report_printf(" Total Size : %8u B (%6.1f KB)\n", total, b2kb(total));
|
||||
chip_report_printf(" Free Bytes : %8u B (%6.1f KB)\n", info.total_free_bytes, b2kb(info.total_free_bytes));
|
||||
chip_report_printf(" Allocated Bytes : %8u B (%6.1f KB)\n", info.total_allocated_bytes, b2kb(info.total_allocated_bytes));
|
||||
chip_report_printf(" Minimum Free Bytes: %8u B (%6.1f KB)\n", info.minimum_free_bytes, b2kb(info.minimum_free_bytes));
|
||||
chip_report_printf(" Largest Free Block: %8u B (%6.1f KB)\n", info.largest_free_block, b2kb(info.largest_free_block));
|
||||
}
|
||||
|
||||
static void printPkgVersion(void) {
|
||||
chip_report_printf(" Package : ");
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_PACKAGE);
|
||||
switch (pkg_ver) {
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3: chip_report_printf("D0WD-R2-V3"); break;
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6: chip_report_printf("D0WD-Q6"); break;
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5: chip_report_printf("D0WD-Q5"); break;
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5: chip_report_printf("D2WD-Q5"); break;
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32U4WDH: chip_report_printf("U4WD-H"); break;
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4: chip_report_printf("PICO-D4"); break;
|
||||
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302: chip_report_printf("PICO-V3-02"); break;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION);
|
||||
switch (pkg_ver) {
|
||||
case 1: chip_report_printf("FH16"); break;
|
||||
case 2: chip_report_printf("FH32"); break;
|
||||
default: chip_report_printf("%lu", pkg_ver); break;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION);
|
||||
chip_report_printf("%lu", pkg_ver);
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_BLK2_DATA1_REG, EFUSE_PKG_VERSION);
|
||||
chip_report_printf("%lu", pkg_ver);
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS_4_REG, EFUSE_PKG_VERSION);
|
||||
chip_report_printf("%lu", pkg_ver);
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS_2_REG, EFUSE_PKG_VERSION);
|
||||
chip_report_printf("%lu", pkg_ver);
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
|
||||
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS2_REG, EFUSE_PKG_VERSION);
|
||||
chip_report_printf("%lu", pkg_ver);
|
||||
#else
|
||||
chip_report_printf("Unknown");
|
||||
#endif
|
||||
chip_report_printf("\n");
|
||||
}
|
||||
|
||||
static void printChipInfo(void) {
|
||||
esp_chip_info_t info;
|
||||
esp_chip_info(&info);
|
||||
chip_report_printf("Chip Info:\n");
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
chip_report_printf(" Model : ");
|
||||
switch (info.model) {
|
||||
case CHIP_ESP32: chip_report_printf("ESP32\n"); break;
|
||||
case CHIP_ESP32S2: chip_report_printf("ESP32-S2\n"); break;
|
||||
case CHIP_ESP32S3: chip_report_printf("ESP32-S3\n"); break;
|
||||
case CHIP_ESP32C2: chip_report_printf("ESP32-C2\n"); break;
|
||||
case CHIP_ESP32C3: chip_report_printf("ESP32-C3\n"); break;
|
||||
case CHIP_ESP32C6: chip_report_printf("ESP32-C6\n"); break;
|
||||
case CHIP_ESP32H2: chip_report_printf("ESP32-H2\n"); break;
|
||||
case CHIP_ESP32P4: chip_report_printf("ESP32-P4\n"); break;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||
case CHIP_ESP32C5: chip_report_printf("ESP32-C5\n"); break;
|
||||
case CHIP_ESP32C61: chip_report_printf("ESP32-C61\n"); break;
|
||||
case CHIP_ESP32H21: chip_report_printf("ESP32-H21\n"); break;
|
||||
#endif
|
||||
default: chip_report_printf("Unknown %d\n", info.model); break;
|
||||
}
|
||||
printPkgVersion();
|
||||
chip_report_printf(" Revision : %.2f\n", (float)(info.revision) / 100.0);
|
||||
chip_report_printf(" Cores : %d\n", info.cores);
|
||||
rtc_cpu_freq_config_t conf;
|
||||
rtc_clk_cpu_freq_get_config(&conf);
|
||||
chip_report_printf(" CPU Frequency : %lu MHz\n", conf.freq_mhz);
|
||||
chip_report_printf(" XTAL Frequency : %d MHz\n", rtc_clk_xtal_freq_get());
|
||||
chip_report_printf(" Features Bitfield : %#010x\n", info.features);
|
||||
chip_report_printf(" Embedded Flash : %s\n", (info.features & CHIP_FEATURE_EMB_FLASH) ? "Yes" : "No");
|
||||
chip_report_printf(" Embedded PSRAM : %s\n", (info.features & CHIP_FEATURE_EMB_PSRAM) ? "Yes" : "No");
|
||||
chip_report_printf(" 2.4GHz WiFi : %s\n", (info.features & CHIP_FEATURE_WIFI_BGN) ? "Yes" : "No");
|
||||
chip_report_printf(" Classic BT : %s\n", (info.features & CHIP_FEATURE_BT) ? "Yes" : "No");
|
||||
chip_report_printf(" BT Low Energy : %s\n", (info.features & CHIP_FEATURE_BLE) ? "Yes" : "No");
|
||||
chip_report_printf(" IEEE 802.15.4 : %s\n", (info.features & CHIP_FEATURE_IEEE802154) ? "Yes" : "No");
|
||||
}
|
||||
|
||||
static void printFlashInfo(void) {
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define ESP_FLASH_IMAGE_BASE 0x1000
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define ESP_FLASH_IMAGE_BASE 0x2000
|
||||
#else
|
||||
#define ESP_FLASH_IMAGE_BASE 0x0000
|
||||
#endif
|
||||
// REG_SPI_BASE is not defined for S3/C3 ??
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifdef REG_SPI_BASE
|
||||
#undef REG_SPI_BASE
|
||||
#endif // REG_SPI_BASE
|
||||
#define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i) > 1) ? (((i) * 0x1000) + 0x20000) : (((~(i)) & 1) * 0x1000)))
|
||||
#endif // TARGET
|
||||
|
||||
chip_report_printf("Flash Info:\n");
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
uint32_t hw_size = 1 << (g_rom_flashchip.device_id & 0xFF);
|
||||
chip_report_printf(" Chip Size : %8lu B (%.0f MB)\n", hw_size, b2mb(hw_size));
|
||||
chip_report_printf(" Block Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.block_size, b2kb(g_rom_flashchip.block_size));
|
||||
chip_report_printf(" Sector Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.sector_size, b2kb(g_rom_flashchip.sector_size));
|
||||
chip_report_printf(" Page Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.page_size, b2kb(g_rom_flashchip.page_size));
|
||||
|
||||
// Runtime flash frequency detection from hardware registers
|
||||
uint32_t actual_freq = ESP.getFlashFrequencyMHz();
|
||||
uint8_t source_freq = ESP.getFlashSourceFrequencyMHz();
|
||||
uint8_t divider = ESP.getFlashClockDivider();
|
||||
|
||||
chip_report_printf(" Bus Speed : %lu MHz\n", actual_freq);
|
||||
chip_report_printf(" Flash Frequency : %lu MHz (source: %u MHz, divider: %u)\n", actual_freq, source_freq, divider);
|
||||
|
||||
chip_report_printf(" Bus Mode : ");
|
||||
#if CONFIG_ESPTOOLPY_OCT_FLASH
|
||||
chip_report_printf("OPI\n");
|
||||
#elif CONFIG_ESPTOOLPY_FLASHMODE_QIO
|
||||
chip_report_printf("QIO\n");
|
||||
#elif CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
chip_report_printf("QOUT\n");
|
||||
#elif CONFIG_ESPTOOLPY_FLASHMODE_DIO
|
||||
chip_report_printf("DIO\n");
|
||||
#elif CONFIG_ESPTOOLPY_FLASHMODE_DOUT
|
||||
chip_report_printf("DOUT\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void printPartitionsInfo(void) {
|
||||
chip_report_printf("Partitions Info:\n");
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
esp_partition_iterator_t iterator = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
if (iterator != NULL) {
|
||||
esp_partition_iterator_t it = iterator;
|
||||
while (it != NULL) {
|
||||
const esp_partition_t *partition = esp_partition_get(it);
|
||||
if (partition) {
|
||||
chip_report_printf(" %17s : addr: 0x%08X, size: %7.1f KB", partition->label, partition->address, b2kb(partition->size));
|
||||
if (partition->type == ESP_PARTITION_TYPE_APP) {
|
||||
chip_report_printf(", type: APP");
|
||||
if (partition->subtype == 0) {
|
||||
chip_report_printf(", subtype: FACTORY");
|
||||
} else if (partition->subtype >= 0x10 && partition->subtype < 0x20) {
|
||||
chip_report_printf(", subtype: OTA_%lu", partition->subtype - 0x10);
|
||||
} else if (partition->subtype == 0x20) {
|
||||
chip_report_printf(", subtype: TEST");
|
||||
} else {
|
||||
chip_report_printf(", subtype: 0x%02X", partition->subtype);
|
||||
}
|
||||
} else {
|
||||
chip_report_printf(", type: DATA");
|
||||
chip_report_printf(", subtype: ");
|
||||
switch (partition->subtype) {
|
||||
case ESP_PARTITION_SUBTYPE_DATA_OTA: chip_report_printf("OTA"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_PHY: chip_report_printf("PHY"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_NVS: chip_report_printf("NVS"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_COREDUMP: chip_report_printf("COREDUMP"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS: chip_report_printf("NVS_KEYS"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM: chip_report_printf("EFUSE_EM"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_UNDEFINED: chip_report_printf("UNDEFINED"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD: chip_report_printf("ESPHTTPD"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_FAT: chip_report_printf("FAT"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: chip_report_printf("SPIFFS"); break;
|
||||
case ESP_PARTITION_SUBTYPE_DATA_LITTLEFS: chip_report_printf("LITTLEFS"); break;
|
||||
default: chip_report_printf("0x%02X", partition->subtype); break;
|
||||
}
|
||||
}
|
||||
chip_report_printf("\n");
|
||||
}
|
||||
it = esp_partition_next(it);
|
||||
}
|
||||
//esp_partition_iterator_release(iterator);
|
||||
}
|
||||
}
|
||||
|
||||
static void printSoftwareInfo(void) {
|
||||
chip_report_printf("Software Info:\n");
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
chip_report_printf(" Compile Date/Time : %s %s\n", __DATE__, __TIME__);
|
||||
#ifdef ARDUINO_HOST_OS
|
||||
chip_report_printf(" Compile Host OS : %s\n", ARDUINO_HOST_OS);
|
||||
#endif
|
||||
chip_report_printf(" ESP-IDF Version : %s\n", esp_get_idf_version());
|
||||
chip_report_printf(" Arduino Version : %s\n", ESP_ARDUINO_VERSION_STR);
|
||||
}
|
||||
|
||||
static void printBoardInfo(void) {
|
||||
chip_report_printf("Board Info:\n");
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
chip_report_printf(" Arduino Board : %s\n", ARDUINO_BOARD);
|
||||
chip_report_printf(" Arduino Variant : %s\n", ARDUINO_VARIANT);
|
||||
#ifdef ARDUINO_FQBN
|
||||
chip_report_printf(" Arduino FQBN : %s\n", ARDUINO_FQBN);
|
||||
#else
|
||||
#ifdef CORE_DEBUG_LEVEL
|
||||
chip_report_printf(" Core Debug Level : %d\n", CORE_DEBUG_LEVEL);
|
||||
#endif
|
||||
#ifdef ARDUINO_RUNNING_CORE
|
||||
chip_report_printf(" Arduino Runs Core : %d\n", ARDUINO_RUNNING_CORE);
|
||||
chip_report_printf(" Arduino Events on : %d\n", ARDUINO_EVENT_RUNNING_CORE);
|
||||
#endif
|
||||
#ifdef ARDUINO_USB_MODE
|
||||
chip_report_printf(" Arduino USB Mode : %d\n", ARDUINO_USB_MODE);
|
||||
#endif
|
||||
#ifdef ARDUINO_USB_CDC_ON_BOOT
|
||||
chip_report_printf(" CDC On Boot : %d\n", ARDUINO_USB_CDC_ON_BOOT);
|
||||
#endif
|
||||
#endif /* ARDUINO_FQBN */
|
||||
}
|
||||
|
||||
static void printPerimanInfo(void) {
|
||||
chip_report_printf("GPIO Info:\n");
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
#if defined(BOARD_HAS_PIN_REMAP)
|
||||
chip_report_printf(" DPIN|GPIO : BUS_TYPE[bus/unit][chan]\n");
|
||||
#else
|
||||
chip_report_printf(" GPIO : BUS_TYPE[bus/unit][chan]\n");
|
||||
#endif
|
||||
chip_report_printf(" -------------------------------------- \n");
|
||||
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
|
||||
if (!perimanPinIsValid(i)) {
|
||||
continue; //invalid pin
|
||||
}
|
||||
peripheral_bus_type_t type = perimanGetPinBusType(i);
|
||||
if (type == ESP32_BUS_TYPE_INIT) {
|
||||
continue; //unused pin
|
||||
}
|
||||
#if defined(BOARD_HAS_PIN_REMAP)
|
||||
int dpin = gpioNumberToDigitalPin(i);
|
||||
if (dpin < 0) {
|
||||
continue; //pin is not exported
|
||||
} else {
|
||||
chip_report_printf(" D%-3d|%4u : ", dpin, i);
|
||||
}
|
||||
#else
|
||||
chip_report_printf(" %4u : ", i);
|
||||
#endif
|
||||
const char *extra_type = perimanGetPinBusExtraType(i);
|
||||
if (extra_type) {
|
||||
chip_report_printf("%s", extra_type);
|
||||
} else {
|
||||
chip_report_printf("%s", perimanGetTypeName(type));
|
||||
}
|
||||
int8_t bus_number = perimanGetPinBusNum(i);
|
||||
if (bus_number != -1) {
|
||||
chip_report_printf("[%u]", bus_number);
|
||||
}
|
||||
int8_t bus_channel = perimanGetPinBusChannel(i);
|
||||
if (bus_channel != -1) {
|
||||
chip_report_printf("[%u]", bus_channel);
|
||||
}
|
||||
chip_report_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void printBeforeSetupInfo(void) {
|
||||
#if ARDUINO_USB_CDC_ON_BOOT
|
||||
Serial.begin();
|
||||
Serial.setDebugOutput(true);
|
||||
uint8_t t = 0;
|
||||
while (!Serial && (t++ < 200)) {
|
||||
delay(10); //wait up to 2 seconds for the IDE to connect
|
||||
}
|
||||
#endif
|
||||
chip_report_printf("=========== Before Setup Start ===========\n");
|
||||
printChipInfo();
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
printMemCapsInfo(INTERNAL);
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
if (psramFound()) {
|
||||
printMemCapsInfo(SPIRAM);
|
||||
chip_report_printf(" Bus Mode : ");
|
||||
#if CONFIG_SPIRAM_MODE_OCT
|
||||
chip_report_printf("OPI\n");
|
||||
#else
|
||||
chip_report_printf("QSPI\n");
|
||||
#endif
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
}
|
||||
printFlashInfo();
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
printPartitionsInfo();
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
printSoftwareInfo();
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
printBoardInfo();
|
||||
chip_report_printf("============ Before Setup End ============\n");
|
||||
delay(100); //allow the print to finish
|
||||
}
|
||||
|
||||
void printAfterSetupInfo(void) {
|
||||
chip_report_printf("=========== After Setup Start ============\n");
|
||||
printMemCapsInfo(INTERNAL);
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
if (psramFound()) {
|
||||
printMemCapsInfo(SPIRAM);
|
||||
chip_report_printf("------------------------------------------\n");
|
||||
}
|
||||
printPerimanInfo();
|
||||
chip_report_printf("============ After Setup End =============\n");
|
||||
delay(20); //allow the print to finish
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void printBeforeSetupInfo(void);
|
||||
void printAfterSetupInfo(void);
|
||||
@@ -0,0 +1,4 @@
|
||||
#define ARDUINO_ESP32_GIT_VER 0xc3e5d72f
|
||||
#define ARDUINO_ESP32_GIT_DESC 3.3.7-1-gc3e5d72fb
|
||||
#define ARDUINO_ESP32_RELEASE_3_3_7
|
||||
#define ARDUINO_ESP32_RELEASE "3_3_7"
|
||||
@@ -0,0 +1,772 @@
|
||||
// Copyright 2015-2023 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 "esp32-hal-adc.h"
|
||||
|
||||
#if SOC_ADC_SUPPORTED
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_adc/adc_continuous.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL >= 300
|
||||
// NOTE: These weak definitions allow successful linkage if the real efuse calibration functions are missing.
|
||||
// This is a workaround for the ESP32P4 rev 3.0+, which is missing efuse calibration functions in the IDF.
|
||||
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_ver(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_init_code(uint32_t atten, uint32_t *code) {
|
||||
if (code) {
|
||||
*code = 0;
|
||||
}
|
||||
return 0; // 0 means success in ESP-IDF conventions
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_chan_compens(uint32_t atten, uint32_t *comp) {
|
||||
if (comp) {
|
||||
*comp = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_cal_voltage(uint32_t atten, uint32_t *voltage) {
|
||||
if (voltage) {
|
||||
*voltage = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ESP32-C2 does not define those two for some reason
|
||||
#ifndef SOC_ADC_DIGI_RESULT_BYTES
|
||||
#define SOC_ADC_DIGI_RESULT_BYTES (4)
|
||||
#endif
|
||||
#ifndef SOC_ADC_DIGI_DATA_BYTES_PER_CONV
|
||||
#define SOC_ADC_DIGI_DATA_BYTES_PER_CONV (4)
|
||||
#endif
|
||||
|
||||
static uint8_t __analogAttenuation = ADC_11db;
|
||||
static uint8_t __analogWidth = SOC_ADC_RTC_MAX_BITWIDTH;
|
||||
static uint8_t __analogReturnedWidth = SOC_ADC_RTC_MAX_BITWIDTH;
|
||||
|
||||
typedef struct {
|
||||
voidFuncPtr fn;
|
||||
void *arg;
|
||||
} interrupt_config_t;
|
||||
|
||||
typedef struct {
|
||||
adc_oneshot_unit_handle_t adc_oneshot_handle;
|
||||
adc_continuous_handle_t adc_continuous_handle;
|
||||
interrupt_config_t adc_interrupt_handle;
|
||||
adc_cali_handle_t adc_cali_handle;
|
||||
uint32_t buffer_size;
|
||||
uint32_t conversion_frame_size;
|
||||
} adc_handle_t;
|
||||
|
||||
adc_handle_t adc_handle[SOC_ADC_PERIPH_NUM];
|
||||
|
||||
static bool adcDetachBus(void *pin) {
|
||||
adc_channel_t adc_channel;
|
||||
adc_unit_t adc_unit;
|
||||
uint8_t used_channels = 0;
|
||||
|
||||
adc_oneshot_io_to_channel((int)(pin - 1), &adc_unit, &adc_channel);
|
||||
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
|
||||
int io_pin;
|
||||
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
|
||||
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
|
||||
used_channels++;
|
||||
}
|
||||
}
|
||||
|
||||
if (used_channels == 1) { //only 1 channel is used
|
||||
esp_err_t err = adc_oneshot_del_unit(adc_handle[adc_unit].adc_oneshot_handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
adc_handle[adc_unit].adc_oneshot_handle = NULL;
|
||||
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
log_e("ADC Calibration scheme is not supported!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
adc_handle[adc_unit].adc_cali_handle = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, int8_t pin) {
|
||||
esp_err_t err = ESP_OK;
|
||||
adc_oneshot_chan_cfg_t config = {
|
||||
.bitwidth = width,
|
||||
.atten = (atten & 3),
|
||||
};
|
||||
if (pin == -1) { //Reconfigure all used analog pins/channels
|
||||
for (int adc_unit = 0; adc_unit < SOC_ADC_PERIPH_NUM; adc_unit++) {
|
||||
if (adc_handle[adc_unit].adc_oneshot_handle != NULL) {
|
||||
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) {
|
||||
int io_pin;
|
||||
adc_oneshot_channel_to_io(adc_unit, channel, &io_pin);
|
||||
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
|
||||
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_oneshot_config_channel failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
//ADC calibration reconfig only if all channels are updated
|
||||
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
log_d("Deleting ADC_UNIT_%d cali handle", adc_unit);
|
||||
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_cali_delete_scheme_curve_fitting failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = adc_unit,
|
||||
.atten = atten,
|
||||
.bitwidth = width,
|
||||
};
|
||||
log_d("Creating ADC_UNIT_%d curve cali handle", adc_unit);
|
||||
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_cali_create_scheme_curve_fitting failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
log_d("Deleting ADC_UNIT_%d line cali handle", adc_unit);
|
||||
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_cali_delete_scheme_line_fitting failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = adc_unit,
|
||||
.atten = atten,
|
||||
.bitwidth = width,
|
||||
};
|
||||
log_d("Creating ADC_UNIT_%d line cali handle", adc_unit);
|
||||
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_cali_create_scheme_line_fitting failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
log_e("ADC Calibration scheme is not supported!");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//make it default for next channels
|
||||
__analogWidth = width;
|
||||
__analogAttenuation = atten;
|
||||
} else { //Reconfigure single channel
|
||||
if (perimanGetPinBusType(pin) == ESP32_BUS_TYPE_ADC_ONESHOT) {
|
||||
adc_channel_t channel;
|
||||
adc_unit_t adc_unit;
|
||||
|
||||
adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Pin %u is not ADC pin!", pin);
|
||||
return err;
|
||||
}
|
||||
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_oneshot_config_channel failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
log_e("Pin is not configured as analog channel");
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static inline uint16_t mapResolution(uint16_t value) {
|
||||
uint8_t from = __analogWidth;
|
||||
if (from == __analogReturnedWidth) {
|
||||
return value;
|
||||
}
|
||||
if (from > __analogReturnedWidth) {
|
||||
return value >> (from - __analogReturnedWidth);
|
||||
}
|
||||
return value << (__analogReturnedWidth - from);
|
||||
}
|
||||
|
||||
void __analogSetAttenuation(adc_attenuation_t attenuation) {
|
||||
if (__analogChannelConfig(__analogWidth, attenuation, -1) != ESP_OK) {
|
||||
log_e("__analogChannelConfig failed!");
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
void __analogSetWidth(uint8_t bits) {
|
||||
if (bits < SOC_ADC_RTC_MIN_BITWIDTH) {
|
||||
bits = SOC_ADC_RTC_MIN_BITWIDTH;
|
||||
} else if (bits > SOC_ADC_RTC_MAX_BITWIDTH) {
|
||||
bits = SOC_ADC_RTC_MAX_BITWIDTH;
|
||||
}
|
||||
if (__analogChannelConfig(bits, __analogAttenuation, -1) != ESP_OK) {
|
||||
log_e("__analogChannelConfig failed!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t __analogInit(uint8_t pin, adc_channel_t channel, adc_unit_t adc_unit) {
|
||||
esp_err_t err = ESP_OK;
|
||||
if (adc_handle[adc_unit].adc_oneshot_handle == NULL) {
|
||||
adc_oneshot_unit_init_cfg_t init_config1 = {
|
||||
.unit_id = adc_unit,
|
||||
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||
};
|
||||
err = adc_oneshot_new_unit(&init_config1, &adc_handle[adc_unit].adc_oneshot_handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_oneshot_new_unit failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_ONESHOT, adcDetachBus);
|
||||
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT, (void *)(pin + 1), adc_unit, channel)) {
|
||||
adcDetachBus((void *)(pin + 1));
|
||||
return err;
|
||||
}
|
||||
|
||||
adc_oneshot_chan_cfg_t config = {
|
||||
.bitwidth = __analogWidth,
|
||||
.atten = __analogAttenuation,
|
||||
};
|
||||
|
||||
err = adc_oneshot_config_channel(adc_handle[adc_unit].adc_oneshot_handle, channel, &config);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_oneshot_config_channel failed with error: %d", err);
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void __analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) {
|
||||
if (__analogChannelConfig(__analogWidth, attenuation, pin) != ESP_OK) {
|
||||
log_e("__analogChannelConfig failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void __analogReadResolution(uint8_t bits) {
|
||||
if (!bits || bits > 16) {
|
||||
return;
|
||||
}
|
||||
__analogReturnedWidth = bits;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
__analogSetWidth(bits); // hardware analog resolution from 9 to 12
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t __analogRead(uint8_t pin) {
|
||||
int value = 0;
|
||||
adc_channel_t channel;
|
||||
adc_unit_t adc_unit;
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
err = adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Pin %u is not ADC pin!", pin);
|
||||
return value;
|
||||
}
|
||||
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL) {
|
||||
log_d("Calling __analogInit! pin = %d", pin);
|
||||
err = __analogInit(pin, channel, adc_unit);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Analog initialization failed!");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
adc_oneshot_read(adc_handle[adc_unit].adc_oneshot_handle, channel, &value);
|
||||
return mapResolution(value);
|
||||
}
|
||||
|
||||
uint32_t __analogReadMilliVolts(uint8_t pin) {
|
||||
int value = 0;
|
||||
adc_channel_t channel;
|
||||
adc_unit_t adc_unit;
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
adc_oneshot_io_to_channel(pin, &adc_unit, &channel);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Pin %u is not ADC pin!", pin);
|
||||
return value;
|
||||
}
|
||||
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_ADC_ONESHOT) == NULL) {
|
||||
err = __analogInit(pin, channel, adc_unit);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Analog initialization failed!");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (adc_handle[adc_unit].adc_cali_handle == NULL) {
|
||||
log_d("Creating cali handle for ADC_%d", adc_unit);
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = adc_unit,
|
||||
.atten = __analogAttenuation,
|
||||
.bitwidth = __analogWidth,
|
||||
};
|
||||
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
|
||||
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = adc_unit,
|
||||
.bitwidth = __analogWidth,
|
||||
.atten = __analogAttenuation,
|
||||
};
|
||||
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
|
||||
#else
|
||||
log_e("ADC Calibration scheme is not supported!");
|
||||
return value;
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_cali_create_scheme_x failed!");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
err = adc_oneshot_get_calibrated_result(adc_handle[adc_unit].adc_oneshot_handle, adc_handle[adc_unit].adc_cali_handle, channel, &value);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_oneshot_get_calibrated_result failed!");
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
extern uint16_t analogRead(uint8_t pin) __attribute__((weak, alias("__analogRead")));
|
||||
extern uint32_t analogReadMilliVolts(uint8_t pin) __attribute__((weak, alias("__analogReadMilliVolts")));
|
||||
extern void analogReadResolution(uint8_t bits) __attribute__((weak, alias("__analogReadResolution")));
|
||||
extern void analogSetAttenuation(adc_attenuation_t attenuation) __attribute__((weak, alias("__analogSetAttenuation")));
|
||||
extern void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) __attribute__((weak, alias("__analogSetPinAttenuation")));
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
extern void analogSetWidth(uint8_t bits) __attribute__((weak, alias("__analogSetWidth")));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ADC Continuous mode
|
||||
*/
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
|
||||
#define ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel)
|
||||
#define ADC_GET_DATA(p_data) ((p_data)->type1.data)
|
||||
#else
|
||||
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
|
||||
#define ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
|
||||
#define ADC_GET_DATA(p_data) ((p_data)->type2.data)
|
||||
#endif
|
||||
|
||||
static uint8_t __adcContinuousAtten = ADC_11db;
|
||||
static uint8_t __adcContinuousWidth = SOC_ADC_DIGI_MAX_BITWIDTH;
|
||||
|
||||
static uint8_t used_adc_channels = 0;
|
||||
adc_continuous_result_t *adc_result = NULL;
|
||||
|
||||
static bool adcContinuousDetachBus(void *adc_unit_number) {
|
||||
adc_unit_t adc_unit = (adc_unit_t)adc_unit_number - 1;
|
||||
|
||||
// Guard against double-cleanup: check if already cleaned up
|
||||
if (adc_handle[adc_unit].adc_continuous_handle == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clean up ADC driver
|
||||
esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
adc_handle[adc_unit].adc_continuous_handle = NULL;
|
||||
|
||||
// Clean up calibration handle if exists
|
||||
if (adc_handle[adc_unit].adc_cali_handle != NULL) {
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
log_e("ADC Calibration scheme is not supported!");
|
||||
return false;
|
||||
#endif
|
||||
adc_handle[adc_unit].adc_cali_handle = NULL;
|
||||
}
|
||||
|
||||
// Don't call perimanClearPinBus() here - the peripheral manager already handles it.
|
||||
// This callback is only responsible for cleaning up the IDF's ADC driver and calibration handles.
|
||||
// It does NOT free the adc_result buffer. The caller is responsible for freeing adc_result.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IRAM_ATTR adcFnWrapper(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *args) {
|
||||
interrupt_config_t *isr = (interrupt_config_t *)args;
|
||||
//Check if edata->size matches conversion_frame_size, else just return from ISR
|
||||
if (edata->size == adc_handle[0].conversion_frame_size) {
|
||||
if (isr->fn) {
|
||||
if (isr->arg) {
|
||||
((voidFuncPtrArg)isr->fn)(isr->arg);
|
||||
} else {
|
||||
isr->fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t __analogContinuousInit(adc_channel_t *channel, uint8_t channel_num, adc_unit_t adc_unit, uint32_t sampling_freq_hz) {
|
||||
//Create new ADC continuous handle
|
||||
adc_continuous_handle_cfg_t adc_config = {
|
||||
.max_store_buf_size = adc_handle[adc_unit].buffer_size,
|
||||
.conv_frame_size = adc_handle[adc_unit].conversion_frame_size,
|
||||
};
|
||||
|
||||
esp_err_t err = adc_continuous_new_handle(&adc_config, &adc_handle[adc_unit].adc_continuous_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_continuous_new_handle failed with error: %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//Configure adc pins
|
||||
adc_continuous_config_t dig_cfg = {
|
||||
.sample_freq_hz = sampling_freq_hz,
|
||||
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
|
||||
.format = ADC_OUTPUT_TYPE,
|
||||
};
|
||||
adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};
|
||||
dig_cfg.pattern_num = channel_num;
|
||||
for (int i = 0; i < channel_num; i++) {
|
||||
adc_pattern[i].atten = __adcContinuousAtten;
|
||||
adc_pattern[i].channel = channel[i];
|
||||
adc_pattern[i].unit = ADC_UNIT_1;
|
||||
adc_pattern[i].bit_width = __adcContinuousWidth;
|
||||
}
|
||||
dig_cfg.adc_pattern = adc_pattern;
|
||||
err = adc_continuous_config(adc_handle[adc_unit].adc_continuous_handle, &dig_cfg);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_continuous_config failed with error: %d", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
used_adc_channels = channel_num;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void)) {
|
||||
adc_channel_t channel[pins_count];
|
||||
adc_unit_t adc_unit = ADC_UNIT_1;
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
//Convert pins to channels and check if all are ADC1s unit
|
||||
for (int i = 0; i < pins_count; i++) {
|
||||
err = adc_continuous_io_to_channel(pins[i], &adc_unit, &channel[i]);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Pin %u is not ADC pin!", pins[i]);
|
||||
return false;
|
||||
}
|
||||
if (adc_unit != 0) {
|
||||
log_e("Only ADC1 pins are supported in continuous mode!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Check if Oneshot and Continuous handle exists
|
||||
if (adc_handle[adc_unit].adc_oneshot_handle != NULL) {
|
||||
log_e("ADC%d is running in oneshot mode. Aborting.", adc_unit + 1);
|
||||
return false;
|
||||
}
|
||||
if (adc_handle[adc_unit].adc_continuous_handle != NULL) {
|
||||
log_e("ADC%d continuous is already initialized. To reconfigure call analogContinuousDeinit() first.", adc_unit + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check sampling frequency
|
||||
if ((sampling_freq_hz < SOC_ADC_SAMPLE_FREQ_THRES_LOW) || (sampling_freq_hz > SOC_ADC_SAMPLE_FREQ_THRES_HIGH)) {
|
||||
log_e("Sampling frequency is out of range. Supported sampling frequencies are %d - %d", SOC_ADC_SAMPLE_FREQ_THRES_LOW, SOC_ADC_SAMPLE_FREQ_THRES_HIGH);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Set periman deinit function and reset all pins to init state.
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_ADC_CONT, adcContinuousDetachBus);
|
||||
for (int j = 0; j < pins_count; j++) {
|
||||
if (!perimanClearPinBus(pins[j])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Set conversion frame and buffer size (conversion frame must be in multiples of SOC_ADC_DIGI_DATA_BYTES_PER_CONV)
|
||||
adc_handle[adc_unit].conversion_frame_size = conversions_per_pin * pins_count * SOC_ADC_DIGI_RESULT_BYTES;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
uint8_t calc_multiple = adc_handle[adc_unit].conversion_frame_size % SOC_ADC_DIGI_DATA_BYTES_PER_CONV;
|
||||
if (calc_multiple != 0) {
|
||||
adc_handle[adc_unit].conversion_frame_size = (adc_handle[adc_unit].conversion_frame_size + calc_multiple);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
// Align conversion frame size to cache line size (required for DMA on targets with cache)
|
||||
uint32_t alignment_remainder = adc_handle[adc_unit].conversion_frame_size % CONFIG_CACHE_L1_CACHE_LINE_SIZE;
|
||||
if (alignment_remainder != 0) {
|
||||
adc_handle[adc_unit].conversion_frame_size += (CONFIG_CACHE_L1_CACHE_LINE_SIZE - alignment_remainder);
|
||||
}
|
||||
#endif
|
||||
|
||||
adc_handle[adc_unit].buffer_size = adc_handle[adc_unit].conversion_frame_size * 2;
|
||||
|
||||
//Conversion frame size buffer cant be bigger than 4092 bytes
|
||||
if (adc_handle[adc_unit].conversion_frame_size > 4092) {
|
||||
log_e("Buffers are too big. Please set lower conversions per pin.");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Initialize continuous handle and pins
|
||||
err = __analogContinuousInit(channel, sizeof(channel) / sizeof(adc_channel_t), adc_unit, sampling_freq_hz);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Analog initialization failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Setup callbacks for complete event
|
||||
adc_continuous_evt_cbs_t cbs = {
|
||||
.on_conv_done = adcFnWrapper,
|
||||
//.on_pool_ovf can be used in future
|
||||
};
|
||||
adc_handle[adc_unit].adc_interrupt_handle.fn = (voidFuncPtr)userFunc;
|
||||
err = adc_continuous_register_event_callbacks(adc_handle[adc_unit].adc_continuous_handle, &cbs, &adc_handle[adc_unit].adc_interrupt_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_continuous_register_event_callbacks failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Allocate and prepare result structure for adc readings
|
||||
adc_result = malloc(pins_count * sizeof(adc_continuous_result_t));
|
||||
for (int k = 0; k < pins_count; k++) {
|
||||
adc_result[k].pin = pins[k];
|
||||
adc_result[k].channel = channel[k];
|
||||
}
|
||||
|
||||
//Initialize ADC calibration handle
|
||||
if (adc_handle[adc_unit].adc_cali_handle == NULL) {
|
||||
log_d("Creating cali handle for ADC_%d", adc_unit);
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = adc_unit,
|
||||
.atten = __adcContinuousAtten,
|
||||
.bitwidth = __adcContinuousWidth,
|
||||
};
|
||||
err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
|
||||
#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = adc_unit,
|
||||
.bitwidth = __adcContinuousWidth,
|
||||
.atten = __adcContinuousAtten,
|
||||
};
|
||||
err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle);
|
||||
#else
|
||||
log_e("ADC Calibration scheme is not supported!");
|
||||
return false;
|
||||
#endif
|
||||
if (err != ESP_OK) {
|
||||
log_e("adc_cali_create_scheme_x failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < pins_count; k++) {
|
||||
if (!perimanSetPinBus(pins[k], ESP32_BUS_TYPE_ADC_CONT, (void *)(adc_unit + 1), adc_unit, channel[k])) {
|
||||
log_e("perimanSetPinBus to ADC Continuous failed!");
|
||||
adcContinuousDetachBus((void *)(adc_unit + 1));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms) {
|
||||
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
|
||||
uint32_t bytes_read = 0;
|
||||
uint32_t read_raw[used_adc_channels];
|
||||
uint32_t read_count[used_adc_channels];
|
||||
|
||||
// Allocate DMA buffer with cache line alignment (required for ESP32-P4 and other targets with cache)
|
||||
size_t buffer_size = adc_handle[ADC_UNIT_1].conversion_frame_size;
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
uint8_t *adc_read = (uint8_t *)heap_caps_aligned_alloc(CONFIG_CACHE_L1_CACHE_LINE_SIZE, buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
uint8_t *adc_read = (uint8_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
#endif
|
||||
if (adc_read == NULL) {
|
||||
log_e("Failed to allocate DMA buffer");
|
||||
*buffer = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(adc_read, 0xcc, buffer_size);
|
||||
memset(read_raw, 0, sizeof(read_raw));
|
||||
memset(read_count, 0, sizeof(read_count));
|
||||
|
||||
esp_err_t err = adc_continuous_read(adc_handle[ADC_UNIT_1].adc_continuous_handle, adc_read, adc_handle[0].conversion_frame_size, &bytes_read, timeout_ms);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_TIMEOUT) {
|
||||
log_e("Reading data failed: No data, increase timeout");
|
||||
} else {
|
||||
log_e("Reading data failed with error: %X", err);
|
||||
}
|
||||
free(adc_read);
|
||||
adc_read = NULL;
|
||||
*buffer = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < bytes_read; i += SOC_ADC_DIGI_RESULT_BYTES) {
|
||||
adc_digi_output_data_t *p = (adc_digi_output_data_t *)&adc_read[i];
|
||||
uint32_t chan_num = ADC_GET_CHANNEL(p);
|
||||
uint32_t data = ADC_GET_DATA(p);
|
||||
|
||||
/* Check the channel number validation, the data is invalid if the channel num exceed the maximum channel */
|
||||
if (chan_num >= SOC_ADC_CHANNEL_NUM(0)) {
|
||||
log_e("Invalid data [%d_%d]", chan_num, data);
|
||||
*buffer = NULL;
|
||||
return false;
|
||||
}
|
||||
if (data >= (1 << SOC_ADC_DIGI_MAX_BITWIDTH)) {
|
||||
data = 0;
|
||||
log_e("Invalid data");
|
||||
}
|
||||
|
||||
for (int j = 0; j < used_adc_channels; j++) {
|
||||
if (adc_result[j].channel == chan_num) {
|
||||
read_raw[j] += data;
|
||||
read_count[j] += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < used_adc_channels; j++) {
|
||||
if (read_count[j] != 0) {
|
||||
adc_result[j].avg_read_raw = read_raw[j] / read_count[j];
|
||||
adc_cali_raw_to_voltage(adc_handle[ADC_UNIT_1].adc_cali_handle, adc_result[j].avg_read_raw, &adc_result[j].avg_read_mvolts);
|
||||
} else {
|
||||
log_w("No data read for pin %d", adc_result[j].pin);
|
||||
}
|
||||
}
|
||||
|
||||
free(adc_read);
|
||||
adc_read = NULL;
|
||||
*buffer = adc_result;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
log_e("ADC Continuous is not initialized!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool analogContinuousStart() {
|
||||
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
|
||||
if (adc_continuous_start(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
log_e("ADC Continuous is not initialized!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool analogContinuousStop() {
|
||||
if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) {
|
||||
if (adc_continuous_stop(adc_handle[ADC_UNIT_1].adc_continuous_handle) == ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
log_e("ADC Continuous is not initialized!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool analogContinuousDeinit() {
|
||||
if (adc_handle[ADC_UNIT_1].adc_continuous_handle == NULL) {
|
||||
log_i("ADC Continuous was not initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear all used pins from peripheral manager
|
||||
// This will trigger adcContinuousDetachBus() callback which cleans up the ADC driver
|
||||
for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_1); channel++) {
|
||||
int io_pin;
|
||||
adc_oneshot_channel_to_io(ADC_UNIT_1, channel, &io_pin);
|
||||
if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) {
|
||||
if (!perimanClearPinBus(io_pin)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free the result buffer (callback doesn't do this)
|
||||
if (adc_result != NULL) {
|
||||
free(adc_result);
|
||||
adc_result = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void analogContinuousSetAtten(adc_attenuation_t attenuation) {
|
||||
__adcContinuousAtten = attenuation;
|
||||
}
|
||||
|
||||
void analogContinuousSetWidth(uint8_t bits) {
|
||||
if ((bits < SOC_ADC_DIGI_MIN_BITWIDTH) || (bits > SOC_ADC_DIGI_MAX_BITWIDTH)) {
|
||||
log_e("Selected width cannot be set. Range is from %d to %d", SOC_ADC_DIGI_MIN_BITWIDTH, SOC_ADC_DIGI_MAX_BITWIDTH);
|
||||
return;
|
||||
}
|
||||
__adcContinuousWidth = bits;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
Arduino.h - Main include file for the Arduino SDK
|
||||
Copyright (c) 2005-2013 Arduino Team. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_ADC_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
typedef enum {
|
||||
ADC_0db,
|
||||
ADC_2_5db,
|
||||
ADC_6db,
|
||||
ADC_11db,
|
||||
ADC_ATTENDB_MAX
|
||||
} adc_attenuation_t;
|
||||
|
||||
/*
|
||||
* Get ADC value for pin
|
||||
* */
|
||||
uint16_t analogRead(uint8_t pin);
|
||||
|
||||
/*
|
||||
* Get MilliVolts value for pin
|
||||
* */
|
||||
uint32_t analogReadMilliVolts(uint8_t pin);
|
||||
|
||||
/*
|
||||
* Set the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096).
|
||||
* If between 9 and 12, it will equal the set hardware resolution, else value will be shifted.
|
||||
* Range is 1 - 16
|
||||
*
|
||||
* Note: compatibility with Arduino SAM
|
||||
*/
|
||||
void analogReadResolution(uint8_t bits);
|
||||
|
||||
/*
|
||||
* Set the attenuation for all channels
|
||||
* Default is 11db
|
||||
* */
|
||||
void analogSetAttenuation(adc_attenuation_t attenuation);
|
||||
|
||||
/*
|
||||
* Set the attenuation for particular pin
|
||||
* Default is 11db
|
||||
* */
|
||||
void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation);
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
/*
|
||||
* Sets the sample bits and read resolution
|
||||
* Default is 12bit (0 - 4095)
|
||||
* Range is 9 - 12
|
||||
* */
|
||||
void analogSetWidth(uint8_t bits);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Analog Continuous mode
|
||||
* */
|
||||
|
||||
typedef struct {
|
||||
uint8_t pin; /*!<ADC pin */
|
||||
uint8_t channel; /*!<ADC channel */
|
||||
int avg_read_raw; /*!<ADC average raw data */
|
||||
int avg_read_mvolts; /*!<ADC average voltage in mV */
|
||||
} adc_continuous_result_t;
|
||||
|
||||
/*
|
||||
* Setup ADC continuous peripheral
|
||||
* */
|
||||
bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void));
|
||||
|
||||
/*
|
||||
* Read ADC continuous conversion data
|
||||
* */
|
||||
bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms);
|
||||
|
||||
/*
|
||||
* Start ADC continuous conversions
|
||||
* */
|
||||
bool analogContinuousStart();
|
||||
|
||||
/*
|
||||
* Stop ADC continuous conversions
|
||||
* */
|
||||
bool analogContinuousStop();
|
||||
|
||||
/*
|
||||
* Deinitialize ADC continuous peripheral
|
||||
* */
|
||||
bool analogContinuousDeinit();
|
||||
|
||||
/*
|
||||
* Sets the attenuation for continuous mode reading
|
||||
* Default is 11db
|
||||
* */
|
||||
void analogContinuousSetAtten(adc_attenuation_t attenuation);
|
||||
|
||||
/*
|
||||
* Sets the read resolution for continuous mode
|
||||
* Default is 12bit (0 - 4095)
|
||||
* Range is 9 - 12
|
||||
* */
|
||||
void analogContinuousSetWidth(uint8_t bits);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_ADC_SUPPORTED */
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015-2026 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_BT_MEM_H
|
||||
#define ESP32_HAL_BT_MEM_H
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if defined(SOC_BT_CLASSIC_SUPPORTED) || defined(SOC_BLE_SUPPORTED)
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Flag defined in esp32-hal-bt.c, set by constructors when BT libraries are linked
|
||||
extern bool _btLibraryInUse;
|
||||
|
||||
// Constructor runs before app_main(), setting the flag if any BT library is used.
|
||||
// Multiple libraries including this header just set the same flag to true.
|
||||
__attribute__((constructor)) static void _setBtLibraryInUse(void) {
|
||||
_btLibraryInUse = true;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_BT_SUPPORTED */
|
||||
|
||||
#endif /* ESP32_HAL_BT_MEM_H */
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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 "esp32-hal-bt.h"
|
||||
|
||||
#if SOC_BT_SUPPORTED
|
||||
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && __has_include("esp_bt.h")
|
||||
|
||||
// Flag set by constructors in esp32-hal-bt-mem.h when BT libraries are linked
|
||||
bool _btLibraryInUse = false;
|
||||
|
||||
// Default behavior: release BTDM memory (~36KB) unless a BT library is used or user overrides.
|
||||
// BT libraries include esp32-hal-bt-mem.h which sets _btLibraryInUse = true via constructor.
|
||||
// Users can also provide their own strong btInUse() implementation.
|
||||
__attribute__((weak)) bool btInUse(void) {
|
||||
return _btLibraryInUse;
|
||||
}
|
||||
|
||||
#include "esp_bt.h"
|
||||
|
||||
#ifdef CONFIG_BTDM_CONTROLLER_MODE_BTDM
|
||||
#define BT_MODE ESP_BT_MODE_BTDM
|
||||
#elif defined(CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY)
|
||||
#define BT_MODE ESP_BT_MODE_CLASSIC_BT
|
||||
#else
|
||||
#define BT_MODE ESP_BT_MODE_BLE
|
||||
#endif
|
||||
|
||||
bool btStarted() {
|
||||
return (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED);
|
||||
}
|
||||
|
||||
bool btStart() {
|
||||
return btStartMode(BT_MODE);
|
||||
}
|
||||
|
||||
bool btStartMode(bt_mode mode) {
|
||||
esp_bt_mode_t esp_bt_mode;
|
||||
esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
switch (mode) {
|
||||
case BT_MODE_BLE: esp_bt_mode = ESP_BT_MODE_BLE; break;
|
||||
case BT_MODE_CLASSIC_BT: esp_bt_mode = ESP_BT_MODE_CLASSIC_BT; break;
|
||||
case BT_MODE_BTDM: esp_bt_mode = ESP_BT_MODE_BTDM; break;
|
||||
default: esp_bt_mode = BT_MODE; break;
|
||||
}
|
||||
// esp_bt_controller_enable(MODE) This mode must be equal as the mode in “cfg” of esp_bt_controller_init().
|
||||
cfg.mode = esp_bt_mode;
|
||||
if (cfg.mode == ESP_BT_MODE_CLASSIC_BT) {
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_BLE);
|
||||
}
|
||||
#else
|
||||
// other esp variants dont support BT-classic / DM.
|
||||
esp_bt_mode = BT_MODE;
|
||||
#endif
|
||||
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
|
||||
return true;
|
||||
}
|
||||
esp_err_t ret;
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
if ((ret = esp_bt_controller_init(&cfg)) != ESP_OK) {
|
||||
log_e("initialize controller failed: %s", esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {}
|
||||
}
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
|
||||
if ((ret = esp_bt_controller_enable(esp_bt_mode)) != ESP_OK) {
|
||||
log_e("BT Enable mode=%d failed %s", BT_MODE, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
|
||||
return true;
|
||||
}
|
||||
log_e("BT Start failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool btStop() {
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
return true;
|
||||
}
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
|
||||
if (esp_bt_controller_disable()) {
|
||||
log_e("BT Disable failed");
|
||||
return false;
|
||||
}
|
||||
while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED);
|
||||
}
|
||||
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
|
||||
if (esp_bt_controller_deinit()) {
|
||||
log_e("BT deint failed");
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(1);
|
||||
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
log_e("BT Stop failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
#else // !__has_include("esp_bt.h") || !(defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED))
|
||||
bool btStarted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool btStart() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool btStop() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* !__has_include("esp_bt.h") || !(defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) */
|
||||
|
||||
#endif /* SOC_BT_SUPPORTED */
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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_ESP32_HAL_BT_H_
|
||||
#define _ESP32_ESP32_HAL_BT_H_
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_BT_SUPPORTED
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
BT_MODE_DEFAULT,
|
||||
BT_MODE_BLE,
|
||||
BT_MODE_CLASSIC_BT,
|
||||
BT_MODE_BTDM
|
||||
} bt_mode;
|
||||
|
||||
// Returns true if BT memory should be kept, false to release it (~36KB).
|
||||
// Default (weak): returns false unless a BT library is linked.
|
||||
// BT libraries include esp32-hal-bt-mem.h which automatically sets a flag.
|
||||
// Users may also provide their own strong btInUse() to override.
|
||||
bool btInUse(void);
|
||||
|
||||
bool btStarted();
|
||||
bool btStart();
|
||||
bool btStartMode(bt_mode mode);
|
||||
bool btStop();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_BT_SUPPORTED */
|
||||
|
||||
#endif /* _ESP32_ESP32_HAL_BT_H_ */
|
||||
@@ -0,0 +1,335 @@
|
||||
// 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 "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/rtc.h"
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/syscon_reg.h"
|
||||
#endif
|
||||
#include "soc/efuse_reg.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-cpu.h"
|
||||
#include "hal/timer_ll.h"
|
||||
#include "esp_private/systimer.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "xtensa_timer.h"
|
||||
#include "esp32/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "PLL", "8.5M", "APLL"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "xtensa_timer.h"
|
||||
#include "esp32s2/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "PLL", "8.5M", "APLL"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "xtensa_timer.h"
|
||||
#include "esp32s3/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#include "esp32c2/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#include "esp32c6/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "PLL", "17.5M"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#include "esp32h2/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "PLL", "8.5M", "FLASH_PLL"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp32p4/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "CPLL", "17.5M"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#include "esp32c5/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "17.5M", "PLL_F160M", "PLL_F240M"};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#include "esp32c61/rom/rtc.h"
|
||||
static const char *clock_source_names[] = {"XTAL", "17.5M", "PLL_F160M"};
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/rtc.h"
|
||||
#endif
|
||||
|
||||
typedef struct apb_change_cb_s {
|
||||
struct apb_change_cb_s *prev;
|
||||
struct apb_change_cb_s *next;
|
||||
void *arg;
|
||||
apb_change_cb_t cb;
|
||||
} apb_change_t;
|
||||
|
||||
static apb_change_t *apb_change_callbacks = NULL;
|
||||
static SemaphoreHandle_t 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;
|
||||
if (r != NULL) {
|
||||
if (ev_type == APB_BEFORE_CHANGE) {
|
||||
while (r != NULL) {
|
||||
r->cb(r->arg, ev_type, old_apb, new_apb);
|
||||
r = r->next;
|
||||
}
|
||||
} else { // run backwards through chain
|
||||
while (r->next != NULL) {
|
||||
r = r->next; // find first added
|
||||
}
|
||||
while (r != NULL) {
|
||||
r->cb(r->arg, ev_type, old_apb, new_apb);
|
||||
r = r->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
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->prev = 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;
|
||||
// look for duplicate callbacks
|
||||
while ((r != NULL) && !((r->cb == cb) && (r->arg == arg))) {
|
||||
r = r->next;
|
||||
}
|
||||
if (r) {
|
||||
log_e("duplicate func=%8p arg=%8p", c->cb, c->arg);
|
||||
free(c);
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
return false;
|
||||
} else {
|
||||
c->next = apb_change_callbacks;
|
||||
apb_change_callbacks->prev = c;
|
||||
apb_change_callbacks = c;
|
||||
}
|
||||
}
|
||||
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;
|
||||
// look for matching callback
|
||||
while ((r != NULL) && !((r->cb == cb) && (r->arg == arg))) {
|
||||
r = r->next;
|
||||
}
|
||||
if (r == NULL) {
|
||||
log_e("not found func=%8p arg=%8p", cb, arg);
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
return false;
|
||||
} else {
|
||||
// patch links
|
||||
if (r->prev) {
|
||||
r->prev->next = r->next;
|
||||
} else { // this is first link
|
||||
apb_change_callbacks = r->next;
|
||||
}
|
||||
if (r->next) {
|
||||
r->next->prev = r->prev;
|
||||
}
|
||||
free(r);
|
||||
}
|
||||
xSemaphoreGive(apb_change_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t calculateApb(rtc_cpu_freq_config_t *conf) {
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
if (conf->freq_mhz >= 80) {
|
||||
return 80 * MHZ;
|
||||
}
|
||||
return (conf->source_freq_mhz * MHZ) / conf->div;
|
||||
#else
|
||||
return APB_CLK_FREQ;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32) && !defined(LACT_MODULE) && !defined(LACT_TICKS_PER_US)
|
||||
void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF
|
||||
#endif
|
||||
|
||||
const char *getClockSourceName(uint8_t source) {
|
||||
if (source < SOC_CPU_CLK_SRC_INVALID) {
|
||||
return clock_source_names[source];
|
||||
}
|
||||
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
const char *getSupportedCpuFrequencyMhz(uint8_t xtal) {
|
||||
char *supported_frequencies = (char *)calloc(256, sizeof(char));
|
||||
int pos = 0;
|
||||
|
||||
#if TARGET_CPU_FREQ_MAX_400
|
||||
#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL < 300
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, "360");
|
||||
#else
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, "400");
|
||||
#endif
|
||||
#elif TARGET_CPU_FREQ_MAX_240
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
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)) {
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, "160, 80");
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, "240, 160, 80");
|
||||
}
|
||||
#elif TARGET_CPU_FREQ_MAX_160
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, "160, 120, 80");
|
||||
#elif TARGET_CPU_FREQ_MAX_120
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, "120, 80");
|
||||
#elif TARGET_CPU_FREQ_MAX_96
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, "96, 64, 48");
|
||||
#else
|
||||
free(supported_frequencies);
|
||||
return "Unknown";
|
||||
#endif
|
||||
|
||||
// Append xtal and its dividers only if xtal is nonzero
|
||||
if (xtal != 0) {
|
||||
// We'll show as: , <xtal>, <xtal/2>[, <xtal/4>] MHz
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, ", %u, %u", xtal, xtal / 2);
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// Only append xtal/4 if it's > 0 and meaningful for higher-frequency chips (e.g., ESP32 40MHz/4=10)
|
||||
if (xtal >= RTC_XTAL_FREQ_40M) {
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, ", %u", xtal / 4);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
pos += snprintf(supported_frequencies + pos, 256 - pos, " MHz");
|
||||
return supported_frequencies;
|
||||
}
|
||||
|
||||
bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz) {
|
||||
rtc_cpu_freq_config_t conf, cconf;
|
||||
uint32_t capb, apb;
|
||||
[[maybe_unused]]
|
||||
uint8_t xtal = 0;
|
||||
|
||||
// ===== Get XTAL Frequency and validate input =====
|
||||
#if TARGET_HAS_XTAL_FREQ
|
||||
xtal = (uint8_t)rtc_clk_xtal_freq_get();
|
||||
#endif
|
||||
|
||||
// ===== Get current configuration and check if change is needed =====
|
||||
rtc_clk_cpu_freq_get_config(&cconf);
|
||||
if (cconf.freq_mhz == cpu_freq_mhz) {
|
||||
return true; // Frequency already set
|
||||
}
|
||||
|
||||
// ===== Get configuration for new frequency =====
|
||||
if (!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)) {
|
||||
log_e("CPU clock could not be set to %u MHz. Supported frequencies: %s", cpu_freq_mhz, getSupportedCpuFrequencyMhz(xtal));
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===== Calculate APB frequencies =====
|
||||
capb = calculateApb(&cconf);
|
||||
apb = calculateApb(&conf);
|
||||
|
||||
// ===== Apply frequency change =====
|
||||
if (apb_change_callbacks) {
|
||||
triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb);
|
||||
}
|
||||
|
||||
rtc_clk_cpu_freq_set_config_fast(&conf);
|
||||
|
||||
// Update APB frequency for targets with dynamic APB
|
||||
#if TARGET_HAS_DYNAMIC_APB
|
||||
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;
|
||||
// }
|
||||
rtc_clk_apb_freq_update(apb);
|
||||
|
||||
// ESP32-specific: Update esp_timer divisor
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#if defined(LACT_MODULE) && defined(LACT_TICKS_PER_US)
|
||||
timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), apb / MHZ / LACT_TICKS_PER_US);
|
||||
#else
|
||||
esp_timer_impl_update_apb_freq(apb / MHZ);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update FreeRTOS Tick Divisor for Xtensa targets
|
||||
#if TARGET_HAS_XTENSA_TICK
|
||||
uint32_t fcpu = (conf.freq_mhz >= 80) ? (conf.freq_mhz * MHZ) : (apb);
|
||||
_xt_tick_divisor = fcpu / XT_TICK_PER_SEC;
|
||||
#endif
|
||||
|
||||
if (apb_change_callbacks) {
|
||||
triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb);
|
||||
}
|
||||
|
||||
// ===== Debug logging =====
|
||||
log_d("%s: %u / %u = %u Mhz, APB: %u Hz", getClockSourceName(conf.source), conf.source_freq_mhz, conf.div, conf.freq_mhz, 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);
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
// When adding a new target, update the appropriate group(s) below
|
||||
|
||||
// Targets that support XTAL frequency queries via rtc_clk_xtal_freq_get()
|
||||
#if (!defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32P4))
|
||||
#define TARGET_HAS_XTAL_FREQ 1
|
||||
#else
|
||||
#define TARGET_HAS_XTAL_FREQ 0
|
||||
#endif
|
||||
|
||||
// Targets that need dynamic APB frequency updates via rtc_clk_apb_freq_update()
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
|
||||
#define TARGET_HAS_DYNAMIC_APB 1
|
||||
#else
|
||||
#define TARGET_HAS_DYNAMIC_APB 0
|
||||
#endif
|
||||
|
||||
// Xtensa architecture targets that need FreeRTOS tick divisor updates
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2))
|
||||
#define TARGET_HAS_XTENSA_TICK 1
|
||||
#else
|
||||
#define TARGET_HAS_XTENSA_TICK 0
|
||||
#endif
|
||||
|
||||
// Targets with APLL support (uses IDF SOC capability macro)
|
||||
// Note: ESP32-P4 APLL support is not yet fully implemented in IDF
|
||||
#if (defined(SOC_CLK_APLL_SUPPORTED) && !defined(CONFIG_IDF_TARGET_ESP32P4))
|
||||
#define TARGET_HAS_APLL 1
|
||||
#else
|
||||
#define TARGET_HAS_APLL 0
|
||||
#endif
|
||||
|
||||
// Targets grouped by maximum CPU frequency support
|
||||
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32P4))
|
||||
#define TARGET_CPU_FREQ_MAX_400 1
|
||||
#else
|
||||
#define TARGET_CPU_FREQ_MAX_400 0
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3))
|
||||
#define TARGET_CPU_FREQ_MAX_240 1
|
||||
#else
|
||||
#define TARGET_CPU_FREQ_MAX_240 0
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C61))
|
||||
#define TARGET_CPU_FREQ_MAX_160 1
|
||||
#else
|
||||
#define TARGET_CPU_FREQ_MAX_160 0
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32C2))
|
||||
#define TARGET_CPU_FREQ_MAX_120 1
|
||||
#else
|
||||
#define TARGET_CPU_FREQ_MAX_120 0
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_IDF_TARGET_ESP32H2))
|
||||
#define TARGET_CPU_FREQ_MAX_96 1
|
||||
#else
|
||||
#define TARGET_CPU_FREQ_MAX_96 0
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
const char *getSupportedCpuFrequencyMhz(uint8_t xtal);
|
||||
const char *getClockSourceName(uint8_t source);
|
||||
uint32_t getCpuFrequencyMhz(); // In MHz
|
||||
uint32_t getXtalFrequencyMhz(); // In MHz
|
||||
uint32_t getApbFrequency(); // In Hz
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP32_HAL_CPU_H_ */
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp32-hal-dac.h"
|
||||
|
||||
#if SOC_DAC_SUPPORTED
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "soc/dac_channel.h"
|
||||
#include "driver/dac_oneshot.h"
|
||||
|
||||
static bool dacDetachBus(void *bus) {
|
||||
esp_err_t err = dac_oneshot_del_channel((dac_oneshot_handle_t)bus);
|
||||
if (err != ESP_OK) {
|
||||
log_e("dac_oneshot_del_channel failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __dacWrite(uint8_t pin, uint8_t value) {
|
||||
esp_err_t err = ESP_OK;
|
||||
if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) {
|
||||
log_e("pin %u is not a DAC pin", pin);
|
||||
return false; //not dac pin
|
||||
}
|
||||
|
||||
dac_oneshot_handle_t bus = (dac_oneshot_handle_t)perimanGetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT);
|
||||
if (bus == NULL) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_DAC_ONESHOT, dacDetachBus);
|
||||
if (!perimanClearPinBus(pin)) {
|
||||
return false;
|
||||
}
|
||||
dac_channel_t channel = (pin == DAC_CHAN0_GPIO_NUM) ? DAC_CHAN_0 : DAC_CHAN_1;
|
||||
dac_oneshot_config_t config = {.chan_id = channel};
|
||||
err = dac_oneshot_new_channel(&config, &bus);
|
||||
if (err != ESP_OK) {
|
||||
log_e("dac_oneshot_new_channel failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT, (void *)bus, -1, channel)) {
|
||||
dacDetachBus((void *)bus);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
err = dac_oneshot_output_voltage(bus, value);
|
||||
if (err != ESP_OK) {
|
||||
log_e("dac_oneshot_output_voltage failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __dacDisable(uint8_t pin) {
|
||||
if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) {
|
||||
log_e("pin %u is not a DAC pin", pin);
|
||||
return false; //not dac pin
|
||||
}
|
||||
void *bus = perimanGetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT);
|
||||
if (bus != NULL) {
|
||||
// will call dacDetachBus
|
||||
return perimanClearPinBus(pin);
|
||||
} else {
|
||||
log_e("pin %u is not attached to DAC", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern bool dacWrite(uint8_t pin, uint8_t value) __attribute__((weak, alias("__dacWrite")));
|
||||
extern bool dacDisable(uint8_t pin) __attribute__((weak, alias("__dacDisable")));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_DAC_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool dacWrite(uint8_t pin, uint8_t value);
|
||||
bool dacDisable(uint8_t pin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_DAC_SUPPORTED */
|
||||
@@ -0,0 +1,284 @@
|
||||
// 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 "esp32-hal-gpio.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
// RGB_BUILTIN is defined in pins_arduino.h
|
||||
// If RGB_BUILTIN is defined, it will be used as a pin number for the RGB LED
|
||||
// If RGB_BUILTIN has a side effect that prevents using RMT Legacy driver in IDF 5.1
|
||||
// Define ESP32_ARDUINO_NO_RGB_BUILTIN in build_opt.h or through CLI to disable RGB_BUILTIN
|
||||
#ifdef ESP32_ARDUINO_NO_RGB_BUILTIN
|
||||
#ifdef RGB_BUILTIN
|
||||
#undef RGB_BUILTIN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// It fixes lack of pin definition for S3 and for any future SoC
|
||||
// this function works for ESP32, ESP32-S2 and ESP32-S3 - including the C3, it will return -1 for any pin
|
||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||
#include "soc/touch_sensor_periph.h"
|
||||
|
||||
int8_t digitalPinToTouchChannel(uint8_t pin) {
|
||||
if (pin < SOC_GPIO_PIN_COUNT) {
|
||||
for (uint8_t i = 0; i < SOC_TOUCH_SENSOR_NUM; i++) {
|
||||
if (touch_sensor_channel_io_map[i] == pin) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_e("No touch pad on selected pin(%u)!", pin);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
// No Touch Sensor available
|
||||
int8_t digitalPinToTouchChannel(uint8_t pin) {
|
||||
log_e("Touch sensor not available on this chip");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SOC_ADC_SUPPORTED
|
||||
#include "soc/adc_periph.h"
|
||||
|
||||
int8_t digitalPinToAnalogChannel(uint8_t pin) {
|
||||
uint8_t channel = 0;
|
||||
if (pin < SOC_GPIO_PIN_COUNT) {
|
||||
for (uint8_t i = 0; i < SOC_ADC_PERIPH_NUM; i++) {
|
||||
for (uint8_t j = 0; j < SOC_ADC_MAX_CHANNEL_NUM; j++) {
|
||||
if (adc_channel_io_map[i][j] == pin) {
|
||||
return channel;
|
||||
}
|
||||
channel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int8_t analogChannelToDigitalPin(uint8_t channel) {
|
||||
if (channel >= (SOC_ADC_PERIPH_NUM * SOC_ADC_MAX_CHANNEL_NUM)) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t adc_unit = (channel / SOC_ADC_MAX_CHANNEL_NUM);
|
||||
uint8_t adc_chan = (channel % SOC_ADC_MAX_CHANNEL_NUM);
|
||||
return adc_channel_io_map[adc_unit][adc_chan];
|
||||
}
|
||||
#else
|
||||
// No Analog channels available
|
||||
int8_t analogChannelToDigitalPin(uint8_t channel) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void *);
|
||||
typedef struct {
|
||||
voidFuncPtr fn;
|
||||
void *arg;
|
||||
bool functional;
|
||||
} InterruptHandle_t;
|
||||
static InterruptHandle_t __pinInterruptHandlers[SOC_GPIO_PIN_COUNT] = {
|
||||
0,
|
||||
};
|
||||
|
||||
#include "driver/rtc_io.h"
|
||||
|
||||
static bool gpioDetachBus(void *bus) {
|
||||
return true;
|
||||
}
|
||||
|
||||
extern void ARDUINO_ISR_ATTR __pinMode(uint8_t pin, uint8_t mode) {
|
||||
#ifdef RGB_BUILTIN
|
||||
if (pin == RGB_BUILTIN) {
|
||||
__pinMode(RGB_BUILTIN - SOC_GPIO_PIN_COUNT, mode);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pin >= SOC_GPIO_PIN_COUNT) {
|
||||
log_e("Invalid IO %i selected", pin);
|
||||
return;
|
||||
}
|
||||
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_GPIO, gpioDetachBus);
|
||||
if (!perimanClearPinBus(pin)) {
|
||||
log_e("Deinit of previous bus from IO %i failed", pin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gpio_hal_context_t gpiohal;
|
||||
gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0);
|
||||
|
||||
gpio_config_t conf = {
|
||||
.pin_bit_mask = (1ULL << pin), /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */
|
||||
.mode = GPIO_MODE_DISABLE, /*!< GPIO mode: set input/output mode */
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE, /*!< GPIO pull-up */
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE, /*!< GPIO pull-down */
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C61
|
||||
.intr_type = gpiohal.dev->pin[pin].int_type /*!< GPIO interrupt type - previously set */
|
||||
#else
|
||||
.intr_type = gpiohal.dev->pinn[pin].pinn_int_type /*!< GPIO interrupt type - previously set */
|
||||
#endif
|
||||
};
|
||||
if (mode < 0x20) { //io
|
||||
conf.mode = mode & (INPUT | OUTPUT);
|
||||
if (mode & OPEN_DRAIN) {
|
||||
conf.mode |= GPIO_MODE_DEF_OD;
|
||||
}
|
||||
if (mode & PULLUP) {
|
||||
conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
}
|
||||
if (mode & PULLDOWN) {
|
||||
conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
}
|
||||
}
|
||||
if (gpio_config(&conf) != ESP_OK) {
|
||||
log_e("IO %i config failed", pin);
|
||||
return;
|
||||
}
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL) {
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_GPIO, (void *)(pin + 1), -1, -1)) {
|
||||
//gpioDetachBus((void *)(pin+1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RGB_BUILTIN
|
||||
uint8_t RGB_BUILTIN_storage = 0;
|
||||
#endif
|
||||
|
||||
extern void ARDUINO_ISR_ATTR __digitalWrite(uint8_t pin, uint8_t val) {
|
||||
#ifdef RGB_BUILTIN
|
||||
if (pin == RGB_BUILTIN) {
|
||||
//use RMT to set all channels on/off
|
||||
RGB_BUILTIN_storage = val;
|
||||
const uint8_t comm_val = val != 0 ? RGB_BRIGHTNESS : 0;
|
||||
rgbLedWrite(RGB_BUILTIN, comm_val, comm_val, comm_val);
|
||||
return;
|
||||
}
|
||||
#endif // RGB_BUILTIN
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) != NULL) {
|
||||
gpio_set_level((gpio_num_t)pin, val);
|
||||
} else {
|
||||
log_e("IO %i is not set as GPIO. Execute digitalMode(%i, OUTPUT) first.", pin, pin);
|
||||
}
|
||||
}
|
||||
|
||||
extern int ARDUINO_ISR_ATTR __digitalRead(uint8_t pin) {
|
||||
#ifdef RGB_BUILTIN
|
||||
if (pin == RGB_BUILTIN) {
|
||||
return RGB_BUILTIN_storage;
|
||||
}
|
||||
#endif // RGB_BUILTIN
|
||||
// This work when the pin is set as GPIO and in INPUT mode. For all other pin functions, it may return inconsistent response
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL) {
|
||||
log_w("IO %i is not set as GPIO. digitalRead() may return an inconsistent value.", pin);
|
||||
}
|
||||
return gpio_get_level((gpio_num_t)pin);
|
||||
}
|
||||
|
||||
static void ARDUINO_ISR_ATTR __onPinInterrupt(void *arg) {
|
||||
InterruptHandle_t *isr = (InterruptHandle_t *)arg;
|
||||
if (isr->fn) {
|
||||
if (isr->arg) {
|
||||
((voidFuncPtrArg)isr->fn)(isr->arg);
|
||||
} else {
|
||||
isr->fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void cleanupFunctional(void *arg);
|
||||
|
||||
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void *arg, int intr_type, bool functional) {
|
||||
static bool interrupt_initialized = false;
|
||||
|
||||
// makes sure that pin -1 (255) will never work -- this follows Arduino standard
|
||||
if (pin >= SOC_GPIO_PIN_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!interrupt_initialized) {
|
||||
esp_err_t err = gpio_install_isr_service((int)ARDUINO_ISR_FLAG);
|
||||
interrupt_initialized = (err == ESP_OK) || (err == ESP_ERR_INVALID_STATE);
|
||||
}
|
||||
if (!interrupt_initialized) {
|
||||
log_e("IO %i ISR Service Failed To Start", pin);
|
||||
return;
|
||||
}
|
||||
|
||||
// if new attach without detach remove old info
|
||||
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg) {
|
||||
cleanupFunctional(__pinInterruptHandlers[pin].arg);
|
||||
}
|
||||
__pinInterruptHandlers[pin].fn = (voidFuncPtr)userFunc;
|
||||
__pinInterruptHandlers[pin].arg = arg;
|
||||
__pinInterruptHandlers[pin].functional = functional;
|
||||
|
||||
gpio_set_intr_type((gpio_num_t)pin, (gpio_int_type_t)(intr_type & 0x7));
|
||||
if (intr_type & 0x8) {
|
||||
gpio_wakeup_enable((gpio_num_t)pin, (gpio_int_type_t)(intr_type & 0x7));
|
||||
}
|
||||
gpio_isr_handler_add((gpio_num_t)pin, __onPinInterrupt, &__pinInterruptHandlers[pin]);
|
||||
|
||||
//FIX interrupts on peripherals outputs (eg. LEDC,...)
|
||||
//Enable input in GPIO register
|
||||
gpio_hal_context_t gpiohal;
|
||||
gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0);
|
||||
gpio_hal_input_enable(&gpiohal, pin);
|
||||
}
|
||||
|
||||
extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void *arg, int intr_type) {
|
||||
__attachInterruptFunctionalArg(pin, userFunc, arg, intr_type, false);
|
||||
}
|
||||
|
||||
extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type) {
|
||||
__attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type, false);
|
||||
}
|
||||
|
||||
extern void __detachInterrupt(uint8_t pin) {
|
||||
gpio_isr_handler_remove((gpio_num_t)pin); //remove handle and disable isr for pin
|
||||
gpio_wakeup_disable((gpio_num_t)pin);
|
||||
|
||||
if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg) {
|
||||
cleanupFunctional(__pinInterruptHandlers[pin].arg);
|
||||
}
|
||||
__pinInterruptHandlers[pin].fn = NULL;
|
||||
__pinInterruptHandlers[pin].arg = NULL;
|
||||
__pinInterruptHandlers[pin].functional = false;
|
||||
|
||||
gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_DISABLE);
|
||||
}
|
||||
|
||||
extern void enableInterrupt(uint8_t pin) {
|
||||
gpio_intr_enable((gpio_num_t)pin);
|
||||
}
|
||||
|
||||
extern void disableInterrupt(uint8_t pin) {
|
||||
gpio_intr_disable((gpio_num_t)pin);
|
||||
}
|
||||
|
||||
extern void pinMode(uint8_t pin, uint8_t mode) __attribute__((weak, alias("__pinMode")));
|
||||
extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__((weak, alias("__digitalWrite")));
|
||||
extern int digitalRead(uint8_t pin) __attribute__((weak, alias("__digitalRead")));
|
||||
extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__((weak, alias("__attachInterrupt")));
|
||||
extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void *arg, int mode) __attribute__((weak, alias("__attachInterruptArg")));
|
||||
extern void detachInterrupt(uint8_t pin) __attribute__((weak, alias("__detachInterrupt")));
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Arduino.h - Main include file for the Arduino SDK
|
||||
Copyright (c) 2005-2013 Arduino Team. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef MAIN_ESP32_HAL_GPIO_H_
|
||||
#define MAIN_ESP32_HAL_GPIO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "pins_arduino.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3)
|
||||
#define NUM_OUPUT_PINS 46
|
||||
#define PIN_DAC1 17
|
||||
#define PIN_DAC2 18
|
||||
#else
|
||||
#define NUM_OUPUT_PINS 34
|
||||
#define PIN_DAC1 25
|
||||
#define PIN_DAC2 26
|
||||
#endif
|
||||
|
||||
#define LOW 0x0
|
||||
#define HIGH 0x1
|
||||
|
||||
//GPIO FUNCTIONS
|
||||
#define INPUT 0x01
|
||||
// Changed OUTPUT from 0x02 to behave the same as Arduino pinMode(pin,OUTPUT)
|
||||
// where you can read the state of pin even when it is set as OUTPUT
|
||||
#define OUTPUT 0x03
|
||||
#define PULLUP 0x04
|
||||
#define INPUT_PULLUP 0x05
|
||||
#define PULLDOWN 0x08
|
||||
#define INPUT_PULLDOWN 0x09
|
||||
#define OPEN_DRAIN 0x10
|
||||
#define OUTPUT_OPEN_DRAIN 0x13
|
||||
#define ANALOG 0xC0
|
||||
|
||||
//Interrupt Modes
|
||||
#define DISABLED 0x00
|
||||
#define RISING 0x01
|
||||
#define FALLING 0x02
|
||||
#define CHANGE 0x03
|
||||
#define ONLOW 0x04
|
||||
#define ONHIGH 0x05
|
||||
#define ONLOW_WE 0x0C
|
||||
#define ONHIGH_WE 0x0D
|
||||
|
||||
#define digitalPinIsValid(pin) GPIO_IS_VALID_GPIO(pin)
|
||||
#define digitalPinCanOutput(pin) GPIO_IS_VALID_OUTPUT_GPIO(pin)
|
||||
|
||||
#define digitalPinToRtcPin(pin) ((RTC_GPIO_IS_VALID_GPIO(pin)) ? rtc_io_number_get(pin) : -1)
|
||||
#define digitalPinToDacChannel(pin) (((pin) == DAC_CHANNEL_1_GPIO_NUM) ? 0 : ((pin) == DAC_CHANNEL_2_GPIO_NUM) ? 1 : -1)
|
||||
|
||||
void pinMode(uint8_t pin, uint8_t mode);
|
||||
void digitalWrite(uint8_t pin, uint8_t val);
|
||||
int digitalRead(uint8_t pin);
|
||||
|
||||
void attachInterrupt(uint8_t pin, void (*)(void), int mode);
|
||||
void attachInterruptArg(uint8_t pin, void (*)(void *), void *arg, int mode);
|
||||
void detachInterrupt(uint8_t pin);
|
||||
void enableInterrupt(uint8_t pin);
|
||||
void disableInterrupt(uint8_t pin);
|
||||
|
||||
int8_t digitalPinToTouchChannel(uint8_t pin);
|
||||
int8_t digitalPinToAnalogChannel(uint8_t pin);
|
||||
int8_t analogChannelToDigitalPin(uint8_t channel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MAIN_ESP32_HAL_GPIO_H_ */
|
||||
@@ -0,0 +1,365 @@
|
||||
// Copyright 2015-2025 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"
|
||||
#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED)
|
||||
|
||||
#include "esp32-hal-hosted.h"
|
||||
#include "esp32-hal-log.h"
|
||||
#include "esp32-hal.h"
|
||||
#include "pins_arduino.h"
|
||||
|
||||
#include "esp_hosted.h"
|
||||
#include "esp_hosted_transport_config.h"
|
||||
// extern esp_err_t esp_hosted_init();
|
||||
// extern esp_err_t esp_hosted_deinit();
|
||||
|
||||
static bool hosted_initialized = false;
|
||||
static bool hosted_ble_active = false;
|
||||
static bool hosted_wifi_active = false;
|
||||
|
||||
static sdio_pin_config_t sdio_pin_config = {
|
||||
#ifdef BOARD_HAS_SDIO_ESP_HOSTED
|
||||
.pin_clk = BOARD_SDIO_ESP_HOSTED_CLK,
|
||||
.pin_cmd = BOARD_SDIO_ESP_HOSTED_CMD,
|
||||
.pin_d0 = BOARD_SDIO_ESP_HOSTED_D0,
|
||||
.pin_d1 = BOARD_SDIO_ESP_HOSTED_D1,
|
||||
.pin_d2 = BOARD_SDIO_ESP_HOSTED_D2,
|
||||
.pin_d3 = BOARD_SDIO_ESP_HOSTED_D3,
|
||||
.pin_reset = BOARD_SDIO_ESP_HOSTED_RESET
|
||||
#else
|
||||
.pin_clk = CONFIG_ESP_SDIO_PIN_CLK,
|
||||
.pin_cmd = CONFIG_ESP_SDIO_PIN_CMD,
|
||||
.pin_d0 = CONFIG_ESP_SDIO_PIN_D0,
|
||||
.pin_d1 = CONFIG_ESP_SDIO_PIN_D1,
|
||||
.pin_d2 = CONFIG_ESP_SDIO_PIN_D2,
|
||||
.pin_d3 = CONFIG_ESP_SDIO_PIN_D3,
|
||||
.pin_reset = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_hosted_coprocessor_fwver_t slave_version_struct = {.major1 = 0, .minor1 = 0, .patch1 = 0};
|
||||
static esp_hosted_coprocessor_fwver_t host_version_struct = {
|
||||
.major1 = ESP_HOSTED_VERSION_MAJOR_1, .minor1 = ESP_HOSTED_VERSION_MINOR_1, .patch1 = ESP_HOSTED_VERSION_PATCH_1
|
||||
};
|
||||
|
||||
static bool hostedInit();
|
||||
static bool hostedDeinit();
|
||||
|
||||
void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) {
|
||||
*major = host_version_struct.major1;
|
||||
*minor = host_version_struct.minor1;
|
||||
*patch = host_version_struct.patch1;
|
||||
}
|
||||
|
||||
void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) {
|
||||
*major = slave_version_struct.major1;
|
||||
*minor = slave_version_struct.minor1;
|
||||
*patch = slave_version_struct.patch1;
|
||||
}
|
||||
|
||||
bool hostedHasUpdate() {
|
||||
if (!hosted_initialized) {
|
||||
log_e("ESP-Hosted is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t host_version = ESP_HOSTED_VERSION_VAL(host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1);
|
||||
uint32_t slave_version = 0;
|
||||
|
||||
esp_err_t ret = esp_hosted_get_coprocessor_fwversion(&slave_version_struct);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Could not get slave firmware version: %s", esp_err_to_name(ret));
|
||||
} else {
|
||||
slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
|
||||
}
|
||||
|
||||
log_i("Host firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1);
|
||||
log_i("Slave firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
|
||||
|
||||
// compare major.minor only
|
||||
// slave_version &= 0xFFFFFF00;
|
||||
// host_version &= 0xFFFFFF00;
|
||||
|
||||
if (host_version == slave_version) {
|
||||
log_i("Versions Match!");
|
||||
} else if (host_version > slave_version) {
|
||||
log_w("Version on Host is NEWER than version on co-processor");
|
||||
log_w("Update URL: %s", hostedGetUpdateURL());
|
||||
return true;
|
||||
} else {
|
||||
log_w("Version on Host is OLDER than version on co-processor");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char *hostedGetUpdateURL() {
|
||||
// https://espressif.github.io/arduino-esp32/hosted/esp32c6-v1.2.3.bin
|
||||
static char url[92] = {0};
|
||||
snprintf(
|
||||
url, 92, "https://espressif.github.io/arduino-esp32/hosted/%s-v%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".bin", CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET,
|
||||
host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1
|
||||
);
|
||||
return url;
|
||||
}
|
||||
|
||||
bool hostedBeginUpdate() {
|
||||
if (!hosted_initialized) {
|
||||
log_e("ESP-Hosted is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_hosted_slave_ota_begin();
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to begin Update: %s", esp_err_to_name(err));
|
||||
}
|
||||
return err == ESP_OK;
|
||||
}
|
||||
|
||||
bool hostedWriteUpdate(uint8_t *buf, uint32_t len) {
|
||||
if (!hosted_initialized) {
|
||||
log_e("ESP-Hosted is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_hosted_slave_ota_write(buf, len);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to write Update: %s", esp_err_to_name(err));
|
||||
}
|
||||
return err == ESP_OK;
|
||||
}
|
||||
|
||||
bool hostedEndUpdate() {
|
||||
if (!hosted_initialized) {
|
||||
log_e("ESP-Hosted is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_hosted_slave_ota_end();
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to end Update: %s", esp_err_to_name(err));
|
||||
}
|
||||
return err == ESP_OK;
|
||||
}
|
||||
|
||||
bool hostedActivateUpdate() {
|
||||
if (!hosted_initialized) {
|
||||
log_e("ESP-Hosted is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Activate can fail on older firmwares and that is not critical
|
||||
uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
|
||||
uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0);
|
||||
|
||||
if (slave_version < min_version) {
|
||||
// Silence messages caused by earlier versions
|
||||
esp_log_level_set("rpc_core", ESP_LOG_NONE);
|
||||
}
|
||||
|
||||
esp_err_t err = esp_hosted_slave_ota_activate();
|
||||
|
||||
// Any further communication will result in logged errors
|
||||
esp_log_level_set("sdmmc_io", ESP_LOG_NONE);
|
||||
esp_log_level_set("H_SDIO_DRV", ESP_LOG_NONE);
|
||||
|
||||
if (err != ESP_OK && slave_version >= min_version) {
|
||||
log_e("Failed to activate Update: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hostedInit() {
|
||||
if (!hosted_initialized) {
|
||||
log_i("Initializing ESP-Hosted");
|
||||
log_d(
|
||||
"SDIO pins: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", sdio_pin_config.pin_clk, sdio_pin_config.pin_cmd, sdio_pin_config.pin_d0,
|
||||
sdio_pin_config.pin_d1, sdio_pin_config.pin_d2, sdio_pin_config.pin_d3, sdio_pin_config.pin_reset
|
||||
);
|
||||
hosted_initialized = true;
|
||||
struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG();
|
||||
conf.pin_clk.pin = sdio_pin_config.pin_clk;
|
||||
conf.pin_cmd.pin = sdio_pin_config.pin_cmd;
|
||||
conf.pin_d0.pin = sdio_pin_config.pin_d0;
|
||||
conf.pin_d1.pin = sdio_pin_config.pin_d1;
|
||||
conf.pin_d2.pin = sdio_pin_config.pin_d2;
|
||||
conf.pin_d3.pin = sdio_pin_config.pin_d3;
|
||||
conf.pin_reset.pin = sdio_pin_config.pin_reset;
|
||||
esp_err_t err = esp_hosted_sdio_set_config(&conf);
|
||||
if (err != ESP_OK) { //&& err != ESP_ERR_NOT_ALLOWED) { // uncomment when second init is fixed
|
||||
log_e("esp_hosted_sdio_set_config failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
err = esp_hosted_init();
|
||||
if (err != ESP_OK) {
|
||||
log_e("esp_hosted_init failed: %s", esp_err_to_name(err));
|
||||
hosted_initialized = false;
|
||||
return false;
|
||||
}
|
||||
log_i("ESP-Hosted initialized!");
|
||||
err = esp_hosted_connect_to_slave();
|
||||
if (err != ESP_OK) {
|
||||
log_e("esp_hosted_connect_to_slave failed: %s", esp_err_to_name(err));
|
||||
hosted_initialized = false;
|
||||
return false;
|
||||
}
|
||||
hostedHasUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attach pins to PeriMan here
|
||||
// Slave chip model is CONFIG_IDF_SLAVE_TARGET
|
||||
// sdio_pin_config.pin_clk
|
||||
// sdio_pin_config.pin_cmd
|
||||
// sdio_pin_config.pin_d0
|
||||
// sdio_pin_config.pin_d1
|
||||
// sdio_pin_config.pin_d2
|
||||
// sdio_pin_config.pin_d3
|
||||
// sdio_pin_config.pin_reset
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hostedDeinit() {
|
||||
if (!hosted_initialized) {
|
||||
log_e("ESP-Hosted is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_hosted_deinit() != ESP_OK) {
|
||||
log_e("esp_hosted_deinit failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
hosted_initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hostedInitBLE() {
|
||||
log_i("Initializing ESP-Hosted for BLE");
|
||||
if (!hostedInit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
|
||||
uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0);
|
||||
if (slave_version >= min_version) {
|
||||
esp_err_t err = esp_hosted_bt_controller_init();
|
||||
if (err != ESP_OK) {
|
||||
log_e("esp_hosted_bt_controller_init failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
err = esp_hosted_bt_controller_enable();
|
||||
if (err != ESP_OK) {
|
||||
log_e("esp_hosted_bt_controller_enable failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
hosted_ble_active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hostedInitWiFi() {
|
||||
log_i("Initializing ESP-Hosted for WiFi");
|
||||
hosted_wifi_active = true;
|
||||
return hostedInit();
|
||||
}
|
||||
|
||||
bool hostedDeinitBLE() {
|
||||
log_i("Deinitializing ESP-Hosted for BLE");
|
||||
uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1);
|
||||
uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0);
|
||||
if (slave_version >= min_version) {
|
||||
esp_err_t err = esp_hosted_bt_controller_disable();
|
||||
if (err != ESP_OK) {
|
||||
log_e("esp_hosted_bt_controller_disable failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
err = esp_hosted_bt_controller_deinit(false);
|
||||
if (err != ESP_OK) {
|
||||
log_e("esp_hosted_bt_controller_deinit failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
hosted_ble_active = false;
|
||||
if (!hosted_wifi_active) {
|
||||
return hostedDeinit();
|
||||
} else {
|
||||
log_i("ESP-Hosted is still being used by Wi-Fi. Skipping deinit.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hostedDeinitWiFi() {
|
||||
log_i("Deinitializing ESP-Hosted for WiFi");
|
||||
hosted_wifi_active = false;
|
||||
if (!hosted_ble_active) {
|
||||
return hostedDeinit();
|
||||
} else {
|
||||
log_i("ESP-Hosted is still being used by BLE. Skipping deinit.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) {
|
||||
if (clk < 0 || cmd < 0 || d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0 || rst < 0) {
|
||||
log_e("All SDIO pins must be defined");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hosted_initialized) {
|
||||
int8_t current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, current_rst;
|
||||
hostedGetPins(¤t_clk, ¤t_cmd, ¤t_d0, ¤t_d1, ¤t_d2, ¤t_d3, ¤t_rst);
|
||||
log_e("SDIO pins must be set before ESP-Hosted is initialized");
|
||||
log_e(
|
||||
"Current pins used: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3,
|
||||
current_rst
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
sdio_pin_config.pin_clk = clk;
|
||||
sdio_pin_config.pin_cmd = cmd;
|
||||
sdio_pin_config.pin_d0 = d0;
|
||||
sdio_pin_config.pin_d1 = d1;
|
||||
sdio_pin_config.pin_d2 = d2;
|
||||
sdio_pin_config.pin_d3 = d3;
|
||||
sdio_pin_config.pin_reset = rst;
|
||||
return true;
|
||||
}
|
||||
|
||||
void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst) {
|
||||
*clk = sdio_pin_config.pin_clk;
|
||||
*cmd = sdio_pin_config.pin_cmd;
|
||||
*d0 = sdio_pin_config.pin_d0;
|
||||
*d1 = sdio_pin_config.pin_d1;
|
||||
*d2 = sdio_pin_config.pin_d2;
|
||||
*d3 = sdio_pin_config.pin_d3;
|
||||
*rst = sdio_pin_config.pin_reset;
|
||||
}
|
||||
|
||||
bool hostedIsBLEActive() {
|
||||
return hosted_ble_active;
|
||||
}
|
||||
|
||||
bool hostedIsWiFiActive() {
|
||||
return hosted_wifi_active;
|
||||
}
|
||||
|
||||
bool hostedIsInitialized() {
|
||||
return hosted_initialized;
|
||||
}
|
||||
|
||||
#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef MAIN_ESP32_HAL_HOSTED_H_
|
||||
#define MAIN_ESP32_HAL_HOSTED_H_
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED)
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t pin_clk;
|
||||
uint8_t pin_cmd;
|
||||
uint8_t pin_d0;
|
||||
uint8_t pin_d1;
|
||||
uint8_t pin_d2;
|
||||
uint8_t pin_d3;
|
||||
uint8_t pin_reset;
|
||||
} sdio_pin_config_t;
|
||||
|
||||
bool hostedInitBLE();
|
||||
bool hostedInitWiFi();
|
||||
bool hostedDeinitBLE();
|
||||
bool hostedDeinitWiFi();
|
||||
bool hostedIsInitialized();
|
||||
bool hostedIsBLEActive();
|
||||
bool hostedIsWiFiActive();
|
||||
bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst);
|
||||
void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst);
|
||||
void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch);
|
||||
void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch);
|
||||
bool hostedHasUpdate();
|
||||
char *hostedGetUpdateURL();
|
||||
bool hostedBeginUpdate();
|
||||
bool hostedWriteUpdate(uint8_t *buf, uint32_t len);
|
||||
bool hostedEndUpdate();
|
||||
bool hostedActivateUpdate();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */
|
||||
#endif /* MAIN_ESP32_HAL_HOSTED_H_ */
|
||||
@@ -0,0 +1,455 @@
|
||||
// Copyright 2015-2025 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 "esp32-hal-i2c.h"
|
||||
|
||||
#if SOC_I2C_SUPPORTED
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
|
||||
#include "esp32-hal.h"
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#endif
|
||||
#include "esp_attr.h"
|
||||
#include "esp_system.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
|
||||
typedef volatile struct {
|
||||
bool initialized;
|
||||
uint32_t frequency;
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
SemaphoreHandle_t lock;
|
||||
#endif
|
||||
int8_t scl;
|
||||
int8_t sda;
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
i2c_master_dev_handle_t dev_handles[128];
|
||||
} i2c_bus_t;
|
||||
|
||||
static i2c_bus_t bus[SOC_I2C_NUM];
|
||||
|
||||
static bool i2cDetachBus(void *bus_i2c_num) {
|
||||
uint8_t i2c_num = (int)bus_i2c_num - 1;
|
||||
if (!bus[i2c_num].initialized) {
|
||||
return true;
|
||||
}
|
||||
esp_err_t err = i2cDeinit(i2c_num);
|
||||
if (err != ESP_OK) {
|
||||
log_e("i2cDeinit failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void *i2cBusHandle(uint8_t i2c_num) {
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return NULL;
|
||||
}
|
||||
return bus[i2c_num].bus_handle;
|
||||
}
|
||||
|
||||
bool i2cIsInit(uint8_t i2c_num) {
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return false;
|
||||
}
|
||||
return bus[i2c_num].initialized;
|
||||
}
|
||||
|
||||
esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) {
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (bus[i2c_num].lock == NULL) {
|
||||
bus[i2c_num].lock = xSemaphoreCreateMutex();
|
||||
if (bus[i2c_num].lock == NULL) {
|
||||
log_e("xSemaphoreCreateMutex failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
//acquire lock
|
||||
if (xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
if (bus[i2c_num].initialized) {
|
||||
log_e("bus is already initialized");
|
||||
ret = ESP_FAIL;
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if (!frequency) {
|
||||
frequency = 100000UL;
|
||||
} else if (frequency > 1000000UL) {
|
||||
frequency = 1000000UL;
|
||||
}
|
||||
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SDA, i2cDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SCL, i2cDetachBus);
|
||||
|
||||
if (!perimanClearPinBus(sda) || !perimanClearPinBus(scl)) {
|
||||
ret = ESP_FAIL;
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
log_i("Initializing I2C Master: num=%u sda=%d scl=%d freq=%lu", i2c_num, sda, scl, frequency);
|
||||
|
||||
i2c_master_bus_handle_t bus_handle = NULL;
|
||||
i2c_master_bus_config_t bus_config;
|
||||
memset(&bus_config, 0, sizeof(i2c_master_bus_config_t));
|
||||
bus_config.i2c_port = (i2c_port_num_t)i2c_num;
|
||||
bus_config.sda_io_num = (gpio_num_t)sda;
|
||||
bus_config.scl_io_num = (gpio_num_t)scl;
|
||||
#if SOC_LP_I2C_SUPPORTED
|
||||
if (i2c_num >= SOC_HP_I2C_NUM) {
|
||||
bus_config.lp_source_clk = LP_I2C_SCLK_DEFAULT;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
||||
}
|
||||
bus_config.glitch_ignore_cnt = 7;
|
||||
bus_config.intr_priority = 0; // auto
|
||||
bus_config.trans_queue_depth = 0; // only valid in asynchronous transaction, which Arduino does not use
|
||||
bus_config.flags.enable_internal_pullup = 1;
|
||||
#if SOC_I2C_SUPPORT_SLEEP_RETENTION
|
||||
bus_config.flags.allow_pd = 1; // backup/restore the I2C registers before/after entering/exist sleep mode
|
||||
#endif
|
||||
|
||||
ret = i2c_new_master_bus(&bus_config, &bus_handle);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_new_master_bus failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
} else {
|
||||
bus[i2c_num].initialized = true;
|
||||
bus[i2c_num].frequency = frequency;
|
||||
bus[i2c_num].scl = scl;
|
||||
bus[i2c_num].sda = sda;
|
||||
bus[i2c_num].bus_handle = bus_handle;
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
bus[i2c_num].dev_handles[i] = NULL;
|
||||
}
|
||||
if (!perimanSetPinBus(sda, ESP32_BUS_TYPE_I2C_MASTER_SDA, (void *)(i2c_num + 1), i2c_num, -1)
|
||||
|| !perimanSetPinBus(scl, ESP32_BUS_TYPE_I2C_MASTER_SCL, (void *)(i2c_num + 1), i2c_num, -1)) {
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock so that i2cDetachBus can execute i2cDeinit
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
i2cDetachBus((void *)(i2c_num + 1));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Silence messages coming from the IDF driver
|
||||
esp_log_level_set("i2c.master", ESP_LOG_NONE);
|
||||
|
||||
init_fail:
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cDeinit(uint8_t i2c_num) {
|
||||
esp_err_t err = ESP_FAIL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
} else {
|
||||
// remove devices from the bus
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
if (bus[i2c_num].dev_handles[i] != NULL) {
|
||||
err = i2c_master_bus_rm_device(bus[i2c_num].dev_handles[i]);
|
||||
bus[i2c_num].dev_handles[i] = NULL;
|
||||
if (err != ESP_OK) {
|
||||
log_e("i2c_master_bus_rm_device failed: [%d] %s", err, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
err = i2c_del_master_bus(bus[i2c_num].bus_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("i2c_del_master_bus failed: [%d] %s", err, esp_err_to_name(err));
|
||||
} else {
|
||||
bus[i2c_num].initialized = false;
|
||||
perimanClearPinBus(bus[i2c_num].scl);
|
||||
perimanClearPinBus(bus[i2c_num].sda);
|
||||
bus[i2c_num].scl = -1;
|
||||
bus[i2c_num].sda = -1;
|
||||
bus[i2c_num].bus_handle = NULL;
|
||||
}
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t i2cAddDeviceIfNeeded(uint8_t i2c_num, uint16_t address) {
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (bus[i2c_num].dev_handles[address] == NULL) {
|
||||
i2c_master_dev_handle_t dev_handle = NULL;
|
||||
i2c_device_config_t dev_config;
|
||||
memset(&dev_config, 0, sizeof(i2c_device_config_t));
|
||||
dev_config.dev_addr_length = I2C_ADDR_BIT_LEN_7; // Arduino supports only 7bit addresses
|
||||
dev_config.device_address = address;
|
||||
dev_config.scl_speed_hz = bus[i2c_num].frequency;
|
||||
dev_config.scl_wait_us = 0;
|
||||
dev_config.flags.disable_ack_check = 0;
|
||||
|
||||
ret = i2c_master_bus_add_device(bus[i2c_num].bus_handle, &dev_config, &dev_handle);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_master_bus_add_device failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
} else {
|
||||
bus[i2c_num].dev_handles[address] = dev_handle;
|
||||
log_v("added device: bus=%u addr=0x%x handle=0x%08x", i2c_num, address, dev_handle);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t *buff, size_t size, uint32_t timeOutMillis) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
// i2c_cmd_handle_t cmd = NULL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (address >= 128) {
|
||||
log_e("Only 7bit I2C addresses are supported");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
// Probe device
|
||||
ret = i2c_master_probe(bus[i2c_num].bus_handle, address, timeOutMillis);
|
||||
if (ret != ESP_OK) {
|
||||
log_v("i2c_master_probe failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
}
|
||||
} else {
|
||||
// writing data to device
|
||||
ret = i2cAddDeviceIfNeeded(i2c_num, address);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_v("i2c_master_transmit: bus=%u addr=0x%x handle=0x%08x size=%u", i2c_num, address, bus[i2c_num].dev_handles[address], size);
|
||||
ret = i2c_master_transmit(bus[i2c_num].dev_handles[address], buff, size, timeOutMillis);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_master_transmit failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
goto end;
|
||||
}
|
||||
|
||||
// wait for transactions to finish (is it needed with sync transactions?)
|
||||
// ret = i2c_master_bus_wait_all_done(bus[i2c_num].bus_handle, timeOutMillis);
|
||||
// if (ret != ESP_OK) {
|
||||
// log_e("i2c_master_bus_wait_all_done failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
// goto end;
|
||||
// }
|
||||
}
|
||||
|
||||
end:
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t *buff, size_t size, uint32_t timeOutMillis, size_t *readCount) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
*readCount = 0;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = i2cAddDeviceIfNeeded(i2c_num, address);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_v("i2c_master_receive: bus=%u addr=0x%x handle=0x%08x size=%u", i2c_num, address, bus[i2c_num].dev_handles[address], size);
|
||||
ret = i2c_master_receive(bus[i2c_num].dev_handles[address], buff, size, timeOutMillis);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_master_receive failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
goto end;
|
||||
}
|
||||
|
||||
// wait for transactions to finish (is it needed with sync transactions?)
|
||||
// ret = i2c_master_bus_wait_all_done(bus[i2c_num].bus_handle, timeOutMillis);
|
||||
// if (ret != ESP_OK) {
|
||||
// log_e("i2c_master_bus_wait_all_done failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
// goto end;
|
||||
// }
|
||||
*readCount = size;
|
||||
|
||||
end:
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cWriteReadNonStop(
|
||||
uint8_t i2c_num, uint16_t address, const uint8_t *wbuff, size_t wsize, uint8_t *rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount
|
||||
) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
*readCount = 0;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = i2cAddDeviceIfNeeded(i2c_num, address);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_v("i2c_master_transmit_receive: bus=%u addr=0x%x handle=0x%08x write=%u read=%u", i2c_num, address, bus[i2c_num].dev_handles[address], wsize, rsize);
|
||||
ret = i2c_master_transmit_receive(bus[i2c_num].dev_handles[address], wbuff, wsize, rbuff, rsize, timeOutMillis);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_master_transmit_receive failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
goto end;
|
||||
}
|
||||
|
||||
// wait for transactions to finish (is it needed with sync transactions?)
|
||||
// ret = i2c_master_bus_wait_all_done(bus[i2c_num].bus_handle, timeOutMillis);
|
||||
// if (ret != ESP_OK) {
|
||||
// log_e("i2c_master_bus_wait_all_done failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
// goto end;
|
||||
// }
|
||||
*readCount = rsize;
|
||||
|
||||
end:
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
goto end;
|
||||
}
|
||||
if (bus[i2c_num].frequency == frequency) {
|
||||
ret = ESP_OK;
|
||||
goto end;
|
||||
}
|
||||
if (!frequency) {
|
||||
frequency = 100000UL;
|
||||
} else if (frequency > 1000000UL) {
|
||||
frequency = 1000000UL;
|
||||
}
|
||||
|
||||
bus[i2c_num].frequency = frequency;
|
||||
|
||||
// loop through devices, remove them and then re-add them with the new frequency
|
||||
for (uint8_t i = 0; i < 128; i++) {
|
||||
if (bus[i2c_num].dev_handles[i] != NULL) {
|
||||
ret = i2c_master_bus_rm_device(bus[i2c_num].dev_handles[i]);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_master_bus_rm_device failed: [%d] %s", ret, esp_err_to_name(ret));
|
||||
goto end;
|
||||
} else {
|
||||
bus[i2c_num].dev_handles[i] = NULL;
|
||||
ret = i2cAddDeviceIfNeeded(i2c_num, i);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t *frequency) {
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
*frequency = bus[i2c_num].frequency;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) */
|
||||
#endif /* SOC_I2C_SUPPORTED */
|
||||
@@ -0,0 +1,925 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_I2C_SUPPORT_SLAVE
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
#include "rom/gpio.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/ringbuf.h"
|
||||
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "soc/i2c_reg.h"
|
||||
#include "soc/i2c_struct.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#include "hal/i2c_ll.h"
|
||||
#include "hal/i2c_types.h"
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32C61)
|
||||
#include "hal/clk_gate_ll.h"
|
||||
#endif
|
||||
#include "esp32-hal-log.h"
|
||||
#include "esp32-hal-i2c-slave.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define I2C_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define I2C_CLOCK_SRC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if !SOC_RCC_IS_INDEPENDENT
|
||||
#define I2C_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define I2C_RCC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#define I2C_SLAVE_USE_RX_QUEUE 0 // 1: Queue, 0: RingBuffer
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
#define I2C_SCL_IDX(p) ((p == 0) ? I2C0_SCL_PAD_OUT_IDX : ((p == 1) ? I2C1_SCL_PAD_OUT_IDX : 0))
|
||||
#define I2C_SDA_IDX(p) ((p == 0) ? I2C0_SDA_PAD_OUT_IDX : ((p == 1) ? I2C1_SDA_PAD_OUT_IDX : 0))
|
||||
#else
|
||||
#if SOC_HP_I2C_NUM > 1
|
||||
#define I2C_SCL_IDX(p) ((p == 0) ? I2CEXT0_SCL_OUT_IDX : ((p == 1) ? I2CEXT1_SCL_OUT_IDX : 0))
|
||||
#define I2C_SDA_IDX(p) ((p == 0) ? I2CEXT0_SDA_OUT_IDX : ((p == 1) ? I2CEXT1_SDA_OUT_IDX : 0))
|
||||
#else
|
||||
#define I2C_SCL_IDX(p) I2CEXT0_SCL_OUT_IDX
|
||||
#define I2C_SDA_IDX(p) I2CEXT0_SDA_OUT_IDX
|
||||
#endif
|
||||
#endif // ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define I2C_TXFIFO_WM_INT_ENA I2C_TXFIFO_EMPTY_INT_ENA
|
||||
#define I2C_RXFIFO_WM_INT_ENA I2C_RXFIFO_FULL_INT_ENA
|
||||
#endif
|
||||
|
||||
enum {
|
||||
I2C_SLAVE_EVT_RX,
|
||||
I2C_SLAVE_EVT_TX
|
||||
};
|
||||
|
||||
typedef struct i2c_slave_struct_t {
|
||||
i2c_dev_t *dev;
|
||||
uint8_t num;
|
||||
int8_t sda;
|
||||
int8_t scl;
|
||||
i2c_slave_request_cb_t request_callback;
|
||||
i2c_slave_receive_cb_t receive_callback;
|
||||
void *arg;
|
||||
intr_handle_t intr_handle;
|
||||
TaskHandle_t task_handle;
|
||||
QueueHandle_t event_queue;
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
QueueHandle_t rx_queue;
|
||||
#else
|
||||
RingbufHandle_t rx_ring_buf;
|
||||
#endif
|
||||
QueueHandle_t tx_queue;
|
||||
uint32_t rx_data_count;
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
SemaphoreHandle_t lock;
|
||||
#endif
|
||||
} i2c_slave_struct_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t event : 2;
|
||||
uint32_t stop : 1;
|
||||
uint32_t param : 29;
|
||||
};
|
||||
uint32_t val;
|
||||
} i2c_slave_queue_event_t;
|
||||
|
||||
static i2c_slave_struct_t _i2c_bus_array[SOC_HP_I2C_NUM] = {
|
||||
{&I2C0, 0, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
,
|
||||
NULL
|
||||
#endif
|
||||
},
|
||||
#if SOC_HP_I2C_NUM > 1
|
||||
{&I2C1, 1, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
,
|
||||
NULL
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
#define I2C_SLAVE_MUTEX_LOCK()
|
||||
#define I2C_SLAVE_MUTEX_UNLOCK()
|
||||
#else
|
||||
#define I2C_SLAVE_MUTEX_LOCK() \
|
||||
if (i2c->lock) { \
|
||||
xSemaphoreTake(i2c->lock, portMAX_DELAY); \
|
||||
}
|
||||
#define I2C_SLAVE_MUTEX_UNLOCK() \
|
||||
if (i2c->lock) { \
|
||||
xSemaphoreGive(i2c->lock); \
|
||||
}
|
||||
#endif
|
||||
|
||||
//-------------------------------------- HAL_LL (Missing Functions) ------------------------------------------------
|
||||
typedef enum {
|
||||
I2C_STRETCH_CAUSE_MASTER_READ,
|
||||
I2C_STRETCH_CAUSE_TX_FIFO_EMPTY,
|
||||
I2C_STRETCH_CAUSE_RX_FIFO_FULL,
|
||||
I2C_STRETCH_CAUSE_MAX
|
||||
} i2c_stretch_cause_t;
|
||||
|
||||
static inline i2c_stretch_cause_t i2c_ll_stretch_cause(i2c_dev_t *hw) {
|
||||
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
|
||||
return hw->sr.stretch_cause;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
return hw->status_reg.stretch_cause;
|
||||
#else
|
||||
return I2C_STRETCH_CAUSE_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void i2c_ll_set_stretch(i2c_dev_t *hw, uint16_t time) {
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
typeof(hw->scl_stretch_conf) scl_stretch_conf;
|
||||
scl_stretch_conf.val = 0;
|
||||
scl_stretch_conf.slave_scl_stretch_en = (time > 0);
|
||||
scl_stretch_conf.stretch_protect_num = time;
|
||||
scl_stretch_conf.slave_scl_stretch_clr = 1;
|
||||
hw->scl_stretch_conf.val = scl_stretch_conf.val;
|
||||
if (time > 0) {
|
||||
//enable interrupt
|
||||
hw->int_ena.val |= I2C_SLAVE_STRETCH_INT_ENA;
|
||||
} else {
|
||||
//disable interrupt
|
||||
hw->int_ena.val &= (~I2C_SLAVE_STRETCH_INT_ENA);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void i2c_ll_stretch_clr(i2c_dev_t *hw) {
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
hw->scl_stretch_conf.slave_scl_stretch_clr = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool i2c_ll_slave_addressed(i2c_dev_t *hw) {
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
return hw->status_reg.slave_addressed;
|
||||
#else
|
||||
return hw->sr.slave_addressed;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool i2c_ll_slave_rw(i2c_dev_t *hw) //not exposed by hal_ll
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
return hw->status_reg.slave_rw;
|
||||
#else
|
||||
return hw->sr.slave_rw;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-------------------------------------- PRIVATE (Function Prototypes) ------------------------------------------------
|
||||
static void i2c_slave_free_resources(i2c_slave_struct_t *i2c);
|
||||
static void i2c_slave_delay_us(uint64_t us);
|
||||
static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode);
|
||||
static bool i2c_slave_check_line_state(int8_t sda, int8_t scl);
|
||||
static bool i2c_slave_attach_gpio(i2c_slave_struct_t *i2c, int8_t sda, int8_t scl);
|
||||
static bool i2c_slave_detach_gpio(i2c_slave_struct_t *i2c);
|
||||
static bool i2c_slave_set_frequency(i2c_slave_struct_t *i2c, uint32_t clk_speed);
|
||||
static bool i2c_slave_send_event(i2c_slave_struct_t *i2c, i2c_slave_queue_event_t *event);
|
||||
static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t *i2c);
|
||||
static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t *i2c, uint32_t len);
|
||||
static size_t i2c_slave_read_rx(i2c_slave_struct_t *i2c, uint8_t *data, size_t len);
|
||||
static void i2c_slave_isr_handler(void *arg);
|
||||
static void i2c_slave_task(void *pv_args);
|
||||
static bool i2cSlaveDetachBus(void *bus_i2c_num);
|
||||
|
||||
//=====================================================================================================================
|
||||
//-------------------------------------- Public Functions -------------------------------------------------------------
|
||||
//=====================================================================================================================
|
||||
|
||||
esp_err_t i2cSlaveAttachCallbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void *arg) {
|
||||
if (num >= SOC_HP_I2C_NUM) {
|
||||
log_e("Invalid port num: %u", num);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
i2c->request_callback = request_callback;
|
||||
i2c->receive_callback = receive_callback;
|
||||
i2c->arg = arg;
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len) {
|
||||
if (num >= SOC_HP_I2C_NUM) {
|
||||
log_e("Invalid port num: %u", num);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (sda < 0 || scl < 0) {
|
||||
log_e("invalid pins sda=%d, scl=%d", sda, scl);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!frequency) {
|
||||
frequency = 100000;
|
||||
} else if (frequency > 1000000) {
|
||||
frequency = 1000000;
|
||||
}
|
||||
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_SLAVE_SDA, i2cSlaveDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_SLAVE_SCL, i2cSlaveDetachBus);
|
||||
|
||||
if (!perimanClearPinBus(sda) || !perimanClearPinBus(scl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_i("Initializing I2C Slave: sda=%d scl=%d freq=%d, addr=0x%x", sda, scl, frequency, slaveID);
|
||||
|
||||
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (!i2c->lock) {
|
||||
i2c->lock = xSemaphoreCreateMutex();
|
||||
if (i2c->lock == NULL) {
|
||||
log_e("RX queue create failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
i2c_slave_free_resources(i2c);
|
||||
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
i2c->rx_queue = xQueueCreate(rx_len, sizeof(uint8_t));
|
||||
if (i2c->rx_queue == NULL) {
|
||||
log_e("RX queue create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
#else
|
||||
i2c->rx_ring_buf = xRingbufferCreate(rx_len, RINGBUF_TYPE_BYTEBUF);
|
||||
if (i2c->rx_ring_buf == NULL) {
|
||||
log_e("RX RingBuf create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
i2c->tx_queue = xQueueCreate(tx_len, sizeof(uint8_t));
|
||||
if (i2c->tx_queue == NULL) {
|
||||
log_e("TX queue create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c->event_queue = xQueueCreate(16, sizeof(i2c_slave_queue_event_t));
|
||||
if (i2c->event_queue == NULL) {
|
||||
log_e("Event queue create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
xTaskCreate(i2c_slave_task, "i2c_slave_task", 4096, i2c, 20, &i2c->task_handle);
|
||||
if (i2c->task_handle == NULL) {
|
||||
log_e("Event thread create failed");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (frequency == 0) {
|
||||
frequency = 100000L;
|
||||
}
|
||||
frequency = (frequency * 5) / 4;
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 \
|
||||
|| CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
if (i2c->num == 0) {
|
||||
periph_ll_enable_clk_clear_rst(PERIPH_I2C0_MODULE);
|
||||
#if SOC_HP_I2C_NUM > 1
|
||||
} else {
|
||||
periph_ll_enable_clk_clear_rst(PERIPH_I2C1_MODULE);
|
||||
#endif
|
||||
}
|
||||
#endif // !defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0)) \
|
||||
|| (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 3) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0))
|
||||
i2c_ll_set_mode(i2c->dev, I2C_BUS_MODE_SLAVE);
|
||||
i2c_ll_enable_pins_open_drain(i2c->dev, true);
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
|
||||
i2c_ll_enable_fifo_mode(i2c->dev, true);
|
||||
#else
|
||||
i2c_ll_slave_set_fifo_mode(i2c->dev, true);
|
||||
#endif
|
||||
#else
|
||||
i2c_ll_slave_init(i2c->dev);
|
||||
i2c_ll_slave_set_fifo_mode(i2c->dev, true);
|
||||
#endif
|
||||
i2c_ll_set_slave_addr(i2c->dev, slaveID, false);
|
||||
i2c_ll_set_tout(i2c->dev, I2C_LL_MAX_TIMEOUT);
|
||||
i2c_slave_set_frequency(i2c, frequency);
|
||||
|
||||
if (!i2c_slave_check_line_state(sda, scl)) {
|
||||
log_e("bad pin state");
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c_slave_attach_gpio(i2c, sda, scl);
|
||||
|
||||
if (i2c_ll_is_bus_busy(i2c->dev)) {
|
||||
log_w("Bus busy, reinit");
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
i2c_ll_clear_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
|
||||
i2c_ll_enable_fifo_mode(i2c->dev, true);
|
||||
#else
|
||||
i2c_ll_slave_set_fifo_mode(i2c->dev, true);
|
||||
#endif
|
||||
|
||||
if (!i2c->intr_handle) {
|
||||
uint32_t flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED;
|
||||
if (i2c->num == 0) {
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#else
|
||||
ret = esp_intr_alloc(ETS_I2C0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#endif
|
||||
#if SOC_HP_I2C_NUM > 1
|
||||
} else {
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||
ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#else
|
||||
ret = esp_intr_alloc(ETS_I2C1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
log_e("install interrupt handler Failed=%d", ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_ll_txfifo_rst(i2c->dev);
|
||||
i2c_ll_rxfifo_rst(i2c->dev);
|
||||
i2c_ll_slave_enable_rx_it(i2c->dev);
|
||||
i2c_ll_set_stretch(i2c->dev, 0x3FF);
|
||||
i2c_ll_update(i2c->dev);
|
||||
if (!perimanSetPinBus(sda, ESP32_BUS_TYPE_I2C_SLAVE_SDA, (void *)(i2c->num + 1), i2c->num, -1)
|
||||
|| !perimanSetPinBus(scl, ESP32_BUS_TYPE_I2C_SLAVE_SCL, (void *)(i2c->num + 1), i2c->num, -1)) {
|
||||
i2cSlaveDetachBus((void *)(i2c->num + 1));
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
i2c_slave_free_resources(i2c);
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cSlaveDeinit(uint8_t num) {
|
||||
if (num >= SOC_HP_I2C_NUM) {
|
||||
log_e("Invalid port num: %u", num);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (!i2c->lock) {
|
||||
log_e("Lock is not initialized! Did you call i2c_slave_init()?");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
#endif
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
int scl = i2c->scl;
|
||||
int sda = i2c->sda;
|
||||
i2c_slave_free_resources(i2c);
|
||||
perimanClearPinBus(scl);
|
||||
perimanClearPinBus(sda);
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
size_t i2cSlaveWrite(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms) {
|
||||
if (num >= SOC_HP_I2C_NUM) {
|
||||
log_e("Invalid port num: %u", num);
|
||||
return 0;
|
||||
}
|
||||
uint32_t to_queue = 0, to_fifo = 0;
|
||||
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (!i2c->lock) {
|
||||
log_e("Lock is not initialized! Did you call i2c_slave_init()?");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
#endif
|
||||
if (!i2c->tx_queue) {
|
||||
return 0;
|
||||
}
|
||||
I2C_SLAVE_MUTEX_LOCK();
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
i2c_ll_slave_disable_tx_it(i2c->dev);
|
||||
uint32_t txfifo_len = 0;
|
||||
i2c_ll_get_txfifo_len(i2c->dev, &txfifo_len);
|
||||
if (txfifo_len < SOC_I2C_FIFO_LEN) {
|
||||
i2c_ll_txfifo_rst(i2c->dev);
|
||||
}
|
||||
#endif
|
||||
i2c_ll_get_txfifo_len(i2c->dev, &to_fifo);
|
||||
if (to_fifo) {
|
||||
if (len < to_fifo) {
|
||||
to_fifo = len;
|
||||
}
|
||||
i2c_ll_write_txfifo(i2c->dev, (uint8_t *)buf, to_fifo);
|
||||
buf += to_fifo;
|
||||
len -= to_fifo;
|
||||
//reset tx_queue
|
||||
xQueueReset(i2c->tx_queue);
|
||||
//write the rest of the bytes to the queue
|
||||
if (len) {
|
||||
to_queue = uxQueueSpacesAvailable(i2c->tx_queue);
|
||||
if (len < to_queue) {
|
||||
to_queue = len;
|
||||
}
|
||||
for (size_t i = 0; i < to_queue; i++) {
|
||||
if (xQueueSend(i2c->tx_queue, &buf[i], timeout_ms / portTICK_PERIOD_MS) != pdTRUE) {
|
||||
xQueueReset(i2c->tx_queue);
|
||||
to_queue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//no need to enable TX_EMPTY if tx_queue is empty
|
||||
if (to_queue) {
|
||||
i2c_ll_slave_enable_tx_it(i2c->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
I2C_SLAVE_MUTEX_UNLOCK();
|
||||
return to_queue + to_fifo;
|
||||
}
|
||||
|
||||
//=====================================================================================================================
|
||||
//-------------------------------------- Private Functions ------------------------------------------------------------
|
||||
//=====================================================================================================================
|
||||
|
||||
static void i2c_slave_free_resources(i2c_slave_struct_t *i2c) {
|
||||
i2c_slave_detach_gpio(i2c);
|
||||
i2c_ll_set_slave_addr(i2c->dev, 0, false);
|
||||
i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
i2c_ll_clear_intr_mask(i2c->dev, I2C_LL_INTR_MASK);
|
||||
|
||||
if (i2c->intr_handle) {
|
||||
esp_intr_free(i2c->intr_handle);
|
||||
i2c->intr_handle = NULL;
|
||||
}
|
||||
|
||||
if (i2c->task_handle) {
|
||||
vTaskDelete(i2c->task_handle);
|
||||
i2c->task_handle = NULL;
|
||||
}
|
||||
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
if (i2c->rx_queue) {
|
||||
vQueueDelete(i2c->rx_queue);
|
||||
i2c->rx_queue = NULL;
|
||||
}
|
||||
#else
|
||||
if (i2c->rx_ring_buf) {
|
||||
vRingbufferDelete(i2c->rx_ring_buf);
|
||||
i2c->rx_ring_buf = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i2c->tx_queue) {
|
||||
vQueueDelete(i2c->tx_queue);
|
||||
i2c->tx_queue = NULL;
|
||||
}
|
||||
|
||||
if (i2c->event_queue) {
|
||||
vQueueDelete(i2c->event_queue);
|
||||
i2c->event_queue = NULL;
|
||||
}
|
||||
|
||||
i2c->rx_data_count = 0;
|
||||
}
|
||||
|
||||
static bool i2c_slave_set_frequency(i2c_slave_struct_t *i2c, uint32_t clk_speed) {
|
||||
if (i2c == NULL) {
|
||||
log_e("no control buffer");
|
||||
return false;
|
||||
}
|
||||
if (clk_speed > 1100000UL) {
|
||||
clk_speed = 1100000UL;
|
||||
}
|
||||
|
||||
// Adjust Fifo thresholds based on frequency
|
||||
uint32_t a = (clk_speed / 50000L) + 2;
|
||||
log_d("Fifo thresholds: rx_fifo_full = %d, tx_fifo_empty = %d", SOC_I2C_FIFO_LEN - a, a);
|
||||
|
||||
i2c_hal_clk_config_t clk_cal;
|
||||
#if SOC_I2C_SUPPORT_APB
|
||||
i2c_ll_master_cal_bus_clk(APB_CLK_FREQ, clk_speed, &clk_cal);
|
||||
I2C_CLOCK_SRC_ATOMIC() {
|
||||
i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_APB); /*!< I2C source clock from APB, 80M*/
|
||||
}
|
||||
#elif SOC_I2C_SUPPORT_XTAL
|
||||
#ifndef XTAL_CLK_FREQ
|
||||
#define XTAL_CLK_FREQ APB_CLK_FREQ
|
||||
#endif
|
||||
i2c_ll_master_cal_bus_clk(XTAL_CLK_FREQ, clk_speed, &clk_cal);
|
||||
I2C_CLOCK_SRC_ATOMIC() {
|
||||
i2c_ll_set_source_clk(i2c->dev, SOC_MOD_CLK_XTAL); /*!< I2C source clock from XTAL, 40M */
|
||||
}
|
||||
#endif
|
||||
i2c_ll_set_txfifo_empty_thr(i2c->dev, a);
|
||||
i2c_ll_set_rxfifo_full_thr(i2c->dev, SOC_I2C_FIFO_LEN - a);
|
||||
i2c_ll_master_set_bus_timing(i2c->dev, &clk_cal);
|
||||
i2c_ll_master_set_filter(i2c->dev, 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void i2c_slave_delay_us(uint64_t us) {
|
||||
uint64_t m = esp_timer_get_time();
|
||||
if (us) {
|
||||
uint64_t e = (m + us);
|
||||
if (m > e) { //overflow
|
||||
while ((uint64_t)esp_timer_get_time() > e);
|
||||
}
|
||||
while ((uint64_t)esp_timer_get_time() < e);
|
||||
}
|
||||
}
|
||||
|
||||
static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode) {
|
||||
gpio_config_t conf = {
|
||||
.pin_bit_mask = 1LL << pin, .mode = mode, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE
|
||||
};
|
||||
gpio_config(&conf);
|
||||
}
|
||||
|
||||
static bool i2c_slave_check_line_state(int8_t sda, int8_t scl) {
|
||||
if (sda < 0 || scl < 0) {
|
||||
return false; //return false since there is nothing to do
|
||||
}
|
||||
// if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles
|
||||
gpio_set_level(sda, 1);
|
||||
gpio_set_level(scl, 1);
|
||||
i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD);
|
||||
i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD);
|
||||
gpio_set_level(scl, 1);
|
||||
|
||||
if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state
|
||||
log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, gpio_get_level(sda), scl, gpio_get_level(scl));
|
||||
for (uint8_t a = 0; a < 9; a++) {
|
||||
i2c_slave_delay_us(5);
|
||||
if (gpio_get_level(sda) && gpio_get_level(scl)) { // bus recovered
|
||||
log_w("Recovered after %d Cycles", a);
|
||||
gpio_set_level(sda, 0); // start
|
||||
i2c_slave_delay_us(5);
|
||||
for (uint8_t b = 0; b < 9; b++) {
|
||||
gpio_set_level(scl, 1);
|
||||
i2c_slave_delay_us(5);
|
||||
gpio_set_level(scl, 0);
|
||||
i2c_slave_delay_us(5);
|
||||
}
|
||||
gpio_set_level(scl, 1);
|
||||
i2c_slave_delay_us(5);
|
||||
gpio_set_level(sda, 1); // stop
|
||||
break;
|
||||
}
|
||||
gpio_set_level(scl, 0);
|
||||
i2c_slave_delay_us(5);
|
||||
gpio_set_level(scl, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state
|
||||
log_e("Bus Invalid State, Can't init sda=%d, scl=%d", gpio_get_level(sda), gpio_get_level(scl));
|
||||
return false; // bus is busy
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool i2c_slave_attach_gpio(i2c_slave_struct_t *i2c, int8_t sda, int8_t scl) {
|
||||
if (i2c == NULL) {
|
||||
log_e("no control block");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((sda < 0) || (scl < 0)) {
|
||||
log_e("bad pins sda=%d, scl=%d", sda, scl);
|
||||
return false;
|
||||
}
|
||||
|
||||
i2c->scl = scl;
|
||||
gpio_set_level(scl, 1);
|
||||
i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT_OUTPUT_OD);
|
||||
gpio_matrix_out(scl, I2C_SCL_IDX(i2c->num), false, false);
|
||||
gpio_matrix_in(scl, I2C_SCL_IDX(i2c->num), false);
|
||||
|
||||
i2c->sda = sda;
|
||||
gpio_set_level(sda, 1);
|
||||
i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT_OUTPUT_OD);
|
||||
gpio_matrix_out(sda, I2C_SDA_IDX(i2c->num), false, false);
|
||||
gpio_matrix_in(sda, I2C_SDA_IDX(i2c->num), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool i2c_slave_detach_gpio(i2c_slave_struct_t *i2c) {
|
||||
if (i2c == NULL) {
|
||||
log_e("no control Block");
|
||||
return false;
|
||||
}
|
||||
if (i2c->scl >= 0) {
|
||||
gpio_matrix_out(i2c->scl, 0x100, false, false);
|
||||
gpio_matrix_in(0x30, I2C_SCL_IDX(i2c->num), false);
|
||||
i2c_slave_gpio_mode(i2c->scl, GPIO_MODE_INPUT);
|
||||
i2c->scl = -1; // un attached
|
||||
}
|
||||
if (i2c->sda >= 0) {
|
||||
gpio_matrix_out(i2c->sda, 0x100, false, false);
|
||||
gpio_matrix_in(0x30, I2C_SDA_IDX(i2c->num), false);
|
||||
i2c_slave_gpio_mode(i2c->sda, GPIO_MODE_INPUT);
|
||||
i2c->sda = -1; // un attached
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool i2c_slave_send_event(i2c_slave_struct_t *i2c, i2c_slave_queue_event_t *event) {
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
if (i2c->event_queue) {
|
||||
if (xQueueSendFromISR(i2c->event_queue, event, (BaseType_t *const)&pxHigherPriorityTaskWoken) != pdTRUE) {
|
||||
//log_e("event_queue_full");
|
||||
}
|
||||
}
|
||||
return pxHigherPriorityTaskWoken;
|
||||
}
|
||||
|
||||
static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t *i2c) {
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
uint32_t d = 0, moveCnt = 0;
|
||||
i2c_ll_get_txfifo_len(i2c->dev, &moveCnt);
|
||||
while (moveCnt > 0) { // read tx queue until Fifo is full or queue is empty
|
||||
if (xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t *const)&pxHigherPriorityTaskWoken) == pdTRUE) {
|
||||
i2c_ll_write_txfifo(i2c->dev, (uint8_t *)&d, 1);
|
||||
moveCnt--;
|
||||
} else {
|
||||
i2c_ll_slave_disable_tx_it(i2c->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pxHigherPriorityTaskWoken;
|
||||
}
|
||||
|
||||
static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t *i2c, uint32_t len) {
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
uint32_t d = 0;
|
||||
#else
|
||||
uint8_t data[SOC_I2C_FIFO_LEN];
|
||||
#endif
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
while (len > 0) {
|
||||
i2c_ll_read_rxfifo(i2c->dev, (uint8_t *)&d, 1);
|
||||
if (xQueueSendFromISR(i2c->rx_queue, &d, (BaseType_t *const)&pxHigherPriorityTaskWoken) != pdTRUE) {
|
||||
log_e("rx_queue_full");
|
||||
} else {
|
||||
i2c->rx_data_count++;
|
||||
}
|
||||
if (--len == 0) {
|
||||
len = i2c_ll_get_rxfifo_cnt(i2c->dev);
|
||||
}
|
||||
#else
|
||||
if (len) {
|
||||
i2c_ll_read_rxfifo(i2c->dev, data, len);
|
||||
if (xRingbufferSendFromISR(i2c->rx_ring_buf, (void *)data, len, (BaseType_t *const)&pxHigherPriorityTaskWoken) != pdTRUE) {
|
||||
log_e("rx_ring_buf_full");
|
||||
} else {
|
||||
i2c->rx_data_count += len;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return pxHigherPriorityTaskWoken;
|
||||
}
|
||||
|
||||
static void i2c_slave_isr_handler(void *arg) {
|
||||
bool pxHigherPriorityTaskWoken = false;
|
||||
i2c_slave_struct_t *i2c = (i2c_slave_struct_t *)arg; // recover data
|
||||
|
||||
uint32_t activeInt = 0;
|
||||
i2c_ll_get_intr_mask(i2c->dev, &activeInt);
|
||||
i2c_ll_clear_intr_mask(i2c->dev, activeInt);
|
||||
uint32_t rx_fifo_len = 0;
|
||||
i2c_ll_get_rxfifo_cnt(i2c->dev, &rx_fifo_len);
|
||||
bool slave_rw = i2c_ll_slave_rw(i2c->dev);
|
||||
|
||||
if (activeInt & I2C_RXFIFO_WM_INT_ENA) { // RX FiFo Full
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
i2c_ll_slave_enable_rx_it(i2c->dev); //is this necessary?
|
||||
}
|
||||
|
||||
if (activeInt & I2C_TRANS_COMPLETE_INT_ENA) { // STOP
|
||||
if (rx_fifo_len) { //READ RX FIFO
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
}
|
||||
if (i2c->rx_data_count) { //WRITE or RepeatedStart
|
||||
//SEND RX Event
|
||||
i2c_slave_queue_event_t event;
|
||||
event.event = I2C_SLAVE_EVT_RX;
|
||||
event.stop = !slave_rw;
|
||||
event.param = i2c->rx_data_count;
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
|
||||
//Zero RX count
|
||||
i2c->rx_data_count = 0;
|
||||
}
|
||||
if (slave_rw) { // READ
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (i2c->dev->status_reg.scl_main_state_last == 6) {
|
||||
//SEND TX Event
|
||||
i2c_slave_queue_event_t event;
|
||||
event.event = I2C_SLAVE_EVT_TX;
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
|
||||
}
|
||||
#else
|
||||
//reset TX data
|
||||
i2c_ll_txfifo_rst(i2c->dev);
|
||||
uint8_t d;
|
||||
while (xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t *const)&pxHigherPriorityTaskWoken) == pdTRUE); //flush partial write
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
if (activeInt & I2C_SLAVE_STRETCH_INT_ENA) { // STRETCH
|
||||
i2c_stretch_cause_t cause = i2c_ll_stretch_cause(i2c->dev);
|
||||
if (cause == I2C_STRETCH_CAUSE_MASTER_READ) {
|
||||
//on C3 RX data disappears with repeated start, so we need to get it here
|
||||
if (rx_fifo_len) {
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
}
|
||||
//SEND TX Event
|
||||
i2c_slave_queue_event_t event;
|
||||
event.event = I2C_SLAVE_EVT_TX;
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event);
|
||||
//will clear after execution
|
||||
} else if (cause == I2C_STRETCH_CAUSE_TX_FIFO_EMPTY) {
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c);
|
||||
i2c_ll_stretch_clr(i2c->dev);
|
||||
} else if (cause == I2C_STRETCH_CAUSE_RX_FIFO_FULL) {
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len);
|
||||
i2c_ll_stretch_clr(i2c->dev);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (activeInt & I2C_TXFIFO_WM_INT_ENA) { // TX FiFo Empty
|
||||
pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c);
|
||||
}
|
||||
|
||||
if (pxHigherPriorityTaskWoken) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static size_t i2c_slave_read_rx(i2c_slave_struct_t *i2c, uint8_t *data, size_t len) {
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
#if I2C_SLAVE_USE_RX_QUEUE
|
||||
uint8_t d = 0;
|
||||
BaseType_t res = pdTRUE;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (data) {
|
||||
res = xQueueReceive(i2c->rx_queue, &data[i], 0);
|
||||
} else {
|
||||
res = xQueueReceive(i2c->rx_queue, &d, 0);
|
||||
}
|
||||
if (res != pdTRUE) {
|
||||
log_e("Read Queue(%u) Failed", i);
|
||||
len = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (data) ? len : 0;
|
||||
#else
|
||||
size_t dlen = 0, to_read = len, so_far = 0, available = 0;
|
||||
uint8_t *rx_data = NULL;
|
||||
|
||||
vRingbufferGetInfo(i2c->rx_ring_buf, NULL, NULL, NULL, NULL, &available);
|
||||
if (available < to_read) {
|
||||
log_e("Less available than requested. %u < %u", available, len);
|
||||
to_read = available;
|
||||
}
|
||||
|
||||
while (to_read) {
|
||||
dlen = 0;
|
||||
rx_data = (uint8_t *)xRingbufferReceiveUpTo(i2c->rx_ring_buf, &dlen, 0, to_read);
|
||||
if (!rx_data) {
|
||||
log_e("Receive %u Failed", to_read);
|
||||
return so_far;
|
||||
}
|
||||
if (data) {
|
||||
memcpy(data + so_far, rx_data, dlen);
|
||||
}
|
||||
vRingbufferReturnItem(i2c->rx_ring_buf, rx_data);
|
||||
so_far += dlen;
|
||||
to_read -= dlen;
|
||||
}
|
||||
return (data) ? so_far : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void i2c_slave_task(void *pv_args) {
|
||||
i2c_slave_struct_t *i2c = (i2c_slave_struct_t *)pv_args;
|
||||
i2c_slave_queue_event_t event;
|
||||
size_t len = 0;
|
||||
bool stop = false;
|
||||
uint8_t *data = NULL;
|
||||
for (;;) {
|
||||
if (xQueueReceive(i2c->event_queue, &event, portMAX_DELAY) == pdTRUE) {
|
||||
// Write
|
||||
if (event.event == I2C_SLAVE_EVT_RX) {
|
||||
len = event.param;
|
||||
stop = event.stop;
|
||||
data = (len > 0) ? (uint8_t *)malloc(len) : NULL;
|
||||
|
||||
if (len && data == NULL) {
|
||||
log_e("Malloc (%u) Failed", len);
|
||||
}
|
||||
len = i2c_slave_read_rx(i2c, data, len);
|
||||
if (i2c->receive_callback) {
|
||||
i2c->receive_callback(i2c->num, data, len, stop, i2c->arg);
|
||||
}
|
||||
free(data);
|
||||
|
||||
// Read
|
||||
} else if (event.event == I2C_SLAVE_EVT_TX) {
|
||||
if (i2c->request_callback) {
|
||||
i2c->request_callback(i2c->num, i2c->arg);
|
||||
}
|
||||
i2c_ll_stretch_clr(i2c->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static bool i2cSlaveDetachBus(void *bus_i2c_num) {
|
||||
uint8_t num = (int)bus_i2c_num - 1;
|
||||
i2c_slave_struct_t *i2c = &_i2c_bus_array[num];
|
||||
if (i2c->scl == -1 && i2c->sda == -1) {
|
||||
return true;
|
||||
}
|
||||
esp_err_t err = i2cSlaveDeinit(num);
|
||||
if (err != ESP_OK) {
|
||||
log_e("i2cSlaveDeinit failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* SOC_I2C_SUPPORT_SLAVE */
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_I2C_SUPPORT_SLAVE
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stddef.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef void (*i2c_slave_request_cb_t)(uint8_t num, void *arg);
|
||||
typedef void (*i2c_slave_receive_cb_t)(uint8_t num, uint8_t *data, size_t len, bool stop, void *arg);
|
||||
esp_err_t i2cSlaveAttachCallbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void *arg);
|
||||
|
||||
esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len);
|
||||
esp_err_t i2cSlaveDeinit(uint8_t num);
|
||||
size_t i2cSlaveWrite(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_I2C_SUPPORT_SLAVE */
|
||||
@@ -0,0 +1,435 @@
|
||||
// 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 "esp32-hal-i2c.h"
|
||||
|
||||
#if SOC_I2C_SUPPORTED
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0)
|
||||
#include "esp32-hal.h"
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#endif
|
||||
#include "esp_attr.h"
|
||||
#include "esp_system.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/i2c_periph.h"
|
||||
#include "hal/i2c_hal.h"
|
||||
#include "hal/i2c_ll.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define I2C_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define I2C_CLOCK_SRC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if !SOC_RCC_IS_INDEPENDENT
|
||||
#define I2C_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define I2C_RCC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if SOC_I2C_SUPPORT_APB || SOC_I2C_SUPPORT_XTAL
|
||||
#include "esp_private/esp_clk.h"
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORT_RTC
|
||||
#include "clk_ctrl_os.h"
|
||||
#endif
|
||||
|
||||
typedef volatile struct {
|
||||
bool initialized;
|
||||
uint32_t frequency;
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
SemaphoreHandle_t lock;
|
||||
#endif
|
||||
int8_t scl;
|
||||
int8_t sda;
|
||||
|
||||
} i2c_bus_t;
|
||||
|
||||
static i2c_bus_t bus[SOC_I2C_NUM];
|
||||
|
||||
static bool i2cDetachBus(void *bus_i2c_num) {
|
||||
uint8_t i2c_num = (int)bus_i2c_num - 1;
|
||||
if (!bus[i2c_num].initialized) {
|
||||
return true;
|
||||
}
|
||||
esp_err_t err = i2cDeinit(i2c_num);
|
||||
if (err != ESP_OK) {
|
||||
log_e("i2cDeinit failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool i2cIsInit(uint8_t i2c_num) {
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return false;
|
||||
}
|
||||
return bus[i2c_num].initialized;
|
||||
}
|
||||
|
||||
esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) {
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (bus[i2c_num].lock == NULL) {
|
||||
bus[i2c_num].lock = xSemaphoreCreateMutex();
|
||||
if (bus[i2c_num].lock == NULL) {
|
||||
log_e("xSemaphoreCreateMutex failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
//acquire lock
|
||||
if (xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
if (bus[i2c_num].initialized) {
|
||||
log_e("bus is already initialized");
|
||||
ret = ESP_FAIL;
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if (!frequency) {
|
||||
frequency = 100000UL;
|
||||
} else if (frequency > 1000000UL) {
|
||||
frequency = 1000000UL;
|
||||
}
|
||||
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SDA, i2cDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_I2C_MASTER_SCL, i2cDetachBus);
|
||||
|
||||
if (!perimanClearPinBus(sda) || !perimanClearPinBus(scl)) {
|
||||
ret = ESP_FAIL;
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
log_i("Initializing I2C Master: sda=%d scl=%d freq=%d", sda, scl, frequency);
|
||||
|
||||
i2c_config_t conf = {};
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
conf.scl_io_num = (gpio_num_t)scl;
|
||||
conf.sda_io_num = (gpio_num_t)sda;
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.master.clk_speed = frequency;
|
||||
conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; //Any one clock source that is available for the specified frequency may be chosen
|
||||
|
||||
ret = i2c_param_config((i2c_port_t)i2c_num, &conf);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_param_config failed");
|
||||
} else {
|
||||
ret = i2c_driver_install((i2c_port_t)i2c_num, conf.mode, 0, 0, 0);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("i2c_driver_install failed");
|
||||
} else {
|
||||
bus[i2c_num].initialized = true;
|
||||
bus[i2c_num].frequency = frequency;
|
||||
bus[i2c_num].scl = scl;
|
||||
bus[i2c_num].sda = sda;
|
||||
//Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2
|
||||
i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT);
|
||||
if (!perimanSetPinBus(sda, ESP32_BUS_TYPE_I2C_MASTER_SDA, (void *)(i2c_num + 1), i2c_num, -1)
|
||||
|| !perimanSetPinBus(scl, ESP32_BUS_TYPE_I2C_MASTER_SCL, (void *)(i2c_num + 1), i2c_num, -1)) {
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock so that i2cDetachBus can execute i2cDeinit
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
i2cDetachBus((void *)(i2c_num + 1));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
init_fail:
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cDeinit(uint8_t i2c_num) {
|
||||
esp_err_t err = ESP_FAIL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
} else {
|
||||
err = i2c_driver_delete((i2c_port_t)i2c_num);
|
||||
if (err == ESP_OK) {
|
||||
bus[i2c_num].initialized = false;
|
||||
perimanClearPinBus(bus[i2c_num].scl);
|
||||
perimanClearPinBus(bus[i2c_num].sda);
|
||||
bus[i2c_num].scl = -1;
|
||||
bus[i2c_num].sda = -1;
|
||||
}
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t *buff, size_t size, uint32_t timeOutMillis) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
i2c_cmd_handle_t cmd = NULL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
goto end;
|
||||
}
|
||||
|
||||
//short implementation does not support zero size writes (example when scanning) PR in IDF?
|
||||
//ret = i2c_master_write_to_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_PERIOD_MS);
|
||||
|
||||
ret = ESP_OK;
|
||||
uint8_t cmd_buff[I2C_LINK_RECOMMENDED_SIZE(1)] = {0};
|
||||
cmd = i2c_cmd_link_create_static(cmd_buff, I2C_LINK_RECOMMENDED_SIZE(1));
|
||||
ret = i2c_master_start(cmd);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
ret = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
if (size) {
|
||||
ret = i2c_master_write(cmd, buff, size, true);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
ret = i2c_master_stop(cmd);
|
||||
if (ret != ESP_OK) {
|
||||
goto end;
|
||||
}
|
||||
ret = i2c_master_cmd_begin((i2c_port_t)i2c_num, cmd, timeOutMillis / portTICK_PERIOD_MS);
|
||||
|
||||
end:
|
||||
if (cmd != NULL) {
|
||||
i2c_cmd_link_delete_static(cmd);
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t *buff, size_t size, uint32_t timeOutMillis, size_t *readCount) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
} else {
|
||||
ret = i2c_master_read_from_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_PERIOD_MS);
|
||||
if (ret == ESP_OK) {
|
||||
*readCount = size;
|
||||
} else {
|
||||
*readCount = 0;
|
||||
}
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cWriteReadNonStop(
|
||||
uint8_t i2c_num, uint16_t address, const uint8_t *wbuff, size_t wsize, uint8_t *rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount
|
||||
) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
} else {
|
||||
ret = i2c_master_write_read_device((i2c_port_t)i2c_num, address, wbuff, wsize, rbuff, rsize, timeOutMillis / portTICK_PERIOD_MS);
|
||||
if (ret == ESP_OK) {
|
||||
*readCount = rsize;
|
||||
} else {
|
||||
*readCount = 0;
|
||||
}
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency) {
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//acquire lock
|
||||
if (bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE) {
|
||||
log_e("could not acquire lock");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
goto end;
|
||||
}
|
||||
if (bus[i2c_num].frequency == frequency) {
|
||||
ret = ESP_OK;
|
||||
goto end;
|
||||
}
|
||||
if (!frequency) {
|
||||
frequency = 100000UL;
|
||||
} else if (frequency > 1000000UL) {
|
||||
frequency = 1000000UL;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
soc_module_clk_t clk; /*!< I2C source clock */
|
||||
uint32_t clk_freq; /*!< I2C source clock frequency */
|
||||
} i2c_clk_alloc_t;
|
||||
|
||||
typedef enum {
|
||||
I2C_SCLK_DEFAULT = 0, /*!< I2C source clock not selected*/
|
||||
#if SOC_I2C_SUPPORT_APB
|
||||
I2C_SCLK_APB, /*!< I2C source clock from APB, 80M*/
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORT_XTAL
|
||||
I2C_SCLK_XTAL, /*!< I2C source clock from XTAL, 40M */
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORT_RTC
|
||||
I2C_SCLK_RTC, /*!< I2C source clock from 8M RTC, 8M */
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORT_REF_TICK
|
||||
I2C_SCLK_REF_TICK, /*!< I2C source clock from REF_TICK, 1M */
|
||||
#endif
|
||||
I2C_SCLK_MAX,
|
||||
} i2c_sclk_t;
|
||||
|
||||
// i2c clock characteristic, The order is the same as i2c_sclk_t.
|
||||
i2c_clk_alloc_t i2c_clk_alloc[I2C_SCLK_MAX] = {
|
||||
{0, 0},
|
||||
#if SOC_I2C_SUPPORT_APB
|
||||
{SOC_MOD_CLK_APB, esp_clk_apb_freq()}, /*!< I2C APB clock characteristic*/
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORT_XTAL
|
||||
{SOC_MOD_CLK_XTAL, esp_clk_xtal_freq()}, /*!< I2C XTAL characteristic*/
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORT_RTC
|
||||
{SOC_MOD_CLK_RC_FAST, periph_rtc_dig_clk8m_get_freq()}, /*!< I2C 20M RTC characteristic*/
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORT_REF_TICK
|
||||
{SOC_MOD_CLK_REF_TICK, REF_CLK_FREQ}, /*!< I2C REF_TICK characteristic*/
|
||||
#endif
|
||||
};
|
||||
|
||||
i2c_sclk_t src_clk = I2C_SCLK_DEFAULT;
|
||||
ret = ESP_OK;
|
||||
for (i2c_sclk_t clk = I2C_SCLK_DEFAULT + 1; clk < I2C_SCLK_MAX; clk++) {
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
if (clk == I2C_SCLK_RTC) { // RTC clock for s3 is inaccessible now.
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (frequency <= i2c_clk_alloc[clk].clk_freq) {
|
||||
src_clk = clk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (src_clk == I2C_SCLK_DEFAULT || src_clk == I2C_SCLK_MAX) {
|
||||
log_e("clock source could not be selected");
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
i2c_hal_context_t hal;
|
||||
hal.dev = I2C_LL_GET_HW(i2c_num);
|
||||
#if SOC_I2C_SUPPORT_RTC
|
||||
if (src_clk == I2C_SCLK_RTC) {
|
||||
periph_rtc_dig_clk8m_enable();
|
||||
}
|
||||
#endif
|
||||
I2C_CLOCK_SRC_ATOMIC() {
|
||||
i2c_hal_set_bus_timing(&(hal), frequency, i2c_clk_alloc[src_clk].clk, i2c_clk_alloc[src_clk].clk_freq);
|
||||
}
|
||||
bus[i2c_num].frequency = frequency;
|
||||
//Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2
|
||||
i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT);
|
||||
}
|
||||
|
||||
end:
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
//release lock
|
||||
xSemaphoreGive(bus[i2c_num].lock);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t *frequency) {
|
||||
if (i2c_num >= SOC_I2C_NUM) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!bus[i2c_num].initialized) {
|
||||
log_e("bus is not initialized");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
*frequency = bus[i2c_num].frequency;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0) */
|
||||
#endif /* SOC_I2C_SUPPORTED */
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.
|
||||
// modified Nov 2017 by Chuck Todd <StickBreaker> to support Interrupt Driven I/O
|
||||
// modified Nov 2021 by Hristo Gochkov <Me-No-Dev> to support ESP-IDF API
|
||||
|
||||
#ifndef _ESP32_HAL_I2C_H_
|
||||
#define _ESP32_HAL_I2C_H_
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_I2C_SUPPORTED
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed);
|
||||
esp_err_t i2cDeinit(uint8_t i2c_num);
|
||||
esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency);
|
||||
esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t *frequency);
|
||||
esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t *buff, size_t size, uint32_t timeOutMillis);
|
||||
esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t *buff, size_t size, uint32_t timeOutMillis, size_t *readCount);
|
||||
esp_err_t i2cWriteReadNonStop(
|
||||
uint8_t i2c_num, uint16_t address, const uint8_t *wbuff, size_t wsize, uint8_t *rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount
|
||||
);
|
||||
bool i2cIsInit(uint8_t i2c_num);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
|
||||
void *i2cBusHandle(uint8_t i2c_num);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_I2C_SUPPORTED */
|
||||
#endif /* _ESP32_HAL_I2C_H_ */
|
||||
@@ -0,0 +1,808 @@
|
||||
// Copyright 2015-2023 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 "soc/soc_caps.h"
|
||||
|
||||
#if SOC_LEDC_SUPPORTED
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-ledc.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "hal/ledc_ll.h"
|
||||
#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#ifdef SOC_LEDC_SUPPORT_HS_MODE
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1)
|
||||
#else
|
||||
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
|
||||
#endif
|
||||
|
||||
//Use XTAL clock if possible to avoid timer frequency error when setting APB clock < 80 Mhz
|
||||
//Need to be fixed in ESP-IDF
|
||||
#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
|
||||
#define LEDC_DEFAULT_CLK LEDC_USE_XTAL_CLK
|
||||
#else
|
||||
#define LEDC_DEFAULT_CLK LEDC_AUTO_CLK
|
||||
#endif
|
||||
|
||||
#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDTH
|
||||
|
||||
typedef struct {
|
||||
int used_channels : LEDC_CHANNELS; // Used channels as a bits
|
||||
} ledc_periph_t;
|
||||
|
||||
ledc_periph_t ledc_handle = {0};
|
||||
|
||||
// Helper function to find a timer with matching frequency and resolution
|
||||
static bool find_matching_timer(uint8_t speed_mode, uint32_t freq, uint8_t resolution, uint8_t *timer_num) {
|
||||
log_d("Searching for timer with freq=%u, resolution=%u", freq, resolution);
|
||||
// Check all channels to find one with matching frequency and resolution
|
||||
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
|
||||
if (!perimanPinIsValid(i)) {
|
||||
continue;
|
||||
}
|
||||
peripheral_bus_type_t type = perimanGetPinBusType(i);
|
||||
if (type == ESP32_BUS_TYPE_LEDC) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->freq_hz == freq && bus->channel_resolution == resolution) {
|
||||
log_d("Found matching timer %u for freq=%u, resolution=%u", bus->timer_num, freq, resolution);
|
||||
*timer_num = bus->timer_num;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
log_d("No matching timer found for freq=%u, resolution=%u", freq, resolution);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper function to find an unused timer
|
||||
static bool find_free_timer(uint8_t speed_mode, uint8_t *timer_num) {
|
||||
// Check which timers are in use
|
||||
uint8_t used_timers = 0;
|
||||
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
|
||||
if (!perimanPinIsValid(i)) {
|
||||
continue;
|
||||
}
|
||||
peripheral_bus_type_t type = perimanGetPinBusType(i);
|
||||
if (type == ESP32_BUS_TYPE_LEDC) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode) {
|
||||
log_d("Timer %u is in use by channel %u", bus->timer_num, bus->channel);
|
||||
used_timers |= (1 << bus->timer_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SOC_LEDC_TIMER_NUM
|
||||
#define SOC_LEDC_TIMER_NUM 4
|
||||
#endif
|
||||
// Find first unused timer
|
||||
for (uint8_t i = 0; i < SOC_LEDC_TIMER_NUM; i++) {
|
||||
if (!(used_timers & (1 << i))) {
|
||||
log_d("Found free timer %u", i);
|
||||
*timer_num = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
log_e("No free timers available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper function to remove a channel from a timer and clear timer if no channels are using it
|
||||
static void remove_channel_from_timer(uint8_t speed_mode, uint8_t timer_num, uint8_t channel) {
|
||||
log_d("Removing channel %u from timer %u in speed_mode %u", channel, timer_num, speed_mode);
|
||||
|
||||
// Check if any other channels are using this timer
|
||||
bool timer_in_use = false;
|
||||
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
|
||||
if (!perimanPinIsValid(i)) {
|
||||
continue;
|
||||
}
|
||||
peripheral_bus_type_t type = perimanGetPinBusType(i);
|
||||
if (type == ESP32_BUS_TYPE_LEDC) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->timer_num == timer_num && bus->channel != channel) {
|
||||
log_d("Timer %u is still in use by channel %u", timer_num, bus->channel);
|
||||
timer_in_use = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!timer_in_use) {
|
||||
log_d("No other channels using timer %u, deconfiguring timer", timer_num);
|
||||
// Stop the timer
|
||||
ledc_timer_pause(speed_mode, timer_num);
|
||||
// Deconfigure the timer
|
||||
ledc_timer_config_t ledc_timer;
|
||||
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
|
||||
ledc_timer.speed_mode = speed_mode;
|
||||
ledc_timer.timer_num = timer_num;
|
||||
ledc_timer.deconfigure = true;
|
||||
ledc_timer_config(&ledc_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static bool fade_initialized = false;
|
||||
|
||||
static ledc_clk_cfg_t clock_source = LEDC_DEFAULT_CLK;
|
||||
|
||||
ledc_clk_cfg_t ledcGetClockSource(void) {
|
||||
return clock_source;
|
||||
}
|
||||
|
||||
bool ledcSetClockSource(ledc_clk_cfg_t source) {
|
||||
if (ledc_handle.used_channels) {
|
||||
log_e("Cannot change LEDC clock source! LEDC channels in use.");
|
||||
return false;
|
||||
}
|
||||
clock_source = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ledcDetachBus(void *bus) {
|
||||
ledc_channel_handle_t *handle = (ledc_channel_handle_t *)bus;
|
||||
bool channel_found = false;
|
||||
// Check if more pins are attached to the same ledc channel
|
||||
for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) {
|
||||
if (!perimanPinIsValid(i) || i == handle->pin) {
|
||||
continue; //invalid pin or same pin
|
||||
}
|
||||
peripheral_bus_type_t type = perimanGetPinBusType(i);
|
||||
if (type == ESP32_BUS_TYPE_LEDC) {
|
||||
ledc_channel_handle_t *bus_check = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus_check->channel == handle->channel) {
|
||||
channel_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pinMatrixOutDetach(handle->pin, false, false);
|
||||
if (!channel_found) {
|
||||
uint8_t group = (handle->channel / SOC_LEDC_CHANNEL_NUM);
|
||||
remove_channel_from_timer(group, handle->timer_num, handle->channel % SOC_LEDC_CHANNEL_NUM);
|
||||
ledc_handle.used_channels &= ~(1UL << handle->channel);
|
||||
}
|
||||
free(handle);
|
||||
if (ledc_handle.used_channels == 0) {
|
||||
ledc_fade_func_uninstall();
|
||||
fade_initialized = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel) {
|
||||
if (channel >= LEDC_CHANNELS) {
|
||||
log_e("Channel %u is not available (maximum %u)!", channel, LEDC_CHANNELS);
|
||||
return false;
|
||||
}
|
||||
if (freq == 0) {
|
||||
log_e("LEDC pin %u - frequency can't be zero.", pin);
|
||||
return false;
|
||||
}
|
||||
if (resolution == 0 || resolution > LEDC_MAX_BIT_WIDTH) {
|
||||
log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_LEDC, ledcDetachBus);
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
log_e("Pin %u is already attached to LEDC (channel %u, resolution %u)", pin, bus->channel, bus->channel_resolution);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!perimanClearPinBus(pin)) {
|
||||
log_e("Pin %u is already attached to another bus and failed to detach", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM);
|
||||
uint8_t timer = 0;
|
||||
bool channel_used = ledc_handle.used_channels & (1UL << channel);
|
||||
|
||||
if (channel_used) {
|
||||
log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel);
|
||||
if (ledc_set_pin(pin, group, channel % SOC_LEDC_CHANNEL_NUM) != ESP_OK) {
|
||||
log_e("Attaching pin to already used channel failed!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Find a timer with matching frequency and resolution, or a free timer
|
||||
if (!find_matching_timer(group, freq, resolution, &timer)) {
|
||||
if (!find_free_timer(group, &timer)) {
|
||||
log_w("No free timers available for speed mode %u", group);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure the timer if we're using a new one
|
||||
ledc_timer_config_t ledc_timer;
|
||||
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
|
||||
ledc_timer.speed_mode = group;
|
||||
ledc_timer.timer_num = timer;
|
||||
ledc_timer.duty_resolution = resolution;
|
||||
ledc_timer.freq_hz = freq;
|
||||
ledc_timer.clk_cfg = clock_source;
|
||||
|
||||
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
|
||||
log_e("ledc setup failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t duty = ledc_get_duty(group, (channel % SOC_LEDC_CHANNEL_NUM));
|
||||
|
||||
ledc_channel_config_t ledc_channel;
|
||||
memset((void *)&ledc_channel, 0, sizeof(ledc_channel_config_t));
|
||||
ledc_channel.speed_mode = group;
|
||||
ledc_channel.channel = (channel % SOC_LEDC_CHANNEL_NUM);
|
||||
ledc_channel.timer_sel = timer;
|
||||
ledc_channel.intr_type = LEDC_INTR_DISABLE;
|
||||
ledc_channel.gpio_num = pin;
|
||||
ledc_channel.duty = duty;
|
||||
ledc_channel.hpoint = 0;
|
||||
|
||||
ledc_channel_config(&ledc_channel);
|
||||
}
|
||||
|
||||
ledc_channel_handle_t *handle = (ledc_channel_handle_t *)malloc(sizeof(ledc_channel_handle_t));
|
||||
handle->pin = pin;
|
||||
handle->channel = channel;
|
||||
handle->timer_num = timer;
|
||||
handle->freq_hz = freq;
|
||||
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
|
||||
handle->lock = NULL;
|
||||
#endif
|
||||
|
||||
//get resolution of selected channel when used
|
||||
if (channel_used) {
|
||||
uint32_t channel_resolution = 0;
|
||||
ledc_ll_get_duty_resolution(LEDC_LL_GET_HW(), group, timer, &channel_resolution);
|
||||
log_i("Channel %u frequency: %u, resolution: %u", channel, ledc_get_freq(group, timer), channel_resolution);
|
||||
handle->channel_resolution = (uint8_t)channel_resolution;
|
||||
} else {
|
||||
handle->channel_resolution = resolution;
|
||||
ledc_handle.used_channels |= 1UL << channel;
|
||||
}
|
||||
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_LEDC, (void *)handle, channel, timer)) {
|
||||
ledcDetachBus((void *)handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_i("LEDC attached to pin %u (channel %u, resolution %u)", pin, channel, resolution);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution) {
|
||||
int free_channel = ~ledc_handle.used_channels & (ledc_handle.used_channels + 1);
|
||||
if (free_channel == 0) {
|
||||
log_e("No more LEDC channels available! (maximum is %u channels)", LEDC_CHANNELS);
|
||||
return false;
|
||||
}
|
||||
uint8_t channel = __builtin_ctz(free_channel); // Convert the free_channel bit to channel number
|
||||
|
||||
// Try the first available channel
|
||||
if (ledcAttachChannel(pin, freq, resolution, channel)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef SOC_LEDC_SUPPORT_HS_MODE
|
||||
// If first attempt failed and HS mode is supported, try to find a free channel in group 1
|
||||
if ((channel / SOC_LEDC_CHANNEL_NUM) == 0) { // First attempt was in group 0
|
||||
log_d("LEDC: Group 0 channel %u failed, trying to find a free channel in group 1", channel);
|
||||
// Find free channels specifically in group 1
|
||||
uint32_t group1_mask = ((1UL << SOC_LEDC_CHANNEL_NUM) - 1) << SOC_LEDC_CHANNEL_NUM;
|
||||
int group1_free_channel = (~ledc_handle.used_channels) & group1_mask;
|
||||
if (group1_free_channel != 0) {
|
||||
uint8_t group1_channel = __builtin_ctz(group1_free_channel);
|
||||
if (ledcAttachChannel(pin, freq, resolution, group1_channel)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
log_e(
|
||||
"No free timers available for freq=%u, resolution=%u. To attach a new channel, use the same frequency and resolution as an already attached channel to "
|
||||
"share its timer.",
|
||||
freq, resolution
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ledcWrite(uint8_t pin, uint32_t duty) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
|
||||
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
|
||||
|
||||
//Fixing if all bits in resolution is set = LEDC FULL ON
|
||||
uint32_t max_duty = (1 << bus->channel_resolution) - 1;
|
||||
|
||||
if ((duty == max_duty) && (max_duty != 1)) {
|
||||
duty = max_duty + 1;
|
||||
}
|
||||
|
||||
if (ledc_set_duty(group, channel, duty) != ESP_OK) {
|
||||
log_e("ledc_set_duty failed");
|
||||
return false;
|
||||
}
|
||||
if (ledc_update_duty(group, channel) != ESP_OK) {
|
||||
log_e("ledc_update_duty failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ledcWriteChannel(uint8_t channel, uint32_t duty) {
|
||||
//check if channel is valid and used
|
||||
if (channel >= LEDC_CHANNELS || !(ledc_handle.used_channels & (1UL << channel))) {
|
||||
log_e("Channel %u is not available (maximum %u) or not used!", channel, LEDC_CHANNELS);
|
||||
return false;
|
||||
}
|
||||
uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM);
|
||||
ledc_timer_t timer;
|
||||
|
||||
// Get the actual timer being used by this channel
|
||||
ledc_ll_get_channel_timer(LEDC_LL_GET_HW(), group, (channel % SOC_LEDC_CHANNEL_NUM), &timer);
|
||||
|
||||
//Fixing if all bits in resolution is set = LEDC FULL ON
|
||||
uint32_t resolution = 0;
|
||||
ledc_ll_get_duty_resolution(LEDC_LL_GET_HW(), group, timer, &resolution);
|
||||
|
||||
uint32_t max_duty = (1 << resolution) - 1;
|
||||
|
||||
if ((duty == max_duty) && (max_duty != 1)) {
|
||||
duty = max_duty + 1;
|
||||
}
|
||||
|
||||
if (ledc_set_duty(group, channel, duty) != ESP_OK) {
|
||||
log_e("ledc_set_duty failed");
|
||||
return false;
|
||||
}
|
||||
if (ledc_update_duty(group, channel) != ESP_OK) {
|
||||
log_e("ledc_update_duty failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t ledcRead(uint8_t pin) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
|
||||
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
|
||||
return ledc_get_duty(group, channel);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ledcReadFreq(uint8_t pin) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
if (!ledcRead(pin)) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM);
|
||||
return ledc_get_freq(group, bus->timer_num);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ledcWriteTone(uint8_t pin, uint32_t freq) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
|
||||
if (!freq) {
|
||||
ledcWrite(pin, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM);
|
||||
|
||||
ledc_timer_config_t ledc_timer;
|
||||
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
|
||||
ledc_timer.speed_mode = group;
|
||||
ledc_timer.timer_num = bus->timer_num;
|
||||
ledc_timer.duty_resolution = 10;
|
||||
ledc_timer.freq_hz = freq;
|
||||
ledc_timer.clk_cfg = clock_source;
|
||||
|
||||
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
|
||||
log_e("ledcWriteTone configuration failed!");
|
||||
return 0;
|
||||
}
|
||||
bus->channel_resolution = 10;
|
||||
|
||||
uint32_t res_freq = ledc_get_freq(group, bus->timer_num);
|
||||
ledcWrite(pin, 0x1FF);
|
||||
return res_freq;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave) {
|
||||
const uint16_t noteFrequencyBase[12] = {// C C# D Eb E F F# G G# A Bb B
|
||||
4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902
|
||||
};
|
||||
|
||||
if (octave > 8 || note >= NOTE_MAX) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t noteFreq = (uint32_t)noteFrequencyBase[note] / (uint32_t)(1 << (8 - octave));
|
||||
return ledcWriteTone(pin, noteFreq);
|
||||
}
|
||||
|
||||
bool ledcDetach(uint8_t pin) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
// will call ledcDetachBus
|
||||
return perimanClearPinBus(pin);
|
||||
} else {
|
||||
log_e("pin %u is not attached to LEDC", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
if (freq == 0) {
|
||||
log_e("LEDC pin %u - frequency can't be zero.", pin);
|
||||
return 0;
|
||||
}
|
||||
if (resolution == 0 || resolution > LEDC_MAX_BIT_WIDTH) {
|
||||
log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH);
|
||||
return 0;
|
||||
}
|
||||
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM);
|
||||
|
||||
ledc_timer_config_t ledc_timer;
|
||||
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
|
||||
ledc_timer.speed_mode = group;
|
||||
ledc_timer.timer_num = bus->timer_num;
|
||||
ledc_timer.duty_resolution = resolution;
|
||||
ledc_timer.freq_hz = freq;
|
||||
ledc_timer.clk_cfg = clock_source;
|
||||
|
||||
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
|
||||
log_e("ledcChangeFrequency failed!");
|
||||
return 0;
|
||||
}
|
||||
bus->channel_resolution = resolution;
|
||||
return ledc_get_freq(group, bus->timer_num);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ledcOutputInvert(uint8_t pin, bool out_invert) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
gpio_set_level(pin, out_invert);
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT_PAD_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0);
|
||||
#else
|
||||
#ifdef SOC_LEDC_SUPPORT_HS_MODE
|
||||
esp_rom_gpio_connect_out_signal(
|
||||
pin, ((bus->channel / SOC_LEDC_CHANNEL_NUM == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0
|
||||
);
|
||||
#else
|
||||
esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0);
|
||||
#endif
|
||||
#endif // ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool ledcFnWrapper(const ledc_cb_param_t *param, void *user_arg) {
|
||||
if (param->event == LEDC_FADE_END_EVT) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)user_arg;
|
||||
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
|
||||
portBASE_TYPE xTaskWoken = 0;
|
||||
xSemaphoreGiveFromISR(bus->lock, &xTaskWoken);
|
||||
#endif
|
||||
if (bus->fn) {
|
||||
if (bus->arg) {
|
||||
((voidFuncPtrArg)bus->fn)(bus->arg);
|
||||
} else {
|
||||
bus->fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ledcFadeConfig(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
|
||||
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (bus->lock == NULL) {
|
||||
bus->lock = xSemaphoreCreateBinary();
|
||||
if (bus->lock == NULL) {
|
||||
log_e("xSemaphoreCreateBinary failed");
|
||||
return false;
|
||||
}
|
||||
xSemaphoreGive(bus->lock);
|
||||
}
|
||||
//acquire lock
|
||||
if (xSemaphoreTake(bus->lock, 0) != pdTRUE) {
|
||||
log_e("LEDC Fade is still running on pin %u! SoC does not support stopping fade.", pin);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
|
||||
|
||||
// Initialize fade service.
|
||||
if (!fade_initialized) {
|
||||
ledc_fade_func_install(0);
|
||||
fade_initialized = true;
|
||||
}
|
||||
|
||||
bus->fn = (voidFuncPtr)userFunc;
|
||||
bus->arg = arg;
|
||||
|
||||
ledc_cbs_t callbacks = {.fade_cb = ledcFnWrapper};
|
||||
ledc_cb_register(group, channel, &callbacks, (void *)bus);
|
||||
|
||||
//Fixing if all bits in resolution is set = LEDC FULL ON
|
||||
uint32_t max_duty = (1 << bus->channel_resolution) - 1;
|
||||
|
||||
if ((target_duty == max_duty) && (max_duty != 1)) {
|
||||
target_duty = max_duty + 1;
|
||||
} else if ((start_duty == max_duty) && (max_duty != 1)) {
|
||||
start_duty = max_duty + 1;
|
||||
}
|
||||
|
||||
#if SOC_LEDC_SUPPORT_FADE_STOP
|
||||
ledc_fade_stop(group, channel);
|
||||
#endif
|
||||
|
||||
if (ledc_set_duty_and_update(group, channel, start_duty, 0) != ESP_OK) {
|
||||
log_e("ledc_set_duty_and_update failed");
|
||||
return false;
|
||||
}
|
||||
// Wait for LEDCs next PWM cycle to update duty (~ 1-2 ms)
|
||||
while (ledc_get_duty(group, channel) != start_duty);
|
||||
|
||||
if (ledc_set_fade_time_and_start(group, channel, target_duty, max_fade_time_ms, LEDC_FADE_NO_WAIT) != ESP_OK) {
|
||||
log_e("ledc_set_fade_time_and_start failed");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
log_e("Pin %u is not attached to LEDC. Call ledcAttach first!", pin);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms) {
|
||||
return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, NULL, NULL);
|
||||
}
|
||||
|
||||
bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, voidFuncPtr userFunc) {
|
||||
return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, (voidFuncPtrArg)userFunc, NULL);
|
||||
}
|
||||
|
||||
bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
|
||||
return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg);
|
||||
}
|
||||
|
||||
#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
|
||||
// Default gamma factor for gamma correction (common value for LEDs)
|
||||
static float ledcGammaFactor = 2.8;
|
||||
// Gamma correction LUT support
|
||||
static const float *ledcGammaLUT = NULL;
|
||||
static uint16_t ledcGammaLUTSize = 0;
|
||||
// Global variable to store current resolution for gamma callback
|
||||
static uint8_t ledcGammaResolution = 13;
|
||||
|
||||
bool ledcSetGammaTable(const float *gamma_table, uint16_t size) {
|
||||
if (gamma_table == NULL || size == 0) {
|
||||
log_e("Invalid gamma table or size");
|
||||
return false;
|
||||
}
|
||||
ledcGammaLUT = gamma_table;
|
||||
ledcGammaLUTSize = size;
|
||||
log_i("Custom gamma LUT set with %u entries", size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ledcClearGammaTable(void) {
|
||||
ledcGammaLUT = NULL;
|
||||
ledcGammaLUTSize = 0;
|
||||
log_i("Gamma LUT cleared, using mathematical calculation");
|
||||
}
|
||||
|
||||
void ledcSetGammaFactor(float factor) {
|
||||
ledcGammaFactor = factor;
|
||||
}
|
||||
|
||||
// Gamma correction calculator function
|
||||
static uint32_t ledcGammaCorrection(uint32_t duty) {
|
||||
if (duty == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t max_duty = (1U << ledcGammaResolution) - 1;
|
||||
if (duty >= (1U << ledcGammaResolution)) {
|
||||
return max_duty;
|
||||
}
|
||||
|
||||
// Use LUT if provided, otherwise use mathematical calculation
|
||||
if (ledcGammaLUT != NULL && ledcGammaLUTSize > 0) {
|
||||
// LUT-based gamma correction
|
||||
uint32_t lut_index = (duty * (ledcGammaLUTSize - 1)) / max_duty;
|
||||
if (lut_index >= ledcGammaLUTSize) {
|
||||
lut_index = ledcGammaLUTSize - 1;
|
||||
}
|
||||
|
||||
float corrected_normalized = ledcGammaLUT[lut_index];
|
||||
return (uint32_t)(corrected_normalized * max_duty);
|
||||
} else {
|
||||
// Mathematical gamma correction
|
||||
double normalized = (double)duty / (1U << ledcGammaResolution);
|
||||
double corrected = pow(normalized, ledcGammaFactor);
|
||||
return (uint32_t)(corrected * (1U << ledcGammaResolution));
|
||||
}
|
||||
}
|
||||
|
||||
static bool ledcFadeGammaConfig(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) {
|
||||
|
||||
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
if (bus->lock == NULL) {
|
||||
bus->lock = xSemaphoreCreateBinary();
|
||||
if (bus->lock == NULL) {
|
||||
log_e("xSemaphoreCreateBinary failed");
|
||||
return false;
|
||||
}
|
||||
xSemaphoreGive(bus->lock);
|
||||
}
|
||||
//acquire lock
|
||||
if (xSemaphoreTake(bus->lock, 0) != pdTRUE) {
|
||||
log_e("LEDC Fade is still running on pin %u! SoC does not support stopping fade.", pin);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM);
|
||||
|
||||
// Initialize fade service.
|
||||
if (!fade_initialized) {
|
||||
ledc_fade_func_install(0);
|
||||
fade_initialized = true;
|
||||
}
|
||||
|
||||
bus->fn = (voidFuncPtr)userFunc;
|
||||
bus->arg = arg;
|
||||
|
||||
ledc_cbs_t callbacks = {.fade_cb = ledcFnWrapper};
|
||||
ledc_cb_register(group, channel, &callbacks, (void *)bus);
|
||||
|
||||
// Prepare gamma curve fade parameters
|
||||
ledc_fade_param_config_t fade_params[SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX];
|
||||
uint32_t actual_fade_ranges = 0;
|
||||
|
||||
// Use a moderate number of linear segments for smooth gamma curve
|
||||
const uint32_t linear_fade_segments = 12;
|
||||
|
||||
// Set the global resolution for gamma correction
|
||||
ledcGammaResolution = bus->channel_resolution;
|
||||
|
||||
// Fill multi-fade parameter list using ESP-IDF API
|
||||
esp_err_t err = ledc_fill_multi_fade_param_list(
|
||||
group, channel, start_duty, target_duty, linear_fade_segments, max_fade_time_ms, ledcGammaCorrection, SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX, fade_params,
|
||||
&actual_fade_ranges
|
||||
);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
log_e("ledc_fill_multi_fade_param_list failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply the gamma-corrected start duty
|
||||
uint32_t gamma_start_duty = ledcGammaCorrection(start_duty);
|
||||
|
||||
// Set multi-fade parameters
|
||||
err = ledc_set_multi_fade(group, channel, gamma_start_duty, fade_params, actual_fade_ranges);
|
||||
if (err != ESP_OK) {
|
||||
log_e("ledc_set_multi_fade failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start the gamma curve fade
|
||||
err = ledc_fade_start(group, channel, LEDC_FADE_NO_WAIT);
|
||||
if (err != ESP_OK) {
|
||||
log_e("ledc_fade_start failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
log_d("Gamma curve fade started on pin %u: %u -> %u over %dms", pin, start_duty, target_duty, max_fade_time_ms);
|
||||
|
||||
} else {
|
||||
log_e("Pin %u is not attached to LEDC. Call ledcAttach first!", pin);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms) {
|
||||
return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, NULL, NULL);
|
||||
}
|
||||
|
||||
bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, voidFuncPtr userFunc) {
|
||||
return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, (voidFuncPtrArg)userFunc, NULL);
|
||||
}
|
||||
|
||||
bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) {
|
||||
return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg);
|
||||
}
|
||||
|
||||
#endif /* SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED */
|
||||
|
||||
static uint8_t analog_resolution = 8;
|
||||
static int analog_frequency = 1000;
|
||||
void analogWrite(uint8_t pin, int value) {
|
||||
// Use ledc hardware for internal pins
|
||||
if (pin < SOC_GPIO_PIN_COUNT) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus == NULL && perimanClearPinBus(pin)) {
|
||||
if (ledcAttach(pin, analog_frequency, analog_resolution) == 0) {
|
||||
log_e("analogWrite setup failed (freq = %u, resolution = %u). Try setting different resolution or frequency");
|
||||
return;
|
||||
}
|
||||
}
|
||||
ledcWrite(pin, value);
|
||||
}
|
||||
}
|
||||
|
||||
void analogWriteFrequency(uint8_t pin, uint32_t freq) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) { // if pin is attached to LEDC change frequency, otherwise update the global frequency
|
||||
if (ledcChangeFrequency(pin, freq, analog_resolution) == 0) {
|
||||
log_e("analogWrite frequency cant be set due to selected resolution! Try to adjust resolution first");
|
||||
return;
|
||||
}
|
||||
}
|
||||
analog_frequency = freq;
|
||||
}
|
||||
|
||||
void analogWriteResolution(uint8_t pin, uint8_t resolution) {
|
||||
ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
|
||||
if (bus != NULL) { // if pin is attached to LEDC change resolution, otherwise update the global resolution
|
||||
if (ledcChangeFrequency(pin, analog_frequency, resolution) == 0) {
|
||||
log_e("analogWrite resolution cant be set due to selected frequency! Try to adjust frequency first");
|
||||
return;
|
||||
}
|
||||
}
|
||||
analog_resolution = resolution;
|
||||
}
|
||||
|
||||
#endif /* SOC_LEDC_SUPPORTED */
|
||||
@@ -0,0 +1,319 @@
|
||||
// Copyright 2015-2023 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_LEDC_H_
|
||||
#define _ESP32_HAL_LEDC_H_
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_LEDC_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "hal/ledc_types.h"
|
||||
|
||||
typedef enum {
|
||||
NOTE_C,
|
||||
NOTE_Cs,
|
||||
NOTE_D,
|
||||
NOTE_Eb,
|
||||
NOTE_E,
|
||||
NOTE_F,
|
||||
NOTE_Fs,
|
||||
NOTE_G,
|
||||
NOTE_Gs,
|
||||
NOTE_A,
|
||||
NOTE_Bb,
|
||||
NOTE_B,
|
||||
NOTE_MAX
|
||||
} note_t;
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void *);
|
||||
|
||||
typedef struct {
|
||||
uint8_t pin; // Pin assigned to channel
|
||||
uint8_t channel; // Channel number
|
||||
uint8_t channel_resolution; // Resolution of channel
|
||||
uint8_t timer_num; // Timer number used by this channel
|
||||
uint32_t freq_hz; // Frequency configured for this channel
|
||||
voidFuncPtr fn;
|
||||
void *arg;
|
||||
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
|
||||
SemaphoreHandle_t lock; //xSemaphoreCreateBinary
|
||||
#endif
|
||||
} ledc_channel_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Get the LEDC clock source.
|
||||
*
|
||||
* @return LEDC clock source.
|
||||
*/
|
||||
ledc_clk_cfg_t ledcGetClockSource(void);
|
||||
|
||||
/**
|
||||
* @brief Set the LEDC clock source.
|
||||
*
|
||||
* @param source LEDC clock source to set.
|
||||
*
|
||||
* @return true if LEDC clock source was successfully set, false otherwise.
|
||||
*/
|
||||
bool ledcSetClockSource(ledc_clk_cfg_t source);
|
||||
|
||||
/**
|
||||
* @brief Attach a pin to the LEDC driver, with a given frequency and resolution.
|
||||
* Channel is automatically assigned.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param freq frequency of PWM signal
|
||||
* @param resolution resolution for LEDC pin
|
||||
*
|
||||
* @return true if configuration is successful and pin was successfully attached, false otherwise.
|
||||
*/
|
||||
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
|
||||
|
||||
/**
|
||||
* @brief Attach a pin to the LEDC driver, with a given frequency, resolution and channel.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param freq frequency of PWM signal
|
||||
* @param resolution resolution for LEDC pin
|
||||
* @param channel LEDC channel to attach to
|
||||
*
|
||||
* @return true if configuration is successful and pin was successfully attached, false otherwise.
|
||||
*/
|
||||
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
|
||||
|
||||
/**
|
||||
* @brief Set the duty cycle of a given pin.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param duty duty cycle to set
|
||||
*
|
||||
* @return true if duty cycle was successfully set, false otherwise.
|
||||
*/
|
||||
bool ledcWrite(uint8_t pin, uint32_t duty);
|
||||
|
||||
/**
|
||||
* @brief Set the duty cycle of a given channel.
|
||||
*
|
||||
* @param channel LEDC channel
|
||||
* @param duty duty cycle to set
|
||||
*
|
||||
* @return true if duty cycle was successfully set, false otherwise.
|
||||
*/
|
||||
bool ledcWriteChannel(uint8_t channel, uint32_t duty);
|
||||
|
||||
/**
|
||||
* @brief Sets the duty to 50 % PWM tone on selected frequency.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param freq select frequency of pwm signal. If frequency is 0, duty will be set to 0.
|
||||
*
|
||||
* @return frequency if tone was successfully set.
|
||||
* If ``0`` is returned, error occurs and LEDC pin was not configured.
|
||||
*/
|
||||
uint32_t ledcWriteTone(uint8_t pin, uint32_t freq);
|
||||
|
||||
/**
|
||||
* @brief Sets the LEDC pin to specific note.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param note select note to be set (NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B).
|
||||
* @param octave select octave for note.
|
||||
*
|
||||
* @return frequency if note was successfully set.
|
||||
* If ``0`` is returned, error occurs and LEDC pin was not configured.
|
||||
*/
|
||||
uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave);
|
||||
|
||||
/**
|
||||
* @brief Read the duty cycle of a given LEDC pin.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
*
|
||||
* @return duty cycle of selected LEDC pin.
|
||||
*/
|
||||
uint32_t ledcRead(uint8_t pin);
|
||||
|
||||
/**
|
||||
* @brief Read the frequency of a given LEDC pin.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
*
|
||||
* @return frequency of selected LEDC pin.
|
||||
*/
|
||||
uint32_t ledcReadFreq(uint8_t pin);
|
||||
|
||||
/**
|
||||
* @brief Detach a pin from the LEDC driver.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
*
|
||||
* @return true if pin was successfully detached, false otherwise.
|
||||
*/
|
||||
bool ledcDetach(uint8_t pin);
|
||||
|
||||
/**
|
||||
* @brief Change the frequency and resolution of a given LEDC pin.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param freq frequency of PWM signal
|
||||
* @param resolution resolution for LEDC pin
|
||||
*
|
||||
* @return frequency configured for the LEDC channel.
|
||||
* If ``0`` is returned, error occurs and LEDC pin was not configured.
|
||||
*/
|
||||
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution);
|
||||
|
||||
/**
|
||||
* @brief Sets inverting of the output signal for a given LEDC pin.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param out_invert select, if output should be inverted (true = inverting output).
|
||||
*
|
||||
* @return true if output inverting was successfully set, false otherwise.
|
||||
*/
|
||||
bool ledcOutputInvert(uint8_t pin, bool out_invert);
|
||||
|
||||
//Fade functions
|
||||
/**
|
||||
* @brief Setup and start a fade on a given LEDC pin.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param start_duty initial duty cycle of the fade
|
||||
* @param target_duty target duty cycle of the fade
|
||||
* @param max_fade_time_ms maximum fade time in milliseconds
|
||||
*
|
||||
* @return true if fade was successfully set and started, false otherwise.
|
||||
*/
|
||||
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
|
||||
|
||||
/**
|
||||
* @brief Setup and start a fade on a given LEDC pin with a callback function.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param start_duty initial duty cycle of the fade
|
||||
* @param target_duty target duty cycle of the fade
|
||||
* @param max_fade_time_ms maximum fade time in milliseconds
|
||||
* @param userFunc callback function to be called after fade is finished
|
||||
*
|
||||
* @return true if fade was successfully set and started, false otherwise.
|
||||
*/
|
||||
bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void));
|
||||
|
||||
/**
|
||||
* @brief Setup and start a fade on a given LEDC pin with a callback function and argument.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param start_duty initial duty cycle of the fade
|
||||
* @param target_duty target duty cycle of the fade
|
||||
* @param max_fade_time_ms maximum fade time in milliseconds
|
||||
* @param userFunc callback function to be called after fade is finished
|
||||
* @param arg argument to be passed to the callback function
|
||||
*
|
||||
* @return true if fade was successfully set and started, false otherwise.
|
||||
*/
|
||||
bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg);
|
||||
|
||||
//Gamma Curve Fade functions - only available on supported chips
|
||||
#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
|
||||
|
||||
/**
|
||||
* @brief Set a custom gamma correction lookup table for gamma curve fading.
|
||||
* The LUT should contain normalized values (0.0 to 1.0) representing
|
||||
* the gamma-corrected brightness curve.
|
||||
*
|
||||
* @param gamma_table Pointer to array of float values (0.0 to 1.0)
|
||||
* @param size Number of entries in the lookup table
|
||||
*
|
||||
* @return true if gamma table was successfully set, false otherwise.
|
||||
*
|
||||
* @note The LUT array must remain valid for as long as gamma fading is used.
|
||||
* Larger tables provide smoother transitions but use more memory.
|
||||
*/
|
||||
bool ledcSetGammaTable(const float *gamma_table, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief Clear the current gamma correction lookup table.
|
||||
* After calling this, gamma correction will use mathematical
|
||||
* calculation with the default gamma factor (2.8).
|
||||
*/
|
||||
void ledcClearGammaTable(void);
|
||||
|
||||
/**
|
||||
* @brief Set the gamma factor for gamma correction.
|
||||
*
|
||||
* @param factor Gamma factor to use for gamma correction.
|
||||
*/
|
||||
void ledcSetGammaFactor(float factor);
|
||||
|
||||
/**
|
||||
* @brief Setup and start a gamma curve fade on a given LEDC pin.
|
||||
* Gamma correction makes LED brightness changes appear more gradual to human eyes.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param start_duty initial duty cycle of the fade
|
||||
* @param target_duty target duty cycle of the fade
|
||||
* @param max_fade_time_ms maximum fade time in milliseconds
|
||||
*
|
||||
* @return true if gamma fade was successfully set and started, false otherwise.
|
||||
*
|
||||
* @note This function is only available on ESP32 variants that support gamma curve fading.
|
||||
*/
|
||||
bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
|
||||
|
||||
/**
|
||||
* @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param start_duty initial duty cycle of the fade
|
||||
* @param target_duty target duty cycle of the fade
|
||||
* @param max_fade_time_ms maximum fade time in milliseconds
|
||||
* @param userFunc callback function to be called after fade is finished
|
||||
*
|
||||
* @return true if gamma fade was successfully set and started, false otherwise.
|
||||
*
|
||||
* @note This function is only available on ESP32 variants that support gamma curve fading.
|
||||
*/
|
||||
bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void));
|
||||
|
||||
/**
|
||||
* @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function and argument.
|
||||
*
|
||||
* @param pin GPIO pin
|
||||
* @param start_duty initial duty cycle of the fade
|
||||
* @param target_duty target duty cycle of the fade
|
||||
* @param max_fade_time_ms maximum fade time in milliseconds
|
||||
* @param userFunc callback function to be called after fade is finished
|
||||
* @param arg argument to be passed to the callback function
|
||||
*
|
||||
* @return true if gamma fade was successfully set and started, false otherwise.
|
||||
*
|
||||
* @note This function is only available on ESP32 variants that support gamma curve fading.
|
||||
*/
|
||||
bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg);
|
||||
#endif // SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_LEDC_SUPPORTED */
|
||||
#endif /* _ESP32_HAL_LEDC_H_ */
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*
|
||||
* This file provides wrapper implementations for esp_log_write and esp_log_writev
|
||||
* when CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP is not enabled.
|
||||
*
|
||||
* When CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP is enabled, the esp_diagnostics component
|
||||
* provides these wrappers. However, when it's disabled, WiFi libraries still expect
|
||||
* these wrapper functions to exist, causing linker errors.
|
||||
*
|
||||
* This implementation provides simple pass-through wrappers that call the real
|
||||
* ESP-IDF logging functions, ensuring compatibility without requiring esp_diagnostics.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
// Declare the real functions that will be wrapped by the linker
|
||||
void __real_esp_log_write(esp_log_level_t level, const char *tag, const char *format, ...);
|
||||
void __real_esp_log_writev(esp_log_level_t level, const char *tag, const char *format, va_list args);
|
||||
|
||||
// Wrapper implementations that simply call through to the real functions
|
||||
void __wrap_esp_log_write(esp_log_level_t level, const char *tag, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
__real_esp_log_writev(level, tag, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void __wrap_esp_log_writev(esp_log_level_t level, const char *tag, const char *format, va_list args) {
|
||||
__real_esp_log_writev(level, tag, format, args);
|
||||
}
|
||||
|
||||
#endif // !CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP
|
||||
@@ -0,0 +1,349 @@
|
||||
// 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 __ARDUHAL_LOG_H__
|
||||
#define __ARDUHAL_LOG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_timer.h"
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
#define ARDUHAL_LOG_LEVEL_NONE (0)
|
||||
#define ARDUHAL_LOG_LEVEL_ERROR (1)
|
||||
#define ARDUHAL_LOG_LEVEL_WARN (2)
|
||||
#define ARDUHAL_LOG_LEVEL_INFO (3)
|
||||
#define ARDUHAL_LOG_LEVEL_DEBUG (4)
|
||||
#define ARDUHAL_LOG_LEVEL_VERBOSE (5)
|
||||
|
||||
#ifndef CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL
|
||||
#define CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL ARDUHAL_LOG_LEVEL_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CORE_DEBUG_LEVEL
|
||||
#define ARDUHAL_LOG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL
|
||||
#else
|
||||
#define ARDUHAL_LOG_LEVEL CORE_DEBUG_LEVEL
|
||||
#ifdef USE_ESP_IDF_LOG
|
||||
#ifndef LOG_LOCAL_LEVEL
|
||||
#define LOG_LOCAL_LEVEL CORE_DEBUG_LEVEL
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ARDUHAL_LOG_COLORS
|
||||
#define CONFIG_ARDUHAL_LOG_COLORS 0
|
||||
#endif
|
||||
|
||||
#if CONFIG_ARDUHAL_LOG_COLORS
|
||||
#define ARDUHAL_LOG_COLOR_BLACK "30"
|
||||
#define ARDUHAL_LOG_COLOR_RED "31" //ERROR
|
||||
#define ARDUHAL_LOG_COLOR_GREEN "32" //INFO
|
||||
#define ARDUHAL_LOG_COLOR_YELLOW "33" //WARNING
|
||||
#define ARDUHAL_LOG_COLOR_BLUE "34"
|
||||
#define ARDUHAL_LOG_COLOR_MAGENTA "35"
|
||||
#define ARDUHAL_LOG_COLOR_CYAN "36" //DEBUG
|
||||
#define ARDUHAL_LOG_COLOR_GRAY "37" //VERBOSE
|
||||
#define ARDUHAL_LOG_COLOR_WHITE "38"
|
||||
|
||||
#define ARDUHAL_LOG_COLOR(COLOR) "\033[0;" COLOR "m"
|
||||
#define ARDUHAL_LOG_BOLD(COLOR) "\033[1;" COLOR "m"
|
||||
#define ARDUHAL_LOG_RESET_COLOR "\033[0m"
|
||||
|
||||
#define ARDUHAL_LOG_COLOR_E ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_RED)
|
||||
#define ARDUHAL_LOG_COLOR_W ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_YELLOW)
|
||||
#define ARDUHAL_LOG_COLOR_I ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GREEN)
|
||||
#define ARDUHAL_LOG_COLOR_D ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_CYAN)
|
||||
#define ARDUHAL_LOG_COLOR_V ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GRAY)
|
||||
#define ARDUHAL_LOG_COLOR_PRINT(letter) log_printf(ARDUHAL_LOG_COLOR_##letter)
|
||||
#define ARDUHAL_LOG_COLOR_PRINT_END log_printf(ARDUHAL_LOG_RESET_COLOR)
|
||||
#else
|
||||
#define ARDUHAL_LOG_COLOR_E
|
||||
#define ARDUHAL_LOG_COLOR_W
|
||||
#define ARDUHAL_LOG_COLOR_I
|
||||
#define ARDUHAL_LOG_COLOR_D
|
||||
#define ARDUHAL_LOG_COLOR_V
|
||||
#define ARDUHAL_LOG_RESET_COLOR
|
||||
#define ARDUHAL_LOG_COLOR_PRINT(letter)
|
||||
#define ARDUHAL_LOG_COLOR_PRINT_END
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP_IDF_LOG
|
||||
#ifndef ARDUHAL_ESP_LOG_TAG
|
||||
#define ARDUHAL_ESP_LOG_TAG "ARDUINO"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const char *pathToFileName(const char *path);
|
||||
int log_printf(const char *fmt, ...);
|
||||
void log_print_buf(const uint8_t *b, size_t len);
|
||||
|
||||
#define ARDUHAL_SHORT_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_##letter format ARDUHAL_LOG_RESET_COLOR "\r\n"
|
||||
#define ARDUHAL_LOG_FORMAT(letter, format) \
|
||||
ARDUHAL_LOG_COLOR_##letter "[%6u][" #letter "][%s:%u] %s(): " format ARDUHAL_LOG_RESET_COLOR "\r\n", (unsigned long)(esp_timer_get_time() / 1000ULL), \
|
||||
pathToFileName(__FILE__), __LINE__, __FUNCTION__
|
||||
|
||||
//esp_rom_printf(DRAM_STR("ST:%d\n"), frame_pos);
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
#ifndef USE_ESP_IDF_LOG
|
||||
#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
|
||||
#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__)
|
||||
#define log_buf_v(b, l) \
|
||||
do { \
|
||||
ARDUHAL_LOG_COLOR_PRINT(V); \
|
||||
log_print_buf(b, l); \
|
||||
ARDUHAL_LOG_COLOR_PRINT_END; \
|
||||
} while (0)
|
||||
#else
|
||||
#define log_v(format, ...) \
|
||||
do { \
|
||||
ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define isr_log_v(format, ...) \
|
||||
do { \
|
||||
ets_printf(LOG_FORMAT(V, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define log_buf_v(b, l) \
|
||||
do { \
|
||||
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_VERBOSE); \
|
||||
} while (0)
|
||||
#endif
|
||||
#else
|
||||
#define log_v(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define isr_log_v(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define log_buf_v(b, l) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
|
||||
#ifndef USE_ESP_IDF_LOG
|
||||
#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
|
||||
#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__)
|
||||
#define log_buf_d(b, l) \
|
||||
do { \
|
||||
ARDUHAL_LOG_COLOR_PRINT(D); \
|
||||
log_print_buf(b, l); \
|
||||
ARDUHAL_LOG_COLOR_PRINT_END; \
|
||||
} while (0)
|
||||
#else
|
||||
#define log_d(format, ...) \
|
||||
do { \
|
||||
ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define isr_log_d(format, ...) \
|
||||
do { \
|
||||
ets_printf(LOG_FORMAT(D, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define log_buf_d(b, l) \
|
||||
do { \
|
||||
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_DEBUG); \
|
||||
} while (0)
|
||||
#endif
|
||||
#else
|
||||
#define log_d(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define isr_log_d(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define log_buf_d(b, l) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
|
||||
#ifndef USE_ESP_IDF_LOG
|
||||
#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
|
||||
#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__)
|
||||
#define log_buf_i(b, l) \
|
||||
do { \
|
||||
ARDUHAL_LOG_COLOR_PRINT(I); \
|
||||
log_print_buf(b, l); \
|
||||
ARDUHAL_LOG_COLOR_PRINT_END; \
|
||||
} while (0)
|
||||
#else
|
||||
#define log_i(format, ...) \
|
||||
do { \
|
||||
ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define isr_log_i(format, ...) \
|
||||
do { \
|
||||
ets_printf(LOG_FORMAT(I, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define log_buf_i(b, l) \
|
||||
do { \
|
||||
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_INFO); \
|
||||
} while (0)
|
||||
#endif
|
||||
#else
|
||||
#define log_i(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define isr_log_i(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define log_buf_i(b, l) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN
|
||||
#ifndef USE_ESP_IDF_LOG
|
||||
#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
|
||||
#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__)
|
||||
#define log_buf_w(b, l) \
|
||||
do { \
|
||||
ARDUHAL_LOG_COLOR_PRINT(W); \
|
||||
log_print_buf(b, l); \
|
||||
ARDUHAL_LOG_COLOR_PRINT_END; \
|
||||
} while (0)
|
||||
#else
|
||||
#define log_w(format, ...) \
|
||||
do { \
|
||||
ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define isr_log_w(format, ...) \
|
||||
do { \
|
||||
ets_printf(LOG_FORMAT(W, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define log_buf_w(b, l) \
|
||||
do { \
|
||||
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_WARN); \
|
||||
} while (0)
|
||||
#endif
|
||||
#else
|
||||
#define log_w(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define isr_log_w(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define log_buf_w(b, l) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
|
||||
#ifndef USE_ESP_IDF_LOG
|
||||
#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define log_buf_e(b, l) \
|
||||
do { \
|
||||
ARDUHAL_LOG_COLOR_PRINT(E); \
|
||||
log_print_buf(b, l); \
|
||||
ARDUHAL_LOG_COLOR_PRINT_END; \
|
||||
} while (0)
|
||||
#else
|
||||
#define log_e(format, ...) \
|
||||
do { \
|
||||
ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define isr_log_e(format, ...) \
|
||||
do { \
|
||||
ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define log_buf_e(b, l) \
|
||||
do { \
|
||||
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_ERROR); \
|
||||
} while (0)
|
||||
#endif
|
||||
#else
|
||||
#define log_e(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define isr_log_e(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define log_buf_e(b, l) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE
|
||||
#ifndef USE_ESP_IDF_LOG
|
||||
#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||
#define log_buf_n(b, l) \
|
||||
do { \
|
||||
ARDUHAL_LOG_COLOR_PRINT(E); \
|
||||
log_print_buf(b, l); \
|
||||
ARDUHAL_LOG_COLOR_PRINT_END; \
|
||||
} while (0)
|
||||
#else
|
||||
#define log_n(format, ...) \
|
||||
do { \
|
||||
ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, ARDUHAL_ESP_LOG_TAG, format, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define isr_log_n(format, ...) \
|
||||
do { \
|
||||
ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), ARDUHAL_ESP_LOG_TAG, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#define log_buf_n(b, l) \
|
||||
do { \
|
||||
ESP_LOG_BUFFER_HEXDUMP(ARDUHAL_ESP_LOG_TAG, b, l, ESP_LOG_ERROR); \
|
||||
} while (0)
|
||||
#endif
|
||||
#else
|
||||
#define log_n(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define isr_log_n(format, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#define log_buf_n(b, l) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#ifndef USE_ESP_IDF_LOG
|
||||
#ifdef CONFIG_ARDUHAL_ESP_LOG
|
||||
#undef ESP_LOGE
|
||||
#undef ESP_LOGW
|
||||
#undef ESP_LOGI
|
||||
#undef ESP_LOGD
|
||||
#undef ESP_LOGV
|
||||
#undef ESP_EARLY_LOGE
|
||||
#undef ESP_EARLY_LOGW
|
||||
#undef ESP_EARLY_LOGI
|
||||
#undef ESP_EARLY_LOGD
|
||||
#undef ESP_EARLY_LOGV
|
||||
|
||||
#define ESP_LOGE(tag, format, ...) log_e("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_LOGW(tag, format, ...) log_w("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_LOGI(tag, format, ...) log_i("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_LOGD(tag, format, ...) log_d("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_LOGV(tag, format, ...) log_v("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGE(tag, format, ...) isr_log_e("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGW(tag, format, ...) isr_log_w("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGI(tag, format, ...) isr_log_i("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGD(tag, format, ...) isr_log_d("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#define ESP_EARLY_LOGV(tag, format, ...) isr_log_v("[%s] " format, tag, ##__VA_ARGS__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ESP_LOGGING_H__ */
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 "esp32-hal-matrix.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#include "soc/gpio_pins.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#include "esp32c2/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#include "esp32c6/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#include "esp32h2/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp32p4/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#include "esp32c5/rom/gpio.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#include "esp32c61/rom/gpio.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/gpio.h"
|
||||
#define GPIO_MATRIX_CONST_ZERO_INPUT GPIO_FUNC_IN_LOW
|
||||
#define GPIO_MATRIX_CONST_ONE_INPUT GPIO_FUNC_IN_HIGH
|
||||
#endif
|
||||
|
||||
void ARDUINO_ISR_ATTR pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable) {
|
||||
gpio_matrix_out(pin, function, invertOut, invertEnable);
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable) {
|
||||
gpio_matrix_out(pin, SIG_GPIO_OUT_IDX, invertOut, invertEnable);
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted) {
|
||||
gpio_matrix_in(pin, signal, inverted);
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR pinMatrixInDetach(uint8_t signal, bool high, bool inverted) {
|
||||
gpio_matrix_in(high ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT, signal, inverted);
|
||||
}
|
||||
/*
|
||||
void ARDUINO_ISR_ATTR intrMatrixAttach(uint32_t source, uint32_t inum){
|
||||
intr_matrix_set(PRO_CPU_NUM, source, inum);
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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_MATRIX_H_
|
||||
#define _ESP32_HAL_MATRIX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
||||
void pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable);
|
||||
void pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable);
|
||||
void pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted);
|
||||
void pinMatrixInDetach(uint8_t signal, bool high, bool inverted);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* COMPONENTS_ARDUHAL_INCLUDE_ESP32_HAL_MATRIX_H_ */
|
||||
@@ -0,0 +1,457 @@
|
||||
// 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/task.h"
|
||||
#include "esp_attr.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#ifdef CONFIG_APP_ROLLBACK_ENABLE
|
||||
#include "esp_ota_ops.h"
|
||||
#endif //CONFIG_APP_ROLLBACK_ENABLE
|
||||
#include "esp_private/startup_internal.h"
|
||||
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h")
|
||||
#include "esp_bt.h"
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include "soc/rtc.h"
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/syscon_reg.h"
|
||||
#endif
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
#include "esp32c2/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include "esp32c3/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#include "esp32c6/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#include "esp32h2/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp32p4/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#include "esp32c5/rom/rtc.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#include "esp32c61/rom/rtc.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
|
||||
#if SOC_TEMP_SENSOR_SUPPORTED
|
||||
#include "driver/temperature_sensor.h"
|
||||
#endif
|
||||
|
||||
#else // ESP32 Before IDF 4.0
|
||||
#include "rom/rtc.h"
|
||||
#endif
|
||||
|
||||
//Undocumented!!! Get chip temperature in Fahrenheit
|
||||
//Source: https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_int_temp_sensor/ESP32_int_temp_sensor.ino
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
uint8_t temprature_sens_read();
|
||||
|
||||
float temperatureRead() {
|
||||
return (temprature_sens_read() - 32) / 1.8;
|
||||
}
|
||||
#elif SOC_TEMP_SENSOR_SUPPORTED
|
||||
static temperature_sensor_handle_t temp_sensor = NULL;
|
||||
|
||||
static bool temperatureReadInit() {
|
||||
static volatile bool initialized = false;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
//Install temperature sensor, expected temp ranger range: 10~50 ℃
|
||||
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(10, 50);
|
||||
if (temperature_sensor_install(&temp_sensor_config, &temp_sensor) != ESP_OK) {
|
||||
initialized = false;
|
||||
temp_sensor = NULL;
|
||||
log_e("temperature_sensor_install failed");
|
||||
} else if (temperature_sensor_enable(temp_sensor) != ESP_OK) {
|
||||
temperature_sensor_uninstall(temp_sensor);
|
||||
initialized = false;
|
||||
temp_sensor = NULL;
|
||||
log_e("temperature_sensor_enable failed");
|
||||
}
|
||||
}
|
||||
return initialized;
|
||||
}
|
||||
|
||||
float temperatureRead() {
|
||||
float result = NAN;
|
||||
if (temperatureReadInit()) {
|
||||
if (temperature_sensor_get_celsius(temp_sensor, &result) != ESP_OK) {
|
||||
log_e("temperature_sensor_get_celsius failed");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
void __yield() {
|
||||
vPortYield();
|
||||
}
|
||||
|
||||
void yield() __attribute__((weak, alias("__yield")));
|
||||
|
||||
#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 = xTaskGetIdleTaskHandleForCore(0);
|
||||
if (idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK) {
|
||||
log_e("Failed to add Core 0 IDLE task to WDT");
|
||||
}
|
||||
}
|
||||
|
||||
bool disableCore0WDT() {
|
||||
TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCore(0);
|
||||
if (idle_0 == NULL || esp_task_wdt_status(idle_0) || esp_task_wdt_delete(idle_0) != ESP_OK) {
|
||||
log_e("Failed to remove Core 0 IDLE task from WDT");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_FREERTOS_UNICORE
|
||||
void enableCore1WDT() {
|
||||
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCore(1);
|
||||
if (idle_1 == NULL || esp_task_wdt_add(idle_1) != ESP_OK) {
|
||||
log_e("Failed to add Core 1 IDLE task to WDT");
|
||||
}
|
||||
}
|
||||
|
||||
bool disableCore1WDT() {
|
||||
TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCore(1);
|
||||
if (idle_1 == NULL || esp_task_wdt_status(idle_1) || esp_task_wdt_delete(idle_1) != ESP_OK) {
|
||||
log_e("Failed to remove Core 1 IDLE task from WDT");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#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 ARDUINO_ISR_ATTR micros() {
|
||||
return (unsigned long)(esp_timer_get_time());
|
||||
}
|
||||
|
||||
unsigned long ARDUINO_ISR_ATTR millis() {
|
||||
return (unsigned long)(esp_timer_get_time() / 1000ULL);
|
||||
}
|
||||
|
||||
void delay(uint32_t ms) {
|
||||
vTaskDelay(ms / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR delayMicroseconds(uint32_t us) {
|
||||
uint64_t m = (uint64_t)esp_timer_get_time();
|
||||
if (us) {
|
||||
uint64_t e = (m + us);
|
||||
if (m > e) { //overflow
|
||||
while ((uint64_t)esp_timer_get_time() > e) {
|
||||
NOP();
|
||||
}
|
||||
}
|
||||
while ((uint64_t)esp_timer_get_time() < e) {
|
||||
NOP();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initVariant() __attribute__((weak));
|
||||
void initVariant() {}
|
||||
|
||||
void init() __attribute__((weak));
|
||||
void init() {}
|
||||
|
||||
#ifdef CONFIG_APP_ROLLBACK_ENABLE
|
||||
/**
|
||||
* @brief Verify the OTA image after boot
|
||||
*
|
||||
* This weak hook is invoked when a newly-updated application image is in
|
||||
* the `ESP_OTA_IMG_PENDING_VERIFY` state. The default implementation
|
||||
* returns `true`, indicating the image is considered valid. Applications
|
||||
* may override this function (provide a non-weak implementation) to
|
||||
* perform custom checks such as running a self-test, validating
|
||||
* application-specific state, or performing connectivity / sensor checks
|
||||
* before confirming the update.
|
||||
*
|
||||
* Usage:
|
||||
* - Return `true` to mark the OTA image as valid; the system will call
|
||||
* `esp_ota_mark_app_valid_cancel_rollback()` to cancel rollback.
|
||||
* - Return `false` to indicate verification failure; the system will
|
||||
* call `esp_ota_mark_app_invalid_rollback_and_reboot()` to rollback.
|
||||
*
|
||||
* @note Keep this function short and non-blocking if possible; blocking
|
||||
* for long periods may delay boot.
|
||||
*
|
||||
* @return true if verification succeeds and the image should be kept
|
||||
* @return false if verification fails and the image should be rolled back
|
||||
*/
|
||||
bool verifyOta() __attribute__((weak));
|
||||
bool verifyOta() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally defer OTA verification to later
|
||||
*
|
||||
* This weak hook allows an application to postpone the default OTA
|
||||
* verification performed at startup. If this function returns `true`, the
|
||||
* initialization code will skip immediate verification and the application
|
||||
* can choose to verify the update at a later time (for example, after a
|
||||
* lengthy hardware initialization or user interaction). The default weak
|
||||
* implementation returns `false`, meaning verification will run during
|
||||
* `initArduino()` when an image is pending verification.
|
||||
*
|
||||
* Usage:
|
||||
* - Return `true` to delay verification and handle it manually later.
|
||||
* - Return `false` to allow the built-in verification flow to run at
|
||||
* startup.
|
||||
*
|
||||
* @return true to defer verification to a later time
|
||||
* @return false to perform verification immediately during init
|
||||
*/
|
||||
bool verifyRollbackLater() __attribute__((weak));
|
||||
bool verifyRollbackLater() {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h")
|
||||
// declared here, defined in esp32-hal-bt.c (weak so users can override)
|
||||
extern bool btInUse(void);
|
||||
#endif
|
||||
|
||||
#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
|
||||
ESP_SYSTEM_INIT_FN(init_psram_new, CORE, BIT(0), 99) {
|
||||
psramInit();
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
void initArduino() {
|
||||
//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;
|
||||
#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
|
||||
#ifndef CONFIG_SPIRAM_BOOT_INIT
|
||||
psramAddToHeap();
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_APP_ROLLBACK_ENABLE
|
||||
if (!verifyRollbackLater()) {
|
||||
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
|
||||
esp_log_level_set("*", CONFIG_LOG_DEFAULT_LEVEL);
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
|
||||
if (partition != NULL) {
|
||||
err = esp_partition_erase_range(partition, 0, partition->size);
|
||||
if (!err) {
|
||||
err = nvs_flash_init();
|
||||
} else {
|
||||
log_e("Failed to format the broken NVS partition!");
|
||||
}
|
||||
} else {
|
||||
log_e("Could not find NVS partition");
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
log_e("Failed to initialize NVS! Error: %u", err);
|
||||
}
|
||||
#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h")
|
||||
if (!btInUse()) {
|
||||
esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
|
||||
}
|
||||
#endif
|
||||
init();
|
||||
initVariant();
|
||||
}
|
||||
|
||||
//used by hal log
|
||||
const char *ARDUINO_ISR_ATTR pathToFileName(const char *path) {
|
||||
size_t i = 0;
|
||||
size_t pos = 0;
|
||||
char *p = (char *)path;
|
||||
while (*p) {
|
||||
i++;
|
||||
if (*p == '/' || *p == '\\') {
|
||||
pos = i;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return path + pos;
|
||||
}
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_debug_helpers.h"
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
#include "esp_cpu_utils.h"
|
||||
#else
|
||||
#include "riscv/rvruntime-frames.h"
|
||||
#endif
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_private/panic_internal.h"
|
||||
|
||||
static arduino_panic_handler_t _panic_handler = NULL;
|
||||
static void *_panic_handler_arg = NULL;
|
||||
|
||||
void set_arduino_panic_handler(arduino_panic_handler_t handler, void *arg) {
|
||||
_panic_handler = handler;
|
||||
_panic_handler_arg = arg;
|
||||
}
|
||||
|
||||
arduino_panic_handler_t get_arduino_panic_handler(void) {
|
||||
return _panic_handler;
|
||||
}
|
||||
|
||||
void *get_arduino_panic_handler_arg(void) {
|
||||
return _panic_handler_arg;
|
||||
}
|
||||
|
||||
static void handle_custom_backtrace(panic_info_t *info) {
|
||||
arduino_panic_info_t p_info;
|
||||
p_info.reason = info->reason;
|
||||
p_info.core = info->core;
|
||||
p_info.pc = info->addr;
|
||||
p_info.backtrace_len = 0;
|
||||
p_info.backtrace_corrupt = false;
|
||||
p_info.backtrace_continues = false;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
XtExcFrame *xt_frame = (XtExcFrame *)info->frame;
|
||||
esp_backtrace_frame_t stk_frame = {.pc = xt_frame->pc, .sp = xt_frame->a1, .next_pc = xt_frame->a0, .exc_frame = xt_frame};
|
||||
uint32_t i = 100, pc_ptr = esp_cpu_process_stack_pc(stk_frame.pc);
|
||||
p_info.backtrace[p_info.backtrace_len++] = pc_ptr;
|
||||
|
||||
bool corrupted = !(esp_stack_ptr_is_sane(stk_frame.sp) && (esp_ptr_executable((void *)esp_cpu_process_stack_pc(stk_frame.pc)) ||
|
||||
/* Ignore the first corrupted PC in case of InstrFetchProhibited */
|
||||
(stk_frame.exc_frame && ((XtExcFrame *)stk_frame.exc_frame)->exccause == EXCCAUSE_INSTR_PROHIBITED)));
|
||||
|
||||
while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) {
|
||||
if (!esp_backtrace_get_next_frame(&stk_frame)) {
|
||||
corrupted = true;
|
||||
}
|
||||
pc_ptr = esp_cpu_process_stack_pc(stk_frame.pc);
|
||||
if (esp_ptr_executable((void *)pc_ptr)) {
|
||||
p_info.backtrace[p_info.backtrace_len++] = pc_ptr;
|
||||
if (p_info.backtrace_len == 60) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (corrupted) {
|
||||
p_info.backtrace_corrupt = true;
|
||||
} else if (stk_frame.next_pc != 0) {
|
||||
p_info.backtrace_continues = true;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
uint32_t sp = (uint32_t)((RvExcFrame *)info->frame)->sp;
|
||||
p_info.backtrace[p_info.backtrace_len++] = sp;
|
||||
uint32_t *spptr = (uint32_t *)(sp);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (esp_ptr_executable((void *)spptr[i])) {
|
||||
p_info.backtrace[p_info.backtrace_len++] = spptr[i];
|
||||
if (p_info.backtrace_len == 60) {
|
||||
if (i < 255) {
|
||||
p_info.backtrace_continues = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_panic_handler(&p_info, _panic_handler_arg);
|
||||
}
|
||||
|
||||
void __real_esp_panic_handler(panic_info_t *);
|
||||
void __wrap_esp_panic_handler(panic_info_t *info) {
|
||||
if (_panic_handler != NULL) {
|
||||
handle_custom_backtrace(info);
|
||||
}
|
||||
__real_esp_panic_handler(info);
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp32-hal-log.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "esp_bit_defs.h"
|
||||
|
||||
typedef struct ATTR_PACKED {
|
||||
peripheral_bus_type_t type;
|
||||
const char *extra_type;
|
||||
void *bus;
|
||||
int8_t bus_num;
|
||||
int8_t bus_channel;
|
||||
} peripheral_pin_item_t;
|
||||
|
||||
static peripheral_bus_deinit_cb_t deinit_functions[ESP32_BUS_TYPE_MAX] = {NULL};
|
||||
static peripheral_pin_item_t pins[SOC_GPIO_PIN_COUNT];
|
||||
|
||||
#define GPIO_NOT_VALID(p) ((p >= SOC_GPIO_PIN_COUNT) || ((SOC_GPIO_VALID_GPIO_MASK & (1ULL << p)) == 0))
|
||||
|
||||
const char *perimanGetTypeName(peripheral_bus_type_t type) {
|
||||
switch (type) {
|
||||
case ESP32_BUS_TYPE_INIT: return "INIT";
|
||||
case ESP32_BUS_TYPE_GPIO: return "GPIO";
|
||||
case ESP32_BUS_TYPE_UART_RX: return "UART_RX";
|
||||
case ESP32_BUS_TYPE_UART_TX: return "UART_TX";
|
||||
case ESP32_BUS_TYPE_UART_CTS: return "UART_CTS";
|
||||
case ESP32_BUS_TYPE_UART_RTS: return "UART_RTS";
|
||||
#if SOC_SDM_SUPPORTED
|
||||
case ESP32_BUS_TYPE_SIGMADELTA: return "SIGMADELTA";
|
||||
#endif
|
||||
#if SOC_ADC_SUPPORTED
|
||||
case ESP32_BUS_TYPE_ADC_ONESHOT: return "ADC_ONESHOT";
|
||||
case ESP32_BUS_TYPE_ADC_CONT: return "ADC_CONT";
|
||||
#endif
|
||||
#if SOC_DAC_SUPPORTED
|
||||
case ESP32_BUS_TYPE_DAC_ONESHOT: return "DAC_ONESHOT";
|
||||
case ESP32_BUS_TYPE_DAC_CONT: return "DAC_CONT";
|
||||
case ESP32_BUS_TYPE_DAC_COSINE: return "DAC_COSINE";
|
||||
#endif
|
||||
#if SOC_LEDC_SUPPORTED
|
||||
case ESP32_BUS_TYPE_LEDC: return "LEDC";
|
||||
#endif
|
||||
#if SOC_RMT_SUPPORTED
|
||||
case ESP32_BUS_TYPE_RMT_TX: return "RMT_TX";
|
||||
case ESP32_BUS_TYPE_RMT_RX: return "RMT_RX";
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTED
|
||||
case ESP32_BUS_TYPE_I2S_STD_MCLK: return "I2S_STD_MCLK";
|
||||
case ESP32_BUS_TYPE_I2S_STD_BCLK: return "I2S_STD_BCLK";
|
||||
case ESP32_BUS_TYPE_I2S_STD_WS: return "I2S_STD_WS";
|
||||
case ESP32_BUS_TYPE_I2S_STD_DOUT: return "I2S_STD_DOUT";
|
||||
case ESP32_BUS_TYPE_I2S_STD_DIN: return "I2S_STD_DIN";
|
||||
case ESP32_BUS_TYPE_I2S_TDM_MCLK: return "I2S_TDM_MCLK";
|
||||
case ESP32_BUS_TYPE_I2S_TDM_BCLK: return "I2S_TDM_BCLK";
|
||||
case ESP32_BUS_TYPE_I2S_TDM_WS: return "I2S_TDM_WS";
|
||||
case ESP32_BUS_TYPE_I2S_TDM_DOUT: return "I2S_TDM_DOUT";
|
||||
case ESP32_BUS_TYPE_I2S_TDM_DIN: return "I2S_TDM_DIN";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_TX_CLK: return "I2S_PDM_TX_CLK";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_TX_DOUT0: return "I2S_PDM_TX_DOUT0";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_TX_DOUT1: return "I2S_PDM_TX_DOUT1";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_RX_CLK: return "I2S_PDM_RX_CLK";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN0: return "I2S_PDM_RX_DIN0";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN1: return "I2S_PDM_RX_DIN1";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN2: return "I2S_PDM_RX_DIN2";
|
||||
case ESP32_BUS_TYPE_I2S_PDM_RX_DIN3: return "I2S_PDM_RX_DIN3";
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORTED
|
||||
case ESP32_BUS_TYPE_I2C_MASTER_SDA: return "I2C_MASTER_SDA";
|
||||
case ESP32_BUS_TYPE_I2C_MASTER_SCL: return "I2C_MASTER_SCL";
|
||||
case ESP32_BUS_TYPE_I2C_SLAVE_SDA: return "I2C_SLAVE_SDA";
|
||||
case ESP32_BUS_TYPE_I2C_SLAVE_SCL: return "I2C_SLAVE_SCL";
|
||||
#endif
|
||||
#if SOC_GPSPI_SUPPORTED
|
||||
case ESP32_BUS_TYPE_SPI_MASTER_SCK: return "SPI_MASTER_SCK";
|
||||
case ESP32_BUS_TYPE_SPI_MASTER_MISO: return "SPI_MASTER_MISO";
|
||||
case ESP32_BUS_TYPE_SPI_MASTER_MOSI: return "SPI_MASTER_MOSI";
|
||||
case ESP32_BUS_TYPE_SPI_MASTER_SS: return "SPI_MASTER_SS";
|
||||
#endif
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
case ESP32_BUS_TYPE_SDMMC_CLK: return "SDMMC_CLK";
|
||||
case ESP32_BUS_TYPE_SDMMC_CMD: return "SDMMC_CMD";
|
||||
case ESP32_BUS_TYPE_SDMMC_D0: return "SDMMC_D0";
|
||||
case ESP32_BUS_TYPE_SDMMC_D1: return "SDMMC_D1";
|
||||
case ESP32_BUS_TYPE_SDMMC_D2: return "SDMMC_D2";
|
||||
case ESP32_BUS_TYPE_SDMMC_D3: return "SDMMC_D3";
|
||||
#endif
|
||||
#if SOC_TOUCH_SENSOR_SUPPORTED
|
||||
case ESP32_BUS_TYPE_TOUCH: return "TOUCH";
|
||||
#endif
|
||||
#if SOC_USB_SERIAL_JTAG_SUPPORTED || SOC_USB_OTG_SUPPORTED
|
||||
case ESP32_BUS_TYPE_USB_DM: return "USB_DM";
|
||||
case ESP32_BUS_TYPE_USB_DP: return "USB_DP";
|
||||
#endif
|
||||
#if SOC_GPSPI_SUPPORTED
|
||||
case ESP32_BUS_TYPE_ETHERNET_SPI: return "ETHERNET_SPI";
|
||||
#endif
|
||||
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||
case ESP32_BUS_TYPE_ETHERNET_RMII: return "ETHERNET_RMII";
|
||||
case ESP32_BUS_TYPE_ETHERNET_CLK: return "ETHERNET_CLK";
|
||||
case ESP32_BUS_TYPE_ETHERNET_MCD: return "ETHERNET_MCD";
|
||||
case ESP32_BUS_TYPE_ETHERNET_MDIO: return "ETHERNET_MDIO";
|
||||
case ESP32_BUS_TYPE_ETHERNET_PWR: return "ETHERNET_PWR";
|
||||
#endif
|
||||
#if CONFIG_LWIP_PPP_SUPPORT
|
||||
case ESP32_BUS_TYPE_PPP_TX: return "PPP_MODEM_TX";
|
||||
case ESP32_BUS_TYPE_PPP_RX: return "PPP_MODEM_RX";
|
||||
case ESP32_BUS_TYPE_PPP_RTS: return "PPP_MODEM_RTS";
|
||||
case ESP32_BUS_TYPE_PPP_CTS: return "PPP_MODEM_CTS";
|
||||
#endif
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
bool perimanSetPinBus(uint8_t pin, peripheral_bus_type_t type, void *bus, int8_t bus_num, int8_t bus_channel) {
|
||||
peripheral_bus_type_t otype = ESP32_BUS_TYPE_INIT;
|
||||
void *obus = NULL;
|
||||
if (GPIO_NOT_VALID(pin)) {
|
||||
log_e("Invalid pin: %u", pin);
|
||||
return false;
|
||||
}
|
||||
if (type >= ESP32_BUS_TYPE_MAX) {
|
||||
log_e("Invalid type: %s (%u) when setting pin %u", perimanGetTypeName(type), (unsigned int)type, pin);
|
||||
return false;
|
||||
}
|
||||
if (type > ESP32_BUS_TYPE_GPIO && bus == NULL) {
|
||||
log_e("Bus is NULL for pin %u with type %s (%u)", pin, perimanGetTypeName(type), (unsigned int)type);
|
||||
return false;
|
||||
}
|
||||
if (type == ESP32_BUS_TYPE_INIT && bus != NULL) {
|
||||
log_e("Can't set a Bus to INIT Type (pin %u)", pin);
|
||||
return false;
|
||||
}
|
||||
otype = pins[pin].type;
|
||||
obus = pins[pin].bus;
|
||||
if (type == otype && bus == obus) {
|
||||
if (type != ESP32_BUS_TYPE_INIT) {
|
||||
log_i("Pin %u already has type %s (%u) with bus %p", pin, perimanGetTypeName(type), (unsigned int)type, bus);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (obus != NULL) {
|
||||
if (deinit_functions[otype] == NULL) {
|
||||
log_e("No deinit function for type %s (%u) (pin %u)", perimanGetTypeName(otype), (unsigned int)otype, pin);
|
||||
return false;
|
||||
}
|
||||
if (!deinit_functions[otype](obus)) {
|
||||
log_e("Deinit function for previous bus type %s (%u) failed (pin %u)", perimanGetTypeName(otype), (unsigned int)otype, pin);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pins[pin].type = type;
|
||||
pins[pin].bus = bus;
|
||||
pins[pin].bus_num = bus_num;
|
||||
pins[pin].bus_channel = bus_channel;
|
||||
pins[pin].extra_type = NULL;
|
||||
log_v("Pin %u successfully set to type %s (%u) with bus %p", pin, perimanGetTypeName(type), (unsigned int)type, bus);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool perimanSetPinBusExtraType(uint8_t pin, const char *extra_type) {
|
||||
if (GPIO_NOT_VALID(pin)) {
|
||||
log_e("Invalid pin: %u", pin);
|
||||
return false;
|
||||
}
|
||||
if (pins[pin].type == ESP32_BUS_TYPE_INIT) {
|
||||
log_e("Can't set extra type for Bus INIT Type (pin %u)", pin);
|
||||
return false;
|
||||
}
|
||||
pins[pin].extra_type = extra_type;
|
||||
log_v("Successfully set extra_type %s for pin %u", extra_type, pin);
|
||||
return true;
|
||||
}
|
||||
|
||||
void *perimanGetPinBus(uint8_t pin, peripheral_bus_type_t type) {
|
||||
if (GPIO_NOT_VALID(pin)) {
|
||||
log_e("Invalid pin: %u", pin);
|
||||
return NULL;
|
||||
}
|
||||
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
|
||||
log_e("Invalid type %s (%u) for pin %u", perimanGetTypeName(type), (unsigned int)type, pin);
|
||||
return NULL;
|
||||
}
|
||||
if (pins[pin].type == type) {
|
||||
return pins[pin].bus;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peripheral_bus_type_t perimanGetPinBusType(uint8_t pin) {
|
||||
if (GPIO_NOT_VALID(pin)) {
|
||||
log_e("Invalid pin: %u", pin);
|
||||
return ESP32_BUS_TYPE_MAX;
|
||||
}
|
||||
return pins[pin].type;
|
||||
}
|
||||
|
||||
const char *perimanGetPinBusExtraType(uint8_t pin) {
|
||||
if (GPIO_NOT_VALID(pin)) {
|
||||
log_e("Invalid pin: %u", pin);
|
||||
return NULL;
|
||||
}
|
||||
return pins[pin].extra_type;
|
||||
}
|
||||
|
||||
int8_t perimanGetPinBusNum(uint8_t pin) {
|
||||
if (GPIO_NOT_VALID(pin)) {
|
||||
log_e("Invalid pin: %u", pin);
|
||||
return -1;
|
||||
}
|
||||
return pins[pin].bus_num;
|
||||
}
|
||||
|
||||
int8_t perimanGetPinBusChannel(uint8_t pin) {
|
||||
if (GPIO_NOT_VALID(pin)) {
|
||||
log_e("Invalid pin: %u", pin);
|
||||
return -1;
|
||||
}
|
||||
return pins[pin].bus_channel;
|
||||
}
|
||||
|
||||
bool perimanSetBusDeinit(peripheral_bus_type_t type, peripheral_bus_deinit_cb_t cb) {
|
||||
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
|
||||
log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type);
|
||||
return false;
|
||||
}
|
||||
if (cb == NULL) {
|
||||
log_e("Callback is NULL when setting deinit function for type %s (%u)", perimanGetTypeName(type), (unsigned int)type);
|
||||
return false;
|
||||
}
|
||||
deinit_functions[type] = cb;
|
||||
log_v("Deinit function for type %s (%u) successfully set to %p", perimanGetTypeName(type), (unsigned int)type, cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This no-op callback is used by perimanClearBusDeinit() to effectively disable bus deinit functionality
|
||||
// without setting the callback to NULL, which would cause errors in perimanSetPinBus() at line 146.
|
||||
static bool empty_bus_deinit_cb(void *bus) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool perimanClearBusDeinit(peripheral_bus_type_t type) {
|
||||
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
|
||||
log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type);
|
||||
return false;
|
||||
}
|
||||
deinit_functions[type] = empty_bus_deinit_cb;
|
||||
log_v("Deinit function for type %s (%u) cleared", perimanGetTypeName(type), (unsigned int)type);
|
||||
return true;
|
||||
}
|
||||
|
||||
peripheral_bus_deinit_cb_t perimanGetBusDeinit(peripheral_bus_type_t type) {
|
||||
if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) {
|
||||
log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type);
|
||||
return NULL;
|
||||
}
|
||||
if (deinit_functions[type] == empty_bus_deinit_cb) {
|
||||
return NULL;
|
||||
}
|
||||
return deinit_functions[type];
|
||||
}
|
||||
|
||||
bool perimanPinIsValid(uint8_t pin) {
|
||||
return !(GPIO_NOT_VALID(pin));
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define perimanClearPinBus(p) perimanSetPinBus(p, ESP32_BUS_TYPE_INIT, NULL, -1, -1)
|
||||
|
||||
typedef enum {
|
||||
ESP32_BUS_TYPE_INIT, // IO has not been attached to a bus yet
|
||||
ESP32_BUS_TYPE_GPIO, // IO is used as GPIO
|
||||
ESP32_BUS_TYPE_UART_RX, // IO is used as UART RX pin
|
||||
ESP32_BUS_TYPE_UART_TX, // IO is used as UART TX pin
|
||||
ESP32_BUS_TYPE_UART_CTS, // IO is used as UART CTS pin
|
||||
ESP32_BUS_TYPE_UART_RTS, // IO is used as UART RTS pin
|
||||
#if SOC_SDM_SUPPORTED
|
||||
ESP32_BUS_TYPE_SIGMADELTA, // IO is used as SigmeDelta output
|
||||
#endif
|
||||
#if SOC_ADC_SUPPORTED
|
||||
ESP32_BUS_TYPE_ADC_ONESHOT, // IO is used as ADC OneShot input
|
||||
ESP32_BUS_TYPE_ADC_CONT, // IO is used as ADC continuous input
|
||||
#endif
|
||||
#if SOC_DAC_SUPPORTED
|
||||
ESP32_BUS_TYPE_DAC_ONESHOT, // IO is used as DAC OneShot output
|
||||
ESP32_BUS_TYPE_DAC_CONT, // IO is used as DAC continuous output
|
||||
ESP32_BUS_TYPE_DAC_COSINE, // IO is used as DAC cosine output
|
||||
#endif
|
||||
#if SOC_LEDC_SUPPORTED
|
||||
ESP32_BUS_TYPE_LEDC, // IO is used as LEDC output
|
||||
#endif
|
||||
#if SOC_RMT_SUPPORTED
|
||||
ESP32_BUS_TYPE_RMT_TX, // IO is used as RMT output
|
||||
ESP32_BUS_TYPE_RMT_RX, // IO is used as RMT input
|
||||
#endif
|
||||
#if SOC_I2S_SUPPORTED
|
||||
ESP32_BUS_TYPE_I2S_STD_MCLK, // IO is used as I2S STD MCLK pin
|
||||
ESP32_BUS_TYPE_I2S_STD_BCLK, // IO is used as I2S STD BCLK pin
|
||||
ESP32_BUS_TYPE_I2S_STD_WS, // IO is used as I2S STD WS pin
|
||||
ESP32_BUS_TYPE_I2S_STD_DOUT, // IO is used as I2S STD DOUT pin
|
||||
ESP32_BUS_TYPE_I2S_STD_DIN, // IO is used as I2S STD DIN pin
|
||||
|
||||
ESP32_BUS_TYPE_I2S_TDM_MCLK, // IO is used as I2S TDM MCLK pin
|
||||
ESP32_BUS_TYPE_I2S_TDM_BCLK, // IO is used as I2S TDM BCLK pin
|
||||
ESP32_BUS_TYPE_I2S_TDM_WS, // IO is used as I2S TDM WS pin
|
||||
ESP32_BUS_TYPE_I2S_TDM_DOUT, // IO is used as I2S TDM DOUT pin
|
||||
ESP32_BUS_TYPE_I2S_TDM_DIN, // IO is used as I2S TDM DIN pin
|
||||
|
||||
ESP32_BUS_TYPE_I2S_PDM_TX_CLK, // IO is used as I2S PDM CLK pin
|
||||
ESP32_BUS_TYPE_I2S_PDM_TX_DOUT0, // IO is used as I2S PDM DOUT0 pin
|
||||
ESP32_BUS_TYPE_I2S_PDM_TX_DOUT1, // IO is used as I2S PDM DOUT1 pin
|
||||
|
||||
ESP32_BUS_TYPE_I2S_PDM_RX_CLK, // IO is used as I2S PDM CLK pin
|
||||
ESP32_BUS_TYPE_I2S_PDM_RX_DIN0, // IO is used as I2S PDM DIN0 pin
|
||||
ESP32_BUS_TYPE_I2S_PDM_RX_DIN1, // IO is used as I2S PDM DIN1 pin
|
||||
ESP32_BUS_TYPE_I2S_PDM_RX_DIN2, // IO is used as I2S PDM DIN2 pin
|
||||
ESP32_BUS_TYPE_I2S_PDM_RX_DIN3, // IO is used as I2S PDM DIN3 pin
|
||||
#endif
|
||||
#if SOC_I2C_SUPPORTED
|
||||
ESP32_BUS_TYPE_I2C_MASTER_SDA, // IO is used as I2C master SDA pin
|
||||
ESP32_BUS_TYPE_I2C_MASTER_SCL, // IO is used as I2C master SCL pin
|
||||
ESP32_BUS_TYPE_I2C_SLAVE_SDA, // IO is used as I2C slave SDA pin
|
||||
ESP32_BUS_TYPE_I2C_SLAVE_SCL, // IO is used as I2C slave SCL pin
|
||||
#endif
|
||||
#if SOC_GPSPI_SUPPORTED
|
||||
ESP32_BUS_TYPE_SPI_MASTER_SCK, // IO is used as SPI master SCK pin
|
||||
ESP32_BUS_TYPE_SPI_MASTER_MISO, // IO is used as SPI master MISO pin
|
||||
ESP32_BUS_TYPE_SPI_MASTER_MOSI, // IO is used as SPI master MOSI pin
|
||||
ESP32_BUS_TYPE_SPI_MASTER_SS, // IO is used as SPI master SS pin
|
||||
#endif
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
ESP32_BUS_TYPE_SDMMC_CLK, // IO is used as SDMMC CLK pin
|
||||
ESP32_BUS_TYPE_SDMMC_CMD, // IO is used as SDMMC CMD pin
|
||||
ESP32_BUS_TYPE_SDMMC_D0, // IO is used as SDMMC D0 pin
|
||||
ESP32_BUS_TYPE_SDMMC_D1, // IO is used as SDMMC D1 pin
|
||||
ESP32_BUS_TYPE_SDMMC_D2, // IO is used as SDMMC D2 pin
|
||||
ESP32_BUS_TYPE_SDMMC_D3, // IO is used as SDMMC D3 pin
|
||||
#endif
|
||||
#if SOC_TOUCH_SENSOR_SUPPORTED
|
||||
ESP32_BUS_TYPE_TOUCH, // IO is used as TOUCH pin
|
||||
#endif
|
||||
#if SOC_USB_SERIAL_JTAG_SUPPORTED || SOC_USB_OTG_SUPPORTED
|
||||
ESP32_BUS_TYPE_USB_DM, // IO is used as USB DM (+) pin
|
||||
ESP32_BUS_TYPE_USB_DP, // IO is used as USB DP (-) pin
|
||||
#endif
|
||||
#if SOC_GPSPI_SUPPORTED
|
||||
ESP32_BUS_TYPE_ETHERNET_SPI, // IO is used as ETHERNET SPI pin
|
||||
#endif
|
||||
#if CONFIG_ETH_USE_ESP32_EMAC
|
||||
ESP32_BUS_TYPE_ETHERNET_RMII, // IO is used as ETHERNET RMII pin
|
||||
ESP32_BUS_TYPE_ETHERNET_CLK, // IO is used as ETHERNET CLK pin
|
||||
ESP32_BUS_TYPE_ETHERNET_MCD, // IO is used as ETHERNET MCD pin
|
||||
ESP32_BUS_TYPE_ETHERNET_MDIO, // IO is used as ETHERNET MDIO pin
|
||||
ESP32_BUS_TYPE_ETHERNET_PWR, // IO is used as ETHERNET PWR pin
|
||||
#endif
|
||||
#if CONFIG_LWIP_PPP_SUPPORT
|
||||
ESP32_BUS_TYPE_PPP_TX, // IO is used as PPP Modem TX pin
|
||||
ESP32_BUS_TYPE_PPP_RX, // IO is used as PPP Modem RX pin
|
||||
ESP32_BUS_TYPE_PPP_RTS, // IO is used as PPP Modem RTS pin
|
||||
ESP32_BUS_TYPE_PPP_CTS, // IO is used as PPP Modem CTS pin
|
||||
#endif
|
||||
ESP32_BUS_TYPE_MAX
|
||||
} peripheral_bus_type_t;
|
||||
|
||||
typedef bool (*peripheral_bus_deinit_cb_t)(void *bus);
|
||||
|
||||
const char *perimanGetTypeName(peripheral_bus_type_t type);
|
||||
|
||||
// Sets the bus type, bus handle, bus number and bus channel for given pin.
|
||||
bool perimanSetPinBus(uint8_t pin, peripheral_bus_type_t type, void *bus, int8_t bus_num, int8_t bus_channel);
|
||||
|
||||
// Returns handle of the bus for the given pin if type of bus matches. NULL otherwise
|
||||
void *perimanGetPinBus(uint8_t pin, peripheral_bus_type_t type);
|
||||
|
||||
// Returns the type of the bus for the given pin if attached. ESP32_BUS_TYPE_MAX otherwise
|
||||
peripheral_bus_type_t perimanGetPinBusType(uint8_t pin);
|
||||
|
||||
// Returns the bus number or unit of the bus for the given pin if set. -1 otherwise
|
||||
int8_t perimanGetPinBusNum(uint8_t pin);
|
||||
|
||||
// Returns the bus channel of the bus for the given pin if set. -1 otherwise
|
||||
int8_t perimanGetPinBusChannel(uint8_t pin);
|
||||
|
||||
// Sets the peripheral destructor callback. Used to destroy bus when pin is assigned another function
|
||||
bool perimanSetBusDeinit(peripheral_bus_type_t type, peripheral_bus_deinit_cb_t cb);
|
||||
|
||||
// Clears the peripheral destructor callback
|
||||
bool perimanClearBusDeinit(peripheral_bus_type_t type);
|
||||
|
||||
// Get the peripheral destructor callback. It allows changing/restoring the peripheral pin function detaching, if necessary
|
||||
// returns NULL if none is set
|
||||
peripheral_bus_deinit_cb_t perimanGetBusDeinit(peripheral_bus_type_t type);
|
||||
|
||||
// Check if given pin is a valid GPIO number
|
||||
bool perimanPinIsValid(uint8_t pin);
|
||||
|
||||
// Sets the extra type for non Init bus. Used to customize pin bus name which can be printed by printPerimanInfo().
|
||||
bool perimanSetPinBusExtraType(uint8_t pin, const char *extra_type);
|
||||
|
||||
// Returns the extra type of the bus for given pin if set. NULL otherwise
|
||||
const char *perimanGetPinBusExtraType(uint8_t pin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
// 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 "esp32-hal.h"
|
||||
|
||||
#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
|
||||
#include "soc/efuse_reg.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_psram.h"
|
||||
#include "esp_private/esp_psram_extram.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/cache.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/cache.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/cache.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#include "esp32p4/rom/cache.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#include "esp32c5/rom/cache.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C61
|
||||
#include "esp32c61/rom/cache.h"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
|
||||
#define TAG "arduino-psram"
|
||||
|
||||
static volatile bool spiramDetected = false;
|
||||
static volatile bool spiramFailed = false;
|
||||
|
||||
//allows user to bypass SPI RAM test routine
|
||||
__attribute__((weak)) bool testSPIRAM(void) {
|
||||
return esp_psram_extram_test();
|
||||
}
|
||||
|
||||
bool psramInit() {
|
||||
if (spiramDetected) {
|
||||
return true;
|
||||
}
|
||||
#ifndef CONFIG_SPIRAM_BOOT_INIT
|
||||
if (spiramFailed) {
|
||||
return false;
|
||||
}
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_PACKAGE);
|
||||
uint32_t pkg_ver = chip_ver & 0x7;
|
||||
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
|
||||
spiramFailed = true;
|
||||
ESP_EARLY_LOGW(TAG, "PSRAM not supported!");
|
||||
return false;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
extern void esp_config_data_cache_mode(void);
|
||||
esp_config_data_cache_mode();
|
||||
Cache_Enable_DCache(0);
|
||||
#endif
|
||||
if (esp_psram_init() != ESP_OK) {
|
||||
spiramFailed = true;
|
||||
ESP_EARLY_LOGW(TAG, "PSRAM init failed!");
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (pkg_ver != EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
|
||||
pinMatrixOutDetach(16, false, false);
|
||||
pinMatrixOutDetach(17, false, false);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
//testSPIRAM() allows user to bypass SPI RAM test routine
|
||||
if (!testSPIRAM()) {
|
||||
spiramFailed = true;
|
||||
ESP_EARLY_LOGE(TAG, "PSRAM test failed!");
|
||||
return false;
|
||||
}
|
||||
//ESP_EARLY_LOGI(TAG, "PSRAM enabled");
|
||||
#endif /* CONFIG_SPIRAM_BOOT_INIT */
|
||||
spiramDetected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool psramAddToHeap() {
|
||||
if (!spiramDetected) {
|
||||
log_e("PSRAM not initialized!");
|
||||
return false;
|
||||
}
|
||||
if (esp_psram_extram_add_to_heap_allocator() != ESP_OK) {
|
||||
log_e("PSRAM could not be added to the heap!");
|
||||
return false;
|
||||
}
|
||||
#if CONFIG_SPIRAM_USE_MALLOC && !CONFIG_ARDUINO_ISR_IRAM
|
||||
heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL);
|
||||
#endif
|
||||
log_i("PSRAM added to the heap.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ARDUINO_ISR_ATTR psramFound() {
|
||||
return spiramDetected;
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR *ps_malloc(size_t size) {
|
||||
if (!spiramDetected) {
|
||||
return NULL;
|
||||
}
|
||||
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size) {
|
||||
if (!spiramDetected) {
|
||||
return NULL;
|
||||
}
|
||||
return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size) {
|
||||
if (!spiramDetected) {
|
||||
return NULL;
|
||||
}
|
||||
return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool psramInit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ARDUINO_ISR_ATTR psramFound() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR *ps_malloc(size_t size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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_PSRAM_H_
|
||||
#define _ESP32_HAL_PSRAM_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
// Clear flags in Arduino IDE when PSRAM is disabled
|
||||
#if defined(ESP32_ARDUINO_LIB_BUILDER) && !defined(BOARD_HAS_PSRAM)
|
||||
#ifdef CONFIG_SPIRAM_SUPPORT
|
||||
#undef CONFIG_SPIRAM_SUPPORT
|
||||
#endif
|
||||
#ifdef CONFIG_SPIRAM
|
||||
#undef CONFIG_SPIRAM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool psramInit();
|
||||
bool psramAddToHeap();
|
||||
bool psramFound();
|
||||
|
||||
void *ps_malloc(size_t size);
|
||||
void *ps_calloc(size_t n, size_t size);
|
||||
void *ps_realloc(void *ptr, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP32_HAL_PSRAM_H_ */
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2024 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 "soc/soc_caps.h"
|
||||
|
||||
#include "esp32-hal-rgb-led.h"
|
||||
|
||||
// Backward compatibility - Deprecated. It will be removed in future releases.
|
||||
void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {
|
||||
log_w("neopixelWrite() is deprecated. Use rgbLedWrite().");
|
||||
rgbLedWrite(pin, red_val, green_val, blue_val);
|
||||
}
|
||||
|
||||
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {
|
||||
rgbLedWriteOrdered(pin, RGB_BUILTIN_LED_COLOR_ORDER, red_val, green_val, blue_val);
|
||||
}
|
||||
|
||||
void rgbLedWriteOrdered(uint8_t pin, rgb_led_color_order_t order, uint8_t red_val, uint8_t green_val, uint8_t blue_val) {
|
||||
#if SOC_RMT_SUPPORTED
|
||||
rmt_data_t led_data[24];
|
||||
|
||||
// Verify if the pin used is RGB_BUILTIN and fix GPIO number
|
||||
#ifdef RGB_BUILTIN
|
||||
pin = pin == RGB_BUILTIN ? pin - SOC_GPIO_PIN_COUNT : pin;
|
||||
#endif
|
||||
if (!rmtInit(pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) {
|
||||
log_e("RGB LED driver initialization failed for GPIO%d!", pin);
|
||||
return;
|
||||
}
|
||||
|
||||
// default WS2812B color order is G, R, B
|
||||
int color[3] = {green_val, red_val, blue_val};
|
||||
|
||||
switch (order) {
|
||||
case LED_COLOR_ORDER_RGB:
|
||||
color[0] = red_val;
|
||||
color[1] = green_val;
|
||||
color[2] = blue_val;
|
||||
break;
|
||||
case LED_COLOR_ORDER_BGR:
|
||||
color[0] = blue_val;
|
||||
color[1] = green_val;
|
||||
color[2] = red_val;
|
||||
break;
|
||||
case LED_COLOR_ORDER_BRG:
|
||||
color[0] = blue_val;
|
||||
color[1] = red_val;
|
||||
color[2] = green_val;
|
||||
break;
|
||||
case LED_COLOR_ORDER_RBG:
|
||||
color[0] = red_val;
|
||||
color[1] = blue_val;
|
||||
color[2] = green_val;
|
||||
break;
|
||||
case LED_COLOR_ORDER_GBR:
|
||||
color[0] = green_val;
|
||||
color[1] = blue_val;
|
||||
color[2] = red_val;
|
||||
break;
|
||||
default: // GRB
|
||||
break;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (int col = 0; col < 3; col++) {
|
||||
for (int bit = 0; bit < 8; bit++) {
|
||||
if ((color[col] & (1 << (7 - bit)))) {
|
||||
// HIGH bit
|
||||
led_data[i].level0 = 1; // T1H
|
||||
led_data[i].duration0 = 8; // 0.8us
|
||||
led_data[i].level1 = 0; // T1L
|
||||
led_data[i].duration1 = 4; // 0.4us
|
||||
} else {
|
||||
// LOW bit
|
||||
led_data[i].level0 = 1; // T0H
|
||||
led_data[i].duration0 = 4; // 0.4us
|
||||
led_data[i].level1 = 0; // T0L
|
||||
led_data[i].duration1 = 8; // 0.8us
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
rmtWrite(pin, led_data, RMT_SYMBOLS_OF(led_data), RMT_WAIT_FOR_EVER);
|
||||
#else
|
||||
log_e("RMT is not supported on " CONFIG_IDF_TARGET);
|
||||
#endif /* SOC_RMT_SUPPORTED */
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef MAIN_ESP32_HAL_RGB_LED_H_
|
||||
#define MAIN_ESP32_HAL_RGB_LED_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#ifndef RGB_BRIGHTNESS
|
||||
#define RGB_BRIGHTNESS 64
|
||||
#endif
|
||||
|
||||
#ifndef RGB_BUILTIN_LED_COLOR_ORDER
|
||||
#define RGB_BUILTIN_LED_COLOR_ORDER LED_COLOR_ORDER_GRB // default WS2812B color order
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LED_COLOR_ORDER_RGB,
|
||||
LED_COLOR_ORDER_BGR,
|
||||
LED_COLOR_ORDER_BRG,
|
||||
LED_COLOR_ORDER_RBG,
|
||||
LED_COLOR_ORDER_GBR,
|
||||
LED_COLOR_ORDER_GRB
|
||||
} rgb_led_color_order_t;
|
||||
|
||||
void rgbLedWriteOrdered(uint8_t pin, rgb_led_color_order_t order, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
|
||||
|
||||
// Will use RGB_BUILTIN_LED_COLOR_ORDER
|
||||
void rgbLedWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val);
|
||||
|
||||
// Backward compatibility - Deprecated. It will be removed in future releases.
|
||||
[[deprecated("Use rgbLedWrite() instead.")]]
|
||||
void neopixelWrite(uint8_t p, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MAIN_ESP32_HAL_RGB_LED_H_ */
|
||||
@@ -0,0 +1,675 @@
|
||||
// Copyright 2024 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 "soc/soc_caps.h"
|
||||
|
||||
#if SOC_RMT_SUPPORTED
|
||||
#include "esp32-hal.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "driver/rmt_rx.h"
|
||||
#include "hal/rmt_ll.h"
|
||||
|
||||
#include "esp32-hal-rmt.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
// Arduino Task Handle indicates if the Arduino Task has been started already
|
||||
extern TaskHandle_t loopTaskHandle;
|
||||
|
||||
// RMT Events
|
||||
#define RMT_FLAG_RX_DONE (1)
|
||||
#define RMT_FLAG_TX_DONE (2)
|
||||
|
||||
/**
|
||||
Internal macros
|
||||
*/
|
||||
|
||||
#if CONFIG_DISABLE_HAL_LOCKS
|
||||
#define RMT_MUTEX_LOCK(busptr)
|
||||
#define RMT_MUTEX_UNLOCK(busptr)
|
||||
#else
|
||||
#define RMT_MUTEX_LOCK(busptr) \
|
||||
do { \
|
||||
} while (xSemaphoreTake(busptr->g_rmt_objlocks, portMAX_DELAY) != pdPASS)
|
||||
#define RMT_MUTEX_UNLOCK(busptr) xSemaphoreGive(busptr->g_rmt_objlocks)
|
||||
#endif /* CONFIG_DISABLE_HAL_LOCKS */
|
||||
|
||||
/**
|
||||
Typedefs for internal structures, enums
|
||||
*/
|
||||
|
||||
struct rmt_obj_s {
|
||||
// general RMT information
|
||||
rmt_channel_handle_t rmt_channel_h; // IDF RMT channel handler
|
||||
rmt_encoder_handle_t rmt_copy_encoder_h; // RMT simple copy encoder handle
|
||||
|
||||
uint32_t signal_range_min_ns; // RX Filter data - Low Pass pulse width
|
||||
uint32_t signal_range_max_ns; // RX idle time that defines end of reading
|
||||
|
||||
EventGroupHandle_t rmt_events; // read/write done event RMT callback handle
|
||||
bool rmt_ch_is_looping; // Is this RMT TX Channel in LOOPING MODE?
|
||||
size_t *num_symbols_read; // Pointer to the number of RMT symbol read by IDF RMT RX Done
|
||||
rmt_reserve_memsize_t mem_size; // RMT Memory size
|
||||
uint32_t frequency_Hz; // RMT Frequency
|
||||
uint8_t rmt_EOT_Level; // RMT End of Transmission Level - default is LOW
|
||||
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
SemaphoreHandle_t g_rmt_objlocks; // Channel Semaphore Lock
|
||||
#endif /* CONFIG_DISABLE_HAL_LOCKS */
|
||||
};
|
||||
|
||||
typedef struct rmt_obj_s *rmt_bus_handle_t;
|
||||
|
||||
/**
|
||||
Internal variables used in RMT API
|
||||
*/
|
||||
static SemaphoreHandle_t g_rmt_block_lock = NULL;
|
||||
|
||||
/**
|
||||
Internal method (private) declarations
|
||||
*/
|
||||
|
||||
// This is called from an IDF ISR code, therefore this code is part of an ISR
|
||||
static bool _rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *data, void *args) {
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
rmt_bus_handle_t bus = (rmt_bus_handle_t)args;
|
||||
// sets the returning number of RMT symbols (32 bits) effectively read
|
||||
*bus->num_symbols_read = data->num_symbols;
|
||||
// set RX event group and signal the received RMT symbols of that channel
|
||||
xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_RX_DONE, &high_task_wakeup);
|
||||
// A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
// This is called from an IDF ISR code, therefore this code is part of an ISR
|
||||
static bool _rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *data, void *args) {
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
rmt_bus_handle_t bus = (rmt_bus_handle_t)args;
|
||||
// set RX event group and signal the received RMT symbols of that channel
|
||||
xEventGroupSetBitsFromISR(bus->rmt_events, RMT_FLAG_TX_DONE, &high_task_wakeup);
|
||||
// A "need to yield" is returned in order to execute portYIELD_FROM_ISR() in the main IDF RX ISR
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
// This function must be called only after checking the pin and its bus with _rmtGetBus()
|
||||
static bool _rmtCheckDirection(uint8_t gpio_num, rmt_ch_dir_t rmt_dir, const char *labelFunc) {
|
||||
// gets bus RMT direction from the Peripheral Manager information
|
||||
rmt_ch_dir_t bus_rmt_dir = perimanGetPinBusType(gpio_num) == ESP32_BUS_TYPE_RMT_TX ? RMT_TX_MODE : RMT_RX_MODE;
|
||||
|
||||
if (bus_rmt_dir == rmt_dir) { // matches expected RX/TX channel
|
||||
return true;
|
||||
}
|
||||
|
||||
// print error message
|
||||
if (rmt_dir == RMT_RX_MODE) {
|
||||
log_w("==>%s():Channel set as TX instead of RX.", labelFunc);
|
||||
} else {
|
||||
log_w("==>%s():Channel set as RX instead of TX.", labelFunc);
|
||||
}
|
||||
return false; // mismatched
|
||||
}
|
||||
|
||||
static rmt_bus_handle_t _rmtGetBus(int pin, const char *labelFunc) {
|
||||
// Is pin RX or TX? Let's find it out
|
||||
peripheral_bus_type_t rmt_bus_type = perimanGetPinBusType(pin);
|
||||
if (rmt_bus_type != ESP32_BUS_TYPE_RMT_TX && rmt_bus_type != ESP32_BUS_TYPE_RMT_RX) {
|
||||
log_e("==>%s():GPIO %u is not attached to an RMT channel.", labelFunc, pin);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (rmt_bus_handle_t)perimanGetPinBus(pin, rmt_bus_type);
|
||||
}
|
||||
|
||||
// Peripheral Manager detach callback
|
||||
static bool _rmtDetachBus(void *busptr) {
|
||||
// sanity check - it should never happen
|
||||
assert(busptr && "_rmtDetachBus bus NULL pointer.");
|
||||
|
||||
bool retCode = true;
|
||||
rmt_bus_handle_t bus = (rmt_bus_handle_t)busptr;
|
||||
log_v("Detaching RMT GPIO Bus");
|
||||
|
||||
// lock it
|
||||
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
|
||||
|
||||
// free Event Group
|
||||
if (bus->rmt_events != NULL) {
|
||||
vEventGroupDelete(bus->rmt_events);
|
||||
bus->rmt_events = NULL;
|
||||
}
|
||||
// deallocate the channel encoder
|
||||
if (bus->rmt_copy_encoder_h != NULL) {
|
||||
if (ESP_OK != rmt_del_encoder(bus->rmt_copy_encoder_h)) {
|
||||
log_w("RMT Encoder Deletion has failed.");
|
||||
retCode = false;
|
||||
}
|
||||
}
|
||||
// disable and deallocate RMT channel
|
||||
if (bus->rmt_channel_h != NULL) {
|
||||
// force stopping rmt TX/RX processing and unlock Power Management (APB Freq)
|
||||
rmt_disable(bus->rmt_channel_h);
|
||||
if (ESP_OK != rmt_del_channel(bus->rmt_channel_h)) {
|
||||
log_w("RMT Channel Deletion has failed.");
|
||||
retCode = false;
|
||||
}
|
||||
}
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
// deallocate channel semaphore
|
||||
if (bus->g_rmt_objlocks != NULL) {
|
||||
vSemaphoreDelete(bus->g_rmt_objlocks);
|
||||
}
|
||||
#endif
|
||||
// free the allocated bus data structure
|
||||
free(bus);
|
||||
|
||||
// release the mutex
|
||||
xSemaphoreGive(g_rmt_block_lock);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
/**
|
||||
Public method definitions
|
||||
*/
|
||||
|
||||
bool rmtSetEOT(int pin, uint8_t EOT_Level) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bus->rmt_EOT_Level = EOT_Level > 0 ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (duty_percent > 1) {
|
||||
log_w("GPIO %d - RMT Carrier must be a float percentage from 0 to 1. Setting to 50%.", pin);
|
||||
duty_percent = 0.5;
|
||||
}
|
||||
rmt_carrier_config_t carrier_cfg;
|
||||
memset((void *)&carrier_cfg, 0, sizeof(rmt_carrier_config_t));
|
||||
carrier_cfg.duty_cycle = duty_percent; // duty cycle
|
||||
carrier_cfg.frequency_hz = carrier_en ? frequency_Hz : 0; // carrier frequency in Hz
|
||||
carrier_cfg.flags.polarity_active_low = carrier_level; // carrier modulation polarity level
|
||||
|
||||
bool retCode = true;
|
||||
RMT_MUTEX_LOCK(bus);
|
||||
// modulate carrier to TX channel
|
||||
if (ESP_OK != rmt_apply_carrier(bus->rmt_channel_h, &carrier_cfg)) {
|
||||
log_w("GPIO %d - Error applying RMT carrier.", pin);
|
||||
retCode = false;
|
||||
}
|
||||
RMT_MUTEX_UNLOCK(bus);
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
bool rmtSetRxMinThreshold(int pin, uint8_t filter_pulse_ticks) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t filter_pulse_ns = (1000000000 / bus->frequency_Hz) * filter_pulse_ticks;
|
||||
// RMT_LL_MAX_FILTER_VALUE is 255 for ESP32, S2, S3, C3, C6 and H2;
|
||||
// filter_pulse_ticks is 8 bits, thus it will not exceed 255
|
||||
#if 0 // for the future, in case some other SoC has different limit
|
||||
if (filter_pulse_ticks > RMT_LL_MAX_FILTER_VALUE) {
|
||||
log_e("filter_pulse_ticks is too big. Max = %d", RMT_LL_MAX_FILTER_VALUE);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
RMT_MUTEX_LOCK(bus);
|
||||
bus->signal_range_min_ns = filter_pulse_ns; // set zero to disable it
|
||||
RMT_MUTEX_UNLOCK(bus);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtSetRxMaxThreshold(int pin, uint16_t idle_thres_ticks) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t idle_thres_ns = (1000000000 / bus->frequency_Hz) * idle_thres_ticks;
|
||||
// RMT_LL_MAX_IDLE_VALUE is 65535 for ESP32,S2 and 32767 for S3, C3, C6 and H2
|
||||
#if RMT_LL_MAX_IDLE_VALUE < 65535 // idle_thres_ticks is 16 bits anyway - save some bytes
|
||||
if (idle_thres_ticks > RMT_LL_MAX_IDLE_VALUE) {
|
||||
log_e("idle_thres_ticks is too big. Max = %ld", RMT_LL_MAX_IDLE_VALUE);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
RMT_MUTEX_LOCK(bus);
|
||||
bus->signal_range_max_ns = idle_thres_ns;
|
||||
RMT_MUTEX_UNLOCK(bus);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rmtDeinit(int pin) {
|
||||
log_v("Deiniting RMT GPIO %d", pin);
|
||||
if (_rmtGetBus(pin, __FUNCTION__) != NULL) {
|
||||
// release all allocated data
|
||||
return perimanClearPinBus(pin);
|
||||
}
|
||||
log_e("GPIO %d - No RMT channel associated.", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, uint32_t loop, uint32_t timeout_ms) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
bool loopCancel = false; // user wants to cancel the writing loop mode
|
||||
if (data == NULL || num_rmt_symbols == 0) {
|
||||
if (!loop) {
|
||||
log_w("GPIO %d - RMT Write Data NULL pointer or size is zero.", pin);
|
||||
return false;
|
||||
} else {
|
||||
loopCancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE
|
||||
log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, num_rmt_symbols, blocking ? "Blocking" : "Non-Blocking", timeout_ms);
|
||||
// loop parameter semantics:
|
||||
// loop == 0: no looping (single transmission)
|
||||
// loop == 1: infinite looping
|
||||
// loop > 1: transmit the data 'loop' times
|
||||
{
|
||||
char buf[17]; // placeholder for up to maximum uint32_t value (4294967295) = 10 digits + " times" (6 chars) + null terminator (17 bytes)
|
||||
snprintf(buf, sizeof(buf), "%lu times", loop);
|
||||
log_v(
|
||||
"GPIO: %d - Currently in Loop Mode: [%s] | Loop Request: [%s], LoopCancel: [%s]", pin, bus->rmt_ch_is_looping ? "YES" : "NO",
|
||||
loop == 0 ? "NO" : (loop == 1 ? "FOREVER" : buf), loopCancel ? "YES" : "NO"
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) == 0) {
|
||||
log_v("GPIO %d - RMT Write still pending to be completed.", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
rmt_transmit_config_t transmit_cfg; // loop mode disabled
|
||||
memset((void *)&transmit_cfg, 0, sizeof(rmt_transmit_config_t));
|
||||
bool retCode = true;
|
||||
|
||||
RMT_MUTEX_LOCK(bus);
|
||||
// wants to start in writing or looping over a previous looping --> resets the channel
|
||||
if (bus->rmt_ch_is_looping == true) {
|
||||
// must force stopping a previous loop transmission first
|
||||
rmt_disable(bus->rmt_channel_h);
|
||||
// enable it again for looping or writing
|
||||
rmt_enable(bus->rmt_channel_h);
|
||||
bus->rmt_ch_is_looping = false; // not looping anymore
|
||||
}
|
||||
// sets the End of Transmission level to HIGH if the user has requested so
|
||||
if (bus->rmt_EOT_Level) {
|
||||
transmit_cfg.flags.eot_level = 1; // EOT is HIGH
|
||||
}
|
||||
if (loopCancel) {
|
||||
// just resets and releases the channel, maybe, already done above, then exits
|
||||
bus->rmt_ch_is_looping = false;
|
||||
} else { // new writing | looping request
|
||||
// looping | Writing over a previous looping state is valid
|
||||
if (loop > 0) {
|
||||
transmit_cfg.loop_count = (loop == 1) ? -1 : loop;
|
||||
// keeps RMT_FLAG_TX_DONE set - it never changes
|
||||
} else {
|
||||
// looping mode never sets this flag (IDF 5.1) in the callback
|
||||
xEventGroupClearBits(bus->rmt_events, RMT_FLAG_TX_DONE);
|
||||
}
|
||||
// transmits just once or looping data
|
||||
if (ESP_OK != rmt_transmit(bus->rmt_channel_h, bus->rmt_copy_encoder_h, (const void *)data, num_rmt_symbols * sizeof(rmt_data_t), &transmit_cfg)) {
|
||||
retCode = false;
|
||||
log_w("GPIO %d - RMT Transmission failed.", pin);
|
||||
} else { // transmit OK
|
||||
if (loop > 0) {
|
||||
// rmt_ch_is_looping is used as a flag to indicate that RMT is in looping execution in order to
|
||||
// be canceled whenever a new _rmtWrite() is executed while it is looping
|
||||
bus->rmt_ch_is_looping = true;
|
||||
} else {
|
||||
if (blocking) {
|
||||
// wait for transmission confirmation | timeout
|
||||
retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_TX_DONE, pdFALSE /* do not clear on exit */, pdFALSE /* wait for all bits */, timeout_ms)
|
||||
& RMT_FLAG_TX_DONE)
|
||||
!= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RMT_MUTEX_UNLOCK(bus);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
static bool _rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, bool waitForData, uint32_t timeout_ms) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
if (data == NULL || num_rmt_symbols == NULL) {
|
||||
log_w("GPIO %d - RMT Read Data and/or Size NULL pointer.", pin);
|
||||
return false;
|
||||
}
|
||||
log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, *num_rmt_symbols, waitForData ? "Blocking" : "Non-Blocking", timeout_ms);
|
||||
bool retCode = true;
|
||||
RMT_MUTEX_LOCK(bus);
|
||||
|
||||
// request reading RMT Channel Data
|
||||
rmt_receive_config_t receive_config;
|
||||
memset((void *)&receive_config, 0, sizeof(rmt_receive_config_t));
|
||||
receive_config.signal_range_min_ns = bus->signal_range_min_ns;
|
||||
receive_config.signal_range_max_ns = bus->signal_range_max_ns;
|
||||
|
||||
xEventGroupClearBits(bus->rmt_events, RMT_FLAG_RX_DONE);
|
||||
bus->num_symbols_read = num_rmt_symbols;
|
||||
|
||||
if (waitForData) {
|
||||
// resets the reading channel to start fresh
|
||||
rmt_disable(bus->rmt_channel_h);
|
||||
rmt_enable(bus->rmt_channel_h);
|
||||
}
|
||||
|
||||
rmt_receive(bus->rmt_channel_h, data, *num_rmt_symbols * sizeof(rmt_data_t), &receive_config);
|
||||
// wait for data if requested
|
||||
if (waitForData) {
|
||||
retCode = (xEventGroupWaitBits(bus->rmt_events, RMT_FLAG_RX_DONE, pdFALSE /* do not clear on exit */, pdFALSE /* wait for all bits */, timeout_ms)
|
||||
& RMT_FLAG_RX_DONE)
|
||||
!= 0;
|
||||
}
|
||||
|
||||
RMT_MUTEX_UNLOCK(bus);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms) {
|
||||
return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, 0 /*looping*/, timeout_ms);
|
||||
}
|
||||
|
||||
bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols) {
|
||||
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/);
|
||||
}
|
||||
|
||||
bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols) {
|
||||
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 1 /*looping*/, 0 /*N/A*/);
|
||||
}
|
||||
|
||||
// Same as rmtWriteLooping(...) but it transmits the data a fixed number of times ("loop_count").
|
||||
// loop_count == 0 is invalid (no transmission); loop_count == 1 transmits once (no looping); loop_count > 1 transmits the data repeatedly (looping).
|
||||
bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count) {
|
||||
if (loop_count == 0) {
|
||||
log_e("RMT TX GPIO %d : Invalid loop_count (%u). Must be at least 1.", pin, loop_count);
|
||||
return false;
|
||||
}
|
||||
if (loop_count == 1) {
|
||||
// send the RMT symbols once using non-blocking write (single non-looping transmission)
|
||||
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/);
|
||||
} else {
|
||||
// write the RMT symbols for loop_count times
|
||||
#if SOC_RMT_SUPPORT_TX_LOOP_COUNT
|
||||
return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, loop_count /*looping*/, 0 /*N/A*/);
|
||||
#else
|
||||
log_e("RMT TX GPIO %d : Loop Count is not supported. Writing failed.", pin);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool rmtTransmitCompleted(int pin) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (!_rmtCheckDirection(pin, RMT_TX_MODE, __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retCode = true;
|
||||
RMT_MUTEX_LOCK(bus);
|
||||
retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) != 0;
|
||||
RMT_MUTEX_UNLOCK(bus);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
bool rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, uint32_t timeout_ms) {
|
||||
return _rmtRead(pin, data, num_rmt_symbols, true /* blocking */, timeout_ms);
|
||||
}
|
||||
|
||||
bool rmtReadAsync(int pin, rmt_data_t *data, size_t *num_rmt_symbols) {
|
||||
return _rmtRead(pin, data, num_rmt_symbols, false /* non-blocking */, 0 /* N/A */);
|
||||
}
|
||||
|
||||
bool rmtReceiveCompleted(int pin) {
|
||||
rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__);
|
||||
if (bus == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (!_rmtCheckDirection(pin, RMT_RX_MODE, __FUNCTION__)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retCode = true;
|
||||
RMT_MUTEX_LOCK(bus);
|
||||
retCode = (xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_RX_DONE) != 0;
|
||||
RMT_MUTEX_UNLOCK(bus);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t mem_size, uint32_t frequency_Hz) {
|
||||
log_v(
|
||||
"GPIO %d - %s - MemSize[%d] - Freq=%dHz", pin, channel_direction == RMT_RX_MODE ? "RX MODE" : "TX MODE", mem_size * RMT_SYMBOLS_PER_CHANNEL_BLOCK,
|
||||
frequency_Hz
|
||||
);
|
||||
|
||||
// create common block mutex for protecting allocs from multiple threads allocating RMT channels
|
||||
if (!g_rmt_block_lock) {
|
||||
g_rmt_block_lock = xSemaphoreCreateMutex();
|
||||
if (g_rmt_block_lock == NULL) {
|
||||
log_e("GPIO %d - Failed creating RMT Mutex.", pin);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check if the RMT peripheral is already initialized with the same parameters
|
||||
rmt_bus_handle_t bus = NULL;
|
||||
peripheral_bus_type_t rmt_bus_type = perimanGetPinBusType(pin);
|
||||
if (rmt_bus_type == ESP32_BUS_TYPE_RMT_TX || rmt_bus_type == ESP32_BUS_TYPE_RMT_RX) {
|
||||
rmt_ch_dir_t bus_rmt_dir = rmt_bus_type == ESP32_BUS_TYPE_RMT_TX ? RMT_TX_MODE : RMT_RX_MODE;
|
||||
bus = (rmt_bus_handle_t)perimanGetPinBus(pin, rmt_bus_type);
|
||||
if (bus->frequency_Hz == frequency_Hz && bus_rmt_dir == channel_direction && bus->mem_size == mem_size) {
|
||||
return true; // already initialized with the same parameters
|
||||
}
|
||||
}
|
||||
|
||||
// set Peripheral Manager deInit Callback
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_TX, _rmtDetachBus);
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_RMT_RX, _rmtDetachBus);
|
||||
|
||||
// check is pin is valid and in the right direction
|
||||
if ((channel_direction == RMT_TX_MODE && !GPIO_IS_VALID_OUTPUT_GPIO(pin)) || (!GPIO_IS_VALID_GPIO(pin))) {
|
||||
log_e("GPIO %d is not valid or can't be used for output in TX mode.", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the RMT ticks by the requested frequency
|
||||
// Based on 80Mhz using a divider of 8 bits (calculated as 1..256)
|
||||
if (frequency_Hz > 80000000 || frequency_Hz < 312500) {
|
||||
log_e("GPIO %d - Bad RMT frequency resolution. Must be between 312.5KHz to 80MHz.", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to detach any (Tx|Rx|Whatever) previous bus or just keep it as not attached
|
||||
if (!perimanClearPinBus(pin)) {
|
||||
log_w("GPIO %d - Can't detach previous peripheral.", pin);
|
||||
return false;
|
||||
}
|
||||
|
||||
// lock it
|
||||
while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {}
|
||||
|
||||
// allocate the rmt bus object and sets all fields to NULL
|
||||
bus = (rmt_bus_handle_t)heap_caps_calloc(1, sizeof(struct rmt_obj_s), MALLOC_CAP_DEFAULT);
|
||||
if (bus == NULL) {
|
||||
log_e("GPIO %d - Bus Memory allocation fault.", pin);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
// store the RMT Freq and mem_size to check Initialization, Filter and Idle valid values in the RMT API
|
||||
bus->frequency_Hz = frequency_Hz;
|
||||
bus->mem_size = mem_size;
|
||||
// pulses with width smaller than min_ns will be ignored (as a glitch)
|
||||
//bus->signal_range_min_ns = 0; // disabled --> not necessary CALLOC set all to ZERO.
|
||||
// RMT stops reading if the input stays idle for longer than max_ns
|
||||
bus->signal_range_max_ns = (1000000000 / frequency_Hz) * RMT_LL_MAX_IDLE_VALUE; // maximum possible
|
||||
// creates the event group to control read_done and write_done
|
||||
bus->rmt_events = xEventGroupCreate();
|
||||
if (bus->rmt_events == NULL) {
|
||||
log_e("GPIO %d - RMT Group Event allocation fault.", pin);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
// Starting with Receive|Transmit DONE bits set, for allowing a new request from user
|
||||
xEventGroupSetBits(bus->rmt_events, RMT_FLAG_RX_DONE | RMT_FLAG_TX_DONE);
|
||||
|
||||
// channel particular configuration
|
||||
if (channel_direction == RMT_TX_MODE) {
|
||||
// TX Channel
|
||||
rmt_tx_channel_config_t tx_cfg;
|
||||
memset((void *)&tx_cfg, 0, sizeof(rmt_tx_channel_config_t));
|
||||
tx_cfg.gpio_num = pin;
|
||||
// CLK_APB for ESP32|S2|S3|C3 -- CLK_PLL_F80M for C6 -- CLK_XTAL for H2
|
||||
tx_cfg.clk_src = RMT_CLK_SRC_DEFAULT;
|
||||
tx_cfg.resolution_hz = frequency_Hz;
|
||||
tx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size;
|
||||
tx_cfg.trans_queue_depth = 10; // maximum allowed
|
||||
tx_cfg.flags.invert_out = 0;
|
||||
tx_cfg.flags.with_dma = 0;
|
||||
tx_cfg.flags.io_loop_back = 0;
|
||||
tx_cfg.flags.io_od_mode = 0;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2)
|
||||
tx_cfg.intr_priority = 0;
|
||||
#endif
|
||||
|
||||
if (rmt_new_tx_channel(&tx_cfg, &bus->rmt_channel_h) != ESP_OK) {
|
||||
log_e("GPIO %d - RMT TX Initialization error.", pin);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
// set TX Callback
|
||||
rmt_tx_event_callbacks_t cbs = {.on_trans_done = _rmt_tx_done_callback};
|
||||
if (ESP_OK != rmt_tx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) {
|
||||
log_e("GPIO %d RMT - Error registering TX Callback.", pin);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
} else {
|
||||
// RX Channel
|
||||
rmt_rx_channel_config_t rx_cfg;
|
||||
memset((void *)&rx_cfg, 0, sizeof(rmt_rx_channel_config_t));
|
||||
rx_cfg.gpio_num = pin;
|
||||
// CLK_APB for ESP32|S2|S3|C3 -- CLK_PLL_F80M for C6 -- CLK_XTAL for H2
|
||||
rx_cfg.clk_src = RMT_CLK_SRC_DEFAULT;
|
||||
rx_cfg.resolution_hz = frequency_Hz;
|
||||
rx_cfg.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL * mem_size;
|
||||
rx_cfg.flags.invert_in = 0;
|
||||
rx_cfg.flags.with_dma = 0;
|
||||
rx_cfg.flags.io_loop_back = 0;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2)
|
||||
rx_cfg.intr_priority = 0;
|
||||
#endif
|
||||
// try to allocate the RMT Channel
|
||||
if (ESP_OK != rmt_new_rx_channel(&rx_cfg, &bus->rmt_channel_h)) {
|
||||
log_e("GPIO %d RMT - RX Initialization error.", pin);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
// set RX Callback
|
||||
rmt_rx_event_callbacks_t cbs = {.on_recv_done = _rmt_rx_done_callback};
|
||||
if (ESP_OK != rmt_rx_register_event_callbacks(bus->rmt_channel_h, &cbs, bus)) {
|
||||
log_e("GPIO %d RMT - Error registering RX Callback.", pin);
|
||||
goto Err;
|
||||
}
|
||||
}
|
||||
|
||||
// allocate memory for the RMT Copy encoder
|
||||
rmt_copy_encoder_config_t copy_encoder_config;
|
||||
memset((void *)©_encoder_config, 0, sizeof(rmt_copy_encoder_config_t));
|
||||
if (rmt_new_copy_encoder(©_encoder_config, &bus->rmt_copy_encoder_h) != ESP_OK) {
|
||||
log_e("GPIO %d - RMT Encoder Memory Allocation error.", pin);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
// create each channel Mutex for multi thread operations
|
||||
#if !CONFIG_DISABLE_HAL_LOCKS
|
||||
bus->g_rmt_objlocks = xSemaphoreCreateMutex();
|
||||
if (bus->g_rmt_objlocks == NULL) {
|
||||
log_e("GPIO %d - Failed creating RMT Channel Mutex.", pin);
|
||||
goto Err;
|
||||
}
|
||||
#endif
|
||||
|
||||
rmt_enable(bus->rmt_channel_h); // starts/enables the channel
|
||||
|
||||
// Finally, allocate Peripheral Manager RMT bus and associate it to its GPIO
|
||||
peripheral_bus_type_t pinBusType = channel_direction == RMT_TX_MODE ? ESP32_BUS_TYPE_RMT_TX : ESP32_BUS_TYPE_RMT_RX;
|
||||
if (!perimanSetPinBus(pin, pinBusType, (void *)bus, -1, -1)) {
|
||||
log_e("Can't allocate the GPIO %d in the Peripheral Manager.", pin);
|
||||
goto Err;
|
||||
}
|
||||
|
||||
// this delay is necessary when CPU frequency changes, but internal RMT setup is "old/wrong"
|
||||
// The use case is related to the RMT_CPUFreq_Test example. The very first RMT Write
|
||||
// goes in the wrong pace (frequency). The delay allows other IDF tasks to run to fix it.
|
||||
if (loopTaskHandle != NULL) {
|
||||
// it can only run when Arduino task has been already started.
|
||||
delay(1);
|
||||
} // prevent panic when rmtInit() is executed within an C++ object constructor
|
||||
// release the mutex
|
||||
xSemaphoreGive(g_rmt_block_lock);
|
||||
return true;
|
||||
|
||||
Err:
|
||||
// release LOCK and the RMT object
|
||||
xSemaphoreGive(g_rmt_block_lock);
|
||||
_rmtDetachBus((void *)bus);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* SOC_RMT_SUPPORTED */
|
||||
@@ -0,0 +1,240 @@
|
||||
// Copyright 2023 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef MAIN_ESP32_HAL_RMT_H_
|
||||
#define MAIN_ESP32_HAL_RMT_H_
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_RMT_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
RMT_RX_MODE = 0, // false
|
||||
RMT_TX_MODE = 1, // true
|
||||
} rmt_ch_dir_t;
|
||||
|
||||
typedef enum {
|
||||
RMT_MEM_NUM_BLOCKS_1 = 1,
|
||||
RMT_MEM_NUM_BLOCKS_2 = 2,
|
||||
#if SOC_RMT_TX_CANDIDATES_PER_GROUP > 2
|
||||
RMT_MEM_NUM_BLOCKS_3 = 3,
|
||||
RMT_MEM_NUM_BLOCKS_4 = 4,
|
||||
#if SOC_RMT_TX_CANDIDATES_PER_GROUP > 4
|
||||
RMT_MEM_NUM_BLOCKS_5 = 5,
|
||||
RMT_MEM_NUM_BLOCKS_6 = 6,
|
||||
RMT_MEM_NUM_BLOCKS_7 = 7,
|
||||
RMT_MEM_NUM_BLOCKS_8 = 8,
|
||||
#endif
|
||||
#endif
|
||||
} rmt_reserve_memsize_t;
|
||||
|
||||
// Each RMT Symbols has 4 bytes
|
||||
// Total number of bytes per RMT_MEM_BLOCK is RMT_SYMBOLS_PER_CHANNEL_BLOCK * 4 bytes
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t duration0 : 15;
|
||||
uint32_t level0 : 1;
|
||||
uint32_t duration1 : 15;
|
||||
uint32_t level1 : 1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rmt_data_t;
|
||||
|
||||
// Reading and Writing shall use as rmt_symbols_size this unit
|
||||
// ESP32 has 8 MEM BLOCKS in total shared with Reading and/or Writing
|
||||
// ESP32-S2 has 4 MEM BLOCKS in total shared with Reading and/or Writing
|
||||
// ESP32-S3 has 4 MEM BLOCKS for Reading and another 4 MEM BLOCKS for Writing
|
||||
// ESP32-C3 has 2 MEM BLOCKS for Reading and another 2 MEM BLOCKS for Writing
|
||||
#define RMT_SYMBOLS_PER_CHANNEL_BLOCK SOC_RMT_MEM_WORDS_PER_CHANNEL
|
||||
|
||||
// Used to tell rmtRead() to wait for ever until reading data from the RMT channel
|
||||
#define RMT_WAIT_FOR_EVER ((uint32_t)portMAX_DELAY)
|
||||
|
||||
// Helper macro to calculate the number of RTM symbols in a array or type
|
||||
#define RMT_SYMBOLS_OF(x) (sizeof(x) / sizeof(rmt_data_t))
|
||||
|
||||
/**
|
||||
Initialize the object
|
||||
|
||||
New Parameters in Arduino Core 3: RMT tick is set in the rmtInit() function by the
|
||||
frequency of the RMT channel. Example: 100ns tick => 10MHz, thus frequency will be 10,000,000 Hz
|
||||
Returns <true> on execution success, <false> otherwise
|
||||
*/
|
||||
bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz);
|
||||
|
||||
/**
|
||||
Sets the End of Transmission level to be set for the <pin> when the RMT transmission ends.
|
||||
This function affects how rmtWrite(), rmtWriteAsync() or rmtWriteLooping() will set the pin after writing the data.
|
||||
The default EOT level is LOW, in case this function isn't used before RMT Writing.
|
||||
This level can be set for each RMT pin and can be changed between writings to the same pin.
|
||||
|
||||
<EOT_Level> shall be Zero (LOW) or non-zero (HIGH) value.
|
||||
It only affects the transmission process, therefore, it doesn't affect any IDLE LEVEL before starting the RMT transmission.
|
||||
The pre-transmission idle level can be set manually calling, for instance, digitalWrite(pin, Level).
|
||||
|
||||
Returns <true> when EOT has been correctly set for <pin>, <false> otherwise.
|
||||
*/
|
||||
bool rmtSetEOT(int pin, uint8_t EOT_Level);
|
||||
|
||||
/**
|
||||
Sending data in Blocking Mode.
|
||||
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
|
||||
It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of <rmt_data_t>.
|
||||
|
||||
Blocking mode - only returns after sending all data or by timeout.
|
||||
If the writing operation takes longer than <timeout_ms> in milliseconds, it will end its
|
||||
execution returning <false>.
|
||||
Timeout can be set as undefined time by passing <RMT_WAIT_FOR_EVER> as <timeout_ms> parameter.
|
||||
When the operation is timed out, rmtTransmitCompleted() will return <false> until the transmission
|
||||
is finished, when rmtTransmitCompleted() will return <true>.
|
||||
|
||||
Returns <true> when there is no error in the write operation, <false> otherwise, including when it
|
||||
exits by timeout.
|
||||
*/
|
||||
bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
Sending data in Async Mode.
|
||||
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
|
||||
It is possible to use the macro RMT_SYMBOLS_OF(data), if <data> is an array of <rmt_data_t>
|
||||
|
||||
If more than one rmtWriteAsync() is executed in sequence, it will wait for the first transmission
|
||||
to finish, resulting in a return <false> that indicates that the rmtWriteAsync() call has failed.
|
||||
In such case, this channel will have to finish the previous transmission before starting a new one.
|
||||
|
||||
Non-Blocking mode - returns right after execution.
|
||||
Returns <true> on execution success, <false> otherwise.
|
||||
|
||||
<bool rmtTransmitCompleted(int pin)> will return <true> when all data is sent.
|
||||
*/
|
||||
bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols);
|
||||
|
||||
/**
|
||||
Writing data up to the reserved memsize, looping continuously (rmtWriteLooping()) or fixed
|
||||
number of <loop_count> times (rmtWriteRepeated())
|
||||
|
||||
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
|
||||
It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t
|
||||
|
||||
If *data or size_byte are NULL | Zero, it will disable the writing loop and stop transmission
|
||||
|
||||
Non-Blocking mode - returns right after execution
|
||||
Returns <true> on execution success, <false> otherwise
|
||||
|
||||
<bool rmtTransmitCompleted(int pin)> will return always <true> while it is looping mode.
|
||||
looping mode is active for rmtWriteLooping() and for rmtWriteRepeated() when loop_count > 1.
|
||||
*/
|
||||
bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols);
|
||||
bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count);
|
||||
|
||||
/**
|
||||
Checks if transmission is completed and the rmtChannel ready for transmitting new data.
|
||||
To be ready for a new transmission, means that the previous transmission is completed.
|
||||
Returns <true> when all data has been sent, <false> otherwise.
|
||||
The data transmission information is reset when a new rmtWrite/Async function is called.
|
||||
If rmtWrite() times out or rmtWriteAsync() is called, this function will return <false> until
|
||||
all data is sent out.
|
||||
rmtTranmitCompleted() will always return <true> when rmtWriteLooping() is called,
|
||||
because it has no effect in such case.
|
||||
*/
|
||||
bool rmtTransmitCompleted(int pin);
|
||||
|
||||
/**
|
||||
Initiates blocking receive. Read data will be stored in a user provided buffer <*data>
|
||||
It will read up to <num_rmt_symbols> RMT Symbols and the value of this variable will
|
||||
change to the effective number of symbols read.
|
||||
<rmt_symbol> is a 32 bits structure as defined by rmt_data_t type.
|
||||
|
||||
If the reading operation takes longer than <timeout_ms> in milliseconds, it will end its
|
||||
execution and the function will return <false>. In a time out scenario, <num_rmt_symbols> won't
|
||||
change and rmtReceiveCompleted() can be used latter to check if there is data available.
|
||||
Timeout can be set as undefined time by passing RMT_WAIT_FOR_EVER as <timeout_ms> parameter
|
||||
|
||||
Returns <true> when there is no error in the read operation, <false> otherwise, including when it
|
||||
exits by timeout.
|
||||
Returns, by value, the number of RMT Symbols read in <num_rmt_symbols> and the user buffer <data>
|
||||
when the read operation has success within the defined <timeout_ms>. If the function times out, it
|
||||
will read RMT data latter asynchronously, affecting <*data> and <*num_rmt_symbols>. After timeout,
|
||||
the application can check if data is already available using <rmtReceiveCompleted(int pin)>
|
||||
*/
|
||||
bool rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
Initiates async (non-blocking) receive. It will return immediately after execution.
|
||||
Read data will be stored in a user provided buffer <*data>.
|
||||
It will read up to <num_rmt_symbols> RMT Symbols and the value of this variable will
|
||||
change to the effective number of symbols read, whenever the read is completed.
|
||||
<rmt_symbol> is a 32 bits structure as defined by <rmt_data_t> type.
|
||||
|
||||
Returns <true> when there is no error in the read operation, <false> otherwise.
|
||||
Returns asynchronously, by value, the number of RMT Symbols read, and also, it will copy
|
||||
the RMT received data to the user buffer <data> when the read operation happens.
|
||||
The application can check if data is already available using <rmtReceiveCompleted(int pin)>
|
||||
*/
|
||||
bool rmtReadAsync(int pin, rmt_data_t *data, size_t *num_rmt_symbols);
|
||||
|
||||
/**
|
||||
Checks if a data reception is completed and the rmtChannel has new data for processing.
|
||||
Returns <true> when data has been received, <false> otherwise.
|
||||
The data reception information is reset when a new rmtRead/Async function is called.
|
||||
*/
|
||||
bool rmtReceiveCompleted(int pin);
|
||||
|
||||
/**
|
||||
Function used to set a threshold (in ticks) used to consider that a data reception has ended.
|
||||
In receive mode, when no edge is detected on the input signal for longer than idle_thres_ticks
|
||||
time, the receiving process is finished and the Data is made available by
|
||||
the rmtRead/Async functions. Note that this time (in RMT channel frequency cycles) will also
|
||||
define how many low/high bits are read at the end of the received data.
|
||||
The function returns <true> if it is correctly executed, <false> otherwise.
|
||||
*/
|
||||
bool rmtSetRxMaxThreshold(int pin, uint16_t idle_thres_ticks);
|
||||
|
||||
/**
|
||||
Parameters changed in Arduino Core 3: low and high (ticks) are now expressed in Carrier Freq in Hz and
|
||||
duty cycle in percentage float 0.0 to 1.0 - example: 38.5KHz 33% High => 38500, 0.33
|
||||
|
||||
Function to set a RX demodulation carrier or TX modulation carrier
|
||||
<carrier_en> is used to enable/disable the use of demodulation/modulation for RX/TX
|
||||
<carrier_level> true means that the polarity level for the (de)modulation is positive
|
||||
<frequency_Hz> is the carrier frequency used
|
||||
<duty_percent> is a float deom 0 to 1 (0.5 means a square wave) of the carrier frequency
|
||||
The function returns <true> if it is correctly executed, <false> otherwise.
|
||||
*/
|
||||
bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent);
|
||||
|
||||
/**
|
||||
Function used to filter input noise in the RX channel.
|
||||
In receiving mode, channel will ignore any input pulse which width (high or low)
|
||||
is smaller than <filter_pulse_ticks>
|
||||
If <filter_pulse_ns> is Zero, it will to disable the filter.
|
||||
The function returns <true> if it is correctly executed, <false> otherwise.
|
||||
*/
|
||||
bool rmtSetRxMinThreshold(int pin, uint8_t filter_pulse_ticks);
|
||||
|
||||
/**
|
||||
Deinitializes the driver and releases all allocated memory
|
||||
It also disables RMT for this gpio
|
||||
*/
|
||||
bool rmtDeinit(int pin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_RMT_SUPPORTED */
|
||||
#endif /* MAIN_ESP32_HAL_RMT_H_ */
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp32-hal-sigmadelta.h"
|
||||
|
||||
#if SOC_SDM_SUPPORTED
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "driver/sdm.h"
|
||||
|
||||
static bool sigmaDeltaDetachBus(void *bus) {
|
||||
esp_err_t err = sdm_channel_disable((sdm_channel_handle_t)bus);
|
||||
if (err != ESP_OK) {
|
||||
log_w("sdm_channel_disable failed with error: %d", err);
|
||||
}
|
||||
err = sdm_del_channel((sdm_channel_handle_t)bus);
|
||||
if (err != ESP_OK) {
|
||||
log_e("sdm_del_channel failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sigmaDeltaAttach(uint8_t pin, uint32_t freq) //freq 1220-312500
|
||||
{
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_SIGMADELTA, sigmaDeltaDetachBus);
|
||||
sdm_channel_handle_t bus = NULL;
|
||||
// pin may be previously attached to other peripheral -> detach it.
|
||||
// if attached to sigmaDelta, detach it and set the new frequency
|
||||
if (perimanGetPinBusType(pin) != ESP32_BUS_TYPE_INIT && !perimanClearPinBus(pin)) {
|
||||
log_e("Pin %u could not be detached.", pin);
|
||||
return false;
|
||||
}
|
||||
sdm_config_t config = {.gpio_num = (int)pin, .clk_src = SDM_CLK_SRC_DEFAULT, .sample_rate_hz = freq, .flags = {.invert_out = 0, .io_loop_back = 0}};
|
||||
esp_err_t err = sdm_new_channel(&config, &bus);
|
||||
if (err != ESP_OK) {
|
||||
log_e("sdm_new_channel failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
err = sdm_channel_enable(bus);
|
||||
if (err != ESP_OK) {
|
||||
sigmaDeltaDetachBus((void *)bus);
|
||||
log_e("sdm_channel_enable failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_SIGMADELTA, (void *)bus, -1, -1)) {
|
||||
sigmaDeltaDetachBus((void *)bus);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sigmaDeltaWrite(uint8_t pin, uint8_t duty) //chan 0-x according to SOC duty 8 bit
|
||||
{
|
||||
sdm_channel_handle_t bus = (sdm_channel_handle_t)perimanGetPinBus(pin, ESP32_BUS_TYPE_SIGMADELTA);
|
||||
if (bus != NULL) {
|
||||
int8_t d = duty - 128;
|
||||
esp_err_t err = sdm_channel_set_duty(bus, d);
|
||||
if (err != ESP_OK) {
|
||||
log_e("sdm_channel_set_duty failed with error: %d", err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
log_e("pin %u is not attached to SigmaDelta", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sigmaDeltaDetach(uint8_t pin) {
|
||||
void *bus = perimanGetPinBus(pin, ESP32_BUS_TYPE_SIGMADELTA);
|
||||
if (bus != NULL) {
|
||||
// will call sigmaDeltaDetachBus
|
||||
return perimanClearPinBus(pin);
|
||||
} else {
|
||||
log_e("pin %u is not attached to SigmaDelta", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_SDM_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//freq 1220-312500 duty 0-255
|
||||
bool sigmaDeltaAttach(uint8_t pin, uint32_t freq);
|
||||
bool sigmaDeltaWrite(uint8_t pin, uint8_t duty);
|
||||
bool sigmaDeltaDetach(uint8_t pin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_SDM_SUPPORTED */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
// 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 MAIN_ESP32_HAL_SPI_H_
|
||||
#define MAIN_ESP32_HAL_SPI_H_
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_GPSPI_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SPI_HAS_TRANSACTION
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#define FSPI 1 //SPI 1 bus attached to the flash (can use the same data lines but different SS)
|
||||
#define HSPI 2 //SPI 2 bus normally mapped to pins 12 - 15, but can be matrixed to any pins
|
||||
#define VSPI 3 //SPI 3 bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins
|
||||
#else
|
||||
#define FSPI 0 // ESP32C2, C3, C5, C6, C61, H2, S2, S3, P4 - SPI 2 bus
|
||||
#define HSPI 1 // ESP32S2, S3, P4 - SPI 3 bus
|
||||
#endif
|
||||
|
||||
// This defines are not representing the real Divider of the ESP32
|
||||
// the Defines match to an AVR Arduino on 16MHz for better compatibility
|
||||
#define SPI_CLOCK_DIV2 0x00101001 //8 MHz
|
||||
#define SPI_CLOCK_DIV4 0x00241001 //4 MHz
|
||||
#define SPI_CLOCK_DIV8 0x004c1001 //2 MHz
|
||||
#define SPI_CLOCK_DIV16 0x009c1001 //1 MHz
|
||||
#define SPI_CLOCK_DIV32 0x013c1001 //500 KHz
|
||||
#define SPI_CLOCK_DIV64 0x027c1001 //250 KHz
|
||||
#define SPI_CLOCK_DIV128 0x04fc1001 //125 KHz
|
||||
|
||||
#define SPI_MODE0 0
|
||||
#define SPI_MODE1 1
|
||||
#define SPI_MODE2 2
|
||||
#define SPI_MODE3 3
|
||||
|
||||
#define SPI_SS0 0
|
||||
#define SPI_SS1 1
|
||||
#define SPI_SS2 2
|
||||
#define SPI_SS_MASK_ALL 0x7
|
||||
|
||||
#define SPI_LSBFIRST 0
|
||||
#define SPI_MSBFIRST 1
|
||||
|
||||
struct spi_struct_t;
|
||||
typedef struct spi_struct_t spi_t;
|
||||
|
||||
spi_t *spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder);
|
||||
void spiStopBus(spi_t *spi);
|
||||
|
||||
//Attach/Detach Signal Pins
|
||||
bool spiAttachSCK(spi_t *spi, int8_t sck);
|
||||
bool spiAttachMISO(spi_t *spi, int8_t miso);
|
||||
bool spiAttachMOSI(spi_t *spi, int8_t mosi);
|
||||
bool spiDetachSCK(spi_t *spi);
|
||||
bool spiDetachMISO(spi_t *spi);
|
||||
bool spiDetachMOSI(spi_t *spi);
|
||||
|
||||
//Attach/Detach SS pin to SPI_SSx signal
|
||||
bool spiAttachSS(spi_t *spi, uint8_t ss_num, int8_t ss);
|
||||
bool spiDetachSS(spi_t *spi);
|
||||
|
||||
//Enable/Disable SPI_SSx pins
|
||||
void spiEnableSSPins(spi_t *spi, uint8_t ss_mask);
|
||||
void spiDisableSSPins(spi_t *spi, uint8_t ss_mask);
|
||||
|
||||
//Enable/Disable hardware control of SPI_SSx pins
|
||||
void spiSSEnable(spi_t *spi);
|
||||
void spiSSDisable(spi_t *spi);
|
||||
|
||||
//Activate enabled SPI_SSx pins
|
||||
void spiSSSet(spi_t *spi);
|
||||
//Deactivate enabled SPI_SSx pins
|
||||
void spiSSClear(spi_t *spi);
|
||||
|
||||
void spiWaitReady(spi_t *spi);
|
||||
//invert hardware SS
|
||||
void spiSSInvert(spi_t *spi, bool invert);
|
||||
|
||||
uint32_t spiGetClockDiv(spi_t *spi);
|
||||
uint8_t spiGetDataMode(spi_t *spi);
|
||||
uint8_t spiGetBitOrder(spi_t *spi);
|
||||
|
||||
/*
|
||||
* Non transaction based lock methods (each locks and unlocks when called)
|
||||
* */
|
||||
void spiSetClockDiv(spi_t *spi, uint32_t clockDiv);
|
||||
void spiSetDataMode(spi_t *spi, uint8_t dataMode);
|
||||
void spiSetBitOrder(spi_t *spi, uint8_t bitOrder);
|
||||
|
||||
void spiWrite(spi_t *spi, const uint32_t *data, uint8_t len);
|
||||
void spiWriteByte(spi_t *spi, uint8_t data);
|
||||
void spiWriteWord(spi_t *spi, uint16_t data);
|
||||
void spiWriteLong(spi_t *spi, uint32_t data);
|
||||
|
||||
void spiTransfer(spi_t *spi, uint32_t *out, uint8_t len);
|
||||
uint8_t spiTransferByte(spi_t *spi, uint8_t data);
|
||||
uint16_t spiTransferWord(spi_t *spi, uint16_t data);
|
||||
uint32_t spiTransferLong(spi_t *spi, uint32_t data);
|
||||
void spiTransferBytes(spi_t *spi, const uint8_t *data, uint8_t *out, uint32_t size);
|
||||
void spiTransferBits(spi_t *spi, uint32_t data, uint32_t *out, uint8_t bits);
|
||||
|
||||
/*
|
||||
* New (EXPERIMENTAL) Transaction lock based API (lock once until endTransaction)
|
||||
* */
|
||||
void spiTransaction(spi_t *spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder);
|
||||
void spiSimpleTransaction(spi_t *spi);
|
||||
void spiEndTransaction(spi_t *spi);
|
||||
|
||||
void spiWriteNL(spi_t *spi, const void *data_in, uint32_t len);
|
||||
void spiWriteByteNL(spi_t *spi, uint8_t data);
|
||||
void spiWriteShortNL(spi_t *spi, uint16_t data);
|
||||
void spiWriteLongNL(spi_t *spi, uint32_t data);
|
||||
void spiWritePixelsNL(spi_t *spi, const void *data_in, uint32_t len);
|
||||
|
||||
#define spiTransferNL(spi, data, len) spiTransferBytesNL(spi, data, data, len)
|
||||
uint8_t spiTransferByteNL(spi_t *spi, uint8_t data);
|
||||
uint16_t spiTransferShortNL(spi_t *spi, uint16_t data);
|
||||
uint32_t spiTransferLongNL(spi_t *spi, uint32_t data);
|
||||
void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint32_t len);
|
||||
void spiTransferBitsNL(spi_t *spi, uint32_t data_in, uint32_t *data_out, uint8_t bits);
|
||||
|
||||
/*
|
||||
* Helper functions to translate frequency to clock divider and back
|
||||
* */
|
||||
uint32_t spiFrequencyToClockDiv(spi_t *spi, uint32_t freq);
|
||||
uint32_t spiClockDivToFrequency(spi_t *spi, uint32_t clockDiv);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_GPSPI_SUPPORTED */
|
||||
#endif /* MAIN_ESP32_HAL_SPI_H_ */
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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 "esp32-hal.h"
|
||||
#include "lwip/apps/sntp.h"
|
||||
//#include "tcpip_adapter.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
#endif
|
||||
|
||||
static void setTimeZone(long offset, int daylight) {
|
||||
char cst[21] = {0};
|
||||
char cdt[21] = "DST";
|
||||
char tz[41] = {0};
|
||||
|
||||
if (offset % 3600) {
|
||||
snprintf(cst, sizeof(cst), "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60));
|
||||
} else {
|
||||
snprintf(cst, sizeof(cst), "UTC%ld", offset / 3600);
|
||||
}
|
||||
if (daylight != 3600) {
|
||||
long tz_dst = offset - daylight;
|
||||
if (tz_dst % 3600) {
|
||||
snprintf(cdt, sizeof(cdt), "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60));
|
||||
} else {
|
||||
snprintf(cdt, sizeof(cdt), "DST%ld", tz_dst / 3600);
|
||||
}
|
||||
}
|
||||
snprintf(tz, sizeof(tz), "%s%s", cst, cdt);
|
||||
setenv("TZ", tz, 1);
|
||||
tzset();
|
||||
}
|
||||
|
||||
/*
|
||||
* configTime
|
||||
* Source: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/time.c
|
||||
* */
|
||||
void configTime(long gmtOffset_sec, int daylightOffset_sec, const char *server1, const char *server2, const char *server3) {
|
||||
//tcpip_adapter_init(); // Should not hurt anything if already inited
|
||||
esp_netif_init();
|
||||
|
||||
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||
if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
|
||||
LOCK_TCPIP_CORE();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sntp_enabled()) {
|
||||
sntp_stop();
|
||||
}
|
||||
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, (char *)server1);
|
||||
sntp_setservername(1, (char *)server2);
|
||||
sntp_setservername(2, (char *)server3);
|
||||
sntp_init();
|
||||
|
||||
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||
if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
|
||||
UNLOCK_TCPIP_CORE();
|
||||
}
|
||||
#endif
|
||||
|
||||
setTimeZone(-gmtOffset_sec, daylightOffset_sec);
|
||||
}
|
||||
|
||||
/*
|
||||
* configTzTime
|
||||
* sntp setup using TZ environment variable
|
||||
* */
|
||||
void configTzTime(const char *tz, const char *server1, const char *server2, const char *server3) {
|
||||
//tcpip_adapter_init(); // Should not hurt anything if already inited
|
||||
esp_netif_init();
|
||||
|
||||
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||
if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
|
||||
LOCK_TCPIP_CORE();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sntp_enabled()) {
|
||||
sntp_stop();
|
||||
}
|
||||
|
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||
sntp_setservername(0, (char *)server1);
|
||||
sntp_setservername(1, (char *)server2);
|
||||
sntp_setservername(2, (char *)server3);
|
||||
sntp_init();
|
||||
|
||||
#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING
|
||||
if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) {
|
||||
UNLOCK_TCPIP_CORE();
|
||||
}
|
||||
#endif
|
||||
|
||||
setenv("TZ", tz, 1);
|
||||
tzset();
|
||||
}
|
||||
|
||||
bool getLocalTime(struct tm *info, uint32_t ms) {
|
||||
uint32_t start = millis();
|
||||
time_t now;
|
||||
while ((millis() - start) <= ms) {
|
||||
time(&now);
|
||||
localtime_r(&now, info);
|
||||
if (info->tm_year > (2016 - 1900)) {
|
||||
return true;
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
// 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 "esp32-hal-timer.h"
|
||||
|
||||
#if SOC_GPTIMER_SUPPORTED
|
||||
#include "driver/gptimer.h"
|
||||
#if defined __has_include && __has_include("clk_tree.h")
|
||||
#include "clk_tree.h"
|
||||
#else
|
||||
#include "esp_clk_tree.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
#define TIMER_IRAM IRAM_ATTR
|
||||
#else
|
||||
#define TIMER_IRAM
|
||||
#endif
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidFuncPtrArg)(void *);
|
||||
|
||||
typedef struct {
|
||||
voidFuncPtr fn;
|
||||
void *arg;
|
||||
} interrupt_config_t;
|
||||
|
||||
struct timer_struct_t {
|
||||
gptimer_handle_t timer_handle;
|
||||
interrupt_config_t interrupt_handle;
|
||||
bool timer_started;
|
||||
};
|
||||
|
||||
inline TIMER_IRAM uint64_t timerRead(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
log_e("Timer handle is NULL");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
uint64_t value;
|
||||
gptimer_get_raw_count(timer->timer_handle, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void TIMER_IRAM timerWrite(hw_timer_t *timer, uint64_t val) {
|
||||
if (timer == NULL) {
|
||||
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
log_e("Timer handle is NULL");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
gptimer_set_raw_count(timer->timer_handle, val);
|
||||
}
|
||||
|
||||
void TIMER_IRAM timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count) {
|
||||
if (timer == NULL) {
|
||||
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
log_e("Timer handle is NULL");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
gptimer_alarm_config_t alarm_cfg = {
|
||||
.alarm_count = alarm_value,
|
||||
.reload_count = reload_count,
|
||||
.flags.auto_reload_on_alarm = autoreload,
|
||||
};
|
||||
err = gptimer_set_alarm_action(timer->timer_handle, &alarm_cfg);
|
||||
if (err != ESP_OK) {
|
||||
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
log_e("Timer Alarm Write failed, error num=%d", err);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t timerGetFrequency(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t frequency;
|
||||
gptimer_get_resolution(timer->timer_handle, &frequency);
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void TIMER_IRAM timerStart(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
log_e("Timer handle is NULL");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
gptimer_start(timer->timer_handle);
|
||||
timer->timer_started = true;
|
||||
}
|
||||
|
||||
void TIMER_IRAM timerStop(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
log_e("Timer handle is NULL");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
gptimer_stop(timer->timer_handle);
|
||||
timer->timer_started = false;
|
||||
}
|
||||
|
||||
void TIMER_IRAM timerRestart(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM
|
||||
log_e("Timer handle is NULL");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
gptimer_set_raw_count(timer->timer_handle, 0);
|
||||
}
|
||||
|
||||
hw_timer_t *timerBegin(uint32_t frequency) {
|
||||
esp_err_t err = ESP_OK;
|
||||
uint32_t counter_src_hz = 0;
|
||||
uint32_t divider = 0;
|
||||
soc_periph_gptimer_clk_src_t clk;
|
||||
|
||||
soc_periph_gptimer_clk_src_t gptimer_clks[] = SOC_GPTIMER_CLKS;
|
||||
for (size_t i = 0; i < sizeof(gptimer_clks) / sizeof(gptimer_clks[0]); i++) {
|
||||
clk = gptimer_clks[i];
|
||||
#if defined __has_include && __has_include("clk_tree.h")
|
||||
clk_tree_src_get_freq_hz(clk, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz);
|
||||
#else
|
||||
esp_clk_tree_src_get_freq_hz(clk, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz);
|
||||
#endif
|
||||
divider = counter_src_hz / frequency;
|
||||
if ((divider >= 2) && (divider <= 65536)) {
|
||||
break;
|
||||
} else {
|
||||
divider = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (divider == 0) {
|
||||
log_e("Resolution cannot be reached with any clock source, aborting!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gptimer_config_t config = {
|
||||
.clk_src = clk,
|
||||
.direction = GPTIMER_COUNT_UP,
|
||||
.resolution_hz = frequency,
|
||||
.flags.intr_shared = true,
|
||||
};
|
||||
|
||||
hw_timer_t *timer = malloc(sizeof(hw_timer_t));
|
||||
|
||||
err = gptimer_new_timer(&config, &timer->timer_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to create a new GPTimer, error num=%d", err);
|
||||
free(timer);
|
||||
return NULL;
|
||||
}
|
||||
gptimer_enable(timer->timer_handle);
|
||||
gptimer_start(timer->timer_handle);
|
||||
timer->timer_started = true;
|
||||
return timer;
|
||||
}
|
||||
|
||||
void timerEnd(hw_timer_t *timer) {
|
||||
if (timer != NULL) {
|
||||
esp_err_t err = ESP_OK;
|
||||
if (timer->timer_started == true) {
|
||||
gptimer_stop(timer->timer_handle);
|
||||
}
|
||||
gptimer_disable(timer->timer_handle);
|
||||
err = gptimer_del_timer(timer->timer_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Failed to destroy GPTimer, error num=%d", err);
|
||||
return;
|
||||
}
|
||||
free(timer);
|
||||
}
|
||||
}
|
||||
|
||||
bool IRAM_ATTR timerFnWrapper(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *args) {
|
||||
interrupt_config_t *isr = (interrupt_config_t *)args;
|
||||
if (isr->fn) {
|
||||
if (isr->arg) {
|
||||
((voidFuncPtrArg)isr->fn)(isr->arg);
|
||||
} else {
|
||||
isr->fn();
|
||||
}
|
||||
}
|
||||
// some additional logic or handling may be required here to appropriately yield or not
|
||||
return false;
|
||||
}
|
||||
|
||||
void timerAttachInterruptFunctionalArg(hw_timer_t *timer, void (*userFunc)(void *), void *arg) {
|
||||
if (timer == NULL) {
|
||||
log_e("Timer handle is NULL");
|
||||
return;
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
gptimer_event_callbacks_t cbs = {
|
||||
.on_alarm = timerFnWrapper,
|
||||
};
|
||||
|
||||
timer->interrupt_handle.fn = (voidFuncPtr)userFunc;
|
||||
timer->interrupt_handle.arg = arg;
|
||||
|
||||
if (timer->timer_started == true) {
|
||||
gptimer_stop(timer->timer_handle);
|
||||
}
|
||||
gptimer_disable(timer->timer_handle);
|
||||
err = gptimer_register_event_callbacks(timer->timer_handle, &cbs, &timer->interrupt_handle);
|
||||
if (err != ESP_OK) {
|
||||
log_e("Timer Attach Interrupt failed, error num=%d", err);
|
||||
}
|
||||
gptimer_enable(timer->timer_handle);
|
||||
if (timer->timer_started == true) {
|
||||
gptimer_start(timer->timer_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void timerAttachInterruptArg(hw_timer_t *timer, void (*userFunc)(void *), void *arg) {
|
||||
timerAttachInterruptFunctionalArg(timer, userFunc, arg);
|
||||
}
|
||||
|
||||
void timerAttachInterrupt(hw_timer_t *timer, voidFuncPtr userFunc) {
|
||||
timerAttachInterruptFunctionalArg(timer, (voidFuncPtrArg)userFunc, NULL);
|
||||
}
|
||||
|
||||
void timerDetachInterrupt(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
log_e("Timer handle is NULL");
|
||||
return;
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
err = gptimer_set_alarm_action(timer->timer_handle, NULL);
|
||||
timer->interrupt_handle.fn = NULL;
|
||||
timer->interrupt_handle.arg = NULL;
|
||||
if (err != ESP_OK) {
|
||||
log_e("Timer Detach Interrupt failed, error num=%d", err);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t timerReadMicros(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
log_e("Timer handle is NULL");
|
||||
return 0;
|
||||
}
|
||||
uint64_t timer_val = timerRead(timer);
|
||||
uint32_t frequency = timerGetFrequency(timer);
|
||||
return timer_val * 1000000 / frequency;
|
||||
}
|
||||
|
||||
uint64_t timerReadMillis(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
log_e("Timer handle is NULL");
|
||||
return 0;
|
||||
}
|
||||
uint64_t timer_val = timerRead(timer);
|
||||
uint32_t frequency = timerGetFrequency(timer);
|
||||
return timer_val * 1000 / frequency;
|
||||
}
|
||||
|
||||
double timerReadSeconds(hw_timer_t *timer) {
|
||||
if (timer == NULL) {
|
||||
log_e("Timer handle is NULL");
|
||||
return 0;
|
||||
}
|
||||
uint64_t timer_val = timerRead(timer);
|
||||
uint32_t frequency = timerGetFrequency(timer);
|
||||
return (double)timer_val / frequency;
|
||||
}
|
||||
|
||||
#endif /* SOC_GPTIMER_SUPPORTED */
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Arduino.h - Main include file for the Arduino SDK
|
||||
Copyright (c) 2005-2013 Arduino Team. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_GPTIMER_SUPPORTED
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "driver/gptimer_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct timer_struct_t;
|
||||
typedef struct timer_struct_t hw_timer_t;
|
||||
|
||||
hw_timer_t *timerBegin(uint32_t frequency);
|
||||
void timerEnd(hw_timer_t *timer);
|
||||
|
||||
void timerStart(hw_timer_t *timer);
|
||||
void timerStop(hw_timer_t *timer);
|
||||
void timerRestart(hw_timer_t *timer);
|
||||
void timerWrite(hw_timer_t *timer, uint64_t val);
|
||||
|
||||
uint64_t timerRead(hw_timer_t *timer);
|
||||
uint64_t timerReadMicros(hw_timer_t *timer);
|
||||
uint64_t timerReadMillis(hw_timer_t *timer);
|
||||
double timerReadSeconds(hw_timer_t *timer);
|
||||
|
||||
uint32_t timerGetFrequency(hw_timer_t *timer);
|
||||
|
||||
void timerAttachInterrupt(hw_timer_t *timer, void (*userFunc)(void));
|
||||
void timerAttachInterruptArg(hw_timer_t *timer, void (*userFunc)(void *), void *arg);
|
||||
void timerDetachInterrupt(hw_timer_t *timer);
|
||||
|
||||
void timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOC_GPTIMER_SUPPORTED */
|
||||
@@ -0,0 +1,931 @@
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_TINYUSB_ENABLED
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "soc/soc.h"
|
||||
#include "soc/efuse_reg.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/usb_struct.h"
|
||||
#include "soc/usb_reg.h"
|
||||
#include "soc/usb_wrap_reg.h"
|
||||
#include "soc/usb_wrap_struct.h"
|
||||
#include "soc/usb_periph.h"
|
||||
#endif
|
||||
|
||||
#include "soc/periph_defs.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "soc/system_reg.h"
|
||||
|
||||
#include "rom/gpio.h"
|
||||
|
||||
#include "hal/gpio_ll.h"
|
||||
#include "hal/clk_gate_ll.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "esp_rom_gpio.h"
|
||||
|
||||
#include "esp32-hal.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
#include "esp32-hal-tinyusb.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/usb/usb_persist.h"
|
||||
#include "esp32s2/rom/usb/usb_dc.h"
|
||||
#include "esp32s2/rom/usb/chip_usb_dw_wrapper.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#if defined __has_include && __has_include("hal/usb_phy_ll.h")
|
||||
#include "hal/usb_phy_ll.h"
|
||||
#elif defined __has_include && __has_include("hal/usb_fsls_phy_ll.h")
|
||||
#include "hal/usb_fsls_phy_ll.h"
|
||||
#endif
|
||||
#include "hal/usb_serial_jtag_ll.h"
|
||||
#include "esp32s3/rom/usb/usb_persist.h"
|
||||
#include "esp32s3/rom/usb/usb_dc.h"
|
||||
#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
TINYUSB_USBDEV_0,
|
||||
} tinyusb_usbdev_t;
|
||||
|
||||
typedef char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
|
||||
|
||||
typedef struct {
|
||||
bool external_phy;
|
||||
} tinyusb_config_t;
|
||||
|
||||
#if __has_include("hal/usb_hal.h")
|
||||
|
||||
#include "hal/usb_hal.h"
|
||||
|
||||
static bool usb_otg_deinit(void *busptr) {
|
||||
// Once USB OTG is initialized, its GPIOs are assigned and it shall never be deinited
|
||||
// except when S3 swithicng usb from cdc to jtag while resetting to bootrom
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void configure_pins(usb_hal_context_t *usb) {
|
||||
for (const usb_iopin_dsc_t *iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) {
|
||||
if ((usb->use_external_phy) || (iopin->ext_phy_only == 0)) {
|
||||
esp_rom_gpio_pad_select_gpio(iopin->pin);
|
||||
if (iopin->is_output) {
|
||||
esp_rom_gpio_connect_out_signal(iopin->pin, iopin->func, false, false);
|
||||
} else {
|
||||
esp_rom_gpio_connect_in_signal(iopin->pin, iopin->func, false);
|
||||
if ((iopin->pin != GPIO_FUNC_IN_LOW) && (iopin->pin != GPIO_FUNC_IN_HIGH)) {
|
||||
gpio_ll_input_enable(&GPIO, iopin->pin);
|
||||
}
|
||||
}
|
||||
esp_rom_gpio_pad_unhold(iopin->pin);
|
||||
}
|
||||
}
|
||||
if (!usb->use_external_phy) {
|
||||
gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3);
|
||||
gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3);
|
||||
if (perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DM, usb_otg_deinit) && perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DP, usb_otg_deinit)) {
|
||||
// Bus Pointer is not used anyway - once the USB GPIOs are assigned, they can't be detached
|
||||
perimanSetPinBus(USBPHY_DM_NUM, ESP32_BUS_TYPE_USB_DM, (void *)usb, -1, -1);
|
||||
perimanSetPinBus(USBPHY_DP_NUM, ESP32_BUS_TYPE_USB_DP, (void *)usb, -1, -1);
|
||||
} else {
|
||||
log_e("USB OTG Pins can't be set into Peripheral Manager.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t init_usb_hal(bool external_phy) {
|
||||
usb_hal_context_t hal = {.use_external_phy = external_phy};
|
||||
usb_hal_init(&hal);
|
||||
configure_pins(&hal);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t deinit_usb_hal() {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#elif __has_include("esp_private/usb_phy.h")
|
||||
|
||||
#include "esp_private/usb_phy.h"
|
||||
|
||||
static usb_phy_handle_t phy_handle = NULL;
|
||||
|
||||
esp_err_t init_usb_hal(bool external_phy) {
|
||||
esp_err_t ret = ESP_OK;
|
||||
usb_phy_config_t phy_config = {
|
||||
.controller = USB_PHY_CTRL_OTG,
|
||||
.target = USB_PHY_TARGET_INT,
|
||||
.otg_mode = USB_OTG_MODE_DEVICE,
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
.otg_speed = USB_PHY_SPEED_HIGH,
|
||||
#else
|
||||
.otg_speed = USB_PHY_SPEED_FULL,
|
||||
#endif
|
||||
.ext_io_conf = NULL,
|
||||
.otg_io_conf = NULL,
|
||||
};
|
||||
|
||||
ret = usb_new_phy(&phy_config, &phy_handle);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to init USB PHY");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t deinit_usb_hal() {
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (phy_handle) {
|
||||
ret = usb_del_phy(phy_handle);
|
||||
if (ret != ESP_OK) {
|
||||
log_e("Failed to deinit USB PHY");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error No way to initialize USP PHY
|
||||
|
||||
void init_usb_hal(bool external_phy) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void deinit_usb_hal() {
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) {
|
||||
init_usb_hal(config->external_phy);
|
||||
tusb_rhport_init_t tinit;
|
||||
memset(&tinit, 0, sizeof(tusb_rhport_init_t));
|
||||
tinit.role = TUSB_ROLE_DEVICE;
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
tinit.speed = TUSB_SPEED_HIGH;
|
||||
if (!tusb_init(1, &tinit)) {
|
||||
#else
|
||||
tinit.speed = TUSB_SPEED_FULL;
|
||||
if (!tusb_init(0, &tinit)) {
|
||||
#endif
|
||||
log_e("Can't initialize the TinyUSB stack.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
typedef char tusb_str_t[127];
|
||||
|
||||
static bool WEBUSB_ENABLED = false;
|
||||
|
||||
static tusb_str_t WEBUSB_URL = "";
|
||||
static tusb_str_t USB_DEVICE_PRODUCT = "";
|
||||
static tusb_str_t USB_DEVICE_MANUFACTURER = "";
|
||||
static tusb_str_t USB_DEVICE_SERIAL = "";
|
||||
static tusb_str_t USB_DEVICE_LANGUAGE = "\x09\x04"; //English (0x0409)
|
||||
|
||||
static uint8_t USB_DEVICE_ATTRIBUTES = 0;
|
||||
static uint16_t USB_DEVICE_POWER = 0;
|
||||
|
||||
/*
|
||||
* Device Descriptor
|
||||
* */
|
||||
static tusb_desc_device_t tinyusb_device_descriptor = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0,
|
||||
.bDeviceClass = 0,
|
||||
.bDeviceSubClass = 0,
|
||||
.bDeviceProtocol = 0,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
|
||||
|
||||
.idVendor = 0,
|
||||
.idProduct = 0,
|
||||
.bcdDevice = 0,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
/*
|
||||
* String Descriptors
|
||||
* */
|
||||
#define MAX_STRING_DESCRIPTORS 20
|
||||
static uint32_t tinyusb_string_descriptor_len = 4;
|
||||
static char *tinyusb_string_descriptor[MAX_STRING_DESCRIPTORS] = {
|
||||
// array of pointer to string descriptors
|
||||
USB_DEVICE_LANGUAGE, // 0: is supported language
|
||||
USB_DEVICE_MANUFACTURER, // 1: Manufacturer
|
||||
USB_DEVICE_PRODUCT, // 2: Product
|
||||
USB_DEVICE_SERIAL, // 3: Serials, should use chip ID
|
||||
};
|
||||
|
||||
/* Microsoft OS 2.0 registry property descriptor
|
||||
Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
|
||||
device should create DeviceInterfaceGUIDs. It can be done by driver and
|
||||
in case of real PnP solution device should expose MS "Microsoft OS 2.0
|
||||
registry property descriptor". Such descriptor can insert any record
|
||||
into Windows registry per device/configuration/interface. In our case it
|
||||
will insert "DeviceInterfaceGUIDs" multistring property.
|
||||
|
||||
GUID is freshly generated and should be OK to use.
|
||||
|
||||
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
|
||||
(Section Microsoft OS compatibility descriptors)
|
||||
*/
|
||||
|
||||
#define MS_OS_20_DESC_LEN 0xB2
|
||||
|
||||
static uint8_t const tinyusb_ms_os_20_descriptor[] = {
|
||||
// Set header: length, type, windows version, total length
|
||||
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
|
||||
|
||||
// Configuration subset header: length, type, configuration index, reserved, configuration total length
|
||||
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A),
|
||||
|
||||
// Function Subset header: length, type, first interface, reserved, subset length
|
||||
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08),
|
||||
|
||||
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
|
||||
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, // sub-compatible
|
||||
|
||||
// MS OS 2.0 Registry property descriptor: length, type
|
||||
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), U16_TO_U8S_LE(0x0007),
|
||||
U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
|
||||
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e',
|
||||
0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
|
||||
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
|
||||
//bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
|
||||
'{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00, '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-',
|
||||
0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C',
|
||||
0x00, 'A', 0x00, '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(tinyusb_ms_os_20_descriptor) == MS_OS_20_DESC_LEN, "Incorrect size");
|
||||
|
||||
/*
|
||||
* BOS Descriptor (required for webUSB)
|
||||
* */
|
||||
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
|
||||
|
||||
enum {
|
||||
VENDOR_REQUEST_WEBUSB = 1,
|
||||
VENDOR_REQUEST_MICROSOFT = 2
|
||||
};
|
||||
|
||||
static uint8_t const tinyusb_bos_descriptor[] = {// total length, number of device caps
|
||||
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
|
||||
|
||||
// Vendor Code, iLandingPage
|
||||
TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
|
||||
|
||||
// Microsoft OS 2.0 descriptor
|
||||
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)
|
||||
};
|
||||
|
||||
/*
|
||||
* URL Descriptor (required for webUSB)
|
||||
* */
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bScheme;
|
||||
char url[127];
|
||||
} tinyusb_desc_webusb_url_t;
|
||||
|
||||
static tinyusb_desc_webusb_url_t tinyusb_url_descriptor = {
|
||||
.bLength = 3,
|
||||
.bDescriptorType = 3, // WEBUSB URL type
|
||||
.bScheme = 255, // URL Scheme Prefix: 0: "http://", 1: "https://", 255: ""
|
||||
.url = ""
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration Descriptor
|
||||
* */
|
||||
|
||||
static tinyusb_descriptor_cb_t tinyusb_loaded_interfaces_callbacks[USB_INTERFACE_MAX];
|
||||
static uint32_t tinyusb_loaded_interfaces_mask = 0;
|
||||
static uint8_t tinyusb_loaded_interfaces_num = 0;
|
||||
static uint16_t tinyusb_config_descriptor_len = 0;
|
||||
static uint8_t *tinyusb_config_descriptor = NULL;
|
||||
|
||||
/*
|
||||
* Endpoint Usage Tracking
|
||||
* */
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t in : 16;
|
||||
uint32_t out : 16;
|
||||
};
|
||||
uint32_t val;
|
||||
} tinyusb_endpoints_usage_t;
|
||||
|
||||
static tinyusb_endpoints_usage_t tinyusb_endpoints;
|
||||
|
||||
/*
|
||||
* TinyUSB Callbacks
|
||||
* */
|
||||
|
||||
/**
|
||||
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
|
||||
*/
|
||||
__attribute__((weak)) uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
//log_d("%u", index);
|
||||
return tinyusb_config_descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invoked when received GET DEVICE DESCRIPTOR.
|
||||
*/
|
||||
__attribute__((weak)) uint8_t const *tud_descriptor_device_cb(void) {
|
||||
//log_d("");
|
||||
return (uint8_t const *)&tinyusb_device_descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invoked when received GET STRING DESCRIPTOR request.
|
||||
*/
|
||||
__attribute__((weak)) uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
//log_d("%u (0x%x)", index, langid);
|
||||
static uint16_t _desc_str[127];
|
||||
uint8_t chr_count;
|
||||
|
||||
if (index == 0) {
|
||||
memcpy(&_desc_str[1], tinyusb_string_descriptor[0], 2);
|
||||
chr_count = 1;
|
||||
} else {
|
||||
// Convert ASCII string into UTF-16
|
||||
if (index >= tinyusb_string_descriptor_len) {
|
||||
return NULL;
|
||||
}
|
||||
const char *str = tinyusb_string_descriptor[index];
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
if (chr_count > 126) {
|
||||
chr_count = 126;
|
||||
}
|
||||
for (uint8_t i = 0; i < chr_count; i++) {
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is len, second byte is string type
|
||||
_desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2);
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Invoked when received GET BOS DESCRIPTOR request.
|
||||
*/
|
||||
uint8_t const *tud_descriptor_bos_cb(void) {
|
||||
//log_v("");
|
||||
return tinyusb_bos_descriptor;
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool tinyusb_vendor_control_request_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle WebUSB and Vendor requests.
|
||||
*/
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
|
||||
if (WEBUSB_ENABLED && (request->bRequest == VENDOR_REQUEST_WEBUSB || (request->bRequest == VENDOR_REQUEST_MICROSOFT && request->wIndex == 7))) {
|
||||
// we only care for SETUP stage
|
||||
if (stage == CONTROL_STAGE_SETUP) {
|
||||
if (request->bRequest == VENDOR_REQUEST_WEBUSB) {
|
||||
// match vendor request in BOS descriptor
|
||||
// Get landing page url
|
||||
tinyusb_url_descriptor.bLength = 3 + strlen(WEBUSB_URL);
|
||||
snprintf(tinyusb_url_descriptor.url, 127, "%s", WEBUSB_URL);
|
||||
return tud_control_xfer(rhport, request, (void *)&tinyusb_url_descriptor, tinyusb_url_descriptor.bLength);
|
||||
}
|
||||
// Get Microsoft OS 2.0 compatible descriptor
|
||||
uint16_t total_len;
|
||||
memcpy(&total_len, tinyusb_ms_os_20_descriptor + 8, 2);
|
||||
return tud_control_xfer(rhport, request, (void *)tinyusb_ms_os_20_descriptor, total_len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
log_v("rhport: %u, stage: %u, type: 0x%x, request: 0x%x", rhport, stage, request->bmRequestType_bit.type, request->bRequest);
|
||||
return tinyusb_vendor_control_request_cb(rhport, stage, request);
|
||||
}
|
||||
|
||||
/*
|
||||
* Required Callbacks
|
||||
* */
|
||||
#if CFG_TUD_DFU
|
||||
__attribute__((weak)) uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state) {
|
||||
return 0;
|
||||
}
|
||||
__attribute__((weak)) void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length) {}
|
||||
__attribute__((weak)) void tud_dfu_manifest_cb(uint8_t alt) {}
|
||||
#endif
|
||||
#if CFG_TUD_HID
|
||||
__attribute__((weak)) const uint8_t *tud_hid_descriptor_report_cb(uint8_t itf) {
|
||||
return NULL;
|
||||
}
|
||||
__attribute__((weak)) uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||
return 0;
|
||||
}
|
||||
__attribute__((weak)) void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, const uint8_t *buffer, uint16_t bufsize) {}
|
||||
#endif
|
||||
#if CFG_TUD_MSC
|
||||
__attribute__((weak)) bool tud_msc_test_unit_ready_cb(uint8_t lun) {
|
||||
return false;
|
||||
}
|
||||
__attribute__((weak)) void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {}
|
||||
__attribute__((weak)) void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {}
|
||||
__attribute__((weak)) int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
|
||||
return -1;
|
||||
}
|
||||
__attribute__((weak)) int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
|
||||
return -1;
|
||||
}
|
||||
__attribute__((weak)) int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
#if CFG_TUD_NCM
|
||||
__attribute__((weak)) bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
|
||||
return false;
|
||||
}
|
||||
__attribute__((weak)) uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
|
||||
return 0;
|
||||
}
|
||||
__attribute__((weak)) void tud_network_init_cb(void) {}
|
||||
#endif
|
||||
|
||||
#if CFG_TUH_HID
|
||||
__attribute__((weak)) void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report_desc, uint16_t desc_len) {}
|
||||
__attribute__((weak)) void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx) {}
|
||||
__attribute__((weak)) void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report, uint16_t len) {}
|
||||
__attribute__((weak)) void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report, uint16_t len) {}
|
||||
__attribute__((weak)) void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
__attribute__((weak)) void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {}
|
||||
__attribute__((weak)) void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {}
|
||||
#endif
|
||||
#if CFG_TUH_CDC
|
||||
__attribute__((weak)) void tuh_cdc_mount_cb(uint8_t idx) {}
|
||||
__attribute__((weak)) void tuh_cdc_umount_cb(uint8_t idx) {}
|
||||
__attribute__((weak)) void tuh_cdc_rx_cb(uint8_t idx) {}
|
||||
__attribute__((weak)) void tuh_cdc_tx_complete_cb(uint8_t idx) {}
|
||||
#endif
|
||||
#if CFG_TUH_MSC
|
||||
__attribute__((weak)) void tuh_msc_mount_cb(uint8_t dev_addr) {}
|
||||
__attribute__((weak)) void tuh_msc_umount_cb(uint8_t dev_addr) {}
|
||||
#endif
|
||||
/*
|
||||
* Private API
|
||||
* */
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
static bool usb_persist_enabled = false;
|
||||
static restart_type_t usb_persist_mode = RESTART_NO_PERSIST;
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
static void hw_cdc_reset_handler(void *arg) {
|
||||
portBASE_TYPE xTaskWoken = 0;
|
||||
uint32_t usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask();
|
||||
usb_serial_jtag_ll_clr_intsts_mask(usbjtag_intr_status);
|
||||
|
||||
if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) {
|
||||
xSemaphoreGiveFromISR((SemaphoreHandle_t)arg, &xTaskWoken);
|
||||
}
|
||||
|
||||
if (xTaskWoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_switch_to_cdc_jtag() {
|
||||
// Disable USB-OTG
|
||||
deinit_usb_hal();
|
||||
periph_ll_reset(PERIPH_USB_MODULE);
|
||||
//periph_ll_enable_clk_clear_rst(PERIPH_USB_MODULE);
|
||||
periph_ll_disable_clk_set_rst(PERIPH_USB_MODULE);
|
||||
|
||||
// Switch to hardware CDC+JTAG
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_USB_CONF_REG, (RTC_CNTL_SW_HW_USB_PHY_SEL | RTC_CNTL_SW_USB_PHY_SEL | RTC_CNTL_USB_PAD_ENABLE));
|
||||
|
||||
// Do not use external PHY
|
||||
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PHY_SEL);
|
||||
|
||||
// Release GPIO pins from CDC+JTAG
|
||||
CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
|
||||
|
||||
// Force the host to re-enumerate (BUS_RESET)
|
||||
pinMode(USBPHY_DM_NUM, OUTPUT_OPEN_DRAIN);
|
||||
pinMode(USBPHY_DP_NUM, OUTPUT_OPEN_DRAIN);
|
||||
digitalWrite(USBPHY_DM_NUM, LOW);
|
||||
digitalWrite(USBPHY_DP_NUM, LOW);
|
||||
|
||||
// Initialize CDC+JTAG ISR to listen for BUS_RESET
|
||||
#if defined __has_include && __has_include("hal/usb_phy_ll.h")
|
||||
usb_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG);
|
||||
#elif defined __has_include && __has_include("hal/usb_fsls_phy_ll.h")
|
||||
usb_fsls_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG);
|
||||
#else
|
||||
// usb_serial_jtag_ll_phy_set_defaults();
|
||||
const usb_serial_jtag_pull_override_vals_t pull_conf = {.dp_pu = 1, .dm_pu = 0, .dp_pd = 0, .dm_pd = 0};
|
||||
usb_serial_jtag_ll_phy_enable_pull_override(&pull_conf);
|
||||
usb_serial_jtag_ll_phy_disable_pull_override();
|
||||
#endif
|
||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
|
||||
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
|
||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_BUS_RESET);
|
||||
intr_handle_t intr_handle = NULL;
|
||||
SemaphoreHandle_t reset_sem = xSemaphoreCreateBinary();
|
||||
if (reset_sem) {
|
||||
if (esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_reset_handler, reset_sem, &intr_handle) != ESP_OK) {
|
||||
vSemaphoreDelete(reset_sem);
|
||||
reset_sem = NULL;
|
||||
log_e("HW USB CDC failed to init interrupts");
|
||||
}
|
||||
} else {
|
||||
log_e("reset_sem init failed");
|
||||
}
|
||||
|
||||
// Connect GPIOs to integrated CDC+JTAG
|
||||
SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE);
|
||||
|
||||
// Wait for BUS_RESET to give us back the semaphore
|
||||
if (reset_sem) {
|
||||
if (xSemaphoreTake(reset_sem, 1000 / portTICK_PERIOD_MS) != pdPASS) {
|
||||
log_e("reset_sem timeout");
|
||||
}
|
||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK);
|
||||
esp_intr_free(intr_handle);
|
||||
vSemaphoreDelete(reset_sem);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
static void IRAM_ATTR usb_persist_shutdown_handler(void) {
|
||||
if (usb_persist_mode != RESTART_NO_PERSIST) {
|
||||
if (usb_persist_enabled) {
|
||||
usb_dc_prepare_persist();
|
||||
}
|
||||
if (usb_persist_mode == RESTART_BOOTLOADER) {
|
||||
//USB CDC Download
|
||||
if (usb_persist_enabled) {
|
||||
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
} else {
|
||||
periph_ll_reset(PERIPH_USB_MODULE);
|
||||
periph_ll_enable_clk_clear_rst(PERIPH_USB_MODULE);
|
||||
#endif
|
||||
}
|
||||
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
|
||||
} else if (usb_persist_mode == RESTART_BOOTLOADER_DFU) {
|
||||
//DFU Download
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
// Reset USB Core
|
||||
USB0.grstctl |= USB_CSFTRST;
|
||||
while ((USB0.grstctl & USB_CSFTRST) == USB_CSFTRST) {}
|
||||
#endif
|
||||
chip_usb_set_persist_flags(USBDC_BOOT_DFU);
|
||||
REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT);
|
||||
} else if (usb_persist_enabled) {
|
||||
//USB Persist reboot
|
||||
chip_usb_set_persist_flags(USBDC_PERSIST_ENA);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void usb_persist_restart(restart_type_t mode) {
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
if (mode < RESTART_TYPE_MAX && esp_register_shutdown_handler(usb_persist_shutdown_handler) == ESP_OK) {
|
||||
usb_persist_mode = mode;
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
if (mode == RESTART_BOOTLOADER) {
|
||||
usb_switch_to_cdc_jtag();
|
||||
}
|
||||
#endif
|
||||
esp_restart();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool tinyusb_reserve_in_endpoint(uint8_t endpoint) {
|
||||
if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.in & BIT(endpoint)) != 0) {
|
||||
return false;
|
||||
}
|
||||
tinyusb_endpoints.in |= BIT(endpoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tinyusb_reserve_out_endpoint(uint8_t endpoint) {
|
||||
if (endpoint > CFG_TUD_NUM_EPS || (tinyusb_endpoints.out & BIT(endpoint)) != 0) {
|
||||
return false;
|
||||
}
|
||||
tinyusb_endpoints.out |= BIT(endpoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tinyusb_has_available_fifos(void) {
|
||||
uint8_t max_endpoints = CFG_TUD_NUM_IN_EPS - 1, active_endpoints = 0;
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
if (tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) {
|
||||
max_endpoints = CFG_TUD_NUM_IN_EPS; //CDC endpoint 0x85 is actually not linked to FIFO and not used
|
||||
}
|
||||
#endif
|
||||
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
|
||||
if ((tinyusb_endpoints.in & BIT(i)) != 0) {
|
||||
active_endpoints++;
|
||||
}
|
||||
}
|
||||
|
||||
return active_endpoints < max_endpoints;
|
||||
}
|
||||
|
||||
static uint16_t tinyusb_load_descriptor(tinyusb_interface_t interface, uint8_t *dst, uint8_t *itf) {
|
||||
if (tinyusb_loaded_interfaces_callbacks[interface]) {
|
||||
return tinyusb_loaded_interfaces_callbacks[interface](dst, itf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool tinyusb_load_enabled_interfaces() {
|
||||
tinyusb_config_descriptor_len += TUD_CONFIG_DESC_LEN;
|
||||
tinyusb_config_descriptor = (uint8_t *)malloc(tinyusb_config_descriptor_len);
|
||||
if (tinyusb_config_descriptor == NULL) {
|
||||
log_e("Descriptor Malloc Failed");
|
||||
return false;
|
||||
}
|
||||
uint8_t *dst = tinyusb_config_descriptor + TUD_CONFIG_DESC_LEN;
|
||||
|
||||
for (int i = 0; i < USB_INTERFACE_MAX; i++) {
|
||||
if (tinyusb_loaded_interfaces_mask & (1U << i)) {
|
||||
uint16_t len = tinyusb_load_descriptor((tinyusb_interface_t)i, dst, &tinyusb_loaded_interfaces_num);
|
||||
if (!len) {
|
||||
log_e("Descriptor Load Failed");
|
||||
return false;
|
||||
} else {
|
||||
dst += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Device");
|
||||
uint8_t descriptor[TUD_CONFIG_DESC_LEN] = {
|
||||
//num configs, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, tinyusb_loaded_interfaces_num, str_index, tinyusb_config_descriptor_len, USB_DEVICE_ATTRIBUTES, USB_DEVICE_POWER)
|
||||
};
|
||||
memcpy(tinyusb_config_descriptor, descriptor, TUD_CONFIG_DESC_LEN);
|
||||
if ((tinyusb_loaded_interfaces_mask == (BIT(USB_INTERFACE_CDC) | BIT(USB_INTERFACE_DFU))) || (tinyusb_loaded_interfaces_mask == BIT(USB_INTERFACE_CDC))) {
|
||||
//usb_persist_enabled = true;
|
||||
//log_d("USB Persist enabled");
|
||||
}
|
||||
log_d("Load Done: if_num: %u, descr_len: %u, if_mask: 0x%x", tinyusb_loaded_interfaces_num, tinyusb_config_descriptor_len, tinyusb_loaded_interfaces_mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline char nibble_to_hex_char(uint8_t b) {
|
||||
if (b < 0xa) {
|
||||
return '0' + b;
|
||||
} else {
|
||||
return 'a' + b - 0xa;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_usb_serial_num(void) {
|
||||
/* Get the MAC address */
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
const uint32_t mac0 = REG_GET_FIELD(EFUSE_RD_MAC_SYS_0_REG, EFUSE_MAC_0);
|
||||
const uint32_t mac1 = REG_GET_FIELD(EFUSE_RD_MAC_SYS_0_REG, EFUSE_MAC_1);
|
||||
#else
|
||||
const uint32_t mac0 = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_0_REG, EFUSE_MAC_0);
|
||||
const uint32_t mac1 = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_1_REG, EFUSE_MAC_1);
|
||||
#endif
|
||||
uint8_t mac_bytes[6];
|
||||
memcpy(mac_bytes, &mac0, 4);
|
||||
memcpy(mac_bytes + 4, &mac1, 2);
|
||||
|
||||
/* Convert to UTF16 string */
|
||||
uint8_t *srl = (uint8_t *)USB_DEVICE_SERIAL;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
uint8_t b = mac_bytes[5 - i]; /* printing from the MSB */
|
||||
if (i) {
|
||||
*srl++ = ':';
|
||||
}
|
||||
*srl++ = nibble_to_hex_char(b >> 4);
|
||||
*srl++ = nibble_to_hex_char(b & 0xf);
|
||||
}
|
||||
*srl++ = '\0';
|
||||
}
|
||||
|
||||
static void tinyusb_apply_device_config(tinyusb_device_config_t *config) {
|
||||
if (config->product_name) {
|
||||
snprintf(USB_DEVICE_PRODUCT, 126, "%s", config->product_name);
|
||||
}
|
||||
|
||||
if (config->manufacturer_name) {
|
||||
snprintf(USB_DEVICE_MANUFACTURER, 126, "%s", config->manufacturer_name);
|
||||
}
|
||||
|
||||
if (config->serial_number && config->serial_number[0]) {
|
||||
snprintf(USB_DEVICE_SERIAL, 126, "%s", config->serial_number);
|
||||
} else {
|
||||
set_usb_serial_num();
|
||||
}
|
||||
|
||||
if (config->webusb_url) {
|
||||
snprintf(WEBUSB_URL, 126, "%s", config->webusb_url);
|
||||
}
|
||||
|
||||
// Windows 10 will not recognize the CDC device if WebUSB is enabled and USB Class is not 2 (CDC)
|
||||
if ((tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) && config->webusb_enabled && (config->usb_class != TUSB_CLASS_CDC)) {
|
||||
config->usb_class = TUSB_CLASS_CDC;
|
||||
config->usb_protocol = 0x00;
|
||||
}
|
||||
|
||||
WEBUSB_ENABLED = config->webusb_enabled;
|
||||
USB_DEVICE_ATTRIBUTES = config->usb_attributes;
|
||||
USB_DEVICE_POWER = config->usb_power_ma;
|
||||
|
||||
tinyusb_device_descriptor.bcdUSB = config->usb_version;
|
||||
tinyusb_device_descriptor.idVendor = config->vid;
|
||||
tinyusb_device_descriptor.idProduct = config->pid;
|
||||
tinyusb_device_descriptor.bcdDevice = config->fw_version;
|
||||
tinyusb_device_descriptor.bDeviceClass = config->usb_class;
|
||||
tinyusb_device_descriptor.bDeviceSubClass = config->usb_subclass;
|
||||
tinyusb_device_descriptor.bDeviceProtocol = config->usb_protocol;
|
||||
}
|
||||
|
||||
// USB Device Driver task
|
||||
// This top level thread processes all usb events and invokes callbacks
|
||||
static void usb_device_task(void *param) {
|
||||
(void)param;
|
||||
while (1) {
|
||||
tud_task(); // RTOS forever loop
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PUBLIC API
|
||||
* */
|
||||
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR
|
||||
const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "CDC2", "MIDI", "CUSTOM"};
|
||||
#endif
|
||||
static bool tinyusb_is_initialized = false;
|
||||
|
||||
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb) {
|
||||
return tinyusb_enable_interface2(interface, descriptor_len, cb, false);
|
||||
}
|
||||
|
||||
esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints) {
|
||||
if (tinyusb_is_initialized) {
|
||||
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX) ? "" : tinyusb_interface_names[interface]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if ((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))) {
|
||||
log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX) ? "" : tinyusb_interface_names[interface]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (interface == USB_INTERFACE_HID && reserve_endpoints) {
|
||||
// Some simple PC BIOS requires specific endpoint addresses for keyboard at boot
|
||||
if (!tinyusb_reserve_out_endpoint(1) || !tinyusb_reserve_in_endpoint(1)) {
|
||||
log_e("HID Reserve Endpoints Failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
if (interface == USB_INTERFACE_CDC) {
|
||||
if (!tinyusb_reserve_out_endpoint(3) || !tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)) {
|
||||
log_e("CDC Reserve Endpoints Failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
tinyusb_loaded_interfaces_mask |= (1U << interface);
|
||||
tinyusb_config_descriptor_len += descriptor_len;
|
||||
tinyusb_loaded_interfaces_callbacks[interface] = cb;
|
||||
log_d("Interface %s enabled", tinyusb_interface_names[interface]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
|
||||
if (tinyusb_is_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
tinyusb_is_initialized = true;
|
||||
|
||||
//tinyusb_endpoints.val = 0;
|
||||
tinyusb_apply_device_config(config);
|
||||
if (!tinyusb_load_enabled_interfaces()) {
|
||||
tinyusb_is_initialized = false;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
bool usb_did_persist = (USB_WRAP.date.val == USBDC_PERSIST_ENA);
|
||||
|
||||
//if(usb_did_persist && usb_persist_enabled){
|
||||
// Enable USB/IO_MUX peripheral reset, if coming from persistent reboot
|
||||
REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_IO_MUX_RESET_DISABLE);
|
||||
REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_USB_RESET_DISABLE);
|
||||
//} else
|
||||
if (!usb_did_persist || !usb_persist_enabled) {
|
||||
// Reset USB module
|
||||
periph_ll_reset(PERIPH_USB_MODULE);
|
||||
periph_ll_enable_clk_clear_rst(PERIPH_USB_MODULE);
|
||||
}
|
||||
#endif
|
||||
|
||||
tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false // In the most cases you need to use a `false` value
|
||||
};
|
||||
esp_err_t err = tinyusb_driver_install(&tusb_cfg);
|
||||
if (err != ESP_OK) {
|
||||
tinyusb_is_initialized = false;
|
||||
return err;
|
||||
}
|
||||
xTaskCreate(usb_device_task, "usbd", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t tinyusb_add_string_descriptor(const char *str) {
|
||||
if (str == NULL || tinyusb_string_descriptor_len >= MAX_STRING_DESCRIPTORS) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t index = tinyusb_string_descriptor_len;
|
||||
tinyusb_string_descriptor[tinyusb_string_descriptor_len++] = (char *)str;
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t tinyusb_get_free_duplex_endpoint(void) {
|
||||
if (!tinyusb_has_available_fifos()) {
|
||||
log_e("No available IN endpoints");
|
||||
return 0;
|
||||
}
|
||||
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
|
||||
if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) == 0) {
|
||||
tinyusb_endpoints.in |= BIT(i);
|
||||
tinyusb_endpoints.out |= BIT(i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
log_e("No available duplex endpoints");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t tinyusb_get_free_in_endpoint(void) {
|
||||
if (!tinyusb_has_available_fifos()) {
|
||||
log_e("No available IN endpoints");
|
||||
return 0;
|
||||
}
|
||||
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
|
||||
if ((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) != 0) {
|
||||
tinyusb_endpoints.in |= BIT(i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 1; i <= CFG_TUD_NUM_IN_EPS; i++) {
|
||||
if ((tinyusb_endpoints.in & BIT(i)) == 0) {
|
||||
tinyusb_endpoints.in |= BIT(i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t tinyusb_get_free_out_endpoint(void) {
|
||||
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
|
||||
if ((tinyusb_endpoints.out & BIT(i)) == 0 && (tinyusb_endpoints.in & BIT(i)) != 0) {
|
||||
tinyusb_endpoints.out |= BIT(i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 1; i <= CFG_TUD_NUM_EPS; i++) {
|
||||
if ((tinyusb_endpoints.out & BIT(i)) == 0) {
|
||||
tinyusb_endpoints.out |= BIT(i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_TINYUSB_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#include "esp32-hal.h"
|
||||
|
||||
#if CONFIG_TINYUSB_ENABLED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "tusb.h"
|
||||
#include "tusb_option.h"
|
||||
#include "tusb_config.h"
|
||||
|
||||
#define USB_ESPRESSIF_VID 0x303A
|
||||
#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 10
|
||||
|
||||
#ifndef CFG_TUD_ENDOINT_SIZE
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#define CFG_TUD_ENDOINT_SIZE 512
|
||||
#else
|
||||
#define CFG_TUD_ENDOINT_SIZE 64
|
||||
#endif
|
||||
#endif
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#define CFG_TUD_NUM_EPS 15
|
||||
#define CFG_TUD_NUM_IN_EPS 8
|
||||
#else
|
||||
#define CFG_TUD_NUM_EPS 6
|
||||
#define CFG_TUD_NUM_IN_EPS 5
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint16_t vid;
|
||||
uint16_t pid;
|
||||
const char *product_name;
|
||||
const char *manufacturer_name;
|
||||
const char *serial_number;
|
||||
uint16_t fw_version;
|
||||
|
||||
uint16_t usb_version;
|
||||
uint8_t usb_class;
|
||||
uint8_t usb_subclass;
|
||||
uint8_t usb_protocol;
|
||||
uint8_t usb_attributes;
|
||||
uint16_t usb_power_ma;
|
||||
|
||||
bool webusb_enabled;
|
||||
const char *webusb_url;
|
||||
} tinyusb_device_config_t;
|
||||
|
||||
#define TINYUSB_CONFIG_DEFAULT() \
|
||||
{ \
|
||||
.vid = USB_ESPRESSIF_VID, .pid = 0x0002, .product_name = CONFIG_TINYUSB_DESC_PRODUCT_STRING, .manufacturer_name = CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, \
|
||||
.serial_number = CONFIG_TINYUSB_DESC_SERIAL_STRING, .fw_version = CONFIG_TINYUSB_DESC_BCDDEVICE, .usb_version = 0x0200, .usb_class = TUSB_CLASS_MISC, \
|
||||
.usb_subclass = MISC_SUBCLASS_COMMON, .usb_protocol = MISC_PROTOCOL_IAD, .usb_attributes = TUSB_DESC_CONFIG_ATT_SELF_POWERED, .usb_power_ma = 500, \
|
||||
.webusb_enabled = false, .webusb_url = "espressif.github.io/arduino-esp32/webusb.html" \
|
||||
}
|
||||
|
||||
esp_err_t tinyusb_init(tinyusb_device_config_t *config);
|
||||
|
||||
/*
|
||||
* USB Persistence API
|
||||
* */
|
||||
typedef enum {
|
||||
RESTART_NO_PERSIST,
|
||||
RESTART_PERSIST,
|
||||
RESTART_BOOTLOADER,
|
||||
RESTART_BOOTLOADER_DFU,
|
||||
RESTART_TYPE_MAX
|
||||
} restart_type_t;
|
||||
|
||||
void usb_persist_restart(restart_type_t mode);
|
||||
|
||||
// The following definitions and functions are to be used only by the drivers
|
||||
typedef enum {
|
||||
USB_INTERFACE_MSC,
|
||||
USB_INTERFACE_DFU,
|
||||
USB_INTERFACE_HID,
|
||||
USB_INTERFACE_VENDOR,
|
||||
USB_INTERFACE_CDC,
|
||||
USB_INTERFACE_CDC2,
|
||||
USB_INTERFACE_MIDI,
|
||||
USB_INTERFACE_CUSTOM,
|
||||
USB_INTERFACE_MAX
|
||||
} tinyusb_interface_t;
|
||||
|
||||
typedef uint16_t (*tinyusb_descriptor_cb_t)(uint8_t *dst, uint8_t *itf);
|
||||
|
||||
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb);
|
||||
esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints);
|
||||
uint8_t tinyusb_add_string_descriptor(const char *str);
|
||||
uint8_t tinyusb_get_free_duplex_endpoint(void);
|
||||
uint8_t tinyusb_get_free_in_endpoint(void);
|
||||
uint8_t tinyusb_get_free_out_endpoint(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_TINYUSB_ENABLED */
|
||||
#endif /* SOC_USB_OTG_SUPPORTED */
|
||||
@@ -0,0 +1,560 @@
|
||||
// Copyright 2024 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 "soc/soc_caps.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#if SOC_TOUCH_SENSOR_SUPPORTED
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3
|
||||
|
||||
#include "esp32-hal-touch-ng.h"
|
||||
#include "esp32-hal-periman.h"
|
||||
|
||||
/*
|
||||
Internal Private Touch Data Structure and Functions
|
||||
*/
|
||||
|
||||
typedef void (*voidFuncPtr)(void);
|
||||
typedef void (*voidArgFuncPtr)(void *);
|
||||
|
||||
typedef struct {
|
||||
voidFuncPtr fn;
|
||||
bool callWithArgs;
|
||||
void *arg;
|
||||
bool lastStatusIsPressed;
|
||||
} TouchInterruptHandle_t;
|
||||
|
||||
static TouchInterruptHandle_t __touchInterruptHandlers[SOC_TOUCH_SENSOR_NUM] = {
|
||||
0,
|
||||
};
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
static uint8_t _sample_num = 1; // only one sample configuration supported
|
||||
static float _duration_ms = 5.0f;
|
||||
static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5;
|
||||
static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_1V7;
|
||||
static touch_intr_trig_mode_t _intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH;
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
|
||||
static uint8_t _sample_num = 1; // only one sample configuration supported
|
||||
static uint32_t _chg_times = 500;
|
||||
static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5;
|
||||
static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_2V2;
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
|
||||
static uint8_t _sample_num = 1; // TODO: can be extended to multiple samples
|
||||
static uint32_t _div_num = 1;
|
||||
static uint8_t _coarse_freq_tune = 1;
|
||||
static uint8_t _fine_freq_tune = 1;
|
||||
#endif
|
||||
|
||||
static uint8_t used_pads = 0;
|
||||
|
||||
static uint32_t __touchSleepTime = 256;
|
||||
static float __touchMeasureTime = 32.0f;
|
||||
|
||||
static touch_sensor_config_t sensor_config;
|
||||
|
||||
static bool initialized = false;
|
||||
static bool enabled = false;
|
||||
static bool running = false;
|
||||
static bool channels_initialized[SOC_TOUCH_SENSOR_NUM] = {false};
|
||||
|
||||
static touch_sensor_handle_t touch_sensor_handle = NULL;
|
||||
static touch_channel_handle_t touch_channel_handle[SOC_TOUCH_SENSOR_NUM] = {};
|
||||
|
||||
// Active threshold to benchmark ratio. (i.e., touch will be activated when data >= benchmark * (1 + ratio))
|
||||
static float s_thresh2bm_ratio = 0.015f; // 1.5% for all channels
|
||||
|
||||
static bool ARDUINO_ISR_ATTR __touchOnActiveISR(touch_sensor_handle_t sens_handle, const touch_active_event_data_t *event, void *user_ctx) {
|
||||
uint8_t pad_num = (uint8_t)event->chan_id;
|
||||
__touchInterruptHandlers[pad_num].lastStatusIsPressed = true;
|
||||
if (__touchInterruptHandlers[pad_num].fn) {
|
||||
// keeping backward compatibility with "void cb(void)" and with new "void cb(void *)"
|
||||
if (__touchInterruptHandlers[pad_num].callWithArgs) {
|
||||
((voidArgFuncPtr)__touchInterruptHandlers[pad_num].fn)(__touchInterruptHandlers[pad_num].arg);
|
||||
} else {
|
||||
__touchInterruptHandlers[pad_num].fn();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ARDUINO_ISR_ATTR __touchOnInactiveISR(touch_sensor_handle_t sens_handle, const touch_inactive_event_data_t *event, void *user_ctx) {
|
||||
uint8_t pad_num = (uint8_t)event->chan_id;
|
||||
__touchInterruptHandlers[pad_num].lastStatusIsPressed = false;
|
||||
if (__touchInterruptHandlers[pad_num].fn) {
|
||||
// keeping backward compatibility with "void cb(void)" and with new "void cb(void *)"
|
||||
if (__touchInterruptHandlers[pad_num].callWithArgs) {
|
||||
((voidArgFuncPtr)__touchInterruptHandlers[pad_num].fn)(__touchInterruptHandlers[pad_num].arg);
|
||||
} else {
|
||||
__touchInterruptHandlers[pad_num].fn();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool touchStop() {
|
||||
if (!running) { // Already stopped
|
||||
return true;
|
||||
}
|
||||
if (touch_sensor_stop_continuous_scanning(touch_sensor_handle) != ESP_OK) {
|
||||
log_e("Touch sensor stop scanning failed!");
|
||||
return false;
|
||||
}
|
||||
running = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchDisable() {
|
||||
if (!enabled) { // Already disabled
|
||||
return true;
|
||||
}
|
||||
if (running) {
|
||||
log_e("Touch sensor still running!");
|
||||
return false;
|
||||
}
|
||||
if (touch_sensor_disable(touch_sensor_handle) != ESP_OK) {
|
||||
log_e("Touch sensor disable failed!");
|
||||
return false;
|
||||
}
|
||||
enabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchStart() {
|
||||
if (running) { // Already running
|
||||
return true;
|
||||
}
|
||||
if (!enabled) {
|
||||
log_e("Touch sensor not enabled!");
|
||||
return false;
|
||||
}
|
||||
if (touch_sensor_start_continuous_scanning(touch_sensor_handle) != ESP_OK) {
|
||||
log_e("Touch sensor failed to start continuous scanning!");
|
||||
return false;
|
||||
}
|
||||
running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchEnable() {
|
||||
if (enabled) { // Already enabled
|
||||
return true;
|
||||
}
|
||||
if (touch_sensor_enable(touch_sensor_handle) != ESP_OK) {
|
||||
log_e("Touch sensor enable failed!");
|
||||
return false;
|
||||
}
|
||||
enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touchBenchmarkThreshold(uint8_t pad) {
|
||||
if (!touchEnable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Scan the enabled touch channels for several times, to make sure the initial channel data is stable */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (touch_sensor_trigger_oneshot_scanning(touch_sensor_handle, 2000) != ESP_OK) {
|
||||
log_e("Touch sensor trigger oneshot scanning failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable the touch channel to rollback the state */
|
||||
if (!touchDisable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reconfigure passed pad with new threshold
|
||||
uint32_t benchmark[_sample_num] = {};
|
||||
#if SOC_TOUCH_SUPPORT_BENCHMARK // ESP32S2, ESP32S3,ESP32P4
|
||||
if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_BENCHMARK, benchmark) != ESP_OK) {
|
||||
log_e("Touch channel read data failed!");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_SMOOTH, benchmark) != ESP_OK) {
|
||||
log_e("Touch channel read data failed!");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Calculate the proper active thresholds regarding the initial benchmark */
|
||||
touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG();
|
||||
for (int i = 0; i < _sample_num; i++) {
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
chan_cfg.abs_active_thresh[i] = (uint32_t)(benchmark[i] * (1 - s_thresh2bm_ratio));
|
||||
log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.abs_active_thresh[i]);
|
||||
#else
|
||||
chan_cfg.active_thresh[i] = (uint32_t)(benchmark[i] * s_thresh2bm_ratio);
|
||||
log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.active_thresh[i]);
|
||||
#endif
|
||||
}
|
||||
/* Update the channel configuration */
|
||||
if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) {
|
||||
log_e("Touch sensor threshold reconfig channel failed!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool touchDetachBus(void *pin) {
|
||||
int8_t pad = digitalPinToTouchChannel((int)(pin - 1));
|
||||
channels_initialized[pad] = false;
|
||||
//disable touch pad and delete the channel
|
||||
if (!touchStop()) {
|
||||
log_e("touchStop() failed!");
|
||||
return false;
|
||||
}
|
||||
if (!touchDisable()) {
|
||||
log_e("touchDisable() failed!");
|
||||
return false;
|
||||
}
|
||||
touch_sensor_del_channel(touch_channel_handle[pad]);
|
||||
used_pads--;
|
||||
if (used_pads == 0) {
|
||||
if (touch_sensor_del_controller(touch_sensor_handle) != ESP_OK) //deinit touch module, as no pads are used
|
||||
{
|
||||
log_e("Touch module deinit failed!");
|
||||
return false;
|
||||
}
|
||||
initialized = false;
|
||||
} else {
|
||||
touchEnable();
|
||||
touchStart();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __touchInit() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
// Support only one sample configuration for now
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V1_DEFAULT_SAMPLE_CONFIG(_duration_ms, _volt_low, _volt_high);
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
|
||||
touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V2_DEFAULT_SAMPLE_CONFIG(_chg_times, _volt_low, _volt_high);
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
|
||||
touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG(_div_num, _coarse_freq_tune, _fine_freq_tune);
|
||||
#endif
|
||||
touch_sensor_sample_config_t sample_cfg[_sample_num] = {};
|
||||
sample_cfg[0] = single_sample_cfg;
|
||||
|
||||
touch_sensor_config_t sens_cfg = {
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
.power_on_wait_us = __touchSleepTime,
|
||||
.meas_interval_us = __touchMeasureTime,
|
||||
.intr_trig_mode = _intr_trig_mode,
|
||||
.intr_trig_group = TOUCH_INTR_TRIG_GROUP_BOTH,
|
||||
.sample_cfg_num = _sample_num,
|
||||
.sample_cfg = sample_cfg,
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
|
||||
.power_on_wait_us = __touchSleepTime,
|
||||
.meas_interval_us = __touchMeasureTime,
|
||||
.max_meas_time_us = 0,
|
||||
.sample_cfg_num = _sample_num,
|
||||
.sample_cfg = sample_cfg,
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
|
||||
.power_on_wait_us = __touchSleepTime,
|
||||
.meas_interval_us = __touchMeasureTime,
|
||||
.max_meas_time_us = 0,
|
||||
.output_mode = TOUCH_PAD_OUT_AS_CLOCK,
|
||||
.sample_cfg_num = _sample_num,
|
||||
.sample_cfg = sample_cfg,
|
||||
#endif
|
||||
};
|
||||
|
||||
if (touch_sensor_new_controller(&sens_cfg, &touch_sensor_handle) != ESP_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
sensor_config = sens_cfg;
|
||||
/* Configure the touch sensor filter */
|
||||
touch_sensor_filter_config_t filter_cfg = TOUCH_SENSOR_DEFAULT_FILTER_CONFIG();
|
||||
if (touch_sensor_config_filter(touch_sensor_handle, &filter_cfg) != ESP_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Register the touch sensor on_active and on_inactive callbacks */
|
||||
touch_event_callbacks_t callbacks = {0};
|
||||
callbacks.on_active = __touchOnActiveISR;
|
||||
callbacks.on_inactive = __touchOnInactiveISR;
|
||||
|
||||
if (touch_sensor_register_callbacks(touch_sensor_handle, &callbacks, NULL) != ESP_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
return;
|
||||
err:
|
||||
log_e(" Touch sensor initialization error.");
|
||||
initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
static void __touchChannelInit(int pad) {
|
||||
if (channels_initialized[pad]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initial setup with default Threshold
|
||||
__touchInterruptHandlers[pad].fn = NULL;
|
||||
|
||||
touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG();
|
||||
|
||||
if (!touchStop() || !touchDisable()) {
|
||||
log_e("Touch sensor stop and disable failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (touch_sensor_new_channel(touch_sensor_handle, pad, &chan_cfg, &touch_channel_handle[pad]) != ESP_OK) {
|
||||
log_e("Touch sensor new channel failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Benchmark active threshold and reconfigure pad
|
||||
if (!touchBenchmarkThreshold(pad)) {
|
||||
log_e("Touch sensor benchmark threshold failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
channels_initialized[pad] = true;
|
||||
used_pads++;
|
||||
|
||||
if (!touchEnable() || !touchStart()) {
|
||||
log_e("Touch sensor enable and start failed!");
|
||||
}
|
||||
}
|
||||
|
||||
static touch_value_t __touchRead(uint8_t pin) {
|
||||
int8_t pad = digitalPinToTouchChannel(pin);
|
||||
if (pad < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus);
|
||||
if (!perimanClearPinBus(pin)) {
|
||||
return 0;
|
||||
}
|
||||
__touchInit();
|
||||
__touchChannelInit(pad);
|
||||
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) {
|
||||
touchDetachBus((void *)(pin + 1));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t touch_read[_sample_num] = {};
|
||||
touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_SMOOTH, touch_read);
|
||||
touch_value_t touch_value = touch_read[0]; // only one sample configuration for now
|
||||
|
||||
return touch_value;
|
||||
}
|
||||
|
||||
static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Args, bool callWithArgs, touch_value_t threshold) {
|
||||
int8_t pad = digitalPinToTouchChannel(pin);
|
||||
if (pad < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userFunc == NULL) {
|
||||
// detach ISR User Call
|
||||
__touchInterruptHandlers[pad].fn = NULL;
|
||||
__touchInterruptHandlers[pad].callWithArgs = false;
|
||||
__touchInterruptHandlers[pad].arg = NULL;
|
||||
} else {
|
||||
// attach ISR User Call
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus);
|
||||
if (!perimanClearPinBus(pin)) {
|
||||
log_e("Failed to clear pin bus");
|
||||
return;
|
||||
}
|
||||
__touchInit();
|
||||
__touchChannelInit(pad);
|
||||
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) {
|
||||
touchDetachBus((void *)(pin + 1));
|
||||
log_e("Failed to set bus to Peripheral manager");
|
||||
return;
|
||||
}
|
||||
}
|
||||
__touchInterruptHandlers[pad].fn = userFunc;
|
||||
__touchInterruptHandlers[pad].callWithArgs = callWithArgs;
|
||||
__touchInterruptHandlers[pad].arg = Args;
|
||||
}
|
||||
|
||||
if (threshold != 0) {
|
||||
if (!touchStop() || !touchDisable()) {
|
||||
log_e("Touch sensor stop and disable failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG();
|
||||
for (int i = 0; i < _sample_num; i++) {
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
chan_cfg.abs_active_thresh[i] = threshold;
|
||||
#else
|
||||
chan_cfg.active_thresh[i] = threshold;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) {
|
||||
log_e("Touch sensor threshold reconfig channel failed!");
|
||||
}
|
||||
|
||||
if (!touchEnable() || !touchStart()) {
|
||||
log_e("Touch sensor enable and start failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it keeps backwards compatibility
|
||||
static void __touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold) {
|
||||
__touchConfigInterrupt(pin, userFunc, NULL, false, threshold);
|
||||
}
|
||||
|
||||
// new additional version of the API with User Args
|
||||
static void __touchAttachArgsInterrupt(uint8_t pin, void (*userFunc)(void), void *args, touch_value_t threshold) {
|
||||
__touchConfigInterrupt(pin, userFunc, args, true, threshold);
|
||||
}
|
||||
|
||||
// new additional API to detach touch ISR
|
||||
static void __touchDettachInterrupt(uint8_t pin) {
|
||||
__touchConfigInterrupt(pin, NULL, NULL, false, 0); // userFunc as NULL acts as detaching
|
||||
}
|
||||
|
||||
// /*
|
||||
// External Public Touch API Functions
|
||||
// */
|
||||
|
||||
bool touchInterruptGetLastStatus(uint8_t pin) {
|
||||
int8_t pad = digitalPinToTouchChannel(pin);
|
||||
if (pad < 0) {
|
||||
return false;
|
||||
}
|
||||
return __touchInterruptHandlers[pad].lastStatusIsPressed;
|
||||
}
|
||||
|
||||
void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold) {
|
||||
int8_t pad = digitalPinToTouchChannel(pin);
|
||||
if (pad < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) {
|
||||
perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus);
|
||||
__touchInit();
|
||||
__touchChannelInit(pad);
|
||||
if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) {
|
||||
log_e("Failed to set bus to Peripheral manager");
|
||||
touchDetachBus((void *)(pin + 1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_v("Touch sensor deep sleep wake-up configuration for pad %d with threshold %d", pad, threshold);
|
||||
if (!touchStop() || !touchDisable()) {
|
||||
log_e("Touch sensor stop and disable failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
touch_sleep_config_t deep_slp_cfg = {
|
||||
.slp_wakeup_lvl = TOUCH_DEEP_SLEEP_WAKEUP,
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
.deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration
|
||||
#else // SOC_TOUCH_SENSOR_VERSION 2 and 3// ESP32S2, ESP32S3, ESP32P4
|
||||
.deep_slp_chan = touch_channel_handle[pad],
|
||||
.deep_slp_thresh = {threshold},
|
||||
.deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration
|
||||
#endif
|
||||
};
|
||||
|
||||
// Register the deep sleep wake-up
|
||||
if (touch_sensor_config_sleep_wakeup(touch_sensor_handle, &deep_slp_cfg) != ESP_OK) {
|
||||
log_e("Touch sensor deep sleep wake-up failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!touchEnable() || !touchStart()) {
|
||||
log_e("Touch sensor enable and start failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void touchSetDefaultThreshold(float percentage) {
|
||||
s_thresh2bm_ratio = (float)percentage / 100.0f;
|
||||
}
|
||||
|
||||
void touchSetTiming(float measure, uint32_t sleep) {
|
||||
if (initialized) {
|
||||
log_e("Touch sensor already initialized. Cannot set cycles.");
|
||||
return;
|
||||
}
|
||||
__touchSleepTime = sleep;
|
||||
__touchMeasureTime = measure;
|
||||
}
|
||||
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
void touchSetConfig(float duration_ms, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) {
|
||||
if (initialized) {
|
||||
log_e("Touch sensor already initialized. Cannot set configuration.");
|
||||
return;
|
||||
}
|
||||
_duration_ms = duration_ms;
|
||||
_volt_low = volt_low;
|
||||
_volt_high = volt_high;
|
||||
}
|
||||
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3
|
||||
void touchSetConfig(uint32_t chg_times, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) {
|
||||
if (initialized) {
|
||||
log_e("Touch sensor already initialized. Cannot set configuration.");
|
||||
return;
|
||||
}
|
||||
_chg_times = chg_times;
|
||||
_volt_low = volt_low;
|
||||
_volt_high = volt_high;
|
||||
}
|
||||
|
||||
#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4
|
||||
void touchSetConfig(uint32_t div_num, uint8_t coarse_freq_tune, uint8_t fine_freq_tune) {
|
||||
if (initialized) {
|
||||
log_e("Touch sensor already initialized. Cannot set configuration.");
|
||||
return;
|
||||
}
|
||||
_div_num = div_num;
|
||||
_coarse_freq_tune = coarse_freq_tune;
|
||||
_fine_freq_tune = fine_freq_tune;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32
|
||||
void touchInterruptSetThresholdDirection(bool mustbeLower) {
|
||||
if (mustbeLower) {
|
||||
_intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH;
|
||||
} else {
|
||||
_intr_trig_mode = TOUCH_INTR_TRIG_ON_ABOVE_THRESH;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
extern touch_value_t touchRead(uint8_t) __attribute__((weak, alias("__touchRead")));
|
||||
extern void touchAttachInterrupt(uint8_t, voidFuncPtr, touch_value_t) __attribute__((weak, alias("__touchAttachInterrupt")));
|
||||
extern void touchAttachInterruptArg(uint8_t, voidArgFuncPtr, void *, touch_value_t) __attribute__((weak, alias("__touchAttachArgsInterrupt")));
|
||||
extern void touchDetachInterrupt(uint8_t) __attribute__((weak, alias("__touchDettachInterrupt")));
|
||||
|
||||
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 */
|
||||
#endif /* SOC_TOUCH_SENSOR_SUPPORTED */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user