Design UX on epaper with ESP32. Add touch!

When I first bought one Good Display 2.7 inch epaper with a FocalTech touch panel, it was a curiosity that I never though it will call my attention, and will end up in one of the wood boxes under my desk. This is the short story of how this small piece of technology really caught my attention and evolved to a full pledged ESP-IDF component that will enable you to make simple UX on epaper for your Firmware.

The cool thing about touch is that wiring 3 cables more, you are able to read X and Y coordinates plus the touch event, and react in the same epaper. Kind of having a small UX playground that enables you to make buttons and a free canvas to draw or take decisions.
It took me one week of research to add the ESP-IDF I2C library, which is based on the work of Strange-V, a developer that did the FT6X36 for the Arduino-ESP32-framework.

My work was to take this as a base and do the I2C communication using the ESP-IDF I2C peripheral documentation as a model. One of the things I love in this framework is that is not a given to do I2C (Wire in Arduino) or SPI. All needs to be low-level instantiated, which makes it a bit harder when you are accustomed to the easy-peasy approach of Arduino, but also unleashes the whole low-level potential of the ESP-IDF.
Don’t take me wrong, I like both frameworks, and ultimately the mission is that it works good together and you achieve in C++ or C an stable Firmware.
So after achieving this first step in the Fork that I renamed to FT6X36-IDF, also to connect to I2C and read what is coming from the FocalTech touch panel, the next immediate goal was to inject this class into my existing epaper component for IDF. The CalEPD component. This is how this new class was inserted:

NOTE: The FT6X36-IDF is injected into the Gdew027w3T class (Same as the SPI class)

The touch panel working together with the epaper class had a very obvious pitfall: My epaper component, same as GxEPD, extends popular Adafruit GFX library to let you use graphics and fonts. This also makes the epaper rotation aware, since we extend the original drawPixel, and make it our own for every different epaper model:

void Gdew027w3T::drawPixel(int16_t x, int16_t y, uint16_t color) {
  if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;

  // check rotation, move pixel around if necessary
  switch (getRotation())
  {
    case 1:
      swap(x, y);
      x = GDEW027W3_WIDTH - x - 1;
      break;
    case 2:
      x = GDEW027W3_WIDTH - x - 1;
      y = GDEW027W3_HEIGHT - y - 1;
      break;
    case 3:
      swap(x, y);
      y = GDEW027W3_HEIGHT - y - 1;
      break;
  }
  uint16_t i = x / 8 + y * GDEW027W3_WIDTH / 8;
  if (color) {
    _buffer[i] = (_buffer[i] & (0xFF ^ (1 << (7 - x % 8))));
    } else {
    _buffer[i] = (_buffer[i] | (1 << (7 - x % 8)));
    }
}

But what happens if we call setRotation(1) and rotate our display 90° to the right (To make it clear, we rotate our code, the epaper knows nothing and just receives a pixel buffer)
The display rotates, and if we sent a pixel to the X:1 Y:1 corner, it will be effectively drawn 90° rotated. But the touch will be still giving our old X and Y data. The touch is a completely hardware detached panel, in the top of the epaper, that again knows nothing about what we are doing.’
So here is the catch, let’s allow this new touch component to be injected in the epaper, just as we inject the SPI IO (Input /Output) class, that is really I for Good Display since the SPI is only a slave that does not communicate with the master, expect of a Busy line. Another epapers, such as the PlasticLogic line, also send temperature readings using the same SPI line so they are IO for real.
This is how it looks in the C++ class Gdew027w3T that is my clon of the non-touch class:

// Constructor of the Gdew027w3T class:
Gdew027w3T::Gdew027w3T(EpdSpi& dio, FT6X36& ts): 
  Adafruit_GFX(GDEW027W3_WIDTH, GDEW027W3_HEIGHT),
  Epd(GDEW027W3_WIDTH, GDEW027W3_HEIGHT), IO(dio), Touch(ts)
{
  printf("Gdew027w3T() %d*%d\n", GDEW027W3_WIDTH, GDEW027W3_HEIGHT);  
}

