Can I program a an ESP32C6 with the esp-idf?
This article is part of a series.
- Part 1: This Article
- Part 2: Can I program an ESP32C6 with the Swift example code?
- Part 3: How does the espressif SDK handle inputs?
- Part 4: How does the espressif SDK handle wifi?
- Part 5: Can I combine an LED and a button on the ESP32C6 with Swift?
- Part 6: Can I add Wifi to the ESP32C6 project with Swift?
- Part 7: Can I make a Swift-wrapped HTTP GET request from the ESP32C6?
So to go along with the server I want to have an object that talks to it. Which means one with a stable WiFi chip. Very popular right now is espressif’s ESP32-C6
The ESP32-C6 SoC (System on Chip) supports Wi-Fi 6 in 2.4 GHz band, Bluetooth 5, Zigbee 3.0 and Thread1.3. It consists of a high-performance (HP) 32-bit RISC-V processor, an low-power (LP) 32-bit RISC-V processor,wireless baseband and MAC (Wi-Fi, Bluetooth LE, and 802.15.4), RF module, and numerous peripherals. Wi-Fi,Bluetooth and 802.15.4 coexist with each other and share the same antenna.
I was interested in trying out a RISC-V chip since I hadn’t played with one of those before.
Seeed Studio’s XIAO ESP32C6 won out as the dev board, honestly because todbot collects boards that have the same form factor as Adafruit’s QT Py and he had an extra. Also its small size is perfect for my target project.
Other ESP32-C6 dev boards include (info links, not purchase links):
About the Board
The XIAO ESP3232C6 board is tiny, but it packs A LOT in.
Of the two led’s on the board one is the classic “built in led” to blink hello world to, and the other is hooked into the battery management system. Hopefully I’ll get back to that later.
The red light behavior for the XIAO ESP32C6 is as follows:
When no battery is connected:
The red light turns on when the Type-C cable is connected and turns off after 30 seconds.
When a battery is connected and the Type-C cable is plugged in for charging:
The red light flashes.
When the battery is fully charged via the Type-C connection:
The red light turns off.
The two buttons are the reset button and the boot button.
While the boot button is tiny I’m hoping later in the project I should be able to find a toothpick or something to temporarily hijack it for hello worlding a button.
Board Proof of Life
It’s fairly rare these days to get a dud board, but it’s best to make sure the board actually works with familiar tooling before launching into unfamiliar tooling.
Seeed actually provides step by step instructions with screen shots on how to get things set up in the Arduino IDE, an environment I know well.
- Quick link to the board profile if you know your way around: https://github.com/espressif/arduino-esp32
Change the blink sketch to uneven
One of the things I do with the example blink sketch is change the the blink patter to be uneven.
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(2000); // wait for a second
}
On the Xiao, this code will have the LED be ON for longer than OFF because the LED’s current is being sinked by the pin, rather than sourced from the pin. This is more the norm in production electronics vs hobby electronics more.
- See the The “GPIO15” labeled net on the schematic (big red X means not populated): https://files.seeedstudio.com/wiki/SeeedStudio-XIAO-ESP32C6/XIAO-ESP32-C6_v1.0_SCH_PDF_24028.pdf
- Where pin 15 is declared as the builtin in the code: https://github.com/espressif/arduino-esp32/blob/561136870b4308d9deb942461e63e42c788546f3/variants/XIAO_ESP32C6/pins_arduino.h#L13
Install Official Tools
Espressif wraps its SDK in a pretty robust set of tools (Integrated Development Framework), for which they’ve written very explicit install instructions:
Here’s a summary, but I suggest reading the full page, making sure its actually about the target you’re looking for. (i.e. the url says esp32c6
NOT simply esp32
. esp32
is actually a different family than esp32c6
and its tooling won’t work!)
## required
brew install cmake ninja dfu-util
## recommended
brew install ccache
Had:
New:
- https://dfu-util.sourceforge.net
- dfu-util is a host side implementation of the DFU 1.0 and DFU 1.1 specifications of the USB forum. DFU is intended to download and upload firmware to/from devices connected over USB.
- https://ccache.dev
- Ccache is a compiler cache. It speeds up recompilation by caching previous compilations and detecting when the same compilation is being done again.
Note, ccache posted a fairly ominous warning… maybe I will start using containers more…
To install symlinks for compilers that will automatically use
ccache, prepend this directory to your PATH:
/opt/homebrew/opt/ccache/libexec
If this is an upgrade and you have previously added the symlinks to
your PATH, you may need to modify it to the path specified above so
it points to the current version.
NOTE: ccache can prevent some software from compiling.
ALSO NOTE: The brew command, by design, will never use ccache.
So espressif wants you to put their tooling at the root of the user, but does provide instruction on how to map to somewhere else.
Also, there is a VSCode plugin that will do this all for you, but I find VSCode tooling very aggressive and prefer custom scripts that don’t start automagically doing things I didn’t ask to be done. I also don’t enjoy vendor lock in. I also find doing these things myself helps with trouble shooting later. YMMV.
mkdir -p ~/esp
cd ~/esp
git clone -b v5.5.1 --recursive https://github.com/espressif/esp-idf.git
## Install the tools
cd ~/esp/esp-idf
./install.sh esp32c6 #note NOT esp32, esp32c6 <---- !!!!!
Hello World with Official Tools
In the terminal window that will be the shell doing the building and running:
## Set up the environment variables
. $HOME/esp/esp-idf/export.sh
## Go to the dev folder
cd ~/esp
## pull out the example
cp -r $IDF_PATH/examples/get-started/hello_world .
## linux
# ls /dev/ | grep tty
## macOS
ls /dev/ | grep cu. ## or ls /dev/cu.*
Mine was called /dev/cu.usbmodem2101, plug into a different port, it will change.
## SPECIFIC TO MINE
SOME_PORT=/dev/cu.usbmodem2101
cd ~/esp/hello_world
idf.py set-target esp32c6
# this creates sdkconfig, look at it
cat sdkconfig
## optional/not needed for hello world. THe below edits sdkconfig
## with verification to help prevent errors
# idf.py menuconfig
idf.py build
idf.py -p $SOME_PORT flash
# assumes 40 MHz, use idf.py menuconfig to change if needed
idf.py -p $SOME_PORT monitor
# CNTRL+] to leave
blink with official tools
The hello world example doesn’t touch the GPIO which, so on to blink
.
If in a totally new Terminal window
. $HOME/esp/esp-idf/export.sh
ls /dev/ | grep cu. ## or ls /dev/cu.*
SOME_PORT= dev.cu.someusbmodem
Pull out the blink example code and go…
## or where ever you want to store your files
cd ~/esp
cp -r $IDF_PATH/examples/get-started/blink .
## Set the GPIO_BLINK pin to 15 here
cd blink
# this is PER PROJECT, do it in the project folder.
idf.py set-target esp32c6
## MUST change Example Configuration. It's not an LED strip, GPIO pin (needs to be 15), etc.
idf.py menuconfig
idf.py build
## do it on one this time
idf.py -p $SOME_PORT flash monitor
Also went fairly smoothly and easily. Yet I don’t know that I’ve actually learned anything really about the SDK, about RISC-V…
Pulling it Apart
One thing I will say about the VSCode plugin is that it did help making a new project off of a base template and that is nice. I will look to see if that’s a feature of the idf.
Starting from the blink example I tore out a lot of code and deleted a lot of files.
Tore out the specialized config stuff, left in the logging
// Needed for logging to serial monitor
#include <stdio.h>
#include "esp_log.h"
// Needed for portTICK_PERIOD_MS
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Needed for io pins
#include "driver/gpio.h"
#define BLINK_GPIO 15
#define BLINK_PERIOD 2000
// logging prefix
static const char *TAG = "simple_blink";
static uint8_t s_led_state = 0;
static void blink_led(void)
{
/* Set the GPIO level according to the state (LOW or HIGH)*/
gpio_set_level(BLINK_GPIO, s_led_state);
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
gpio_reset_pin(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}
void app_main(void)
{
/* Configure the peripheral according to the LED type */
configure_led();
while (1) {
// pin HIGH is __LED OFF__ for the XIAO built in LED.
ESP_LOGI(TAG, "Turning the LED %s!", s_led_state == true ? "OFF":"ON");
blink_led();
/* Toggle the LED state */
s_led_state = !s_led_state;
// calculating the millis by hand based on the crystal speed is
// ANNNNNOOOOOOYYYYING. Keeping FreeRTOS.
vTaskDelay(BLINK_PERIOD / portTICK_PERIOD_MS);
}
}
Links to the functions that are left:
- gpio_set_level
- gpio_reset_pin
- gpio_set_direction
- ESP_LOGI
- how it differs from
printf
- how it differs from
- vTaskDelay and portTICK_PERIOD_MS
- also available: pdMS_TO_TICKS
Blink without the SDK?
To do this without the SDK, one has to do things like the make a vector table, etc. I found one example that did that for an older esp32 chip that was not a RISC-V.
I think I want to save that energy for once I’ve started writing is Swift, if at all this project.
Summary
Pretty smooth sailing. Next post, blinking with Swift.
This article is part of a series.
- Part 1: This Article
- Part 2: Can I program an ESP32C6 with the Swift example code?
- Part 3: How does the espressif SDK handle inputs?
- Part 4: How does the espressif SDK handle wifi?
- Part 5: Can I combine an LED and a button on the ESP32C6 with Swift?
- Part 6: Can I add Wifi to the ESP32C6 project with Swift?
- Part 7: Can I make a Swift-wrapped HTTP GET request from the ESP32C6?