Repurposing the MIDI protocol to control Led Matrixes

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.

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

NNSVV  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

   -D SERIAL2_BAUDS=31250
   -D RXD2=26
   -D TXD2=27
   -D MIDI_LISTEN_CHANNEL1=1
   -D MIDI_LISTEN_CHANNEL2=2
   -D MIDI_LISTEN_CHANNEL3=15
   -D MIDI_FIXED_VELOCITY=0
   -D MIDI_BASE_OCTAVE=4
   -D MIDI_TOP_OCTAVE=12

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.

Credits go to this awesome libraries

https://github.com/marcmerlin/Framebuffer_GFX.git
With Framebuffer Marc Merlin did an awesome job
https://github.com/FastLED/FastLED.git
FastLED is one of the most powerful RGB Libraries for Arduino / ESP32
https://github.com/marcmerlin/FastLED_NeoMatrix.git
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
Demo videos, just for fun, not representing at the moment the finished work

Understanding how to make good decisions when betting on Crypo Multi HODL bets

Multi HODL is an innovative tool to bet on price changes on Crypto assets invented by YouHodler.com . YouHodler is a Swiss based company that is innovating in the field of Crypto financials.
I enjoyed a lot using Multi HODL but I was not well informed when I started with it and I make a lot silly mistakes, hence I decided to write this blog post with my advice, to help potential users have a fresh start. Now let’s take a look at the chart below with the 2 main Crypto assets in 2020. The fact is that since Covid started, Crypto values sinked abruptly, and after that they grown with an unusual force until being on the maximum tops cornering the end of 2020. This is how an Ethereum vs. Bitcoin looks like in 2020.

ETH vs Bitcoin price in 2020 – TradingView.com

Looking at both charts, ETH on the top although the king on the price is Bitcoin, there is something obvious: The grow direction with some temporary drop prices it’s always going UP.
It’s good to follow Bitcoin and Ether, or the coin you follow, together. As Gold is the king of the metals value, so it’s Bitcoin in the Cryptocurrency world, so if you open BTC and ETH in two horizontal panels you will see they mostly correlate over time. Usually, but not always!
So it’s essential to follow both to have a clear tendency indicator.
Multi Hodl let’s you bet on price changes in both directions, Up or down changes:

Going UP is always better than down

This are my personal recommendations about using it to avoid falling in common mistakes.

You should only reserve for this kind of bets as small percentage of your assets while leaving the rest in YouHodler savings account so it generates a steady 4.5% year interest on Crypto and 12% in Stablecoins. And the best is to not play with your Crypto assets directly, but to bet with a Stablecoin, like EURs or USDT. That way you can have more control of the gains and looses without altering your precious holdings in Crypto.

Disclaimer: This are only my views and recommendations on how to bet on UP Multi HODL Bets, with the least risk as possible and without falling in very common traps. I’m not an financial expert and this are hints that I summed up after my own mistakes.

Be aware of momentum of the year and special days. For example if you are close to black friday, check how it was historically on that day. Avoid days where you don’t see market volume, avoid special dates like Black fridays and any other internet related thing since this Crypto is attached to the internet somehow. And this special days can be specially tricky!!!’
Do a search, research the field, before planting a seed. If you are not going to take the time to do this research it’s better not to bet.

This easy tips are only based in the last price movements from Ethereum which may be very different from those of other cryptocurrencies. ETH price was hectic in the last weeks and it’s recommended to bet only UP when you see a clear grow pattern.

1. For starters: Use only the UP button and a low Risk factor (X2 or X3).

Background: Even if you make a bad decision, you can always wait, and at some moment it will go up. The price per hour is calculated using the initial Bet and your Risk factor. If you use take a big risk is not optimal to wait long hours.

Checking last months on ETH or BTC it’s going up. It’s a no brainier!
Unless you know what you are doing and you know that there is a big price drop you may try the DOWN button. But it must be a fast action, and if you loose the timing, is very probable that it will never go down again. Avoid the DOWN button. Be wise, make only UP bets in crypto!

