diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 81aec3b..af29c99 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -10,8 +10,10 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] python-version: [3.7] example: + - "examples/arduino-ble5-advertising" - "examples/arduino-blink" - "examples/arduino-briki-internal-libs" + - "examples/arduino-usb-keyboard" - "examples/arduino-wifiscan" - "examples/espidf-arduino-blink" - "examples/espidf-arduino-wifiscan" diff --git a/boards/atmegazero_esp32s2.json b/boards/atmegazero_esp32s2.json new file mode 100644 index 0000000..bfc4069 --- /dev/null +++ b/boards/atmegazero_esp32s2.json @@ -0,0 +1,53 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s2_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_atmegazero_esp32s2", + "-DARDUINO_USB_CDC_ON_BOOT=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x239A", + "0x800A" + ] + ], + "mcu": "esp32s2", + "variant": "atmegazero_esp32s2" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s2.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "EspinalLab ATMegaZero ESP32-S2", + "upload": { + "arduino": { + "flash_extra_images": [ + [ + "0x2d0000", + "variants/atmegazero_esp32s2/tinyuf2.bin" + ] + ] + }, + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://shop.atmegazero.com/", + "vendor": "EspinalLab" +} diff --git a/boards/denky32.json b/boards/denky32.json new file mode 100644 index 0000000..3b2c407 --- /dev/null +++ b/boards/denky32.json @@ -0,0 +1,39 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_DENKY_WROOM32", + "-DBOARD_HAS_PSRAM", + "-mfix-esp32-psram-cache-issue", + "-mfix-esp32-psram-cache-strategy=memw" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "ch_denky" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Denky32 (WROOM32)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Denky" +} diff --git a/boards/denky_d4.json b/boards/denky_d4.json new file mode 100644 index 0000000..0e668ec --- /dev/null +++ b/boards/denky_d4.json @@ -0,0 +1,39 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_DENKY_PICOV3", + "-DBOARD_HAS_PSRAM", + "-mfix-esp32-psram-cache-issue", + "-mfix-esp32-psram-cache-strategy=memw" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "ch_denky" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Denky D4 (PICO-V3-02)", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Denky" +} diff --git a/boards/esp32-c3-devkitm-1.json b/boards/esp32-c3-devkitm-1.json index 5b101a8..bc4ba05 100644 --- a/boards/esp32-c3-devkitm-1.json +++ b/boards/esp32-c3-devkitm-1.json @@ -7,6 +7,7 @@ "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", + "extra_flags": "-DARDUINO_ESP32C3_DEV", "mcu": "esp32c3", "variant": "esp32c3" }, @@ -17,6 +18,7 @@ "openocd_target": "esp32c3.cfg" }, "frameworks": [ + "arduino", "espidf" ], "name": "Espressif ESP32-C3-DevKitM-1", diff --git a/boards/esp32-s2-kaluga-1.json b/boards/esp32-s2-kaluga-1.json index ae785b7..ff0ee5a 100644 --- a/boards/esp32-s2-kaluga-1.json +++ b/boards/esp32-s2-kaluga-1.json @@ -21,6 +21,7 @@ "openocd_target": "esp32s2.cfg" }, "frameworks": [ + "arduino", "espidf" ], "name": "Espressif ESP32-S2-Kaluga-1 Kit", diff --git a/boards/esp32-s2-saola-1.json b/boards/esp32-s2-saola-1.json index ab4f9fe..4f4a36b 100644 --- a/boards/esp32-s2-saola-1.json +++ b/boards/esp32-s2-saola-1.json @@ -17,6 +17,7 @@ "openocd_target": "esp32s2.cfg" }, "frameworks": [ + "arduino", "espidf" ], "name": "Espressif ESP32-S2-Saola-1", diff --git a/boards/featheresp32-s2.json b/boards/featheresp32-s2.json index 00bf22d..bf6f0cb 100644 --- a/boards/featheresp32-s2.json +++ b/boards/featheresp32-s2.json @@ -4,11 +4,29 @@ "ldscript": "esp32s2_out.ld" }, "core": "esp32", + "extra_flags": [ + "-DARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1" + ], "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "dio", + "hwids": [ + [ + "0x239A", + "0x80EB" + ], + [ + "0x239A", + "0x00EB" + ], + [ + "0x239A", + "0x80EC" + ] + ], "mcu": "esp32s2", - "variant": "esp32s2" + "variant": "adafruit_feather_esp32s2" }, "connectivity": [ "wifi" @@ -17,13 +35,24 @@ "openocd_target": "esp32s2.cfg" }, "frameworks": [ + "arduino", "espidf" ], "name": "Adafruit ESP32-S2 Feather Development Board", "upload": { + "arduino": { + "flash_extra_images": [ + [ + "0x2d0000", + "variants/adafruit_feather_esp32s2/tinyuf2.bin" + ] + ] + }, "flash_size": "4MB", "maximum_ram_size": 327680, "maximum_size": 4194304, + "use_1200bps_touch": true, + "wait_for_upload_port": true, "require_upload_port": true, "speed": 460800 }, diff --git a/boards/kb32-ft.json b/boards/kb32-ft.json new file mode 100644 index 0000000..586494f --- /dev/null +++ b/boards/kb32-ft.json @@ -0,0 +1,42 @@ +{ + "build": { + "arduino": { + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_ESP32_DEV", + "-DBOARD_HAS_PSRAM", + "-mfix-esp32-psram-cache-issue", + "-mfix-esp32-psram-cache-strategy=memw" + ], + "f_cpu": "240000000L", + "f_flash": "40000000L", + "flash_mode": "dio", + "mcu": "esp32", + "variant": "esp32" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_board": "esp-wroom-32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "MakerAsia KB32-FT", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://kb32ft.makerasia.com/", + "vendor": "MakerAsia" +} diff --git a/boards/m5stack-fire.json b/boards/m5stack-fire.json index 4f3ff39..1a500ab 100644 --- a/boards/m5stack-fire.json +++ b/boards/m5stack-fire.json @@ -5,7 +5,12 @@ "partitions": "default_16MB.csv" }, "core": "esp32", - "extra_flags": "-DARDUINO_M5STACK_FIRE -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue", + "extra_flags": [ + "-DARDUINO_M5STACK_FIRE", + "-DBOARD_HAS_PSRAM", + "-mfix-esp32-psram-cache-issue", + "-mfix-esp32-psram-cache-strategy=memw" + ], "f_cpu": "240000000L", "f_flash": "40000000L", "flash_mode": "dio", diff --git a/boards/m5stack-timer-cam.json b/boards/m5stack-timer-cam.json index 41ff2a8..5f76a86 100644 --- a/boards/m5stack-timer-cam.json +++ b/boards/m5stack-timer-cam.json @@ -4,7 +4,12 @@ "ldscript": "esp32_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_M5Stack_Timer_CAM -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue", + "extra_flags": [ + "-DARDUINO_M5Stack_Timer_CAM", + "-DBOARD_HAS_PSRAM", + "-mfix-esp32-psram-cache-issue", + "-mfix-esp32-psram-cache-strategy=memw" + ], "f_cpu": "240000000L", "f_flash": "40000000L", "flash_mode": "dio", diff --git a/boards/micros2.json b/boards/micros2.json new file mode 100644 index 0000000..93243bd --- /dev/null +++ b/boards/micros2.json @@ -0,0 +1,48 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s2_out.ld", + "partitions": "ffat.csv" + }, + "core": "esp32", + "extra_flags": [ + "-DARDUINO_MICROS2", + "-DBOARD_HAS_PSRAM", + "-DARDUINO_USB_CDC_ON_BOOT=1", + "-DARDUINO_USB_MSC_ON_BOOT=1" + ], + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "hwids": [ + [ + "0x239A", + "0x80C5" + ] + ], + "mcu": "esp32s2", + "variant": "micro_s2" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s2.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "microS2", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://github.com/MicroDev1/microS2", + "vendor": "microS2" +} diff --git a/boards/sparkfun_esp32s2_thing_plus.json b/boards/sparkfun_esp32s2_thing_plus.json new file mode 100644 index 0000000..269d962 --- /dev/null +++ b/boards/sparkfun_esp32s2_thing_plus.json @@ -0,0 +1,43 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s2_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32S2_THING_PLUS", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "qio", + "hwids": [ + [ + "0x1B4F", + "0x0027" + ] + ], + "mcu": "esp32s2", + "variant": "esp32s2thing_plus" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s2.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "SparkFun ESP32-S2 Thing Plus", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "mode": "usb", + "use_1200bps_touch": true, + "wait_for_upload_port": true, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://www.sparkfun.com/products/17743", + "vendor": "SparkFun" +} diff --git a/boards/ttgo-t-watch.json b/boards/ttgo-t-watch.json index c58c273..354f54f 100644 --- a/boards/ttgo-t-watch.json +++ b/boards/ttgo-t-watch.json @@ -5,7 +5,12 @@ "partitions": "default_16MB.csv" }, "core": "esp32", - "extra_flags": "-DARDUINO_TWATCH_BASE -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue", + "extra_flags": [ + "-DARDUINO_TWATCH_BASE", + "-DBOARD_HAS_PSRAM", + "-mfix-esp32-psram-cache-issue", + "-mfix-esp32-psram-cache-strategy=memw" + ], "f_cpu": "240000000L", "f_flash": "40000000L", "flash_mode": "dio", diff --git a/builder/frameworks/espidf.py b/builder/frameworks/espidf.py index 0b8e356..c36fd94 100644 --- a/builder/frameworks/espidf.py +++ b/builder/frameworks/espidf.py @@ -50,18 +50,20 @@ mcu = board.get("build.mcu", "esp32") idf_variant = mcu.lower() FRAMEWORK_DIR = platform.get_package_dir("framework-espidf") -TOOLCHAIN_DIR = platform.get_package_dir( - "toolchain-%s" - % ( - "riscv32-esp" - if mcu == "esp32c3" - else ("xtensa-esp32s2" if mcu == "esp32s2" else "xtensa-esp32") - ) -) # Legacy toolchains for mixed IDF/Arduino projects if "arduino" in env.subst("$PIOFRAMEWORK"): TOOLCHAIN_DIR = platform.get_package_dir("toolchain-xtensa32") +else: + TOOLCHAIN_DIR = platform.get_package_dir( + "toolchain-%s" + % ( + "riscv32-esp" + if mcu == "esp32c3" + else ("xtensa-esp32s2" if mcu == "esp32s2" else "xtensa-esp32") + ) + ) + assert os.path.isdir(FRAMEWORK_DIR) assert os.path.isdir(TOOLCHAIN_DIR) diff --git a/builder/main.py b/builder/main.py index 1f01bf8..0d87ab3 100644 --- a/builder/main.py +++ b/builder/main.py @@ -20,11 +20,28 @@ from SCons.Script import ( ARGUMENTS, COMMAND_LINE_TARGETS, AlwaysBuild, Builder, Default, DefaultEnvironment) +from platformio.util import get_serial_ports + # # Helpers # +def BeforeUpload(target, source, env): + upload_options = {} + if "BOARD" in env: + upload_options = env.BoardConfig().get("upload", {}) + + env.AutodetectUploadPort() + + before_ports = get_serial_ports() + if upload_options.get("use_1200bps_touch", False): + env.TouchSerialPort("$UPLOAD_PORT", 1200) + + if upload_options.get("wait_for_upload_port", False): + env.Replace(UPLOAD_PORT=env.WaitForNewSerialPort(before_ports)) + + def _get_board_f_flash(env): frequency = env.subst("$BOARD_F_FLASH") frequency = str(frequency).replace("L", "") @@ -343,8 +360,7 @@ elif upload_protocol == "esptool": ) upload_actions = [ - env.VerboseAction(env.AutodetectUploadPort, - "Looking for upload port..."), + env.VerboseAction(BeforeUpload, "Looking for upload port..."), env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE") ] diff --git a/examples/arduino-ble5-advertising/.gitignore b/examples/arduino-ble5-advertising/.gitignore new file mode 100644 index 0000000..03f4a3c --- /dev/null +++ b/examples/arduino-ble5-advertising/.gitignore @@ -0,0 +1 @@ +.pio diff --git a/examples/arduino-ble5-advertising/README.md b/examples/arduino-ble5-advertising/README.md new file mode 100644 index 0000000..f34cc0d --- /dev/null +++ b/examples/arduino-ble5-advertising/README.md @@ -0,0 +1,27 @@ +How to build PlatformIO based project +===================================== + +1. [Install PlatformIO Core](https://docs.platformio.org/page/core.html) +2. Download [development platform with examples](https://github.com/platformio/platform-espressif32/archive/develop.zip) +3. Extract ZIP archive +4. Run these commands: + +```shell +# Change directory to example +$ cd platform-espressif32/examples/arduino-ble5-advertising + +# Build project +$ pio run + +# Upload firmware +$ pio run --target upload + +# Build specific environment +$ pio run -e esp32-c3-devkitm-1 + +# Upload firmware for the specific environment +$ pio run -e esp32-c3-devkitm-1 --target upload + +# Clean build files +$ pio run --target clean +``` \ No newline at end of file diff --git a/examples/arduino-ble5-advertising/include/README b/examples/arduino-ble5-advertising/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/examples/arduino-ble5-advertising/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/examples/arduino-ble5-advertising/lib/README b/examples/arduino-ble5-advertising/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/examples/arduino-ble5-advertising/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/examples/arduino-ble5-advertising/platformio.ini b/examples/arduino-ble5-advertising/platformio.ini new file mode 100644 index 0000000..638c205 --- /dev/null +++ b/examples/arduino-ble5-advertising/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32-c3-devkitm-1] +platform = espressif32 +board = esp32-c3-devkitm-1 +framework = arduino +monitor_speed = 115200 diff --git a/examples/arduino-ble5-advertising/src/BLE5_periodic_advertising.ino b/examples/arduino-ble5-advertising/src/BLE5_periodic_advertising.ino new file mode 100644 index 0000000..1b36bbb --- /dev/null +++ b/examples/arduino-ble5-advertising/src/BLE5_periodic_advertising.ino @@ -0,0 +1,72 @@ +/* + Simple BLE5 multi advertising example on esp32 C3/S3 + only ESP_BLE_GAP_SET_EXT_ADV_PROP_NONCONN_NONSCANNABLE_UNDIRECTED can be used for periodic advertising + + author: chegewara +*/ + +#include +#include + + +esp_ble_gap_ext_adv_params_t ext_adv_params_2M = { + .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_NONCONN_NONSCANNABLE_UNDIRECTED, + .interval_min = 0x40, + .interval_max = 0x40, + .channel_map = ADV_CHNL_ALL, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, + .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + .primary_phy = ESP_BLE_GAP_PHY_1M, + .max_skip = 0, + .secondary_phy = ESP_BLE_GAP_PHY_2M, + .sid = 1, + .scan_req_notif = false, +}; + +static uint8_t raw_scan_rsp_data_2m[] = { + 0x02, 0x01, 0x06, + 0x02, 0x0a, 0xeb, + 0x12, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A', + 'D', 'V', '_', '2', 'M', 0X0 +}; + +static esp_ble_gap_periodic_adv_params_t periodic_adv_params = { + .interval_min = 0x320, // 1000 ms interval + .interval_max = 0x640, + .properties = 0, // Do not include TX power +}; + +static uint8_t periodic_adv_raw_data[] = { + 0x02, 0x01, 0x06, + 0x02, 0x0a, 0xeb, + 0x03, 0x03, 0xab, 0xcd, + 0x11, 0x09, 'E', 'S', 'P', '_', 'P', 'E', 'R', 'I', 'O', 'D', 'I', + 'C', '_', 'A', 'D', 'V' +}; + + +uint8_t addr_2m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x02}; + +BLEMultiAdvertising advert(1); // max number of advertisement data + +void setup() { + Serial.begin(115200); + Serial.println("Multi-Advertising..."); + + BLEDevice::init(""); + + advert.setAdvertisingParams(0, &ext_adv_params_2M); + advert.setAdvertisingData(0, sizeof(raw_scan_rsp_data_2m), &raw_scan_rsp_data_2m[0]); + advert.setInstanceAddress(0, addr_2m); + advert.setDuration(0, 0, 0); + + delay(100); + advert.start(); + advert.setPeriodicAdvertisingParams(0, &periodic_adv_params); + advert.setPeriodicAdvertisingData(0, sizeof(periodic_adv_raw_data), &periodic_adv_raw_data[0]); + advert.startPeriodicAdvertising(0); +} + +void loop() { + delay(2000); +} diff --git a/examples/arduino-ble5-advertising/test/README b/examples/arduino-ble5-advertising/test/README new file mode 100644 index 0000000..df5066e --- /dev/null +++ b/examples/arduino-ble5-advertising/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/examples/arduino-usb-keyboard/.gitignore b/examples/arduino-usb-keyboard/.gitignore new file mode 100644 index 0000000..03f4a3c --- /dev/null +++ b/examples/arduino-usb-keyboard/.gitignore @@ -0,0 +1 @@ +.pio diff --git a/examples/arduino-usb-keyboard/README.md b/examples/arduino-usb-keyboard/README.md new file mode 100644 index 0000000..e9bd6a2 --- /dev/null +++ b/examples/arduino-usb-keyboard/README.md @@ -0,0 +1,27 @@ +How to build PlatformIO based project +===================================== + +1. [Install PlatformIO Core](https://docs.platformio.org/page/core.html) +2. Download [development platform with examples](https://github.com/platformio/platform-espressif32/archive/develop.zip) +3. Extract ZIP archive +4. Run these commands: + +```shell +# Change directory to example +$ cd platform-espressif32/examples/arduino-usb-keyboard + +# Build project +$ pio run + +# Upload firmware +$ pio run --target upload + +# Build specific environment +$ pio run -e esp32-s2-saola-1 + +# Upload firmware for the specific environment +$ pio run -e esp32-s2-saola-1 --target upload + +# Clean build files +$ pio run --target clean +``` \ No newline at end of file diff --git a/examples/arduino-usb-keyboard/include/README b/examples/arduino-usb-keyboard/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/examples/arduino-usb-keyboard/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/examples/arduino-usb-keyboard/lib/README b/examples/arduino-usb-keyboard/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/examples/arduino-usb-keyboard/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/examples/arduino-usb-keyboard/platformio.ini b/examples/arduino-usb-keyboard/platformio.ini new file mode 100644 index 0000000..3f51b83 --- /dev/null +++ b/examples/arduino-usb-keyboard/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env] +platform = espressif32 +framework = arduino +monitor_speed = 115200 + +[env:esp32-s2-saola-1] +board = esp32-s2-saola-1 + +[env:esp32-s2-kaluga-1] +board = esp32-s2-kaluga-1 \ No newline at end of file diff --git a/examples/arduino-usb-keyboard/src/KeyboardSerial.ino b/examples/arduino-usb-keyboard/src/KeyboardSerial.ino new file mode 100644 index 0000000..e3bb876 --- /dev/null +++ b/examples/arduino-usb-keyboard/src/KeyboardSerial.ino @@ -0,0 +1,40 @@ +/* + Keyboard test + + Reads a byte from the serial port, sends a keystroke back. + The sent keystroke is one higher than what's received, e.g. if you send a, + you get b, send A you get B, and so forth. + + The circuit: + - none + + created 21 Oct 2011 + modified 27 Mar 2012 + by Tom Igoe + + This example code is in the public domain. + + http://www.arduino.cc/en/Tutorial/KeyboardSerial +*/ + +#include "USB.h" +#include "USBHIDKeyboard.h" +USBHIDKeyboard Keyboard; + +void setup() { + // open the serial port: + Serial.begin(115200); + // initialize control over the keyboard: + Keyboard.begin(); + USB.begin(); +} + +void loop() { + // check for incoming serial data: + if (Serial.available() > 0) { + // read incoming serial data: + char inChar = Serial.read(); + // Type the next ASCII value from what you received: + Keyboard.write(inChar + 1); + } +} diff --git a/examples/arduino-usb-keyboard/test/README b/examples/arduino-usb-keyboard/test/README new file mode 100644 index 0000000..df5066e --- /dev/null +++ b/examples/arduino-usb-keyboard/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/platform.json b/platform.json index 113652d..ade7ced 100644 --- a/platform.json +++ b/platform.json @@ -47,17 +47,31 @@ } }, "packages": { - "toolchain-xtensa32": { + "toolchain-xtensa-esp32": { "type": "toolchain", - "owner": "platformio", - "version": "~2.50200.0", - "optionalVersions": ["~2.80400.0"] + "owner": "espressif", + "version": "8.4.0+2021r1", + "optionalVersions": [ + "8.4.0+2021r2-patch2" + ] }, - "toolchain-xtensa32s2": { + "toolchain-xtensa-esp32s2": { "type": "toolchain", "optional": true, - "owner": "platformio", - "version": "~1.80400.0" + "owner": "espressif", + "version": "8.4.0+2021r1", + "optionalVersions": [ + "8.4.0+2021r2-patch2" + ] + }, + "toolchain-riscv32-esp": { + "type": "toolchain", + "optional": true, + "owner": "espressif", + "version": "8.4.0+2021r1", + "optionalVersions": [ + "8.4.0+2021r2-patch2" + ] }, "toolchain-esp32ulp": { "type": "toolchain", @@ -71,35 +85,11 @@ "owner": "platformio", "version": "~1.22851.0" }, - "toolchain-riscv-esp": { - "type": "toolchain", - "optional": true, - "owner": "platformio", - "version": "~1.80400.0" - }, - "toolchain-xtensa-esp32": { - "type": "toolchain", - "optional": true, - "owner": "espressif", - "version": "8.4.0+2021r2-patch2" - }, - "toolchain-xtensa-esp32s2": { - "type": "toolchain", - "optional": true, - "owner": "espressif", - "version": "8.4.0+2021r2-patch2" - }, - "toolchain-riscv32-esp": { - "type": "toolchain", - "optional": true, - "owner": "espressif", - "version": "8.4.0+2021r2-patch2" - }, "framework-arduinoespressif32": { "type": "framework", "optional": true, "owner": "platformio", - "version": "~3.10006.0" + "version": "~3.20000.0" }, "framework-arduino-mbcwb": { "type": "framework", diff --git a/platform.py b/platform.py index f620980..62aaee2 100644 --- a/platform.py +++ b/platform.py @@ -14,6 +14,11 @@ import copy import os +import urllib +import sys +import json +import re +import requests from platformio import fs from platformio.managers.platform import PlatformBase @@ -29,49 +34,86 @@ class Espressif32Platform(PlatformBase): mcu = variables.get("board_build.mcu", board_config.get("build.mcu", "esp32")) frameworks = variables.get("pioframework", []) - # Legacy toolchain names are default value. This logic is temporary as the - # platform is gradually being switched to the toolchain packages from the - # Espressif organization. - - xtensa32_toolchain = "toolchain-xtensa32" - xtensa32s2_toolchain = "toolchain-xtensa32s2" - riscv_toolchain = "toolchain-riscv-esp" - if "buildfs" in targets: self.packages["tool-mkspiffs"]["optional"] = False if variables.get("upload_protocol"): self.packages["tool-openocd-esp32"]["optional"] = False if os.path.isdir("ulp"): self.packages["toolchain-esp32ulp"]["optional"] = False + + build_core = variables.get( + "board_build.core", board_config.get("build.core", "arduino") + ).lower() + + if len(frameworks) == 1 and "arduino" in frameworks and build_core == "esp32": + # In case the upstream Arduino framework is specified in the configuration + # file then we need to dynamically extract toolchain versions from the + # Arduino index file. This feature can be disabled via a special option: + if ( + variables.get( + "board_build.arduino.upstream_packages", + board_config.get("build.arduino.upstream_packages", "yes"), + ).lower() + == "yes" + ): + package_version = self.packages["framework-arduinoespressif32"][ + "version" + ] + + url_items = urllib.parse.urlparse(package_version) + # Only GitHub repositories support dynamic packages + if ( + url_items.scheme in ("http", "https") + and url_items.netloc.startswith("github") + and url_items.path.endswith(".git") + ): + try: + self.configure_upstream_arduino_packages(url_items) + except Exception as e: + sys.stderr.write( + "Error! Failed to extract upstream toolchain" + "configurations:\n%s\n" % str(e) + ) + sys.stderr.write( + "You can disable this feature via the " + "`board_build.arduino.upstream_packages = no` setting in " + "your `platformio.ini` file.\n" + ) + sys.exit(1) + if "espidf" in frameworks: - # Legacy setting for mixed IDF+Arduino projects - if "arduino" in frameworks: - self.packages[xtensa32_toolchain]["version"] = "~2.80400.0" - # Arduino component is not compatible with ESP-IDF >=4.1 - self.packages["framework-espidf"]["version"] = "~3.40001.0" - else: - xtensa32_toolchain = "toolchain-xtensa-esp32" - xtensa32s2_toolchain = "toolchain-xtensa-esp32s2" - riscv_toolchain = "toolchain-riscv32-esp" - - for p in self.packages.copy(): - # Disable old toolchains used by default - if ( - p.startswith("toolchain") - and "ulp" not in p - ): - if self.packages[p]["owner"] == "espressif": - self.packages[p]["optional"] = False - else: - del self.packages[p] - + # Common package for IDF and mixed Arduino+IDF projects for p in self.packages: if p in ("tool-cmake", "tool-ninja", "toolchain-%sulp" % mcu): self.packages[p]["optional"] = False elif p in ("tool-mconf", "tool-idf") and "windows" in get_systype(): self.packages[p]["optional"] = False - else: - # Remove the latest toolchains from PATH for frameworks except IDF + + # Toolchains for stable IDF are different from Arduino + if len(frameworks) == 1: + for toolchain in ( + "toolchain-xtensa-esp32", + "toolchain-xtensa-esp32s2", + "toolchain-riscv32-esp", + ): + self.packages[toolchain]["version"] = "8.4.0+2021r2-patch2" + + if mcu in ("esp32s2", "esp32c3"): + self.packages.pop("toolchain-xtensa-esp32", None) + self.packages.pop("toolchain-esp32ulp", None) + # RISC-V based toolchain for ESP32C3 and ESP32S2 ULP + self.packages["toolchain-riscv32-esp"]["optional"] = False + if mcu == "esp32s2": + self.packages["toolchain-xtensa-esp32s2"]["optional"] = False + + is_legacy_project = ( + build_core == "mbcwb" + or set(("simba", "pumbaa")) & set(frameworks) + or set(("arduino", "espidf")) == set(frameworks) + ) + + if is_legacy_project: + # Remove the main toolchains from PATH for toolchain in ( "toolchain-xtensa-esp32", "toolchain-xtensa-esp32s2", @@ -79,22 +121,25 @@ class Espressif32Platform(PlatformBase): ): self.packages.pop(toolchain, None) - if mcu in ("esp32s2", "esp32c3"): - self.packages.pop(xtensa32_toolchain, None) - self.packages.pop("toolchain-esp32ulp", None) - # RISC-V based toolchain for ESP32C3 and ESP32S2 ULP - self.packages[riscv_toolchain]["optional"] = False - if mcu == "esp32s2": - self.packages[xtensa32s2_toolchain]["optional"] = False + # Add legacy toolchain with specific version + self.packages["toolchain-xtensa32"] = { + "type": "toolchain", + "owner": "platformio", + "version": "~2.80400.0" + if "arduino" in frameworks and build_core != "mbcwb" + else "~2.50200.0", + } - build_core = variables.get( - "board_build.core", board_config.get("build.core", "arduino") - ).lower() - if build_core == "mbcwb": - self.packages["framework-arduinoespressif32"]["optional"] = True - self.packages["framework-arduino-mbcwb"]["optional"] = False - self.packages["tool-mbctool"]["type"] = "uploader" - self.packages["tool-mbctool"]["optional"] = False + # Legacy setting for mixed IDF+Arduino projects + if set(("arduino", "espidf")) == set(frameworks): + # Arduino component is not compatible with ESP-IDF >=4.1 + self.packages["framework-espidf"]["version"] = "~3.40001.0" + + if build_core == "mbcwb": + self.packages["framework-arduinoespressif32"]["optional"] = True + self.packages["framework-arduino-mbcwb"]["optional"] = False + self.packages["tool-mbctool"]["type"] = "uploader" + self.packages["tool-mbctool"]["optional"] = False return PlatformBase.configure_default_packages(self, variables, targets) @@ -271,3 +316,100 @@ class Espressif32Platform(PlatformBase): ) debug_options["load_cmds"] = load_cmds return debug_options + + @staticmethod + def extract_toolchain_versions(tool_deps): + def _parse_version(original_version): + assert original_version + match = re.match(r"^gcc(\d+)_(\d+)_(\d+)\-esp\-(.+)$", original_version) + if not match: + raise ValueError("Bad package version `%s`" % original_version) + assert len(match.groups()) == 4 + return "%s.%s.%s+%s" % (match.groups()) + + if not tool_deps: + raise ValueError( + ("Failed to extract tool dependencies from the remote package file") + ) + + toolchain_remap = { + "xtensa-esp32-elf-gcc": "toolchain-xtensa-esp32", + "xtensa-esp32s2-elf-gcc": "toolchain-xtensa-esp32s2", + "xtensa-esp32s3-elf-gcc": "toolchain-xtensa-esp32s3", + "riscv32-esp-elf-gcc": "toolchain-riscv32-esp", + } + + result = dict() + for tool in tool_deps: + if tool["name"] in toolchain_remap: + result[toolchain_remap[tool["name"]]] = _parse_version(tool["version"]) + + return result + + @staticmethod + def parse_tool_dependencies(index_data): + for package in index_data.get("packages", []): + if package["name"] == "esp32": + for platform in package["platforms"]: + if platform["name"] == "esp32": + return platform["toolsDependencies"] + + return [] + + @staticmethod + def download_remote_package_index(url_items): + def _prepare_url_for_index_file(url_items): + tag = "master" + if url_items.fragment: + tag = url_items.fragment + return ( + "https://raw.githubusercontent.com/%s/" + "%s/package/package_esp32_index.template.json" + % (url_items.path.replace(".git", ""), tag) + ) + + index_file_url = _prepare_url_for_index_file(url_items) + r = requests.get(index_file_url, timeout=10) + if r.status_code != 200: + raise ValueError( + ( + "Failed to download package index file due to a bad response (%d) " + "from the remote `%s`" + ) + % (r.status_code, index_file_url) + ) + return r.json() + + def configure_arduino_toolchains(self, package_index): + if not package_index: + return + + toolchain_packages = self.extract_toolchain_versions( + self.parse_tool_dependencies(package_index) + ) + for toolchain_package, version in toolchain_packages.items(): + if toolchain_package not in self.packages: + self.packages[toolchain_package] = dict() + self.packages[toolchain_package]["version"] = version + self.packages[toolchain_package]["owner"] = "espressif" + + def configure_upstream_arduino_packages(self, url_items): + framework_index_file = os.path.join( + self.get_package_dir("framework-arduinoespressif32") or "", + "package", + "package_esp32_index.template.json", + ) + + # Detect whether the remote is already cloned + if os.path.isfile(framework_index_file) and os.path.isdir( + os.path.join( + self.get_package_dir("framework-arduinoespressif32") or "", ".git" + ) + ): + with open(framework_index_file) as fp: + self.configure_arduino_toolchains(json.load(fp)) + else: + print("Configuring toolchain packages from a remote source...") + self.configure_arduino_toolchains( + self.download_remote_package_index(url_items) + )