RFID and Arduino (1)

In this tutorial, divided in two posts, I’ll show you how to use RFID (Radio-frequency Identification) tags with Arduino.

In the first part you’ll learn how to connect the reader to your Arduino and how to write a simple sketch to display the tag’s ID, while in the second part you’ll learn how to build a complete access control system based on RFID tags.

PN532

I chose as RFID reader a board based on the PN532 chip by NXP. This is a very versatile chip: it can work as a tag reader/writer but it can also act as a RFID tag; moreover it supports both I2C and SPI communication buses.

Adafruit created a breakout board for the PN532 chip and the Arduino libraries we’re going to use. Alternatively you can find on several webstores the following board, that I’m also going to use for this tutorial:

rfid-03 rfid-04

Connections

As I wrote before, the PN532 chip supports both I2C and SPI. For simplicity, I’ll use the first one, connecting the SDA and SCL pins of the board to the corresponding pins of Arduino. You have also to connect the IRQ pin to a digital pin of your Arduino (I chose pin 2); thanks to this connection the PN532 pin “warns” Arduino if a new tag is being read:

rfid-06 rfid-07

To select the I2C bus, you have to set the board’s dip switches as explained on the silk screen:

rfid-05

Finally, power the board connecting the VCC and GND pins to pins 5V and GND of Arduino.

For this project, I used the beta version of a new LCD shield by Lemontech. The main feature of this shield is that the LCD is connected to Arduino via an I2C expander; moreover all the buttons are connected to only one pin, the analog pin A0. This means that almost all the Arduino pins are still available for connecing other devices. The display’s default address – but you can change it – is 0x27 while the PN532 chip has address 0x24 so there’s no conflict.

rfid-10 rfid-11jpg

Having two devices connected to the I2C bus, I had to add two 10Kohm pull-up resistors for SCL and SDA as explained in the following schematics:

rfid-13

To keep things simple, I place them on a small breadboard:

rfid-08

I also added a small speaker (connected to pin 8 and GND) to play a sound everytime Arduino reads a tag:

rfid-09

Libraries

To be able to compile the sketch of this tutorial you have to install the following libraries in your IDE:

  • Adafruit PN532 di Adafruit
  • hd44780 di Bill Perry

Both the libraries are available in the Library Manager:

rfid-01

Sketch

The complete sketch is available in my Github repository.

To use the LCD, first you have to define its size (rows and columns), the address on the I2C bus and the pins it’s connected to. You can then initialize the library in the setup():

#define LCD_COLS      16
#define LCD_ROWS      2
hd44780_I2Cexp lcd(0x27, I2Cexp_PCF8574, 0,1,2,4,5,6,7,3,HIGH);
[...]
if(lcd.begin(LCD_COLS, LCD_ROWS) != 0) {
  Serial.println("- Unable to initialize LCD!");
  while(1);
}

The same for the PN532 chip: you have to declare the pin connected to the IRQ signal (RESET is optional) and then initialize it. Using the getFirmwareVersion method you can get the chip version and therefore verify that it’s working correctly:

#define PN532_IRQ     2
#define PN532_RESET   3
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
[...]
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if(!versiondata) {
  Serial.println("- Unable to find a PN532 board");
  while(1);
}
Serial.print("- found chip PN5"); 
Serial.println((versiondata>>24) & 0xFF, HEX);

Lastly, call the SAMConfig() method to configure the chip in normal mode and to enable the IRQ pin:

rfid-02

It’s very easy to read a tag. The readPassiveTargetID method returns true if a tag is near the reader:

