2023-03-01
md
First Look at the Seeed Studio XIAO ESP32C3
A Wi-Fi Switch for Domoticz using a XIAO ESP32C3 - Part 1-> <-First Look at the SAMD21 Arm Cortex-M0+ Based Seeeduino XIAO
<-Blink with a Wio Lite RISC-V with ESP8266

This is an account of my first look at another member of the XIAO family of tiny development boards from Seeed Studio, the XIAO ESP32C3. There's no big project here, I just wanted to compile and upload a few simple projects from the PlatformIO and Arduino IDEs. I also wanted to look at MicroPython on the XIAO.

The source code for all the C++ projects is made available in a single GitHub repository. Some care was taken to set up each project in a way that both PlatformIO and the Arduino IDE can use the same source code. The repository notes explain how that was done. The repository does not contain the example MicroPython script presented in this post. In keeping with the use of the Arduino-ESP32, two example projects which do the same thing as the MicroPython code are included in the repository. These are not discussed at all in this post.

This is a very personal examination of the XIAO ESP32C3. It was the first time I used ESP32-C3 based development board, but it is not my first project with PlatformIO, the Arduino IDE, and MicroPython. While I go into excruciating details where I was doing things for the first time, instructions for the rank beginner are not given. In particular there is nothing about installing the PlatformIO or Arduino IDE. There are numerous tutorials for beginners including the Espressif Installing Guide in the Arduino-ESP32 documentation which seems good with respect to the Arduino IDE, but amounts to providing a link to PlatfomrIO's own getting started guide. The screen captures shown below were done either in the Arduino IDE version 2.0.3 for Linux or in the PlatformIO IDE 2.4.0 extension of VSCodium (version 1.75.1) which "is a community-driven, freely-licensed binary distribution of Microsoft’s editor VS Code."

Table of Content

  1. Disclosure
  2. Tiny, Cute and Markedly Different
  3. A Rose is a Rose...
  4. Bootloader, Buttons and Bows
  5. Where is the Board?
  6. A Do Nothing PlatformIO Project
  7. Doin' Nothin' in the Arduino IDE
  8. Pin Numbers and Names
  9. At Last, the XIAO ESP32C3 Blinks
  10. Button Controlled LED
  11. MicroPython: REPL Controlled LED
  12. MicroPython: Web Controlled LED
  13. Bluetooth Controlled LED
  14. Uncurated Bibliography and Concluding Thoughts

Disclosure toc

Seeed Studio Invoice It is only right to disclose that I was given two XIAO ESP32C3 boards along with a XIAO nRF52840 Sense and a XIAO starter kit by Seeed Studio. Click on the image of the invoice on the right for details. The understanding was that Seeed Studio hoped that I would write a post about some of these products along the lines of my Overview of the SAMD21 Arm Cortex-M0+ Based Seeed Studio XIAO, but that there was no obligation, nor was a time line imposed. That's good because the package arrived five months ago, but I was so occupied elsewhere that I opened the first of the platic bags containing an ESP32C3 only on the 20th of February. As I write this, I assume that my contact at Seeed Studio must have given up hope, as he had the package send express by DHL back in October of last year!

If trying to judge how much I am beholden "to the man", take into consideration that there was no payment from Seeed Studio and that these notes and future posts have not and will not be submitted to Seeed Studio before being published. Besides, all I really do is describe how I go about doing things for my own use and I publish some of my notes in the hope that others may find the information useful. So this post is not a recommendation about various products because I simply do not have enough knowledge or experience to make judgments about the relative merits of the numerous devices that are available from many vendors. Now is a good time to repeat that I do not claim any expertise about the subject matter covered in this post or any other post on this site.

With that out of the way, let me add that I purchased another two XIAO ESP32C3 from Seeed Studio as soon as they became available again in late December 2022 even if I had no experience with them. When Seeed Studio offered low shipping rates for these cheap ($5 US) devices with attractive specifications, I jumped at the chance to get them, along with a couple of XIAO nRF52840, especially given the supply issues that have been plaguing the markets for electronic enthusiasts in the last few years. At the same time, I also purchased a similar number of WEMOS W600-PICO development boards which is based on another Wi-Fi enabled microcontroller. Blame that purchase, in part, for the delay into looking at the ESP32C3. More accurately, I made an error in my initial look at the W600-PICO that I felt was so egregious that my priority was to rewrite the whole thing as quickly as possible. The similarities and differences are interesting enough that I may write a comparison of the two boards in the future.

Tiny, Cute and Markedly Different toc

The XIAO ESP32C3 by Seeed Studio is the fifth and latest addition to the Seeed Studio XIAO Series of diminutive development boards. As the name implies it is based on the ESP32-C3 microcontroller from Espressif which some, Elliot Williams at Hackaday and digiblurDIY notably, have touted as a replacement for the ESP8266. The ESP32-C3 is a departure for Espressif because it has a RISC-V processor instead of an Xtensa L106 processor. It is also a departure for the XIAO series since this is the first member that is not based on an ARM Cortex processor.

Because this is a first look and there may be a future detailed comparison with other boards, I will not describe the processor and development board in much detail at this time. Let's just say that the RISC-V processor is single core and runs at 160 MHz. It has wireless connectivity: Wi-Fi (IEEE 802.11 b/g/n; 2.4 GHz; HT20/40; up to 150 Mbps) and Bluetooth LE (v5.0). It comes with two 12-bit ADC (analog to digital converters) available on 6 channels and 22 or 16 (depending on the package) programmable GPIO pins. There's also a full complement of digital interfaces including three SPI, two UART, one I²C and one I²S. As for storage, there is 400 Kbytes of static RAM, 284 Kbytes of ROM and 8Kbytes of static ram on the real-time clock. The chip used in the XIAO ESP32C3, the ESP32-C3FN4, comes with 4 Mbytes of on-board flash memory. The list goes on of course (source).

A Rose is a Rose... toc

While currently named Seeed Studio XIAO ESP32C3 (SKU 113991054), the board was initially released as the XIAO WiFi/BLE - EPS32C3 (source). Consequently, there may be libraries for the device that use the older name, but I suspect that this will be much rarer than in the case of the Seeeduino XIAO renamed Seeed Studio XIAO SAMD21 much later after its release.

Saying that the XIAO ESP32C3 belonged to the "XIAO family of tiny development boards" was redundant because, as I found out months ago, XIAO means small or tiny. In any case, the board conforms to the XIAO 18x22 mm form factor shown on the right. There are 14 pads along the edges of the board, three of which are dedicated to power and ground connections and the remaining 11 pins are I/O. Four of these pins have no predefined functions, while the remaining seven are used to provide three serial peripherals: UART, I²C and SPI. Below is the specific pin assignments for the XIAO ESP32C3.

XIAO ESP32C3 - front XIAO ESP32C3 - back