2. The starting point is essential! The best to start an UP bet will be at the lowest point possible on a trend that looks going upwards. Look macro movements, not mini like just 2 hours range!

Explore last 5 days, even last 2 weeks, and double check that your prediction matches. Starting your race in a good point gives you the leverage to wait in a comfortable position in case there is a temporary descending path.

3. Micro betting examples: To start playing with it and avoid big looses is a very good idea to try a micro-bet. Just use a low value like 0.05 ETH or even less, like 10/20€ for example of a stable coin. Doing that you can also afford to have a bigger risk factor since the bet is small and in the worst case there is not much to loose. This way you can learn the first days/weeks till you get a feeling for it without loosing part of your assets.

4. Clearly identify grow direction: Check the Macro movements on last 5 days, last month. Usually it’s always UP but is important to detect that after days of continuous grow there is usually a DOWN cascade. So don’t trust the always UP game. The key to make a good Bet is to hold it from a low point, so you have leverage to stop the Multi HODL, at the beginning of that cascade and not at the end. Also is a good tip to see the top of the saw mountains, if the peaks go UP, it’s a GROW pattern . If they go DOWN is a DECREASING pattern. Sounds easy but it’s not always straight-forward to see.

5. Don’t double bet at the beginning: It’s very tempting to make a second bet to cover the looses of the first one (Ex. I though was going, but is going down, let’s make an additional DOWN bet!). Although can be a good idea, unless you master points 1 to 4, this is not recommended, and as mentioned before DOWN bets are a bad idea that have to be done precisely.

What it may be a good idea, is to make a “seed Bet” of a minimal import like 0.04 ETH for example, to see your idea was good before making a bigger bet, then you can close them at different times knowing that with the small one there is a minimal risk. Both going UP!

6. Mind the GAP: From the starting point, let’s imagine the scenario where you start an UP Multi HODL bet of 0.7 Ether when ETH price is 590 u$. Mind the origination fee of 0.45% cost that you can see in Details plus the hourly fee, that can be high if you choose a high risk factor (X).

Example: 0.7 ETH Multi HODL bet at X7 factor taking in account the costs, will start generating something only above the 595 u$. So this 5% (about 0.5% UP) is only to cover the costs. So make the appropriate markings in your favorite Stock Chart online viewer, in order to visualize this risk, and to get away at the proper time in case goes down abruptly. Using lower risk factors like X2/X3 makes the initial cost lower and has also the advantage that if you start in the wrong point, you can wait till the new upward Wave in order to avoid stopping the Bet on RED numbers. Theoretically, unless the exceptions when the starting point is not wisely chosen or the bad luck to start when a DOWN cascade is flowing, you should only quit an UP bet when it’s on the winning side and not before.

7. Mind the Growing vs. Decreasing speed: Usually it’s quite normal that the grow movements are like a chain Saw of short upward movements with down cascades every now and then. If the cascades are short and do not cut all the grow is great since it’s a clear signal that there is increasing GROW.
But it’s not uncommon that after a long upward Saw shape comes a BIG cascade DOWN. And that’s sometimes really fast down compared to the grow speed, be ready for it, and away from your danger top bottom line (Where you start loosing)

It’s very important not to freak out in this point and stop the Bet in the most down part of the cascade. This is a very common error that stopped many of my good bets. Get accostumed to this movements, make your margins visible drawing over the live stock Chart and HOLD it unless you see clear signs of a catastrophe!

8. Don’t overuse it and do not get addicted to it: Making one or two good Bets you can call this a day and just walk away happy till next day. The luck to be always in the growing side is not take for granted. Many times you may not see a DOWN cascade of death coming, and in one fail, loose the gains of many good guesses. Use this consciously, you are playing with real money here. And start slowly, making Micro bets as described in point 3, to get the feeling and timing for it. Keep in mind doing this is time consuming and may make you tired!!! Find the good moment to start it.
This is the key for a very good Bet!

9. Be aware of context on the Crypto you are making the bet. For example if you are betting on Ethereum, then follow the ETH developers and related channels. Starting this month they are launching ETH2 and I bet that had an effect on the super bullish unusual grow on the last few days.