success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (success) {

In this case, you can play a sound and display the tag’s ID on the display:

tone(SPEAKER_PIN, TONE_FREQ, TONE_TIME);
lcd.clear();
lcd.print("Found RFID tag!");
lcd.setCursor(1,2);
lcd.print("ID: 0x");
for(int i = 0; i < uidLength; i++) {
  if(uid[i] <= 0xF) lcd.print("0");
  lcd.print(uid[i] & 0xFF, HEX);    
}

Demo

ESP32 (21) – Mutual authentication

After having published my post about how to implement a webserver on the esp32 chip, some readers correctly warned me that everyone, once connected to the wifi network, could control the relay and asked me how to limit access to the webpage.

A classic solution (that I also used in this previous tutorial) is to ask the user to enter a password. It’s very easy to implement, but has a negative aspect, that is you have to type the password everytime you need to switch the relay.

You can avoid asking the password everytime the user access the page if you implement a login page and keep the user logged in using a session cookie as I’ll explain in a future tutorial!

Today I’ll show you how to protect your website using a feature of the SSL/TLS protocol: the mutual authentication.

Mutual authentication

Everytime you connect to a website using the https protocol, the server which hosts the site sends to your browser the SSL certificate of the site. Thanks to this certificate, you can verify the identity of the server and establish a protected connection to it:

ssl-02

The TLS protocol also offers the possibility for the server to require a certificate from the client: this means a two-way authentication, that is server and client authenticates each other using SSL certificates:

ssl-01

With this authentication method, a user has only to install on his device the client certificate (and the corresponding private key) and choose which certificate to send to the server if more than one is present:

ssl-03

The authentication with SSL certificates is therefore based on the principle of something I own (for example, a key) and not of something I know (for example, a password).

Certificates

Generally speaking, SSL certificates can be:

  • self-signed
  • signed by a Certification Authority

The first ones are perfect for internal use or for testing, while the second ones, normally signed by trusted CAs, are widely used in production environments or on the Internet. Sometimes you probably received this warning message when surfing on the Internet:

ssl-04

that means that your browser doesn’t trust the certificate sent by the server because it’s self-signed or signed by a non trusted CA.

For this tutorial, I’m going to use OpenSSL as a certification authority to create the certificates we need. If you’re using the toolchain provided by Espressif, you don’t need to install anything else because OpenSSL is included.

First create a folder that will contains all the files required by OpenSSL to operate as a CA in your home directory:

cd 
mkdir myCA

Now move into that folder and create some empty files:

cd myCA
mkdir csr certs crl newcerts private
touch index.txt
echo 1000 > serial

sslca-01

Copy the certification authority‘s configuration file (openssl.cnf) from my Github repository into the myCA folder. Open the file and change the main path accordingly to your system:

sslca-02

Now you have to generate the private key and the certificate for the CA. Let’s start with the key (its length will be 2048bit, enough to ensure good security):

winpty openssl genrsa -aes256 -out private/ca.key 2048

(all the commands must start with winpty only if you’re using Windows!)

You’re prompted for a password; it’s very important to remember it because you’ll have to type it everytime you use the CA:

sslca-03

Now generate the CA certificate (with a duration of 3650 days, that is 10 years):

winpty openssl req -config openssl.cnf -key private/ca.key -new -x509 -days 3650 -sha256 -extensions ca_cert -out certs/ca.cer

You have to enter some informations… the only mandatory is the common name which is the name of the CA, that will be included in all the certificates the CA will sign. At the end of the process you can open the file (ca.cer) and verify name and duration:

sslca-04

Let’s now generate the server and client certificates. The steps are the same for both:

  • generate a new private key – openssl genrsa
  • generate the CSR (Certificate Signing Request) file – openssl req
  • sign the CSR file with the CA to obtain the final certificate – openssl ca

Server certificate:

winpty openssl genrsa -out private/esp-server.key 2048
winpty openssl req -config openssl.cnf -key private/esp-server.key -new -sha256 -out csr/esp-server.csr
winpty openssl ca -config openssl.cnf -extensions server_cert -days 365 -notext -md sha256 -in csr/esp-server.csr -out certs/esp-server.cer

Client certificate:

winpty openssl genrsa -out private/esp-user.key 2048
winpty openssl req -config openssl.cnf -key private/esp-user.key -new -sha256 -out csr/esp-user.csr
winpty openssl ca -config openssl.cnf -extensions usr_cert -days 365 -notext -md sha256 -in csr/esp-user.csr -out certs/esp-user.cer

As said before, the client device must own both the certificate and the private key. You can merge certificate and key in a single p12 (or pfx) file with the following command:

winpty openssl pkcs12 -export -out esp-user.pfx -inkey private/esp-user.key -in certs/esp-user.cer

The way you install the P12 file on your device depends on the specific operating system it runs: on Windows you only need to double-click the file and follow the import wizard.

SSL webserver

The source code for the program running on the esp32 devboard is available in my Github repository.

First you have to copy in the prioject folder the certificates (CA and server ones) and the private key for the server certificate and include them – as text data – in the program (I’ve already explained how to include binary files in my previous post):

esp-ssl01

To implement the SSL protocol I’m using the mbedTLS library, included in the esp framework.

First include all the required header files:

#include "mbedtls/platform.h"
#include "mbedtls/net.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/error.h"

Before being able to accept SSL connections, you have to define and initialize all the variables you’re going to use:

// mbed TLS variables
mbedtls_ssl_config conf;
mbedtls_ssl_context ssl;
mbedtls_net_context listen_fd, client_fd;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_x509_crt srvcert;
mbedtls_x509_crt cachain;
mbedtls_pk_context pkey;
 
[...]
 
// initialize mbedTLS components
mbedtls_net_init(&listen_fd);
mbedtls_net_init(&client_fd);
mbedtls_ssl_config_init(&conf);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);	
mbedtls_x509_crt_init(&srvcert);
mbedtls_x509_crt_init(&cachain);
mbedtls_pk_init(&pkey);

