There are two affordable models that I would like to cover in this post. First one is the 400×300 FT6336 from Good Display, we can see this one in action (2nd video, 1st had still wrong Waveform)
Better but not perfect. Maybe someone will find a faster mode since my bed seems much more interesting at the moment than making float ink particles pic.twitter.com/njz1j950XE
1 GND
2 VDD -> 3.3V only, not 5V!
3 RST (Not used in this component)
4 INT
5 SDA
6 SCL
It’s important to know that each different model uses a different pinup in the 6 pin FPC cable, so if you had the great idea to make an universal adapter, you will fail doing it (Guess who tried?) Please note also that I do not use GoodDisplay touch adapter for this since I do not like the fact that you can use it only for touch or for SPI but not both, so I choose to use a simple 6 pin FPC adapter that you can find cheap in Aliexpress.
Note there is a small gotcha with this ones! Sometimes the FPC is bottom-contact only so make sure to plug it with the contacts facing down (Disregard this if it has double sided contacts) Also we are using a very raw touch interface to MCU connection there are some technical things to keep in mind:
1. I2C is a bidirectional connection and needs pull-up resistors to 3.3V in both SDA and SCL lines (4.7K or even 6K Ω will work) 2. INT pin goes low when there is an event to read via I2C. Same as last case, you should not leave this pin floating and add also a pullup to 3.3V.
The 2.7″ smaller brother
The smaller 2.7″ has also an optional touch screen. This model called Gdey027T91T with 264*176 pixels resolution seems to be at the moment out of stock, but you can get the touch separately, and just stick it with care in the top of the epaper display.
In the video you can see how fast partial update works in this model, compared with my 4.2″ version
1 GND
2 INT
3 RST (Not used in this component)
4 VDD
5 SCL
6 SDA
Also if you want to use Espressif IDF framework to handle touch events plus epaper component with GFX, we highly recommend to try Cale-idf Cale is a component that is already 2 years in development and has the most common epapers from Goodisplay / Waveshare, including this two touch models. It also has the interesting fact, and sometimes important, if you design something that is able to rotate screen of adapting touch to this rotation. Only on certain classes like the 2.7″ since we are no-one requested yet to add this feature to the 4.2″ display class. On our example demo-keyboard.cpp on cale component you can clearly see how this is implemented:
// Include touch plus the right class for your display
#include "FT6X36.h"
#include "goodisplay/touch/gdey027T91T.h"
// INTGPIO is touch interrupt, goes low when it detects a touch, which coordinates are read by I2C
// Use an RTC IO if you need to wake-up with touch!
FT6X36 ts(CONFIG_TOUCH_INT);
EpdSpi io;
// At this moment we insert touch into the display class!
Gdey027T91T display(io, ts);
uint8_t display_rotation = 3; // 1 or 3: Landscape mode
int t_counter = 0;
// This callback function will be fired on each touch event
void touchEvent(TPoint p, TEvent e)
{
++t_counter;
printf("X: %d Y: %d count:%d Ev:%d\n", p.x, p.y, t_counter, int(e));
}
// Entry point of our Firmware
void app_main()
{
// Initialize display and SPI
display.init(false);
// When using the ClassT integrated with touch then rotating this
// rotates touch X,Y coordinates too.
display.setRotation(display_rotation);
display.registerTouchHandler(touchEvent);
// You could launch this also in a FreeRTOS task
for (;;) {
display.touchLoop();
}
}
The price is 45 USD since I only made 5 and otherwise it will be impossible to cover the costs. But it might go lower if we can make a budget version without Bluetooth, that is, provided there is some interested parties in using it. This along with DEXA-C097 IT8951 controller sold by GoodDisplay, and fabricated by CINREAD, can be a very powerful option to use very fast 8-bit parallel displays, such as ED097OC4 or ED097TC2. There are many models available and all of them should work, they just have different contrast and VCOM voltage adjustments. Making this board a very good option to control them and make a digital clock for a store, or add sensors in it’s dedicated I2C connector, such as Humidity, CO2 air quality or anything that can be plugged in this super fast MCU from Espressif.
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.
DATA FLOW: Midi out > Sparkfun midi HAT > ESP32 > RX Uart > C++ Processing > FastLED RMT output
Meet Remora-matrix a project that started with the idea to sniff MIDI messages and make simple visuals on a LED-Matrix. Our previous Firmware Remora was intended to receive short commands from ORCΛ and make very simple Neopixels animations on addressable LEDs stripes (WS2812B like) With this one I decided to go a step further and make two versions, one that uses nodejs as a middleware (Requires WiFi and UDP) and other that uses MIDI serial, received directly via Serial2 (TX, RX) UART and hence requires no WiFi connection.
NODEJS VERSION This version uses a middleware script that sniffs a MIDI port (Ex. USB) and converts the messages into UDP messages that fly over WiFi to a destination IP. This script lives at the moment in middleware directory. An npm install needs to be run in order to install the required JS libraries.
cd middleware/midi-to-udp
nodejs midi.js
// Will list available midi entries. Requires port and udp destination IP
-p, --port_id ID: Port name
0: Midi Through:Midi Through Port-0 14:0
1: USB MIDI Interface:USB MIDI Interface MIDI 1 20:0
[number] [required]
-u, --udp_ip [string] [required]
// EX. listen to port 1 USB Midi and forward them to UDP x.x.x.x:49161
// Port is fixed to the ORCA default port, feel free to update it ^
nodejs midi.js -p 1 -u 192.168.12.109
This script will simply run in the background and redirect them using a simple short message that we designed to be compatible with both UDP and Serial versions.
2. MIDI SERIAL version This uses the Sparkfun Arduino midi HAT that cost around 15 u$ and it can be found both in eBay and Aliexpress. The easy task is to build a connecting PCB below that hosts the ESP32 at the side of the HAT with the RX and TX cables from the HAT outputs connected to the ESP32. This HAT has an opto-isolator, also called octocoupler, that converts the MIDI signals into readable UART messages. My prototype construction looks like this:
MIDI Hat alternative, but you could also build the MIDI to Serial yourself, in my case was easier to get it build
The MIDI Hat was designed for Arduino and requires 5 volts to run, so the 4 cables wiring is pretty straightforward: HAT midi > ESP32 5v . . . . 5v GND . . GND RX . . . . 26 TX . . . . 27
I’m quite sure the TX goes to TX in the ESP32 but may be also the opposite. There is no standard for this I think. But in case it does not work just invert it, there are only signals, so you won’t break anything for trying this. The advantage of Serial are that have less latence than WiFi. Depending on how clean your connection is, sometimes WiFi UDP packages can become clogged, and get out all together which is quite an undesirable effect if you are working with LIVE music. Also UDP nature is designed to be very fast, but has no order like TCP. It’s possible that a note played comes in different order as expected. Or if WiFi is shared, that while your Router is busy, the packages will accumulate and then sent all together, causing a burst of shapes in the Matrix in a moment that does not correlate with the music. This is not happening with the Serial version since there is no middleware redirecting packages and it cam be run without any PC in the middle. As there are no flying WiFi messages, has less latence and it’s much more reactive and fun to work with. Being the only pitfall being that you need a MIDI cable from your computer or Synthesizer to the MIDI Hat + ESP32 controller. Most LIVE music lighting equipment does not rely on WiFi and there is a good reason for it! Reliability.
Building our own internal Midi messaging system
Since this two versions want to achieve the same goal, that is converting the MIDI played into shapes, I though to create an internal messaging that is shared. Maybe this can be a C++ class or component in the future so it should speak the same language, no matter what version you use. The result is very simple, we will keep Channel internally and will use only Note + Status + Velocity. Status and Channel come in the first byte. Then comes the Note and at the end the Velocity. Once we get the last byte, we will assemble this with the following syntax:
2 chars (HEXA) representing Note played 1 boolean representing Status (1 note on, 0 note off) 2 chars (HEXA) representing VelocityNNSVV Note, Status, Velocity
Example: Playing DO in octave 3 that is 36 in decimal, velocity 60, Note ON message would be:
2413B When the same note is released it could be: 24000
After building the message the channel is analyzed. It can either hear on all channels leaving the constants in platformio.ini to 0. Or hear in 3 different channels (Also 3 instruments) this can be of course modified, but 3 is a good balance, to see something that can be correlated with the music. The configuration for this is on platformio.ini file using build_flags
There you can see that this will only forward packages for channels 1,2,15 all the rest will not be sent to the matrix. There is also an option to ignore Velocity and use a fixed number ( MIDI_FIXED_VELOCITY ) And depending on the song, it could played on a high tone, or in a lower tone. Because our matrix is limited, we need to define BASE_OCTAVE and TOP_OCTAVE so we can have a drawing range. That is the most important midi configuration. It would be desirable to have a “learning phase” where you can simply hear the first 10 seconds of a song and calculate this BASE and TOP margins automatically. This a future idea that might be implemented.
Interpreting the messages
I left just a demo of how to interpret this in C++. As we have 2 different firmware versions, one that listens UDP messages, and another one that get’s MIDI via UART you have to select what to compile editing platformio.ini File:
[platformio]
default_envs = esp32
# Uncomment only one of the folders to select what example to run:
#src_dir = firmware/udp-midi-matrix
src_dir = firmware/midi-in-matrix
Every message at the end triggers a function that draws a shape. And that part is open to every different implementation. For example, you can draw a different shape per channel, like: Ch1 – Usually piano or main instrument – Rectangles Ch2 – Triangles Ch3 – Circles Ch4 – Lines and so on
As said this is just an example, but it’s open to draw anything you want, since we are using GFX over the RGB Led matrix. Also you have the Velocity, that is the pressure that is applied to the key, so you can use this factor to make the shape bigger or change colors. The possibilities are unlimited. There is only one important thing to keep in mind. A note with status 1 should be drawn, but same note with status 0, signalizes that the key was release hence we should delete the shape. At the moment is just an experiment that will may never see the light out of my studio, but nevertheless I wanted to leave this post as a declaration of intentions, in case someone wants to fork this and make his own take.
NeoMatrix let’s you map the RGB Led matrix to use GFX
I writed to Marc Merlin who did the amazing job of adding GFX to FastLED and here I wanted to quote his answer
About FrameBuffer GFX: The good news is that your code will now run mostly unmodified on other displays like LCDs, or RGBPanels, or even display on linux. Like this you can write all your code, run it, and debug it on linux, and then upload it to ESP32 or rPI when it’s done. After that, you can go big!
Marc
Remora is starting to be a bit more responsive now that midi is not flying via UDP but directly connected to the ESP32 via @sparkfun midi Cc @hputzekpic.twitter.com/hCA59eY6FT
Recently I stepped upon superrare.co that is a marketplace to collect and trade unique digital artworks. This and the fact that I follow Josh Katzenmeyer that is a member of this network made more aware of the fact I enjoy watching this artworks very much and got me interested about it.
My idea is very simple. Is just to make a gallery using big epaper displays or a combination of them with traditional prints. Maybe in different sizes and with different technologies (3 color, with and without gray-scales, etc)
This is just a very raw idea of how such an exposition room would like. The main point is that is pure digital technology to showcase digital art. But also it’s adventage is that a small room with 5 epapers like we can see in the bottom of the picture, could have “rotating contents” that are updated every 4 or 5 minutes, giving the visitors the oportunity to see different artworks depending on how long time they want to stay in the room. Or even the invitation to press a button to load the next image.
Exposition room with mixed prints and epaper displays
Content idea for a blog post I’m writing about showcasing digital art in epaper. This is “Measured confinement” by @luxprispic.twitter.com/d9C7XfCupj
Preview of a render in a 12.48 inches black/white 1304 x 984 pixels screen. Showcased art is Measured confinement and you can buy the original in superrare website. Epaper model is a Waveshare electronics product but the display itself is manufactured by Good Display
Now as cool as the idea may sound there are a couple of limitations:
It’s not possible or limited to render animations since most maker big displays do not support partial updates. Or if they do is not well documented how to do it. However in smaller displays is supported and will be possible to do something like a GIF
Only black&white have 3 grays like the model exposed in the video
There are 3 color displays, in red or yellow versions, but those do not have grayscales neither partial updates
There is a 7 colors one but it’s too small and not documented enough for me to use it at the moment
And known advantages can be:
WiFi epapers so the content could be controlled from anywhere. A version that has an SD card reader in the controller is also feasible
Very low consumption so the installation can be cable-less provided it has a battery that has enough power to sustain the refreshes for days
Enable a user interaction. Example with a push Button, sensors like movement or for example infrared reading visitor temperature and interacting in some way with the visitors of the exposition.
C++ Firmware and WiFi microcontrollers to send the image buffer
I’ve been researching and making my own controllers for this epapers, based in the ESP32 family, as a component for Espressif ESP-IDF framework. The name of the component is Cale-IDF and this the WiKi entry for the epaper showcased in the video.
In order to support a dynamic generation of Bitmaps I created also an image generator Webservice that is called CALE.es and let’s you compose images with a simple admin backend.
ESP32-S2 integrates a rich set of peripherals, with 43 programmable GPIOs which can be flexibly configured to provide USB OTG, LCD interface, camera interface, SPI, I2S, UART, ADC, DAC and other common functionality. ESP32-S2 provides the optimal HMI solution for touchscreen and touchpad-based devices. The arduino-espressif32 is getting it’s finals touches of grace before being available and I will just do a small repository and try to test as much as I can and also make some benchmark comparisons.
In the first tests the impression is that most of the arduino framework is already working with a few exceptions. Not all the features are supported yet but they will be soon. Sadly the deepsleep is still not optimized correctly in this first revisions of the Saola board and on 3.4 v I measured 0.79 mA/hour. On a good ESP32 board like tinyPICO it’s 0.08 mA/hour (10 times less) now that’s something you can plug to a battery and make it wake up every hour to do something. Please note this is not a fair comparison at this point. It’s only a test with same firmware, same conditions and bypassing power regulator to see how much it consumes directly powering the board with the 3.3V pin. On the other hand, making processor intensive tests like receiving UDP packets or receiving and decompressing at the same time, it showed similar speed with much less consumption than the ESP32.
But other than this small annoyance in this first board revision it’s a really promising product and very powerful System on a Chip. The feature list is impressive:
Xtensa® single-core 32-bit LX7 microcontroller
43 programmable GPIOs.
Standard peripherals including SPI, I2C, I2S, UART, ADC/DAC and PWM.
LCD (8-bit parallel RGB/8080/6800) interface and also support for 16/24-bit parallel.
Camera interface supports 8 or 16-bit DVP image sensor, with clock frequency of up to 40 MHz.
#ESP32-S2 Saola board arrived today. I'll try to help debugging and doing some testing over the weekend. Looks nice! pic.twitter.com/nxKALxaVM0
Please follow the repository and add issues or just leave a comment here if you want me to test any of the features and I will publish the results. Note that the only condition is that the results should be shared with the community on the internet. It’s all open-source, so we will keep the same spirit and share the results with everyone else. Another example of ESP32-S2 in action, controlling 144 RGB Neopixels using Makuna library https://twitter.com/martinfasani/status/1267015931689144320
NOTE: GPIO26 it applies primarily to WROVER as those come with PSRAM. * not sure if you should not use 43,44 but if you will use UART0 output in your project is the best you don’t use them
As a preamble I wanted to make clear that is not my intention to teach C or C++ here, since they are very good online resources for that, and more to make my point in some easy ways to get the most of espressif/arduino-esp32 Framework.
WebServer class – the ESP32 will listen on a port and reply or react to your query Additionally, we will use the ESPmDNS class to enable multicast DNS
HttpClient class – it will send a GET or POST request to an endpoint
For the HttpClient I will make a special endpoint that it will be available in a subdomain of fasani.de and it will just answer with the time (HH:MM). Please note that after chapter 3 I will only focus on ESP32 since it was already explained how you can write code that works in both Espressif chips ESP8266 or ESP32 using Platformio environments and #ifdef conditionals.
1. WebServer example: ESP32 listens on port 80
The first example will be very simple and to the point. We will just take as a base what we wrote in chapter 2 to connect to WiFi and we will extend it using WebServer class. Once the ESP32 is online and we got the IP Address in the Serial output we will hit the route: http://ESP_IP_ADDRESS/switch
And in this route, using WebServer event handlers, we are going to toggle a GPIO high and low. Pretty simple, but powerful since if instead of a LED, we could use a relay that turns a 220v light or any other device On/Off via WiFi, so you can control it from any other terminal connected to the same WiFi Network. Using your imagination, you may build multiple endpoints that perform different things using predefined routes and modifying this example.
#include "Arduino.h"
#include <WiFi.h>
#include <ESPmDNS.h> // multicast DNS
#include <WebServer.h>
#define LED_GPIO 25 // Update to a internal LED or just connect one with a resistance in that GPIO
int lostConnectionCount = 0;
char apName[] = "fasani";
bool switchState = false;
WebServer server(80); // port where we start the server
// Callback see defineServerRouting
void onServerSwitch(){
switchState = !switchState;
digitalWrite(LED_GPIO, switchState);
String switchStatus = "OFF";
if (switchState) {
switchStatus = "ON";
}
server.send(200, "text/html", switchStatus);
}
void onServerNotFound(){
Serial.println("404");
server.send(200, "text/html", "404 this route is not defined on "We could not read the time.\nPlease check in the browser that the given url: %d is correct anddefineServerRouting()");
}
// ROUTING Definitions. Add your server routes here
void defineServerRouting() {
server.on("/switch", HTTP_GET, onServerSwitch);
server.onNotFound(onServerNotFound);
}
/** Callback for receiving IP address from AP */
void gotIP(system_event_id_t event) {
Serial.println("Online. Local IP address:");
Serial.println(WiFi.localIP().toString());
MDNS.begin(apName);
MDNS.addService("http", "tcp", 80);
Serial.println(String(apName)+".local mDns started");
defineServerRouting();
server.begin();
Serial.println("Server started");
}
/** Callback for connection loss */
void lostCon(system_event_id_t event) {
++lostConnectionCount;
Serial.printf("WiFi lost connection try %d to connect again\n", lostConnectionCount);
WiFi.begin(WIFI_SSID, WIFI_PASS);
if (lostConnectionCount==4) {
Serial.println("Cannot connect to the internet. Check your WiFI credentials");
}
}
void setup() {
Serial.begin(115200);
Serial.println("setup() started let's connect to WiFI");
pinMode(LED_GPIO,OUTPUT);
Serial.printf("WiFi name: %s\nWiFi pass: %s\n", WIFI_SSID, WIFI_PASS);
// Start our connection attempt
WiFi.begin(WIFI_SSID, WIFI_PASS);
// Event driven WiFi callbacks
// Setup callback function for successful connection
WiFi.onEvent(gotIP, SYSTEM_EVENT_STA_GOT_IP);
// Setup callback function for lost connection
WiFi.onEvent(lostCon, SYSTEM_EVENT_STA_DISCONNECTED);
}
void loop() {
// Run our server engine
server.handleClient();
}
Note: Only Linux and Mac support mDNS out of the box. If you use windows you will need to install additional software for that. Now since we use a multicast DNS server if we are using a client that supports it once we upload that code to an ESP32 we could access it from this 2 URLs:
Please note that even that WordPress makes the links above are not clickable and I also used fasani as ApName as shameless self-promotion. Please feel free to change it to your own updating the apName char variable.
This little program will just connect to WiFi, start a mDNS server and a WebServer in port 80, define the route /switch and simply turn a GPIO High and give 3.3v (ON) or Low 0v (OFF) to a GPIO. I just added a function where the routes are defined, since I’m a web developer, and I would like to emulate how other Web frameworks do to keep the routing in a single place. It’s useful, both for the program and for the documentation, to have a single function to place all the actions that your Firmware will support. This example is very simple but it has potential since you can expand it with as many actions you want, and not only turn High or Low a Gpio but also change variables using your server routes, or any other thing you desire.
Of course you can use any timezone you want. The source of time.php is just 4 lines and you can put it in any server that supports PHP no matter what version:
So this example will be even easier than number 1. It will simply connect to WiFi and query this URL to print in the Serial output the current time. The applications can be endless, for example, you can return only the hour as an integer, and your program can decide to turn on something depending on the hour.
#include "Arduino.h"
#include <WiFi.h>
#include <HTTPClient.h>
int lostConnectionCount = 0;
HTTPClient http;
String url = "http://fs.fasani.de/time.php?tz=Europe/Berlin";
void gotIP(system_event_id_t event) {
Serial.println("Online. Local IP address:");
Serial.println(WiFi.localIP().toString());
http.begin(url);
int httpCode = http.GET(); //Make the request
Serial.printf("http status:%d\n", httpCode);
if (httpCode == 200) {
String response = http.getString();
Serial.printf("The time now is: %s\n", response);
} else {
Serial.printf("We could not read the time.\nPlease check in the browser that the given url: %s is correct and replies with an HTTP 200 OK status", url);
}
}
void lostCon(system_event_id_t event) {
++lostConnectionCount;
Serial.printf("WiFi lost connection try %d to connect again\n", lostConnectionCount);
WiFi.begin(WIFI_SSID, WIFI_PASS);
if (lostConnectionCount==4) {
Serial.println("Cannot connect to the internet. Check your WiFI credentials");
}
}
void setup() {
Serial.begin(115200);
Serial.println("setup() started let's connect to WiFI");
WiFi.begin(WIFI_SSID, WIFI_PASS);
WiFi.onEvent(gotIP, SYSTEM_EVENT_STA_GOT_IP);
WiFi.onEvent(lostCon, SYSTEM_EVENT_STA_DISCONNECTED);
}
void loop() {
}
I hope this two easy examples will serve you to build more powerful things on top. I will be monitoring the commentaries if you want to achieve things that are not explained here. In the next chapters we will explore more possibilities, based on my last 2 years journey, but also on your feedback.
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.
On this new release we will focus on connecting our Espressif board to WiFi and will also the powerful build_flags to inject the defines directly on compilation time. This will make our WiFi configuration easier and leave it in a place where it can be easily configured, again in our platformio.ini project configuration file:
[env:espressif8266]
build_flags =
-DWIFI_SSID=\"MyWiFiName\"
-DWIFI_PASS=\"password\"
; Password with special chars: My pass'word
-DWIFI_PASS=\"My\ pass\'word\"
-D instructs the compiler to #define the variable WIFI_* and make it available in my program So actually doing this in main.cpp will accomplish exactly the same:
But as you can see if we do that we would be hardcoding our credentials in a C++ file which is not the best if we would like to share our program in github so other people can use it. Doing it the build_flags way, we are keeping it in a place tight to our code repository, but that can be configured withouth touching our main code. Nice, doesn’t it? And of course you can discover more settings about this platformio.ini configuration and define many other things there, like GPIOs that your sensor uses and any other variable that need to be injected in compilation time. Pretty handy to distribute your projects and leave the configuration where it needs to be. So now that we’ve the credentials let’s make our program connect to the WiFi in an event-driven way:
#include "Arduino.h"
#include <WiFi.h>
int lostConnectionCount = 0;
/** Callback for receiving IP address from AP */
void gotIP(system_event_id_t event) {
Serial.println("Online. Local IP address:");
Serial.println(WiFi.localIP().toString());
// We are connected, we could already receive or send something w/WiFi
}
/** Callback for connection loss */
void lostCon(system_event_id_t event) {
++lostConnectionCount;
Serial.printf("WiFi lost connection try %d to connect again\n", lostConnectionCount);
WiFi.begin(WIFI_SSID, WIFI_PASS);
if (lostConnectionCount==4) {
Serial.println("Cannot connect to the internet. Check your WiFI credentials");
}
}
void setup() {
Serial.begin(115200);
Serial.println("setup() started let's connect to WiFI");
Serial.printf("WiFi name: %s\nWiFi pass: %s\n", WIFI_SSID, WIFI_PASS);
// Start our connection attempt
WiFi.begin(WIFI_SSID, WIFI_PASS);
// Event driven WiFi callbacks
// Setup callback function for successful connection
WiFi.onEvent(gotIP, SYSTEM_EVENT_STA_GOT_IP);
// Setup callback function for lost connection
WiFi.onEvent(lostCon, SYSTEM_EVENT_STA_DISCONNECTED);
}
void loop() {
}
That was already a step ahead of Chapter 1. This won’t work in Arduino ATMega chip, for this code we already need an Espressif chip that can connect to WiFi. My take on connecting to WiFi are the events that the WiFi library incorporates, since I find it easy to cope with, and with only those two events GOT_IP and STA_DISCONNECTED you should be ready to go. Now that we are on this point, there is a very handy thing to learn in case we want to make this code work also in ESP8266, since the 8266 version uses a different WiFi library. Plataformio injects some variables to identify what environment we are compiling with. I really don’t know exactly how many they are and you can just read this on their own documentation but I know that this two come very hande for our purpouse:
So basically if we compile this, editing the ini default_envs in platformio.ini, and compile it on an esp32 then there will be a #define ESP32 set and the first include <WiFi.h> will take place. But if we use an esp8266 then the ESP32 will be not define and the ESP8266 instead. Then the other ESP8266WiFi.h header file will be included. The #ifdef statements in C are processed in compile time. Is not like a real conditional if in our program, you can imagine this #ifdef as a way to let us shape our program to different environments, or even do different things if a sensor is defined. For example we could have a Firmware that has an optional TEMPERATURE_SENSOR. Well if that is defined, then it should measure the temperature and run an extra code for that, if not then it will simply not include this part letting the program run anyways and do it’s thing but without that part of the code.
Important, the WiFi classes are not the same and ESP8266WiFi class does not have the same event handlers as the ESP32 class so here is the rewritten part if you want to do a version that supports both chips:
#include "Arduino.h"
#include <ESP8266WiFi.h>
int lostConnectionCount = 0;
#ifdef ESP32
/** Callback for receiving IP address from AP */
void gotIP(system_event_id_t event) {
Serial.println("Online. Local IP address:");
Serial.println(WiFi.localIP().toString());
// We are connected, we could already receive or send something w/WiFi
}
/** Callback for connection loss */
void lostCon(system_event_id_t event) {
++lostConnectionCount;
Serial.printf("WiFi lost connection try %d to connect again\n", lostConnectionCount);
WiFi.begin(WIFI_SSID, WIFI_PASS);
if (lostConnectionCount==4) {
Serial.println("Cannot connect to the internet. Check your WiFI credentials");
}
}
#elif ESP8266
void gotIP() {
Serial.println("Online. Local IP address:");
Serial.println(WiFi.localIP().toString());
// We are connected, we could already receive or send something w/WiFi
}
#endif
void setup() {
Serial.begin(115200);
Serial.println("setup() started let's connect to WiFI");
Serial.printf("WiFi name: %s\nWiFi pass: %s\n", WIFI_SSID, WIFI_PASS);
// Start our connection attempt
WiFi.begin(WIFI_SSID, WIFI_PASS);
#ifdef ESP32
// Event driven WiFi callbacks
// Setup callback function for successful connection
WiFi.onEvent(gotIP, SYSTEM_EVENT_STA_GOT_IP);
// Setup callback function for lost connection
WiFi.onEvent(lostCon, SYSTEM_EVENT_STA_DISCONNECTED);
#elif ESP8266
Serial.print("Connecting.");
while (!WiFi.isConnected()) {
Serial.print(".");
delay(200);
}
gotIP();
#endif
}
void loop() {
}
So we’ve used this example to make this WiFi part be environment independant and run in both ESP32 or ESP8266 but actually is much more powerful than that and can be used for many other things.
This will be the Serial output when running the program for this chapter
First of all, this small series of blog posts, assume you have installed and are familiar with Platformio IDE to edit and upload code to your Espressif chips. We ‘ve covered in another post the installation and getting started part with this nice IoT editor. Please refer to this section of Platformio to get the basics right of the editor. Prerequisites: Just grab any ESP32 or ESP8266 and make sure it works. Check that you can see the Serial monitor so we can debug the code.
The platformio.ini project configuration file
This is the configuration file where we can define what are the environment targets (chips) where we want to upload and run our program. For example, we can have the same code, to run in ESP8266 or ESP32 using the same source.
; File: platformio.ini
[platformio]
; This is the default environment but we can also use command line
; or change here to espressif8266 to upload it to this target
default_envs = lolin_d32
[env:lolin_d32]
platform = https://github.com/platformio/platform-espressif32.git
board = lolin_d32
framework = arduino
monitor_speed = 115200
; set frequency to 80/ 160MHz 160000000L
board_build.f_cpu = 160000000L
[env:espressif8266]
platform = espressif8266
board = d1_mini_lite
framework = arduino
monitor_speed = 115200
So this will be our first program, will just include the Arduino framework, start a counter on 0. As every program on Arduino framework, will have a setup() that is executed only once when the chip is powered, and a loop() method that is precisely on a loop. It will just print what you see on setup and then a new line every second. I also invite you to to be curious enough to use the CTRL+LEFT click over Arduino.h and to explore what it does. It includes the framework. Actually it includes the FreeRTOS that is a real-time OS for microcontrollers and also some other important definitions and includes for the Arduino ESP32 framework itself to work. And this is the way to discover any library or thing you include in your program, just CTRL+LEFT and explore the code, trying to understand what function it has. So now let’s code our first little program:
Very simple right? Not too many things. Just a variable in the global scope, that get’s incremented in the loop, and is getting printed using printf in a single line. So we created a very short and nice program that actually does nothing except printing some Serial output. The variable scope is easy to grasp and is also the same for other languages as javascript. A global scope variable that is defined on top, can be incremented from each function. That means that if we would move the:
int counter = 0;
At the beginning of the loop() the loop itself would have no sense, since it will add one and go back to 0 all the time.
In the next chapter, we will learn how to use the environments we defined in platformio.ini, to add different codes keeping the same program to run in esp8266 and esp32. Just follow this blog or keep tuned to @martinfasani twitter account to read the next release.
NOTE: Up to here we could also have compiled and run the very same code in an Arduino board and it will do exactly the same. Since we are not using any of the Espressif extra goodies like WiFi, this could be run also in another boards, keep tuned to see what is coming next that will be using WiFi or Bluetooth.