10. If possible don’t use your Crypto on this! Your mission is to HOLD your Crypto and have an stable financial future. So playing with it’s a bad mix. Unless you are sitting on a stronghold of Ether or Bitcoin and you are wise enough to keep your balances right I strongly recommend to use any Stablecoin that you can afford to loose to do this kind of bets.
Do not forget: You are playing with real money when you start a Multi HODL, and even that it looks like always going up on a Macro zoomed out view, the reality is that is not always a constant growth. And in a single day can be a 10% drop on the price, if you are there on one of this UP bets, you will loose at least 1/3 of what you bet if you don’t get out in the right time.
So I will say it again in case it was not clear:
Separate your betting risk money from your Crypto assets if you want to be in the safe side!

11. Last but not least, do not play with Bottom margins. Let it breathe!
It’s very human to think: I will just control my risk setting a very low margin so I can sleep in peace!
There is two problems with this approach:
1. As described in point 6 there is a gap from the Bet start till you start winning so setting a very short down limit can let you out with the worst scenario. I will describe this with an image after declaring my intentions. Also mind that the margin stop loss has a high cost!
In case of margin call or stop loss you will be charged a 2% service fee instead of origination and hourly fees. In the App/Website says 3.8% fee but is wrong (Already reported this)
This 2% is taken from the total amount depending on your risk factor so it’s much better to stop it by hand if you want to do so. This limit is just a safety ring to avoid loosing absolutely everything. But it’s not recommended to use it unless there is a good reason to do so and always tight to your starting point!!!
Failing to do so and having a bad starting point is a guaranteed way to loose the bet. To have a real scenario, let’s say the total borrowed money for the bet is 2000 u$, margin call with take 2% of that so you will end up paying 40 u$ just for the automatic margin call. Don’t do this.
If you don’t find time to be relaxed and follow your bet is better not to start it. This is the wisest advice I can give to you after gaining some experience.

2. Is very normal that you think is the best start point and it’s not. You cannot control the snake and cannot possible predict the future. So just let it breathe and at some point it will grow. And if it does not and hope is lost then at least wait enough time to get at the highest point and stop the bet manually.

Anatomy of a Bad bet and a very bad decision: To set a low margin call, see it was just about to wait longer!

This is fun but also risky and time consuming. And it’s also something that can be done in a wild, hectic coin like ETH, but also on more stable ones and doing it with more calm waters can be the key to success in this crazy oceans of 2020.
I highly recommend to do this confortably from a Desktop computer since the UX is much nicer defined and is more precise. I had two times problems when starting this from the iPad since the rist leverage is close to the UP button and withouth intention you can start with the Highest risk. The mobile App is good to stop the bet, but to be on the safe side, start it confortably from your laptop.

Managing risk

As mentioned before this takes risks of loosing real money. And it’s supposed to be done with assets you can afford to loose. If you don’t like risk at all then you don’t have to do it. You can just leave your Ether, Bitcoin, Litecoin or many other coins just there and use it as your wallet, generating 4.5% yearly interest.
Or 12 % in stable coins. I have some EURs assets that is 1 coin, 1 euro and this generates a nice passive income that you use as a end of the year savings or any time when you want to invest in buying more Crypto.
This is are only my personal views and I’m not a financial expert. I’m just a worker that is tired to be in the office 9 hours / 5 days a week and is looking for financial freedom.

Related articles

Keeping your Crypto assets to generate 4.5% (ETH, BTC) to 12% (stablecoin) yearly interest

As an addition to latest article “Financing a web-project with Ethereum” I wanted to show how to make your Crypto assets grow a small percentage per year and also to have some additional cash per month using Stablecoins.