Now parse the certificates and the private key and store them in the variables:

mbedtls_x509_crt_parse(&cachain, ca_cer_start, ca_cer_end - ca_cer_start);
mbedtls_x509_crt_parse(&srvcert, espserver_cer_start, espserver_cer_end - espserver_cer_start);
mbedtls_pk_parse_key(&pkey, espserver_key_start, espserver_key_end - espserver_key_start, NULL, 0);

Tell the library which variables to use for CA chain and own certificate and require the mutual authentication (MBEDTLS_SSL_VERIFY_REQUIRED):

mbedtls_ssl_conf_ca_chain(&conf, &cachain, NULL);
mbedtls_ssl_conf_own_cert(&conf, &srvcert, &pkey);
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);

After having configured other elements of the library (the random number generator and the debug function) you can bind it to the standard TCP port for the https protocol (443) and accept new incoming connections:

mbedtls_net_bind(&listen_fd, NULL, "443", MBEDTLS_NET_PROTO_TCP);
mbedtls_net_accept(&listen_fd, &client_fd, NULL, 0, NULL);

After having accepted a connection, you only have to call the function:

mbedtls_ssl_handshake(&ssl);

during the handshake process the library sends to the client its own certificate and require the one from the client, verifying that this is signed by the specified CA: the mutual authentication is implemented!

After the handshake, the program is similar to the previous example, you only have to use mbedTLS specific send/receive functions (mbedtls_ssl_read e mbedtls_ssl_write).

Test

PCBWay

If you follow my blog, you probably noticed that I use different online services to make my PCBs… it’s a way to test them in terms of quality and production time.

On the advice of my friend Mauro, I recently chose PCBWay to produce some PCBs for my future projects.

The procedure for sending a new order is very simple; first you have to enter some information about your PCB:

pcbway-00

then, in the next page, you can configure som specific details of your printed circuit board (color, material, finishing, copper size…). It’s interesting the possibility – not often available – to choose the color of the silkscreen:

pcbway-03

Once selected the production mode (nomal = 2-3 days or express = 24h) and the shipping method, you can add the PCB to your shopping cart and upload the Gerber files of your project. PCBWay doesn’t offer the CAM jobs for Eagle, but I verified that they accept files in the same format of Seeedstudio. If you use Eagle, you can therefore follow my tutorial to create the needed Gerbers.

Before being able to complete the payment and send the PCB to the factory, the files must pass a manual check. Yes, PCBWay manually verifies every file you upload… this process requires only some minutes and it’s definitely an added value: in one of my orders I made a small mistake and I was promptly contacted by a PCBWay engineer with an email reporting the mistake and asking me for the correct file.

When the order is in production, you can follow the progress in your personal area:

pcbway-01

Even the website displays every step of the production process:

pcbway-02

The quality of the PCBs I received is very good, certainly comparable to other services I used in the past. The price also is alined to the other manufacturers and PCBWay does offer an entry price of 5€ for 10 PCBs (10x10cm max).

pcbway-04

ESP32, Wemos or not to Wemos

Wemos is a chinese manufacturer of IoT products, well known for its D1 Mini family, that includes boards based on the ESP-8266EX chip and expansion shields.

Some weeks ago, Wemos announced a board based on the new ESP32 chip, named LOLIN32. This board hosts an ESP-WROOM-32 module and can be powered by a single cell (1S) LiPo battery. The board can also recharge the battery thanks to a circuit based on the TP4054 chip.

I brought a LOLIN32 board from Wemos’s official store on Aliexpress. The board is shipped inside an anti-static bag, with a label with your name:

lolin-01

