lv_port_esp32-epaper is the latest successful attempt to design UX in C using Espressif ESP32. If you like the idea please hit the ★ in my repository fork. What is LVGL?
LVGL stands for “Light and Versatile Graphics Library” and allows you to design an object oriented user interface in supported devices. So far it supports mostly TFT screens and only some slow SPI epapers where supported. My idea is to add driver support so it works also in fast parallel epapers.
It took me some work but at the end the light controller is working. So far added only Hue, Bright and White channel. LVGL with @lilygo9 EPD47 pic.twitter.com/6ZudrEb1OW
That is Lilygo EPD47. Parallel epaper with ESP32 WROVER and I2C touch interface that can be found in Aliexpress
The main idea is to use a bridge driver that pushes the pixels to EPDiy component using the set_px_cb and the flush callbacks in order to render the layouts on the supported epapers. This will have a performance hit but it will also allow us to draw UX interfaces in parallel epapers that are quite fast flushing partial refresh. The development took about one month of research and many iterations until it became usable. I started with an easy choice since Lilygo sent me an parallel epaper as a gift once and I bough the rest in their official store. The idea is that this acts as proof-of-concept to demostrate that is possible and that it’s working as expected. It’s possible to design an UX directly in C and then using a controller like ESP32 you can directly interact with Home appliances such as lights or other devices, to control them or to read information such as sensors that can respond with short JSON messages to inform your epaper control board about temperature or other matters that you choose.
I want to state here that I’m not an electronics engineer and I know the basics only after years of tinkering and because I use to soldier PCBs for my father since I’m 8 or so. So if there is anything that is not correctly explained just comment and I will try to document it better. The ESP8266 has 17 GPIO pins (0-16), however, you can only use 11 of them, because 6 pins (GPIO 6 – 11) are used to connect the flash memory chip. The ESP32 chip has 40 physical GPIO pins. Not all of them can be used and some of them are only input GPIOs meaning you cannot use them for output communication (Refer to your board tech specs fot that) According to Espressif documentation on the ESP32:
GPIO 6-11 are usually used for SPI flash.
GPIO 34-39 can only be set as input mode and do not have software pullup or pulldown functions.
Electronics ABC A pull-up resistor connects unused input pins to the dc supply voltage, (3.3 Vcc in ESP32) to keep the given input HIGH A pull-down resistor connects unused input pins to ground, (GND 0V) to keep the given input LOW. Analog-to-Digital Converter (ADC) The Esp32 integrates 12-bit ADCs and supports measurements on 18 channels (analog-enabled pins). The Ultra low power (ULP-coprocessor) in the ESP32 is also designed to measure voltages while operating in sleep mode, which allows for low power consumption. Digital-to-Analog Converter (DAC) Two 8-bit DAC channels can be used to convert two digital signals to two analog voltage outputs. These dual DACs support the power supply as an input voltage reference and can drive other circuits. Dual channels support independent conversions.
A detailed walkthrough over GPIOs and electronics is out-of-scope in this blog post series since it’s a thema on it’s own and I think a engineer could be the best suited to expand on this properly. There are many instructables and videos about it if you are interested in researching more.
So now back to ESP32 Arduino framework coding, let’s use this GPIO information, to declare one as output and blink a LED.
#include "Arduino.h"
// HINT: Many ESP32 boards have an internal LED on GPIO5
// If it's on another PIN just change it here:
int ledGpio = 5;
void setup() {
Serial.begin(115200);
Serial.println("Hello blinking LED");
pinMode(ledGpio, OUTPUT);
}
void loop() {
digitalWrite(ledGpio, HIGH);
delay(500);
digitalWrite(ledGpio, LOW);
delay(200);
}
As a good reference here is the pinMode entry in Arduino documentation. modes can be:
INPUT
OUTPUT
INPUT_PULLUP
This very short program will just set the GPIO5 in output mode and in the loop just turn it HIGH(1) and send 3.3V to the GPIO or LOW(0). Keep in mind that the ESP32 can draw a max. consumtion of about 10 mA per GPIO so always use a resistance (10K or similar) if you connect your own LED or you may damage the board. Now seeing that the GPIO state is 1 or 0, we can also write this program in a shorter way, let’s give it a second round:
include "Arduino.h"
int ledGpio = 25;
bool ledState = 0; // New bool variable
void setup() {
pinMode(ledGpio, OUTPUT);
}
void loop() {
digitalWrite(ledGpio, ledState);
ledState = !ledState;
delay(20);
}
We are just doing one digitalWrite per loop now and right after that just redeclaring the variable using the logical NOT operator. That way it will flip between 0 and 1, turning the LED on and off quite fast since we added just a 20 millis delay. So fast is almost always on! So now we are in this point we can start with the next topic that is called pulse width modulation or PWM.
PWM provides the ability to ‘simulate’ varying levels of power by oscillating the output from the microcontroller. By varying (or ‘modulating’) the pulsing width we can effectively control the light output from the LED, hence the term PWM or Pulse Width Modulation. Arduino also had PWM pins with a scale from 0 – 255 so we could have 255 brightness level on a LED.
// Since someone asked about a modern AnalogWrite PWM example
// Just try to compile this one
include "Arduino.h"
// use first channel of 16 channels (started from zero)
define LEDC_CHANNEL_0 0
// use 13 bit precission for LEDC timer
define LEDC_TIMER_13_BIT 13
// use 5000 Hz as a LEDC base frequency
define LEDC_BASE_FREQ 5000
// fade LED PIN (replace with LED_BUILTIN constant for built-in LED)
define LED_PIN 25
bool brightDirection = 0;
int ledBright = 0;
void setup() {
Serial.begin(115200);
Serial.println("Hello PWM LED");
// Setup timer and attach timer to a led pin
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(LED_PIN, LEDC_CHANNEL_0);
}
// Arduino like analogWrite: value has to be between 0 and valueMax
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
// calculate duty, 8191 from 2 ^ 13 - 1
uint32_t duty = (8191 / valueMax) * min(value, valueMax);
ledcWrite(channel, duty);
}
void loop() {
// set the brightness on LEDC channel 0
ledcAnalogWrite(LEDC_CHANNEL_0, ledBright);
if (ledBright == 255 || ledBright == 0) {
brightDirection = !brightDirection;
}
if (brightDirection) {
++ledBright;
} else {
--ledBright;
}
delay(15);
Serial.println(ledBright);
}
The ESP32 has a PWM controller with 16 independent channels that can be configured to generate PWM signals with different properties. Just wanted to mention this possibility but I’m not going to extend myself in this topic since they are hundred of pages that explain it much better than I could do. Please check https://randomnerdtutorials.com/esp32-pwm-arduino-ide to have a nice PWM example.
Keep tuned and follow this blog to get more. In next chapter we are going to explore I2C communication and connect a thermometer to our ESP32 to display via Serial the room temperature.
I started to dig a bit more into the Iot development network from Espressif with the intention to slowly learn something new. All the official examples of Mesh lights, ESP32-Camera and ESP-WHO that presents a preliminary version of face recognition are built on the top of the IDF.
So sooner or later, I think will be the official way to do “internet of the things” devices with these Boards. But I’m still not ready for it, I need to do simple examples and experimenting before taking over and do something for real.
So the first things I tackled on is to take this libraries “as is” and use them in existing Arduino framework projects. And it’s fully possible, below is a small example about this using the TTGO Camera with Pir sensor
camera_index.html template loaded from the SPIFFS so you can modify it for your project (No Gziped non-editable file)
Added V-Flip setting that was not existing in the original example
Libraries loaded using platform.io file
This is my humble try to make a hackeable and modificable esp32 Camera example using ARDUINO as a framework but with the official Espressif Camera Libraries.
One important step is that without using the IDF, just adding the libraries in plataform.io project configuration file:
It won’t compile. The reason is that it tries to find the C header files but they do not have the needed mapping out of the box if you use framework: arduino. It will complain that:
Compiling .pioenvs/ttgo-lora32-v1/libe0c/esp32-camera/conversions/jpge.cpp.o .piolibdeps/esp32-camera/conversions/jpge.cpp:11:18: fatal error: jpge.h: No such file or directory
And the file is there but in another directory. So my workaround was to add this directory in the build_flags configuration:
Think about C header files as your interfaces, your function prototypes! Code organization is very important. 3D-models for the TTGO Cameras available in Thingiverse, my own prototype design (be aware!)
New ESP-Mesh RGB+White Light soldered. You can do your own for about 10€ materials and 2 hours soldiering ;) Small preview:
Just mounted another RGB W #esp32 mesh light. Did the white channel with additional leds between the RGB lines. Each channel is about 20mA. This time is a present for a good friend of mine ;) pic.twitter.com/Df8iUtisr8