// Sample of how this is injected in main.cpp
#include "FT6X36.h"
#include <gdew027w3T.h>

// INTGPIO is touch interrupt, goes low when it detects a touch, which coordinates are read by I2C
FT6X36 ts(CONFIG_TOUCH_INT);
EpdSpi io;
Gdew027w3T display(io, ts);

So that’s it now we can inject the FT6X36 into the epaper class itself. This is the perfect moment to think what methods we can add to Gdew027w3T class to make the rotation touch aware:

/**
 * Helper method to set both epaper and touch rotation
 */
void Gdew027w3T::displayRotation(uint8_t rotation) {
  if (rotation>3) {
    printf("INVALID rotation value (valid: 0 to 3, got %d) rotation*90\n",rotation);
    return;
  }
  setRotation(rotation);
  Touch.setRotation(rotation);
}

Perfect. Now if we call display.displayRotation(1) our display will rotate and also signalize the Touch that we’ve rotated so we can recalculate the X and Y rotation in the touch panel.
Note that the Adafuit method setRotate is still there, since I didn’t wanted to extend it, so if we call setRotate then the same as before will happen. I just wanted to demonstrate how powerful is to build on top of C++ object oriented classes.
I hope you can get one of this touch displays, the only low-down is that you need to get one 6 flat adapters, and to check the PDF documentation on Good Display to wire this 3 cables to the ESP32.

Parts list:

Flat cable to PINs adapter – 3 to 4 u$ in Aliexpress, important: The INT pin should be wired to an Input pin in ESP32

One 2.7 inch b/w epaper with touch – 15 u$

One epaper SPI adapter (24 flat cable to SPI Pins) please be aware that this two connectors work independently of each other and both need a common ground and 3.3 volts to work.

That’s all you need. The schematic is the same as any other SPI epaper with this I2C on top, I can provide samples if needed, here my list of how I wired this for this example:

#
# Mosi and Clock should be set for any epaper
#
CONFIG_EINK_SPI_MOSI=23
CONFIG_EINK_SPI_CLK=18
CONFIG_EINK_SPI_CS=5
CONFIG_EINK_DC=16
CONFIG_EINK_RST=4
CONFIG_EINK_BUSY=15

#
# The FT6X36 touch chip needs an I2C port and one input GPIO
#
CONFIG_TOUCH_SDA=21
CONFIG_TOUCH_SDL=22
CONFIG_TOUCH_INT=17
CONFIG_I2C_MASTER_FREQUENCY=200000

To browse a full implementation example you can just checkout the following repository:

https://github.com/martinberlin/cale-idf this touch example you can find in main/demos/demo-touch-epd-implemented.cpp (Make sure to select this target in CMakeLists.txt)

Cale-idf is the repository where I test this components together before updating them to new releases

If you like this and it works for you I will be really happy to get a ★ Star in the repository.
And if you use it for a real life project I will appreciate a small donation following the links on the github repository. As I mention sometimes:

Open source is for free. But the developer’s time has value. So I think is fair sometimes to ask for a donation and also to support another developers donating to them when we use their components.

Using WiFi epapers to showcase digital art

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
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.

Arduino-esp32 course – Espressif is on the www WebServer vs HttpClient – Chapter 4

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.

In chapter 2 it was explained how to connect the ESP32 to WiFi so I thought it would be great to make an additional chapter that makes use of the connection in both directions:

  1. 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
  2. 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:

  1. http://ESP_IP_ADDRESS/switch
  2. http://fasani.local/switch

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.

2. HttpClient: The ESP32 will make a GET request

For this I prepared a very simple endpoint:

http://fs.fasani.de/time.php -> Will return the time in Europe/Berlin as default
http://fs.fasani.de/time.php?tz=America/Los_Angeles will return the time of that timezone

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:

<?php
$tz = isset($_GET['tz'])?$_GET['tz']:'Europe/Berlin';
date_default_timezone_set($tz);
echo date("H:i");

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.