Stablecoins are cryptocurrencies designed to minimize volatility, relative to some “stable” asset or basket of assets. For example there is EURS that is tied to the € price also 1 EURS = 1 €.
Another one is PAX Gold that is tied to the price of one ounce of Gold. Both of this stablecoins are a subset of Ethereum network. With PAX Gold you can if you go to London, exchange your coins, for real gold. That’s what they claim at least since I would not fly there to prove it. More than that, the point I want to make here, is that in sites like YouHodler you can make 12% APR + compounding interest per year.
Let’s say you have 10.000 € there for one year untouched.
At the end of the year you should have 11.200 € with a 12% interest.
If you want you could extract this 1200 € to your account and have some extra cash at the end of the year for further investments of a short holidays. That is a quite interesting method to generate a passive income over the years.
Below there is a talk about stablecoins that was recorded and had 464 registered participants from 58 different countries, on April 17 2020

Now the only risk I see is that this money is not on your domains. It’s hosted in a secure wallet in the domains of a company. So what I recommend, no matter what site you choose, is to check very well and read about who is behind the company and how secured are your assets in the event something happens with the company you choose.
I use to trust the banks a lot until one day at the middle of 2001 in my hometown, Buenos Aires, the economy crashed and the banks simply will not let you take your dollars out of your acccount. Or even worse, they where converted to argentinian money, something that at that moment was the equivalent to flush your cash into the toilet.
Ater that happened to me, got an offer to work in Spain and moved to the EU in 2004, and I always take care of where I put my assets and making an analysis of the risks. Of course we are in Europe and here the protection laws, would simply not allow a bank run like that to take place, still with that assumptions it is wise to check where you put your money.

Team of YouHodler

In case of YouHodler my analysis was good and as I mentioned in last post, the fact that they are a members of the BlockChain Association and the Financial Commission, and that they open profiles show an extensive and proved career in the Financial tech industry was enough for me. The fact that they have real offices in Malta but also in Switzerland is also a proof that they real and solid.

Disclaimer about 100% transparency: I’ve decided to sponsor YouHodler and will also make some videos and further analysis openly. The links starting with track.youhodler.com are affiliate links since I want to get additional cash in order to film the videos and use my time to share with my readers the road to financial freedom and to generate an additional income each year.
‘This links won’t work from Germany or from Spain due to country restrictions. But from those countries you can enter directly to youHodler.com
This does not mean that I blindly recommend this company. There are also others in the market like Nexo that offer a similar percentage (2% less on stablecoin). Feel free to explore and take your decision solely on your assumptions.

You can also earn crypto faster with Multi HODL but that is something that must be handled with care and deserves a new Blog post. For the moment I only recommend to keeping combined assets, like for example some Bitcoin and Ethereum, along with Stablecoins like EURS or PAX Gold in order to generate a passive income.
Do not try Multi HODL with the dream of making a fast buck. It’s very risky to use it, specially with the variations value of cryptocurrency, that is like trying to take control of a wild snake. This must be used with care and meditation.

Questions for my subscribers:
What do you think about this?
Would you trust an external company part of your assets provided they are trustable and generate a yearly passive income?

I bough some ETH back when it was 260 -> 280€ now it has gone up almost 100€ in two months. That was a lucky desicion. But is not always like this and it can be that due to market changes, external factors, or just the team Ethereum development roadmap that it simple goes down too.
This was a good analysis made by an user of TradingView months ago:

This second analysis was also quite accurate. 1 ETH is worth today 19 Nov. 2020 about 470 dollars (395 €).

Financing CALE.es using Ethereum

CALE.es is a screen administrator where you can mix the content of different APIs and generate output images for your WiFi displays (Epapers / TFT)
An idea that starting taking form when I started playing and building epaper WiFi displays using ESP32 “system on a Chip” boards back in November 2019. I needed a web-service to get a simple image URL and test my epaper builds. That triggered the idea to build something more powerful where you can configure APIs, like Google Calendar, iCal or weather and use this outputs to compose your Screens. The idea flourished into a full pfledged website and web-service that is currently used by 60 developers to feed their WiFi screens.
All this part is slowly getting new features and functionality, both in the web-service side (PHP / Symfony 4) and on the ESP32 Firmware (C++ both for arduino-esp32 and ESP-IDF Firmwares)

5.83 Good display black/red epaper