The pin naming convention is not the same as with the XIAO SAMD21 which is a bit unfortunate. Maybe it would have been better to adopt a "gender neutral" naming convention such as P0 to P10 at the very start with the XIAO SAMD21, but that's with the benefit of hindsight, of course. The perfect match between pin name and pin number of the XIAO SAMD21 (i.e., where "An" is the name of pin number n) is not present here. That is not a problem because symbolic names instead of "magic numbers" should always be used.

Because the ESP32C3 does not have an ARM Cortex microcontroller the connections at the back of the board are quite different from the other members of the XIAO family. The SWD debug interface is replaced with a JTAG interface. The board also has battery connection pads as it can be powered from a connected battery and can charge the battery when powered from the USB connector or the 5 volt pin. The board has no power or user-controlled LEDS. The small red LED near the USB-C connector labelled CH is a charge indicator.

As with other members of the Seeed XIAO family, Adafruit has an equivalent product, the QT Py ESP32-C3 in its line-up of QT Py / XIAO development boards. At roughly twice the price of the Seeed model, it is based on the same the ESP32-C3FN4 SoC. It boasts a neo-pixel, an on-board antenna and on-board STEMMA QT connector parallel to the I²C pins which is somewhat compatible with the Seeed Grove connector. The QT Py does not have JTAG pads as far as I can make out. Furthermore components are mounted on both sides of the board so if the QT Py were to be soldered using its castellated pads, a cutout would have to be made on the larger board.

Bootloader, Buttons and Bows toc

The XIAO has two normally open push buttons and one large bow. It's not really a bow of course, but it is an elegant, mat black, Wi-Fi and Bluetooth antenna. While very thin, it's area is almost twice that of the XIAO itself. The U.FL coaxial connector is very small, a bit fiddly and will probably not survive very many connections (the female connector on the antenna is rated for only 30 reconnections, see Hirose U.FL), so I will try to avoid disconnecting it.

There are two small push buttons at the bottom of the board along the edge opposite the USB-C connector. The one on the right is the reset button while the on the left is the boot mode button needed to put the board in bootloader mode. The reset button works as expected. It is a welcome addition compared to the bare pads of the XIAO SAMD21 which generated (warning! hyperbole about to be committed) half of the postings on the forum when that board was launched. The Seeed Wiki talks about the boot button and bootloader mode only in the context of reflashing the factory firmware, something I have not done nor see the need to do. Nevertheless, that boot button very useful. The sequence of button presses and releases to get to bootloader mode is pretty common.

  1. Press and keep depressed the reset button.
  2. Press and keep depressed the boot button.
  3. Release the reset button.
  4. Release the boot button.

Another way of putting the board in bootloader mode is described in the Wiki.

  1. Remove power to the board, usually by disconnecting the USB cable.
  2. Press and keep depressed the boot button.
  3. Apply power to the board, usually by connecting the USB cable.
  4. Release the boot button.

Be deliberate about these steps, there is no rush. Once the sequence is completed, the board will be in bootloader mode waiting for new firmware. Uploading should now work. Remember that after the upload is finished, the board remains in bootloader mode and the reset button must be pressed and released to return the board to normal mode where it will execute the newly uploaded firmware.

If I were a bit more careful, there would hardly be any need to put the XIAO into bootloader mode. The following uploading error occurs regularly in PlatformIO.

Uploading .pio/build/seeed_xiao_esp32c3/firmware.bin esptool.py v4.5 Serial port /dev/ttyACM0 Connecting... Chip is ESP32-C3 (revision v0.4) Features: WiFi, BLE Crystal is 40MHz MAC: 34:85:18:03:f7:04 Uploading stub... Running stub... Stub running... Changing baud rate to 460800 A fatal error occurred: No serial data received. *** [upload] Error 2

Putting the XIAO in bootloader mode is a possible solution, but usually that is not necessary. I carelessly let windows accumulate, piled one on top of the other, at the bottom of the VSCode window as I push the build, upload, and terminal buttons in the PlatformIO toolbar. Then I'll fail to spot a spinner in the stack indicating that a terminal is waiting for some input, which invariable interferes with an upload. If every window was closed properly, most times an upload will go through without a problem.

Where is the Board? toc

Enough with the introduction, it's time to play with the board. Because it has a USB-C port used to supply current and as a programming interface just like the XIAO SAMD21, I assumed that it would show up as terminal (TTY) device. To find it, I started printing the kernel ring buffer on my Linux Mint desktop machine with the dmesg -w command. The -w (or --follow) flag means that utility will not terminate once it has printed all previous messages but will remain active and will print new messages as they come in. I waited until dmesg had finished printing all the old kernel messages and then I plugged in the USB cable from the XIAO to a USB 2 port on my Linux Mint desktop.

michel@hp:~$ dmesg -w ... plug in USB C cable [113978.024059] usb 3-9.4.2: new full-speed USB device number 18 using xhci_hcd [113978.125377] usb 3-9.4.2: New USB device found, idVendor=303a, idProduct=1001, bcdDevice= 1.01 [113978.125381] usb 3-9.4.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [113978.125384] usb 3-9.4.2: Product: USB JTAG/serial debug unit [113978.125386] usb 3-9.4.2: Manufacturer: Espressif [113978.125388] usb 3-9.4.2: SerialNumber: 34:85:18:xx:xx:xx [113978.128960] cdc_acm 3-9.4.2:1.0: ttyACM0: USB ACM device

As usual the CtrlC keyboard combination halts execution of the process. The USB vendor and product ID were new values that I had not encountered before. When listing all USB devices, not much information is shown, but it is possible to get much more information by explicitely asking for it.

michel@hp:~$ lsusb ... Bus 003 Device 017: ID 303a:1001 ... michel@hp:~$ sudo lsusb -v -d 303a:1001 Bus 003 Device 018: ID 303a:1001 Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x303a idProduct 0x1001 bcdDevice 1.01 iManufacturer 1 Espressif iProduct 2 USB JTAG/serial debug unit iSerial 3 34:85:18:xx:xx:xx bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0062 bNumInterfaces 3 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xc0 Self Powered MaxPower 500mA Interface Association: bLength 8 bDescriptorType 11 bFirstInterface 0 bInterfaceCount 2 bFunctionClass 2 Communications bFunctionSubClass 2 Abstract (modem) bFunctionProtocol 0 iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 0 CDC Header: bcdCDC 1.10 CDC ACM: bmCapabilities 0x02 line coding and serial state CDC Union: bMasterInterface 0 bSlaveInterface 1 CDC Call Management: bmCapabilities 0x03 call management use DataInterface bDataInterface 1 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x82 EP 2 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 1 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 2 bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 1 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 1 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 255 Vendor Specific Subclass bInterfaceProtocol 1 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 1 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 1 can't get device qualifier: Resource temporarily unavailable can't get debug descriptor: Resource temporarily unavailable Device Status: 0x0001 Self Powered

Most of this information is not particularly important at this point, but it is nice to see that the USB port has a serial number. Investigating by looking up the serial numbers of the four XIAO that I have in a Media Access Control or MAC address database, it looks very much as if that serial number is a MAC address assigned by Espressif to each ESP32-C3 microcontroller produced. The fact that the USB connection has a unique identifier can be very useful when trying to set up udev rules for specific devices that are connected to a USB port.