Arduino-esp32 course – General-purpose input/output (GPIO) – Chapter 3

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
pull-up resistor connects unused input pins to the dc supply voltage, (3.3 Vcc in ESP32) to keep the given input HIGH
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.

This PWM image is from Arduino tutorial – credits: https://www.arduino.cc/en/tutorial/PWM

ESP32 / ESP8266 PWM example for one Channel

// 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.

Arduino-esp32 course – Event driven WiFi and build_flags configuration – Chapter 2

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:

#define WIFI_SSID "MyWiFiName"
#define WIFI_PASS "password"

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:

#ifdef ESP32
   #include <WiFi.h>

#elif ESP8266
   #include <ESP8266WiFi.h>

#endif

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

Arduino-esp32 Basic course – Getting started – Chapter 1

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:

// File: src/main.cpp

#include "Arduino.h";

int counter = 0;

void setup() { 
   Serial.begin(115200); 
   Serial.println("setup() started");
}

void loop() { 
   ++counter;
   Serial.printf("Counter: %d\n", counter);
   delay(1000);
}

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.

Hackaday projects update: CALE Eink Calendar and udpx

I must say although I’m not proud of all the projects I tried to document in Hackaday I do like a lot udpx and Remora, that are made initially to control Addressable LEDs, but they could be expanded and used for another uses.

CALE

This is actually an old project for the agency I work that is finally seeing the light. I started using Waveshare V1 and this is the new 7.5″ e-Paper V2

CALE E-eink calendar ESP32
CALE Eink calendar ESP32

This will be powered by a TinyPICO ESP32 that claims to consume as little as 20uA when in deepsleep. Let’s see when it’s connected to the Waveshare also in deepsleep ;)
But the idea is to connect, refresh the meeting room labels at each door, and then go to sleep for at least 2 hours. And in wakeup check the day of the week, it it’s Saturday or Sunday when we are not in the office, then just keep on sleeping. There is no need to update stuff when no one will watch.
So designed with low-consumption in mind, will be actually my first real battery project, that I hope last at least 30 days without charging. The Firmware is actually not ultra complicated except the BMP image reading part, that I took mostly from the GxEPD Eink library example, with the add-on that I implemented Zlib compression so download takes one second instead of five. Other than that the only thing that does, is to wake up, query a backend passing an URL that renders that webpage screenshot. The image is sent per SPI and it goes to sleep.
EVOLUTION:
Firmware: 90% done and open source
Case: Work in progress
CLIENT: http://www.skygate.de

udpx

udpx sending frames to a 44*11 RGB Led matrix
One of my personal favorites since we are working in team long time already is udpx which is a transmission protocol with decompression support (Zlib and Brotli)
That we are using at the moment to receive animation frames but I can see much more potential in it, like to be used to send small JSON beacons and have real-time charts for anything, they are endless possibilities to use a streaming technology that is proving to work stable. The Max. transport Unit (MTU) of the ESP32 is about 1470 bytes, but you can send much more in that payload if you use Brotli or Zlib, so I see that it can be applied in many other use-cases.

udpx is the first project using Espressif boards where we can use the full connectivity possibilities that the ESP32 offers:

  1. Bluetooth WiFi configuration. That’s one of the main reasons the ESP32 offers Ble and Bluetooth serial, so here we put it to use.
    udpx-app is an open source Android application that offers Ble/Bluetooth serial configuration to send WiFi credentials and also mDNS discovery using zeroconf
  2. WiFi UDP and decompression. ESP32 has enough memory to pack in one sketch both Brotli, miniz (Zlib) and Bluetooth. I did not believe this initially, but udpx is the proof-of-concept that it can be done.