So far I can call it a success project. It was online 99.9% of the time and the users seem to stick to it with about 20 % of them using it for their online Calendars, epapers and other displays. You can also use it to generate a JPG image that is added into your Website.
But of course all this also generates a cost. Not only domain name per year that is minimal, but also AWS server costs and my own maintenance and development costs. Even if I do it because is my hobby and I like it, I could be working this time for other clients for paid projects, so I count also the aprox. 14 to 20 hrs that I spent on it as if it was a job that somehow needs to generate something. And also the 1500€ that where spent in hardware and electronics, for example buying epapers and 3D-printing materials, in order to built at least 12 test displays that I partly own and others where sent to testers and clients.

After spending countless hours in projects that at the end where not successful or went into the project cemetary after months of the launch day I become wiser. So this time I had to find a better alternative to self-finance this project on it’s own.
I’ve explained openly how this project was going to be financed, and also added CALE Ethereum address in my github repositories, to receive donations from users that use the Firmware in their businesses. In all this about turning to Ethereum to finance the project I made a mistake, that was to start a mining server, since it’s not a wise idea to do it in a country like Germany where the electricity is expensive. But overall it was a good idea to receive mini-donations and also to stack my crypto assets in a platform like YouHodler where you can receive 4.5 % annual interest rate.

I researched quite a lot before taking this decision to see if Youhodler was trustable enough to do this operation and this is what I found:

  1. YouHodler is a member of the BlockChain Association and the Financial Commission. This independent Association will ensure all YouHodler users have an outlet for dispute resolution outside of the YouHodler platform. If you think YouHodler fails to meet your expectations, feel free to submit a complaint and you’ll get a quick, unbiased and non-binding resolution.
  2. YouHodler Trustpilot review is Excellent.
  3. They are a young company but with a deep knowledge in Financials and the persons that appear in the about us section are real, linking to their LinkedIn profiles, with proved experience in the sector.
  4. They hold at least more than half a billion dollars in Ethereum and more in Bitcoin. I don’t think that there is any interest in disrupting that trust, since that would be the end of their reputation in any other company.

At the end is all about trust in this. And this is just my risk but at least is a way that this site can be working free of charge for everybody. Not all the other projects that I started had the same luck!

I would like to ask my readers and followers:
What is your experiency with Cryptocurrency?
Can be this the key to future financial freedom?

Related blog posts

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.

New epaper component for ESP-IDF

E-paper component driver for the ESP-IDF framework and compatible with ESP32 / ESP32S2 can be found in this repository:

https://github.com/martinberlin/CalEPD

Codenamed Cal e-pe-de for the initials of E-Paper Display this is basically all what I’ve been doing the last weeks. Learning more about C++ object oriented coding and preparing a class that can be used to send graphic Buffers to different epaper displays. In order to do that I’m documenting on the go and building at the same time CALE-IDF that is our Firmware version for CALE.es but this time built on top of the Espressif IoT Development Framework (IDF)

The mission of this new component is to have a similar library for ESP-IDF that is easier to understand. If possible strictly meeting these requirements:

  • Extendable
  • Maintainable
  • Object-Oriented Programming with human-readable code and function names
  • Easy to add a new Epaper display drivers
  • Easy to implement and send stuff to your Epaper displays
  • Human-understandable
  • Well documented
  • ESP-IDF only (No Arduino classes)

Please find here the Wiki with the models that are already supported in the component:

https://github.com/martinberlin/cale-idf/wiki

Leave us a note if you want to try this. We can guarantee that for big epaper displays (>=400 pix. wide) it runs faster than GxEPD also supporting Adafruit GFX fonts and geometric functions. If you tried and it worked out for you please don’t forget to add a ✰ Star and spread the idea.

Testing the new ESP32-S2

I got one ESP32-S2 Saola board and I will be making some tests on this repository:

https://github.com/martinberlin/ESP32-S2

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.

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

What GPIOs not to use in ESP32-S2

GPIODescription / default configuration
0Pull-up
26PSRAM pin
45Pull-down
46Pull-down – INPUT only
43 *default UART0 pin on S2
44 *default UART0 pin on S2
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

Important documentation
ESP32-S2 Technical reference manual (page 39-40 for the not-use GPIOs)

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.