The bag also include some pin headers:

lolin-02 lolin-03

Curiously I noticed that other Aliexpress stores sell boards named “lolin”. I brought one of them:

lolin-04 lolin-05

Although this board has the “wemos” logo printed on it, it actually looks like a clone of the D-duino-32 project by Travis Lin: unlike the LOLIN32 indeed this board does not include the LiPo battery connector and the charging circuit while it adds a 0.96″ OLED display.

Here’s a visual comparison between the two boards:

lolin-06 lolin-07

The advantage of purchasing an original Wemos card is the availability of the electrical schematic, some tutorials and a support forum… all that is missing for the clone board (how is the OLED display connected?). My suggestion is therefore that if you want to buy a Wemos board, you should get it from the original store.

If instead you are interested in a board with a OLED display, why not buy it directly from the designer’s tindie page, to support its development?

Updated CH340G board

Some weeks ago I blogged about my project of a minimal board based on the CH340G chip.

After some tests, I slightly modified the project:

  • I added two leds that blink when data are transmitted/received
  • I added a jumper that allows to decide if you want power supply (5V) in the connector or not
  • added a 10nF capacitor to make the circuit more stable.

Eagle files in my Github repository have been updated.

Here are some photos of the new PCB (the purple color should tell you that it was manufactured by OshPark):

ch340g-10

The new board with all the components and a comparison with the first version:

ch340g-11

ch340g-12

 

STM32 and Arduino

STM32 is a family of 32bit microcontrollers manufactured by STMicroelectronics and based on the ARM Cortex M core.

The STM32 family is divided into different lines of microcontrollers (L0-1-4, F0-1-2…) depending on their features and the use they are designed for:

stm32-16

These microcontroller are widely used in the industrial world… for example both Pebble watches and Fitbit bracelets are based on STM32 MCUs.

If you’re interested in quadricopters or drones, you probably have heard or used F1, F3, F4 flight control boards. The board name (“F…”) indicated indeed the STM32 microcontroller they are based on.

Thanks to the work made by the stm32duino community and to the support of ST itself, starting from the past June STM32 microcontrollers can be easily used with the Arduino IDE and it’s also possible to take advantage of most of the libraries available for Arduino.

I decided to buy a minimal development board based on the STM32F103C8T6 microcontroller (these boards are sometimes known as blue pills); let’s see how to use it with Arduino.

Bootloader

Many boards are sold unprogrammed: the first thing to do is therefore to program a bootloader, that is a small program which will allow to upload the “real” program via USB port.

To flash the bootloader, you need an USB -> serial adapter, connected to your dev board as it follows:

  • RX -> A9
  • TX -> A10
  • VCC -> 5V
  • GND -> GND

stm32-18 stm32-19

You have also to enable the programming mode, moving the first jumper (labeled BOOT0) to position 1:

stm32-17

Now you need a software to flash the file with the bootloader into the chip. If you’re under Windows, you can download the Flash Loader from ST’s official website: after having registered (for free) you’ll receive the download link via email.

The bootloader is developed and maintained by Roger Clark and it’s available in his Github repository. For STMF1 boards there are several binary files, depending on the PIN the onboard led is connected to. My board uses P13, so I downloaded the file generic_boot20_pb13.bin.

Run the Demonstrator GUI program and select the serial port your adapter is connected to:

stm32-03

If all the connections are ok, the software should be able to detect the microcontroller:

stm32-04

Now you can select the specific MCU your board uses:

stm32-05

then select the file with the bootloader and program it. To be sure, you can ask the software to perform also a global erase of the memory:

stm32-06

The program will flash the chip and, when complete, will confirm the operation with a message:

stm32-08

If you now move back the BOOT0 jumper to the original position, you should see the led blink: this means that the bootloader is running and can’t find a program to execute… now it’s time to configure the IDE.

Arduino IDE

Open your IDE and select File – Preferences. Type the following address in the Additional Boards Manager URLs field:

https://github.com/stm32duino/BoardManagerFiles/raw/master/STM32/package_stm_index.json

stm32-00

Now open the Boards Manager:

stm32-01

Search STM32F1 and install the corresponding Cores package:

stm32-02

You’re almost ready to compile and run your first program…

Drivers

Connect the dev board to your PC using an USB cable and verify how it is identified.

It may happen that Windows cannot correctly identify the board and displays it as Maple 003, is this case you have to install the correct drivers:

stm32-09

Download the following files from Clark’s repository:

  • install_STM_COM_drivers.bat
  • install_drivers.bat
  • wdi-simple.exe

Run the two .bat:

stm32-10

now Windows should identify the board correctly:

stm32-11

Sometimes Windows cannot see the additional serial port of the board. To solve the problem, you can try to program on the board a simple sketch that uses the Serial object. Open for example the blink sketch and change it as it follows:

stm32-12

Program the sketch choosing BluePill F103C8 as board and STM32duino bootloader as upload method.

Once the sketch is programmed and executed, Windows should detect and install the new COM port:

stm32-14

You’re done, now the board is fully integrated with the Arduino IDE:

stm32-15

ESP32 (20) – Webserver

One of the most popular projects among the ones included in my tutorial about the enc28j60 chip is fore sure WebRelay. This project allows to control an output pin of Arduino using a simple web page, designed to be accessed also using your smartphone. Today I’m going to show you how to implement a similar project with the esp32 chip; it’s also the opportunity to teach how to write a TCP server, especially a web server.

Netconn API

As you know, the esp-idf framework uses the lwip library to manage network communication. This library offers different abstraction levels: the programmer can decide to work on the raw packets or to leverage pre-build components.

To develop my TCP server, I decided to use one of those pre-build components, the Netconn API.

Using Netconn API it’s very easy to implement a server and the required steps are described below:

webrelay-01

The netconn_new() method creates a new connection and returns a a pointer to struct netconn which represents the connection:

struct netconn *conn;
conn = netconn_new(NETCONN_TCP);

The parameter defines the connection type… the most common ones are NETCONN_TCP for a connection using the TCP protocol and NETCONN_UDP for a connection using the UDP protocol.

To use the connection in server mode, you have then to associate (bind) it to a specific port… for example a webserver normally listens on port 80 (443 if HTTPS):

netconn_bind(conn, NULL, 80);

The second parameter (NULL above) allows to bind the connection to a specific IP address and may be useful if the device has more than a network interface. If you pass NULL (or the equivalent IP_ADDR_ANY) you ask the library to bind the connection to any available interfaces.

Now you can start listening on that port with:

netconn_listen(conn);

Working with a client connection

With the netconn_accept() method, your program can accept a new incoming connection:

struct netconn *newconn;
netconn_accept(conn, &newconn);

The method returns a pointer to a new struct netconn that represents the connection established with the client. This method is blocking: the program is stopped until a client makes a connection request.

Once the connection is established, you can use the netconn_recv() and netconn_write() methods to receive or send data to the client:

netconn_recv(struct netconn* aNetConn, struct netbuf** aNetBuf );
netconn_write(struct netconn* aNetConn, const void* aData, size_t aSize, u8_t aApiFlags);

To optimize RAM memory usage, the netconn_recv() method handles data using an internal buffer (zero-copy mode). To access the received data you therefore have to:

  • declare a new variable as a pointer to struct netbuf
  • pass the pointer address as the second parameter of the recv method
  • use the netbuf_data() method to obtain a pointer to the data within the netbuffer structure and its length

webrelay-02

struct netbuf *inbuf;
char *buf;
u16_t buflen;
netconn_recv(conn, &inbuf);
netbuf_data(inbuf, (void**)&buf, &buflen);

Likewise, the netconn_write() method accepts, as last parameter, a flag to indicate whether or not to copy the buffer content before sending it. To save memory you can therefore, if you’re sure that the buffer won’t be modified by other theads, use NETCONN_NOCOPY as flag:

netconn_write(conn, outbuff, sizeof(outbuff), NETCONN_NOCOPY);

When the communication with the client is complete, you can close the connection and free the buffer:

netconn_close(conn);
netbuf_delete(inbuf);

HTTP server

What you learned so far can be applied to every TCP server. If you want to communicate with an Internet browser, you have to “talk” the same language, that is the HTTP protocol.

In the example program (available on Github) I implemented a very minimal version of that protocol. When you type an address in your browser (for example www.google.com), the browser connects to Google server and sends a request with the form:

GET <resource>
[...]

The request can have different fields, but the first line always contains the name of the requested resource (html page, image…). In particular if you access the homepage of the website the request will simply be GET /.

The website published by the esp32 chip to control the relay consists of only two pages:

  • off.html, displayed when the relay is off
  • on.html, displayed when the relay is on