With all that said, the XIAO ESP32C3 shows up as a ttyACMxx device just like the XIAO SAMD21. At this point, I connected directly to the device with the old, dependable "Call up another system" cu command.

michel@hp:~$ cu -l /dev/ttyACM0 -s 115200 Connected. BLE TEST IS FALT! Begin First Test.... 0 0 1 0 First Test finish! Begin Second Test.... 0 0 1 0 0 0 0 0 GPIO TEST FALT! 927 ADC TEST FALT! TEST DONE!

This output looks like the firmware could be a factory test to ensure that the board is functioning properly. Everything is failing which makes sense as there is no testing fixture connected to the board. Whatever this firmware is, it will be erased in the next steps. I presume that it could be restored by flashing the Seeed Studio XIAO ESP32C3 Factory firmware available from the Seeed Studio Wiki. Note how the linked file is named ESP32-C3_RFTest... so that sort of makes sense.

It did not matter if the baud was set to 115200, 9600 or 500000 or probably anything else when connecting to the board with the cu . Just like the XIAO SAMD21, the USB data lines are connected directly to the microcontroller which has a built-in USB serial controller with a CDC-ACM virtual serial port which is USB 2.0 compliant and capable of 12 Mbits/s transfer speeds (Source ESP32C3 Series Datasheet, Version 1.4 pp 25).

A Do Nothing PlatformIO Project toc

Check if platform Espressif 32 is already installed in PlatformIO or PIO for short. If it is, make sure it is up to date. Do that by clicking on the Platforms icon in the PIO Home side bar, clicking on the Installed tab at the top of the window, and by entering Espressif in the Filter platforms by name... box at the top of the list of installed platforms.

Current Espressif 32 platform version

If the Espressif 32 platform is installed, it will be displayed along with the currently installed version number. To check if it is up to date, click on the Updates tab at the top and enter Espressif in the Filter platforms by name... box once again.

Update Espressif 32 platform

If Espressif 32 is not shown as above, then the installed version is the most recent. Otherwise, the platform is displayed and clicking on the Update to ... button will install the newest version.

If the platform is not installed, there is nothing much to do because the newest version will be installed automatically when a new project using the XIAO ESP32C3 is created in the next step.

I created a new project by first clicking on the Home icon in the PIO Home and then clicking on + New Project under Quick Access.

Create New PIO Project

Then it's a matter of filling in the fields in the project wizard setup page.

FirstRun project in PIO

There are so many supported boards in PIO, it is best to start typing some words that will narrow down the search. It will be easy to select the XIAO ESP32C3 after entering xiao into the Board: drop down edit box. Note that I did not use the default location and instead let PIO create a project directory named FirstRun in a directory called ~/Documents/PlatformIO/Projects/esp32c3. When I clicked Finish PIO automatically created the project directory and subdirectories, a do-nothing sketch main.cpp in the src subdirectory.

Empty main.cpp and project directory

Click on the Explorer icon at the left edge of VS-Code to see the project directory and open the src subdirectory and click on the main.cpp file name to see its content in the editor. PIO also created a default platformi.ini configuration file in the project directory.

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:seeed_xiao_esp32c3] platform = espressif32 board = seeed_xiao_esp32c3 framework = arduino

The update port and serial monitor ports are not specified so adjustments may be needed. Indeed, there was another serial device connected to a USB port on the desktop, which caused confusion. In a way, that was fortunate because it's best to iron out connection problems as soon as possible.

michel@hp:~$ ls /dev/ttyACM* /dev/ttyUSB* /dev/ttyACM0 /dev/ttyUSB0

When the PIO serial monitor was started, it latched onto the wrong serial port.

* Executing task: platformio device monitor --- Terminal on /dev/ttyUSB0 | 9600 8-N-1 --- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at https://bit.ly/pio-monitor-filters --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H

Of course nothing showed up because the terminal took over /dev/ttyUSB0 thus disabling my very basic macro key pad which is unfortunate because I use it writing these posts. Supposedly, autodetection is better when uploading firmware to the microcontroller. So I compiled the empty sketch and tried to flash it on the XIAO.

... RAM: [ ] 4.7% (used 15488 bytes from 327680 bytes) Flash: [== ] 16.4% (used 214832 bytes from 1310720 bytes) Configuring upload protocol... AVAILABLE: cmsis-dap, esp-bridge, esp-builtin, esp-prog, espota, esptool, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa CURRENT: upload_protocol = esptool Looking for upload port... Auto-detected: /dev/ttyUSB0 Uploading .pio/build/seeed_xiao_esp32c3/firmware.bin esptool.py v4.5 Serial port /dev/ttyUSB0 Connecting...........................^C

As we can see, autodetection got confused just as before and tried to upload the binary to the macro keypad. I checked in the terminal to confirm that PIO was seeing both serial devices.

michel@hp:~/Documents/PlatformIO/Projects/esp32c3/xiao_blink$ pio device list /dev/ttyUSB1 ------------ Hardware ID: USB VID:PID=1A86:7523 LOCATION=3-9.2 Description: USB2.0-Serial /dev/ttyACM0 ------------ Hardware ID: USB VID:PID=303A:1001 SER=34:85:18:03:F7:04 LOCATION=3-9.4.2:1.0 Description: USB JTAG/serial debug unit

One way to get around this problem is to specify, in the configuration file, which device to use for uploads and for the serial monitor.

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:seeed_xiao_esp32c3] platform = espressif32 board = seeed_xiao_esp32c3 framework = arduino ; Needed additions upload_port = /dev/ttyACM0 monitor_speed = 115200 monitor_port = /dev/ttyACM0

It does work but these additions to platformio.ini would be required for each project. Furthermore, when I sometimes plug and unplug the board from the USB port of the computer, it happens with some frequency that it gets mapped to /dev/ttyACM1. Fortunately, there is a "universal" solution which involves modifying the PIO JSON configuration file for the board. The file, seeed_xiao_esp32c3.json, is in a "hidden" subdirectory in the user home directory ~./platformio/platforms/espressif32/boards/. The hardware IDs array (hwids) did not contain the vendor and product ID values for the particular board I had, so I added those values.