Pixels is the library that is implemented to do the UDP interpretation, read the headers and send the RGB(w) brigthness values of each LEDs to Neopixels. Initially developed by Samuel our colleague in the USA, we work in team also with Hendrik in Frankfurt am Main to develop a Backend controller that will be able to chain animations and route them to multiple ESP32 controllers every millisecond. So this is a very exciting project, that is going slow because everyone is busy in many things at once, but one that I personally steal hours of sleep every time I can just because it’s interesting and beautiful to work on it.
Last addition was the PIX565 protocol, one that’s been inspired in Spectre’s own take on OctoWiFi LED Controller, but one that has been also used in old game consoles. Didn’t know about this, but Neopixels developer Michael Miller put it in words today:

Games also use alternate bit depth images like 565 and even others; to compress in memory textures. Lots of writeups on that and how shaders can use them to render. The graphics stuff you learn for LEDs often overlaps with gaming techniques.

Remora

This streaming little fish is swimming since a while and I’m proud that some music hackers are using it in professional Eurorack setups.

It was initially built for ORCΛ sequencer, and receives short UDP commands of 4 to 6 characters, to launch fast animations at a very high framerate. In the last months it also had the addition that if receives more than 9 bytes, it renders also a Pixel animation frame, so you could send short animations, but also a small animated video.

Remora was also the first product that I could imagine can be a Tindie product, but I must confess that I never selled one, even that I put a price that is mostly the Hardware price. I’m good at building, but not at selling. Everyone has to find their strong point.

 

PHP Symfony versioning and composer

Lately I’ve been switching to new versions and upgrading Symfony for client projects that I cannot reveal, but what I would like to share in this simple and short post is what combinations worked for me after many hours switching versions and dependancies.

Symfony 4

{
    "require": {
        "php": "^7.1.3",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "sensio/framework-extra-bundle": "^5.4",
        "symfony/asset": "4.3.*",
        "symfony/console": "4.3.*",
        "symfony/dotenv": "4.3.*",
        "symfony/expression-language": "4.3.*",
        "symfony/flex": "^1.3.1",
        "symfony/form": "4.3.*",
        "symfony/framework-bundle": "4.3.*",
        "symfony/http-client": "4.3.*",
        "symfony/intl": "4.3.*",
        "symfony/monolog-bundle": "^3.1",
        "symfony/orm-pack": "*",
        "symfony/process": "4.3.*",
        "symfony/security-bundle": "4.3.*",
        "symfony/serializer-pack": "*",
        "symfony/swiftmailer-bundle": "^3.2",
        "symfony/translation": "4.3.*",
        "symfony/twig-bundle": "4.3.*",
        "symfony/validator": "4.3.*",
        "symfony/web-link": "4.3.*",
        "symfony/webpack-encore-bundle": "^1.6",
        "symfony/yaml": "4.3.*",
        "jms/serializer-bundle": "3.4.1",
        "friendsofsymfony/user-bundle": "2.1.2"
    }
}

This is the easiest one since is the last version and you can just install it following the docs. Added some nice packages that I use always just to JSON serialize entities.

Symfony 3.4

{
"require" : {
        "php" : "^7.1",
        "symfony/symfony" : "3.4.x-dev",
        "symfony/webpack-encore-bundle" : "~1.6",
        "symfony/swiftmailer-bundle" : "3.3.*",
        "symfony/monolog-bundle" : "3.4.0",
        "symfony/console" : "^3.4",
        "symfony/serializer": "^3.4",
        "sensio/distribution-bundle" : "5.0.25",
        "sensio/framework-extra-bundle" : "5.4.0",
        "doctrine/orm": "2.6.4",
        "doctrine/doctrine-bundle": "2.0.x-dev",
        "twig/extensions" : "1.5.4"
    }
}

Basic install. This where you would like to go in case you are still using 2.8 version that will be soon out of maintenance

Symfony 3.3