Each page contains a label (“Relay is ON|OFF“) and an image. The image itself is a link to the other page and when you click on it, the relay status is also changed:

webrelay-03

The program identifies the resource to be sent analyzing the first line of the request with strstr():

char *first_line = strtok(buf, "\n");
if(strstr(first_line, "GET / ")) [...]
else if(strstr(first_line, "GET /on.html ")) [...]
else if(strstr(first_line, "GET /on.png ")) [...]

An HTTP server responds to the browser by first indicating the result of the request. If ok, the return code is 200:

HTTP/1.1 200 OK

then it returns the media type of the resource and finally it sends the resource file. In this example, the possible media types are:

  • text/html for HTML pages
  • image/png for images

Static content

A webserver it normally stores the resouces for the website it publishes on an external device (memory card, hard drive…). For simple projects, you may consider to include all the content inside your program.

For example in my code HTML pages and HTTP protocol messages are defined as static arrays:

const static char http_html_hdr[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n";
const static char http_png_hdr[] = "HTTP/1.1 200 OK\nContent-type: image/png\n\n";
const static char http_off_hml[] = "";

To be able to include also the images, I used a feature of the framework (embedding binary data). You can indeed specify in the component.mk file the binary resources to be included:

webrelay-04

In your program you can access the content of the embedded files using the following pointers:

extern const uint8_t on_png_start[] asm("_binary_on_png_start");
extern const uint8_t on_png_end[]   asm("_binary_on_png_end");
extern const uint8_t off_png_start[] asm("_binary_off_png_start");
extern const uint8_t off_png_end[]   asm("_binary_off_png_end");

The syntax is _binary_filename_start|end, replace “.” with “_” in the filename. Having pointers to both the start and the end of the resource content, it’s easy to send it with the netconn_write() method:

netconn_write(conn, on_png_start, on_png_end - on_png_start, NETCONN_NOCOPY);

Demo

CH340G board

On this site I’ve already posted about the CH340G chip, manufactured by a chinese company and often adopted as a cheap solution when a USB -> serial converter is needed.

As the chip package (SOP16) is quite easy to solder, I ordered some ICs from AliExpress and I designed a minimal demoboard, based on the reference schematics:

ch340g-00

You can download Eagle files (both schematics and board) in my Github repository.

I used Seeedstudio’s FusionPCB service to manufacture the PCBs (if you want to learn how to use it for your PCBs you can follow my tutorial) and hare are some photos of the first prototype:

ch340g-01

ch340g-02

To verify if it works, you can connect the TX and RX pins with a jumper;  if you open a serial terminal, you should see each character you type:

ch340g-03

In the end, here’s the link to the WCH official website where you can download the drivers.

AiR – Let’s build the robot

Today we start building the chassis kit from Banggood that will become the robotic platform we’ll play with in the next posts…

But first, remove the protective paper from all the acrylic parts:

air-10 air-11

Let’s mount the motor on the chassis. You need the motor, two screws with their nuts, two mounting plates and the plastic wheel for the encoder. Insert the wheel on the motor shaft and place the two plates in the corresponding chassis holes:

air-12 air-13

For convenience, you can solder two wires (red and black) to the motor before mounting it on the chassis. Then secure the motor to the plates using screws and nuts. Make sure that the encoder wheel is inside:

air-14 air-15

With the help of a screwdriver, tighten the two screws so that the motor is securely fastened to the base:

air-16 air-17

Do the same for the second motor and make sure that the wheel can freely move and does not touch the chassis:

air-18 air-19

Complete the assembly inserting the two wheels in the motor shafts:

air-20 air-21

Now the caster, that is the front wheel. You can mount it on the chassis using 4 screws and nuts and the holes already present:

air-22 air-23

After having completed the assembly, you will notice that the caster is lower than the rear wheels; the robot is tilted forward. You can fix it by adding the 4 spacers between the caster and the base::

air-24 air-25

Here’s the final result:

air-26

ESP32 (19) – NVS

In today’s tutorial you’ll learn how to permanently store information, so that they are preserved even if the esp32 chip is reset or power is removed.

NVS

NVS (Non Volatile Storage) is a library included in the esp-idf framework that allows to store information (in the form of key/value) in the flash memory, the content of which is not erased when you reset the chip or remove the power.

If you remember, in a previous post I explained how the external flash memory is organized. The main purpose of that memory is for sure to store the program that will be executed by the esp32 chip. However, it’s possible to divide the flash memory in partitions: the framework offers some ready-to-use partition tables but you’re free to define custom ones.

If you use the default partition table (“Single factory app, no OTA”), you may notice that it contains a partition of type data and subtype nvs:

nvs-01

The default size for that partition is 24Kbyte.

Thanks to the NVS library, you can store custom data into that partition. Information is organized in key/value pairs; a label (= key) with a maximum length of 15 characters is assigned to each value:

nvs-02

You can store different types of data: from numeric values to text strings and byte sequences (blobbinary large object). You’ll learn that the library provides specific methods based on the type of data you want to store or retrieve.

To be able to use the library in your code, first include the following headers:

#include "esp_partition.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "nvs.h"

Initialization

The first step to use the nvs partition is to initialize the library, with the command:

esp_err_t err = nvs_flash_init();

The command returns ESP_OK if successful; on the contrary it returns one of the error codes defined in the nvs.h file (see below). In particular, if the partition was resized or changed, you may get the ESP_ERR_NVS_NO_FREE_PAGES error code. This error can be resolved “formatting” the partition.

First you have to identify the nvs partition in the flash memory:

const esp_partition_t* nvs_partition = 
 esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);      