{ "build": { "arduino": { "ldscript": "esp32c3_out.ld" }, "core": "esp32", "extra_flags": [ "-DARDUINO_XIAO_ESP32C3", "-DARDUINO_USB_MODE=1", "-DARDUINO_USB_CDC_ON_BOOT=1" ], "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", "hwids": [ [ "0x2886", "0x0047" ], [ "0x303A", "0x1001" ] ], "mcu": "esp32c3," ...

I was pleasantly surprised to see that this worked as I compiled and uploaded the "do nothing" binary without interfering with the serial device at /dev/ttyUSB0.

No dependencies Building in release mode Retrieving maximum program size .pio/build/seeed_xiao_esp32c3/firmware.elf Checking size .pio/build/seeed_xiao_esp32c3/firmware.elf Advanced Memory Usage is available via "PlatformIO Home > Project Inspect" RAM: [ ] 4.7% (used 15488 bytes from 327680 bytes) Flash: [== ] 16.4% (used 214832 bytes from 1310720 bytes) Configuring upload protocol... AVAILABLE: cmsis-dap, esp-bridge, esp-builtin, esp-prog, espota, esptool, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa CURRENT: upload_protocol = esptool Looking for upload port... Auto-detected: /dev/ttyACM0 Uploading .pio/build/seeed_xiao_esp32c3/firmware.bin esptool.py v4.5 Serial port /dev/ttyACM0 Connecting.... Chip is ESP32-C3 (revision v0.4) Features: WiFi, BLE Crystal is 40MHz MAC: 34:85:18:03:f7:04 Uploading stub... Running stub... Stub running... Changing baud rate to 460800 Changed. Configuring flash size... Flash will be erased from 0x00000000 to 0x00003fff... Flash will be erased from 0x00008000 to 0x00008fff... Flash will be erased from 0x0000e000 to 0x0000ffff... Flash will be erased from 0x00010000 to 0x00046fff... Compressed 12944 bytes to 9311... Writing at 0x00000000... (100 %) Wrote 12944 bytes (9311 compressed) at 0x00000000 in 0.2 seconds (effective 484.9 kbit/s)... Hash of data verified. Compressed 3072 bytes to 146... Writing at 0x00008000... (100 %) Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.0 seconds (effective 594.7 kbit/s)... Hash of data verified. Compressed 8192 bytes to 47... Writing at 0x0000e000... (100 %) Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 738.3 kbit/s)... Hash of data verified. Compressed 224800 bytes to 126485... Writing at 0x00010000... (12 %) Writing at 0x0001a7ad... (25 %) Writing at 0x000216a2... (37 %) Writing at 0x00027781... (50 %) Writing at 0x0002dcc6... (62 %) Writing at 0x00033939... (75 %) Writing at 0x0003b5c8... (87 %) Writing at 0x00042410... (100 %) Wrote 224800 bytes (126485 compressed) at 0x00010000 in 1.9 seconds (effective 960.5 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... ===================================================== [SUCCESS] Took 4.34 seconds ===================================================== * Terminal will be reused by tasks, press any key to close it. Executing task: platformio device monitor --- Terminal on /dev/ttyACM0 | 9600 8-N-1 --- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at https://bit.ly/pio-monitor-filters --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H

The emphasis shown in the listing above was added by me.

Normally, I would not recommend modifying the seeed_xiao_esp32c3.json board definition file, at least not as a long-term solution. The problem is that the modified file will be overwritten with the next update of the platform. However, Valerii Koval (valeros) quickly merged my correction into the develop branch of the platform-espressif32 repository. The change should be included in the next release of the Espressif 32 platform. Consequently modifying the JSON board definition by hand at this point should be safe. In the source on the GitHub repository, I used a third approach. The develop branch of the platform-espressif32 repository is used since it contains the USB ID correction and avoids editing the board definition file. That is not a perfect solution either; a later version of the development branch could break something.

Doin' Nothin' in the Arduino IDE toc

The recently released version 2.0 of the Arduino IDE represented a major improvement. While PlatformIO remains a better choice for me, I think the new Arduino IDE will be good enough for many. As a matter of fact, both are installed on my desktop computer and both get used. Consequently, it seemed appropriate to upload a "do nothing" sketch using the Arduino IDE to check if there are connection problems. To do this, I used the latest, self-contained, 2.0.3 Linux 64-bit appImage of the IDE and added support for third-party ESP32 microcontrollers within the IDE. That is done by adding the URL of the JSON index file of the arduino-esp32 package, https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json in the Additional boards manager URLs: list in the Settings tab of the application Preferences found in the File menu. It is simpler than described, details can be found under Software setup in the Getting Started Seeed Wiki.

I also ensured that another serial device was plugged into the desktop computer just as with PlatformIO. I am pleased to announce that no significant confusion, as encountered with PIO, arose.

Wrong board shown

I chose the correct board and then selected the serial device (or port) to use. Both serial devices were displayed, and I was able to select the correct device /dev/ttyACM0 from the list, compile the do-nothing sketch and upload the generated binary successfully. One can see a part of the Output window below the Tools which shows that 100% of the binary was uploaded and that the data was verified. The following bit was also shown in the output window but it has scrolled out of view by the end.

esptool.py v4.5 Serial port /dev/ttyACM0 Connecting... Chip is ESP32-C3 (revision v0.4) Features: WiFi, BLE Crystal is 40MHz MAC: 34:85:18:03:f7:04 Uploading stub... Running stub... Stub running... Changing baud rate to 921600 Changed. Configuring flash size...

I added the emphasis to underline the fact that the correct serial port was used. Of course, that is not too surprising because the choice of a serial port is a manual operation in the Arduino IDE.

It was a bit disconcerting to see that the serial port was associated with the Adafruit QT Py ESP32-C3 instead of the XIAO, so I looked at the latter's definition in boards.txt in the esp32 package found in the following directory ~/.arduino15/packages/esp32/hardware/esp32/2.0.7/. The definition starts at line 19576 (yes it is a huge file!).

############################################################# XIAO_ESP32C3.name=XIAO_ESP32C3 XIAO_ESP32C3.vid.0=0x2886 XIAO_ESP32C3.pid.0=0x0047 XIAO_ESP32C3.bootloader.tool=esptool_py XIAO_ESP32C3.bootloader.tool.default=esptool_py ...

As in PIO, the USB vendor and product ID listed for the board does not correspond to the values of the four XIAO ESP32C3 that I have. I decided to add the newer IDs following the instructions at Board VID/PID in the Arduino CLI Platform specification documentation .

############################################################# XIAO_ESP32C3.name=XIAO_ESP32C3 XIAO_ESP32C3.vid.0=0x2886 XIAO_ESP32C3.pid.0=0x0047 XIAO_ESP32C3.vid.1=0x303A XIAO_ESP32C3.pid.1=0x1001 XIAO_ESP32C3.bootloader.tool=esptool_py XIAO_ESP32C3.bootloader.tool.default=esptool_py ...

Unfortunately, that did not work as I had hoped.

Again not the correct board shown

There is probably no acceptable work around this problem:

"The Arduino development software uses the vid and pid properties to automatically identify the boards connected to the computer. This convenience feature isn't available for boards that don't present a unique VID/PID pair." (source)

Sometimes, there is a good reason to use the Arduino IDE instead PlatformIO because it can happen that it is updated more quickly when changes to the underlying ESP-IDF (Espressif IoT Development Framework) are made. However this is not always the case. Currently (2023-02-22), the latest 2.0.7 esp32 package installed in the Arduino IDE is based on ESP-IDF v4.4.4, while the latest 6.0.1 platform-espressif32 release is based on ESP-IDF v5.0, "a major update for ESP-IDF v4.x".

Another reason to use the Arduino IDE is its better management of examples sketches. This could be an important criterion especially for beginners. Of course, it is possible to use both. Indeed, that is what is done in the GitHub repository that contains most of the code shown here.

Pin Numbers and Names toc

Adafruit QT Py ESP32-C3 pinout I have been on pins and needles after seeing the Adafruit QT Py pinout. A simplified version is shown on the right.

Same processors, same form factor but the I/O pin assignment is different! I had to make sure that there was no error in the XIAO pinout. The direct way of checking that is to look at the pin assignment in pins_arduino.h found in the Arduino Espressif32 package in the following directory: ~/.platformio/packages/framework-arduinoespressif32/variants/XIAO_ESP32C3/. But that's not as much fun as writing a first sketch to merely spit out the pin numbers assigned to the symbolic names and finally do something with the board.

* Executing task: platformio device monitor --- Terminal on /dev/ttyACM0 | 9600 8-N-1 --- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at https://bit.ly/pio-monitor-filters --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H The symbolic names for the 4 analogue pins A0 = 2 A1 = 3 A2 = 4 A3 = 5 The symbolic names for the 11 digital pins D0 = 2 D1 = 3 D2 = 4 D3 = 5 D4 = 6 D5 = 7 D6 = 21 D7 = 20 D8 = 8 D9 = 9 D10 = 10

The serial output proves that there was no mistake in the Seeed documentation. It is unlikely that there is an error in the Adafruit pinout. I expect to look into this in more detail in further investigation of the ESP32-C3. In any case, here is the simple source of the sketch that printed the above results.

#include <Arduino.h> void setup() { Serial.begin(); delay(1000); // 1 second delay Serial.println("\nThe symbolic names for the 4 analogue pins"); Serial.printf(" A0 = %d\n", A0); Serial.printf(" A1 = %d\n", A1); Serial.printf(" A2 = %d\n", A2); Serial.printf(" A3 = %d\n", A3); Serial.println("\nThe symbolic names for the 11 digital pins"); Serial.printf(" D0 = %d\n", D0); Serial.printf(" D1 = %d\n", D1); Serial.printf(" D2 = %d\n", D2); Serial.printf(" D3 = %d\n", D3); Serial.printf(" D4 = %d\n", D4); Serial.printf(" D5 = %d\n", D5); Serial.printf(" D6 = %d\n", D6); Serial.printf(" D7 = %d\n", D7); Serial.printf(" D8 = %d\n", D8); Serial.printf(" D9 = %d\n", D9); Serial.printf("D10 = %d\n", D10); } void loop() { // do nothing }

At Last, the XIAO ESP32C3 Blinks toc

schematicNow that the connection issues are resolved, it's time to run a blink sketch, the microcontroller equivalent of the "Hello world!" program. The Seeed Studio Wiki already contains a blink sketch in Getting Started with Seeed Studio XIAO ESP32C3, but I have modified it slightly because it is hard to show a blinking LED on a static Web page. This version of the sketch prints the state of the LED out to the serial port as it toggles it on and off.

#include <Arduino.h> // Needed in platformIO // Connecting an external LED: // The diode's cathode (-, usually the short lead on the flat side of the LED) is connected to GND. // The diode's anode (+, usually the long lead on the round side of the LED) is connected to a // current limiting 240 ohm resistor. The other lead of the resistor is connected to an I/O pin. // int led = D10; void setup() { // Set the digital pin connected to the LED as an output pinMode(led, OUTPUT); Serial.begin(); delay(1000); // 1 second delay should be sufficient Serial.println("Setup complete"); } void loop() { digitalWrite(led, HIGH); // turn the LED on Serial.print("ON, "); delay(50); // for 1/20th of a second digitalWrite(led, LOW); // turn the LED off Serial.println("Off..."); delay(1000); // for a second }

Note how the default baud of the serial port is not set with the Serial.begin() statement. The baud could be anything; indeed the default value is 0 and the parameter is actually ignored in the void HWCDC::begin(unsigned long baud) function found in ~/.platformio/packages/framework-arduinoespressif32/cores/esp32/HWCDC.cpp. Here is the serial output of the program.

* Executing task in folder xiao_blink: platformio device monitor --- Terminal on /dev/ttyACM0 | 9600 8-N-1 --- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at https://bit.ly/pio-monitor-filters --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H Setup complete ON, Off... ON, Off... ON, Off... ON, Off... ON, Off... ON, Off... ...

One should check that the program will not hang waiting for the serial port to be activated by powering the board from a USB power source.

Button Controlled LED toc

schematic Let's control the LED with a push button. The on-board boot push button could be used, but my test setup is not ideal making it hard to reach the button. I prefer connecting another normally open push button connected to ground and D1 instead. Being lazy and hoping for some free publicity to the many thousands who will be reading this post, my own button library will be used. Since it is available on GitHub, a simple lib_deps directive in the platformio.ini configuration file will take care of the dependancy.

[env:seeed_xiao_esp32c3] platform = espressif32 board = seeed_xiao_esp32c3 framework = arduino lib_deps = https://github.com/sigmdel/mdPushButton.git

Here is the listing, which is nothing more than the basic button example from the mdPushButton library with the addition of the toggleLed() function.

#include <Arduino.h> // Needed in platformIO #include "mdPushButton.h" // Connecting an external LED: // The diode's cathode (-, usually the short lead on the flat side of the LED) is connected to GND. // The diode's anode (+, usually the long lead on the round side of the LED) is connected to a // current limiting 240 ohm resistor. The other lead of the resistor is connected to an I/O pin. // int led = D10; void toggleLed() { digitalWrite(led, 1-digitalRead(led)); Serial.printf("LED now %s.\n", (digitalRead(led) ? "on" : "off")); } // Connecting an external button: // Because an internal pullup resistor will be enabled, connect one lead of a normally // open push button to ground and the other lead to a free I/O pin, D9 here. // mdPushButton button = mdPushButton(D9); void setup() { // Set the digital pin connected to the LED as an output pinMode(led, OUTPUT); Serial.begin(); delay(1000); // 1 second delay should be sufficient Serial.println("Setup complete"); } void loop() { switch (int clicks = button.status()) { case -1: Serial.println("Long button press"); break; case 0: /* ignore this case - no button press */; break; case 1: Serial.println("Button pressed once"); toggleLed(); break; default : Serial.print("Button pressed "); Serial.print(clicks); Serial.println(" times"); break; } }

I was very pleased to see that my push button library worked with the ESP32-C3 without any tweaking. In other words, the program works as expected, the LED is toggled on or off with each single press of the added button. Long presses and two or more successive button presses done in quick succession do not toggle the LED, but they are reported to the serial monitor.

--- More details at https://bit.ly/pio-monitor-filters --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H Setup complete Button pressed once LED now on. Button pressed once LED now off. Button pressed 2 times Button pressed once LED now on. Long button press Button pressed once LED now off.

MicroPython: REPL Controlled LED toc

Following the instructions at MicroPython ESP32-C3 with USB, I downloaded the latest v1.19.1 release of MicroPyton into a directory in my desktop machine. It was a matter of using esptool to upload the binary to the XIAO ESP32C3. I knew that there were many copies of that utility on my machine, so a search in the PlatformIO packages directory to find the most recent version seemed advisable.

michel@hp:~/.platformio/packages$ find . -name esptool.py -exec echo {} \; -exec {} version \; ./framework-espidf/components/esptool_py/esptool/esptool.py esptool.py v3.2-dev 3.2-dev ./tool-esptoolpy/esptool.py esptool.py v4.5 4.5 ./tool-esptoolpy@1.30100.210531/esptool.py esptool.py v3.1 3.1 ./framework-arduinoespressif8266/tools/esptool/esptool.py find: ‘./framework-arduinoespressif8266/tools/esptool/esptool.py’: Permission non accordée ./framework-arduinoespressif32@3.10006.210326/tools/esptool.py find: ‘./framework-arduinoespressif32@3.10006.210326/tools/esptool.py’: Permission non accordée ./tool-esptoolpy@1.30000.201119/esptool.py esptool.py v3.0 3.0

A check of the esptool GitHub repository showed that version 4.5 was indeed the latest release. Since the binary is not on my search path, I created an alias, to avoid typing a very long command and tested it by displaying the esptool version and then using it to view some information about the ESP32-C3 on the XIAO.

michel@hp:~/.platformio/packages$ cd ~ michel@hp:~$ alias flash='/home/michel/.platformio/packages/tool-esptoolpy/esptool.py' michel@hp:~$ flash version esptool.py v4.5 4.5 michel@hp:~$ ls /dev/ttyA* /dev/ttyACM0 just checking that the serial device connected to the XIAO is still there michel@hp:~/$ flash --chip esp32c3 --port /dev/ttyACM0 chip_id esptool.py v4.5 Serial port /dev/ttyACM0 Connecting... Chip is ESP32-C3 (revision v0.4) Features: WiFi, BLE Crystal is 40MHz MAC: 34:85:18:xx:xx:xx Uploading stub... Running stub... Stub running... Warning: ESP32-C3 has no Chip ID. Reading MAC instead. MAC: 34:85:18:xx:xx:xx Hard resetting via RTS pin... michel@hp:~/$ flash --chip esp32c3 --port /dev/ttyACM0 flash_id esptool.py v4.5 Serial port /dev/ttyACM0 Connecting... Chip is ESP32-C3 (revision v0.4) Features: WiFi, BLE Crystal is 40MHz MAC: 34:85:18:xx:xx:xx Uploading stub... Running stub... Stub running... Manufacturer: 20 Device: 4016 Detected flash size: 4MB Hard resetting via RTS pin...

Warning, do not do like me. I wasted a lot of time trying to get the chip_id. After enough resets into bootloader mode, I could get the flash_id but never the chip_id. It turns out that PlatformIO had some control over the serial port, even if it looked as if I had stopped the serial monitor. Once I unloaded the PIO project that was open, everything worked perfectly.

To simplify things, I made the directory where the firmware was uploaded the current working directory and checked the firmware's name. Note that a upy- prefix had been added to the name when the file was downloaded to make it easier to identify. Uploading the micropython firmware according to the instructions was a breeze.

michel@hp:~$ cd Téléchargements/Devices/ESP/ESP32-C3 michel@hp:~/Téléchargements/Devices/ESP/ESP32-C3$ ls *bin upy-esp32c3-usb-20220618-v1.19.1.bin michel@hp:~/Téléchargements/Devices/ESP/ESP32-C3$ flash --chip esp32c3 --port /dev/ttyACM0 --baud 460800 write_flash -z 0x0 upy-esp32c3-usb-20220618-v1.19.1.bin esptool.py v4.5 Serial port /dev/ttyACM0 Connecting... Chip is ESP32-C3 (revision v0.4) Features: WiFi, BLE Crystal is 40MHz MAC: 34:85:18:03:f7:04 Uploading stub... Running stub... Stub running... Changing baud rate to 460800 Changed. Configuring flash size... Flash will be erased from 0x00000000 to 0x0015dfff... Compressed 1431808 bytes to 868690... Wrote 1431808 bytes (868690 compressed) at 0x00000000 in 11.2 seconds (effective 1024.4 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin...

Even if esptool says it reset the XIAO, it didn't because there is no RST connection through the USB port. So activate the XIAO reset button to exit bootloader mode.

Once again I used cu to connect to the MicroPython REPL. Nothing seemed to be happening until I pressed the Enter key at which point the REPL prompt >>> appeared. A keyboard combination CtrlD initiated a soft reset which in turn displayed the initial MicroPython screen.

michel@hp:~$ cu -l /dev/ttyACM0 -s 115200 Connected. Enter >>> CtrlD MPY: soft reboot MicroPython v1.19.1 on 2022-06-18; ESP32C3 module with ESP32C3 Type "help()" for more information. >>>

Let's turn on and off the LED connection to I/O pin 10. Note the GPIO pin number is used in MicroPython, not an alias such as D10.

>>> led = machine.Pin(10, machine.Pin.OUT) >>> led.on() >>> led.off() >>>

Of course that's not very practical, but with the XIAO Wi-Fi capabilities it is relatively easy to control the LED with a web server built with MicroPython.


MicroPython: Web Controlled LED toc

For my next trick, I decided to just modify an example web server W600-PICO. I used the same Thonny IDE as before, the only difference was that I had to configure the interpreter to use a different port: USB JTAG/serial debug unit (/dev/ttyACMO). The firmware can only serve two web pages, the default page at http://192.168.1.59/ and a 404 error page whenever a non-existent page is requested or an unknown request is made.

index.html index.html

To toggle the state of the LED, click on the Toggle button. A request, http://192.168.1.59/?led=toggle, will be sent by the client web browser back to the Web server which will then act on receipt of that request to change the state of the LED. The Web server code is spread over three files. First there is a secrets.py file that contains the Wi-Fi network credentials and the fixed IP address to be assigned to the Web server. Here is a template for that file, which will have to be modified in accordance with the local network.

STA_SSID = "rosebud" STA_PSWD = "87654321" STA_FIXED_IP = "192.168.1.59" STA_SUBNET = "255.255.255.0" STA_GATEWAY = "192.168.1.1" STA_DNS = "192.168.1.1"

The httpresonses.py module has two functions. The first returns an HTTP response header whose content will be different depending on whether a 404 error HTML page or the index HTML page will be sent. The second function returns the full text of the HTML page to be displayed by the client web browser. Its content depends on whether it is a 404 error page or the index page. The state of the LED is passed onto this second function so that it displays the state correctly.

def HttpResponseHeader(is404): if is404: return 'HTTP/1.1 404 Not Found\n', 'Content-Type: text/html\n', 'Connection: close\n\n' else: return 'HTTP/1.1 200 OK\n', 'Content-Type: text/html\n', 'Connection: keep-alive\n\n' def HttpPage(is404, Status): # The web server will serve either an index.html page or a 404 error page. # Both pages share common elements htmlTop = """<!DOCTYPE html> <html> <head> <title>XIAO ESP32C3 Web Server Example</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="icon" href="data:,"> <style> html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;} h1{color: #0F3376; padding: 2vh;} p{font-size: 1.5rem;} .button{display: inline-block; background-color: blue; border: none; border-radius: 6px; color: white; font-size: 1.5rem; width: 5em; height: 3em; text-decoration: none; margin: 2px; cursor: pointer;} .button2{background-color: silver;} </style> </head> <body> <h1>XIAO ESP32C3 Web Server Example</h1>""" htmlBottom = "</body></html>" if is404: html = htmlTop + """<h1 style="color: red">404 Error</h1><p><a href="/"><button class="button button2">Return</button></a></p>""" + htmlBottom print('404 response') else: html = htmlTop + """<p>LED: <strong>""" + ("ON" if Status else "OFF") + """</strong></p> <p><a href="/?led=toggle"><button class="button">Toggle</button></a></p> <p><a href="/quit"><button class="button button2">Quit</button></a></p>""" + htmlBottom print('index.html response') return html

Here is the main.py module which is executed automatically whenever the XIAO is booted.

from machine import Pin import network import socket import time import secrets import webresponses # Built in blue LED connected to pin labeled PA0 on the board Led = Pin(10, Pin.OUT) # signal power on Led.on() time.sleep(2) Led.off() time.sleep(1) ledstatus = 0 def toggleled(): global ledstatus ledstatus = 1-ledstatus Led.value(ledstatus) print('LED turned {}'.format("on" if ledstatus else "off")) CONNECT_TIMEOUT = 20 sta_if = network.WLAN(network.STA_IF) # loop until connection to Wi-Fi network made attempt = 1 while not sta_if.isconnected(): sta_if.active(True) print('Network connection attempt {}...'.format(attempt)) attempt += 1 sta_if.connect(secrets.STA_SSID, secrets.STA_PSWD) for i in range(CONNECT_TIMEOUT): if sta_if.isconnected(): break; else: time.sleep(1) sta_if.ifconfig((secrets.STA_FIXED_IP, secrets.STA_SUBNET, secrets.STA_GATEWAY, secrets.STA_DNS)) print("Successful connection to {} in less {} seconds.".format(secrets.STA_SSID, attempt)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # avoid [Errno 112] EADDRINUSE on restarting the script quickly # https://forum.micropython.org/viewtopic.php?t=10412#p57684 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Binding to all interfaces - server will be accessible to other hosts! s.bind(('0.0.0.0', 80)) s.listen(3) print('Web server started at http://' + sta_if.ifconfig()[0]) print('Click on Quit button to stop the script') # signal web server started for i in range(5): for j in range(2): Led.on() time.sleep_ms(50) Led.off() time.sleep_ms(50) time.sleep_ms(250) Led.off() # LED initially off ledstatus = 0 while True: conn, addr = s.accept() print() print('Client connection from {}'.format(addr)) request = conn.recv(1024) request = str(request) print('Resquest {}...'.format(request[0:31])) if request.find('/quit ') == 6: reply = 1 # quit elif request.find('/?led=toggle ') == 6: toggleled() reply = 0 # show index.html elif request.find('/ ') == 6: reply = 0 # show index.html else: reply = -1 # not found error rh1, rh2, rh3 = webresponses.HttpResponseHeader(reply < 0) #print('rh1: {}, rh2: {}, rh3: {}'.format(rh1, rh2, rh3)) conn.send(rh1) if reply < 1: conn.send(rh2) conn.send(rh3) conn.sendall(webresponses.HttpPage(reply < 0, ledstatus)) conn.close() if reply == 1: break

Notice how the LED is first turned on for a two seconds at the beginning, and then flashed 10 times just after the web server starts accepting connections. That helped in figuring out what was happening when starting the application. It turns out that the XIAO draws so little power when the web server is waiting for a client connection that the power bank I was using to test the firmware without a connection would disconnect the XIAO entirely.

Finally here is the serial output as displayed in the REPL.

>>> %Run -c $EDITOR_CONTENT Successful connection to COROBRUN-2 in less 1 seconds. Web server started at http://192.168.1.59 Click on Quit button to stop the script Client connection from ('192.168.1.103', 41924) Resquest b'GET / HTTP/1.1\r\nHost: 192.1... index.html response Client connection from ('192.168.1.103', 49532) Resquest b'GET /?led=toggle HTTP/1.1\r\n... LED turned on index.html response Client connection from ('192.168.1.103', 49540) Resquest b'GET /?led=toggle HTTP/1.1\r\n... LED turned off index.html response Client connection from ('192.168.1.103', 56972) Resquest b'GET /?led=off HTTP/1.1\r\nHos... 404 response Client connection from ('192.168.1.103', 56976) Resquest b'GET / HTTP/1.1\r\nHost: 192.1... index.html response Client connection from ('192.168.1.103', 56992) Resquest b'GET /quit HTTP/1.1\r\nHost: 1... >>>

That unusual Quit button is just a quick way to return to the REPL.

The source code for this MicroPython script is not included in the GitHub repository, but it can be downloaded from this site: upy_web_led.zip.

Bluetooth Controlled LED toc

Unlike the ESP8266, the ESP32-C3 has Bluetooth® Low Energy (BLE) capabilities. My knowledge in this area is so limited (one tiny post: BLE Beacons with Tasmota32 - Proof of Concept) that I do not want to say much. Let's just say that I was able to simplify an example from Shahzada Fahad (Engr), XIAO ESP32C3 Bluetooth Tutorial, Range test, and Home Automation to control the LED connected to the XIAO ESP32C3.

The sketch uses the ArduinoBLE library, which is added as a dependency in the ble_ledproject's platformio.ini configuration file.

[env:seeed_xiao_esp32c3] platform = espressif32 board = seeed_xiao_esp32c3 framework = arduino lib_deps = arduino-libraries/ArduinoBLE@^1.3.2

The source is almost comprehensible.

#include <Arduino.h> // Needed in platformIO #include "ArduinoBLE.h" // Connecting an external LED: // The diode's cathode (-, usually the short lead on the flat side of the LED) is connected to GND. // The diode's anode (+, usually the long lead on the round side of the LED) is connected to a // current limiting 240 ohm resistor. The other lead of the resistor is connected to an I/O pin. // int led = D10; void setLed(int value) { digitalWrite(led, value); Serial.printf("LED now %s.\n", (digitalRead(led) ? "on" : "off")); } BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // Bluetooth® Low Energy LED Service // Bluetooth® Low Energy LED Switch Characteristic - custom 128-bit UUID, read and writable by central BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); void setup() { // Set the digital pin connected to the LED as an output pinMode(led, OUTPUT); Serial.begin(); delay(1000); // 1 second delay should be sufficient // begin initialization if (!BLE.begin()) { Serial.println("Could not start Bluetooth® Low Energy module!"); while (1); } Serial.println("Bluetooth® Low Energy (BLE) module started."); // set advertised local name and service UUID: BLE.setLocalName("HOME Automation"); // this will appear in the App search result. BLE.setAdvertisedService(ledService); // add the characteristic to the service ledService.addCharacteristic(switchCharacteristic); // add service BLE.addService(ledService); // set the initial value for the characteristic, i.e. LED off switchCharacteristic.writeValue(0); Serial.println("LED service added."); BLE.advertise(); Serial.println("\"HOME Automation\" device now being advertised"); Serial.println("Setup completed."); } void loop() { // listen for Bluetooth® Low Energy peripherals to connect: BLEDevice central = BLE.central(); // if a central is connected to peripheral: if (central) { Serial.print("Connected to \"Home Automation\" device: "); Serial.println(central.address()); // print the central's MAC address: while (central.connected()) { if (switchCharacteristic.written()) { int Rvalue=switchCharacteristic.value(); Serial.printf("Received switchCharacteristic = %02x.\n", Rvalue); if ((Rvalue == 0) || (Rvalue == 1)) { setLed(Rvalue); } else { Serial.println("Ignored, expected 00 or 01.\n"); } } } Serial.print("Disconnected from \"Home Automation\" device: "); Serial.println(central.address()); } }

On uploading the firmware to the XIAO it seems to stop once the setup function is completed.

--- Terminal on /dev/ttyACM0 | 9600 8-N-1 --- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time --- More details at https://bit.ly/pio-monitor-filters --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H Bluetooth® Low Energy (BLE) module started. LED service added. "HOME Automation" device now being advertised Setup completed.

But that is not the case, the XIAO is simply waiting for a connection and I could see that "Home Automation" was indeed visible in the list of Bluetooth devices found by my desktop computer.

Home Automation device displayed in desktop Bluetooth device list

There's no point in pairing the XIAO with the desktop at least on my machine. I could not find a light application that would connect and interact with BLE for Linux, so I opted to use a tablet to continue with this example. Shahzada Fahad used nRF Connect for Mobile (Android, IOS). I tried the Android version and it worked. However I will be showing the use of LightBlue for Android below (there is an IOS version).

Once installed, start LightBlue. There is no need to pair the XIAO to the phone or tablet, LightBlue will take care of this. Click on the thumbnails on the right to see a full-sized version of the screen capture.

Chances are it will ask permission to access the location of the device on which the app is running and chances are that it will have to be given. You could always refuse to see the reason why the app asks for the permission. access to location requested
If the Bluetooth radio was not turned on, then the app will ask for permission to turn it on. Of course refusing to do that will bring this exercise to an abrupt end. (Refusers is an shall we say "interesting" error). request to start Bluetooth radio
Press on the GET STARTED button to get beyond the welcome screen of the app. welcome screen
In the list of nearby Bluetooth devices, press on the CONNECT button beside "HOME Automation" list of nearby Bluetooth devices
LightBlue will show information about the BLE device on the XIAO in detail. Scroll down to the end of that page. data and information about 'HOME Automation'
Press on the Readable, Writeable entry at the end of the GENERIC ATTRIBUTE generic attribute
The properties and other data about the generic attribute will be displayed, and again it was necessary to scroll down to get to the important field. Properties of the attribute
Write 0 or 1 in the WRITTEN&npbs;VALUES Hex data... field and then press on the WRITE button to send it back to the XIAO. It will turn the LED off or on as desired. welcome screen
Note the hexadecimal keyboard used to enter the attribute value. welcome screen
Once a value is written, it is added to a list of attribute values previously send back to the XIAO. welcome screen

The sketch shows the received values in the serial monitor. It also reports when an invalid value is received.

Connected to "Home Automation" device: 48:12:5b:3d:ba:cc Received switchCharacteristic = 01. LED now on. Received switchCharacteristic = 00. LED now off. Received switchCharacteristic = 01. LED now on. Received switchCharacteristic = 00. LED now off. Received switchCharacteristic = 07. Ignored, expected 00 or 01.

Uncurated Bibliography and Concluding Thoughts toc

Here is an uncurated bibliography, meaning here are links to various sources that I have consulted in the last few days while writing this post. I am sure there are other sources that would be just as informative if not more than what I found. Any suggestions would be welcome.

Is it an ESP8266 replacement? It does not look as if Espressif is planning to drop the older device. The device still shows up in its product line and, this very month of February 2023, the Hardware Design Guidelines for the ESP8266 have been updated. However looking at pricing, the future is not so clear. Here are the listing from Mauser Canada for single core Espressif microcontrollers that were in stock on Feb. 26, 2023. All prices in Canadian dollars.

partprice ($)SPI Flash (MB)PSRAM (MB)Notes
ESP8266EX2.2100
ESP82852.4810Used in Sonoff Basic
ESP82851.6620
ESP32-C3FN41.9040Used in XIAO ESP32C3
ESP32-S2FN4R22.3742Xtensa LX7 core

These prices do not seem to make much sense when compared to the comparative capabilities of the controllers. Perhaps the surprisingly high price of the older 8266/8285 parts has something to do with meeting the production demand for older devices. In the long run, demand for the ESP8266 may disappear as older products are removed from the markets and replaced with new designs based on newer chips.

This is just the opinion of an amateur, but the ESP32-C3 by Espressif is a very good SoC which is a better than ESP8266 as far as I can tell from my preliminary examination. It has more static RAM, much more flash memory, more analog input pins, a faster TCP stack, a built-in USB-to-serial peripheral (this is not native USB, so no HID capabilities) and so on.

The ESP32-C3 is not the first Risc-V microcontroller. I toyed with the Longan Nano and Wio Lite RISC-V back in March and April of 2020. However there is no real comparison from the amateur's point of view between the older boards. The Arduino framework for the ESP32-C3, which I believe is the work of Espressif, is much better than for the older board. It really feels as if one is programming for any ESP32 (aside from the single core restriction) when working with the ESP32-C3. This is probably an unfair judgment because I have not looked at the older boards since those first experiments.

As for the Seeed XIAO ESP32C3, again my first impressions are very positive. I believe this board can be seen as a replacement for the Wemos D1 Mini based on the ESP8266. It offers the same number of I/O pins as the bigger D1 mini (and even bigger nodeMCU ESP8266 boards), with the greater flexibility of the ESP32-C3. It was possible to install Tasmota for the ESP32-C3 on the XIAO and, after some difficulties with the installation, it was possible to integrate the XIAO ESP32C3 into my Domoticz based home automation system. I am looking forward to looking at improving some of my old projects, especially Domoticz Button, a light-weight physical interface to a home automation system based on Domoticz. It would be interesting to redo it in MicroPython to see if it could be made more "user-friendly".

A Wi-Fi Switch for Domoticz using a XIAO ESP32C3 - Part 1-> <-First Look at the SAMD21 Arm Cortex-M0+ Based Seeeduino XIAO
<-Blink with a Wio Lite RISC-V with ESP8266