{
"require": {
        "php": ">=5.6.27",
        "symfony/symfony": "^3.3",
        "doctrine/orm": "^2",
        "doctrine/doctrine-bundle": "1.10.0",
        "symfony/swiftmailer-bundle": "~2",
        "symfony/monolog-bundle": "^3",
        "sensio/distribution-bundle": "~5.0",
        "sensio/framework-extra-bundle": "^3",
        "incenteev/composer-parameter-handler": "~2.0",
        "knplabs/knp-snappy-bundle": "~1.4",
        "knplabs/doctrine-behaviors": "~1.4",
        "friendsofsymfony/user-bundle": "~2.0@dev",
        "doctrine/doctrine-fixtures-bundle": "^2.3",
        "twig/extensions": "^1.4",
        "mopa/bootstrap-bundle": "dev-master",
        "m6web/guzzle-http-bundle": "~2.0",
        "doctrine/migrations": "^1.5",
        "doctrine/doctrine-migrations-bundle": "^1.3",
        "jms/serializer-bundle": "1.5.0"
    }
}

The important difference in this 3.3 version is that it still runs on PHP 5.6+ but if you can it would be the best to go to 3.4 or even 4 but that requires a big refatoring in many corners.

Cordova cross platform mobile applications

After the small Chrome App experience that resulted in a very compact app that reads Video and sends an UDP binary stream to ESP32 Led controllers I decided to start learning how to program a real mobile app.
Real because I find that the way to do it must end in the App Store and the user should be able to install it with a few clicks.
This start guide is only valid for Linux but it could be a similar approach in Mac.

1.- Install Java 8 SDK
https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
2.-Cordova
https://cordova.apache.org/#getstarted

It’s very straight-forward to start if you have previous html/javascript experience building a web page.

In this github repository:
https://github.com/martinberlin/udpx-app

I’m building my very first Android app. Follow the commits and watch the Repository to see my new steps and also my failures in this new world.

This is a preview of how the app looks like in it’s first version:

udpx Android application is available in the Play store.
udpx app

Soon more posts will follow explaining how to interface with Android OS (Camera as an example). There will be soon more issues to expand and implement in this repository. And my new philosophy to build Firmware for Espressif chips will be simply:

“One firmware, one mobile application”

Meaning that every firmware will have his app, where you can configure and test it, just with one click install in google Play store and setting up some very easy configuration. Starting from WiFi credentials using BLE (Bluetooth low energy) and setting up the App IP address as a last step.

My goal at last, is that internet of the things is easy to use, and not something where you need a 20 minutes complicated setup to start using something. So the mission is that for first weeks 2020 all master branches from our main ESP32 Firmwares like udpx and Remora should be bluetooth configurable.

Building Visual Code in a Raspberry Pi

When I’m on the road without laptop I miss having something to compile Espressif boards. So I decided to document this a little bit.
Visual Code is the base for Platformio that is my IDE of choice when coding in C / C++

It turns out that for ARM like many other software you need to go the hard way to build it since there is no Debian package ready for this architecture. So to start with, you need to have latest version of Node for this arm version: uname -m
will tell you what version of ARM you are on. Then just follow Node instructions to get the latest version on your system. After node installation, there are some extra packages that are needed for the build:

sudo apt install libx11-dev libxkbfile-dev libsecret-1-dev

Then just follow this guide and get some cofee because it takes at least 20 minutes to build:
Building Visual Studio Code on a Raspberry Pi 3
Official repository: https://github.com/microsoft/vscode

I must say I’m really impressed at the stuff you can build using npm/nodejs this times. Combined with cross platform desktop apps like Electron it’s really amazing to see how the community is building software with this.

Using the Extension marketplace

It’s important to note that the build version of VSCODE does not support the official marketplace. So in order to install Platformio you will need to use the extended … menu in Extensions and use “Install from VSIX” and first install C/C++ :

1. https://github.com/Microsoft/vscode-cpptools/releases Select cpptools-linux.vsix
2. And then search for Platformio VSIX
Install them in this order since Platformio requires cpptools otherwise it won’t start. It’s a pity that Microsoft did not enable the Marketplace for VSCODE builds but at least there is a way to install extensions manually.

Some very useful out-topic commands in Debian/Raspbian

// List installed packages
sudo dpkg-query

// Create a list of all installed packages
sudo dpkg-query -f '${binary:Package}\n' -W > packages_list.txt

// Using apt
sudo apt list --installed | less