if(!nvs_partition) printf("FATAL ERROR: No NVS partition found\n");

then you can format it with the esp_partition_erase_range() command:

err = (esp_partition_erase_range(nvs_partition, 0, nvs_partition->size));
if(err != ESP_OK) printf("FATAL ERROR: Unable to erase the partition\n");

The last step is to open the partition, you can do it in READONLY o READWRITE mode:

nvs_handle my_handle;
err = nvs_open("storage", NVS_READWRITE, &my_handle);
if (err != ESP_OK) printf("FATAL ERROR: Unable to open NVS\n");

Set – Get

Once the partition is open, you can store (set) new values or retrieve (get) existing ones:

There are different methods, based on the data type (i8 stands for integer with 8bits, u8 per unsigned integer 8bit…):

  • nvs_set_i8(), nvs_set_u8(), nvs_set_i16(), nvs_set_u16()…
  • nvs_set_str()
  • nvs_set_blob()

All the set methods require as parameters the partition handler, the key name and the value to be stored:

esp_err_t nvs_set_i8(nvs_handle handle, const char* key, int8_t value);

The only exception is the nvs_set_blob() method which requires as additional parameter the length of the byte array to be stored:

esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, size_t length);

After having called a set method, you have to commit the change with the method:

esp_err_t nvs_commit(nvs_handle handle);

To get data from the flash the library offers similar methods:

  • nvs_get_i8(), nvs_get_u8(), nvs_get_i16(), nvs_get_u16()…
  • nvs_get_str()
  • nvs_get_blob()

The parameters for those methods are the partition handler, the key name and a pointer to the variable that has to be updated with the value retrieved:

esp_err_t nvs_get_i8(nvs_handle handle, const char* key, int8_t* out_value);

Because of you cannot know a priori the size for string or blob values, you can do a “trick”: first call the nvs_get_string() method passing NULL as pointer to get the length of the value, then allocate a variable with the correct size and finally call again the method passing that variable to get the value:

size_t string_size;
esp_err_t err = nvs_get_str(my_handle, parameter, NULL, &string_size);
char* value = malloc(string_size);
err = nvs_get_str(my_handle, parameter, value, &string_size);

Erase

The library also include a couple of methods to erase the partition content:

esp_err_t nvs_erase_key(nvs_handle handle, const char* key);
esp_err_t nvs_erase_all(nvs_handle handle);

The first method is more selective and allows you to delete a single key while the second deletes the entire memory content.

Both the methods must be followed by the nvs_commit() command as explained before.

Error handling

All the methods in the nvs library return an error code (esp_err_t variable).

The possible errors are listed in the nvs.h file:

nvs-03

For example, if you’re calling a get method with a key that is not present in the flash, you’ll get the ESP_ERR_NOT_FOUND error. You should handle the different errors in your program:

esp_err_t err = nvs_get_i32(my_handle, parameter, &value);
if(err == ESP_ERR_NVS_NOT_FOUND) printf("\nKey %s not found\n", parameter);

Demo

For this tutorial I wrote a program that allows, using a simple command line, to store and retrieve information from the nvs partition. The source code is available on Github, here’s a video that shows how it works: