MusiCubes project

MusicCube

Michael Teeuw published a new build:

Now that the MusiCubes tray is assembled and the RFID-sensor and LEDs are working as expected, It’s time to add the last feature of the original concept: invisible capacitive touch sensors to control the volume of the music.

See the full post on Xonay Labs blog. Be sure to see Part 1 and 2.

Check out the video after the break.

Multisensor LoRa device

p-tlmv2-600

Mare published a new build:

The described device is nearly matchbox-sized board (50 x 24 mm) packed with sensors. Auxilary board is 10x50mm with additional sensors. The module is developed around the Murata ABZ LoRa module, which integrates STM32L072 and samtech SX1276 in tiny 12.5 x 11.6 x 1.76 mm package.

More details at Mare & Gal Electronics.

Reading a VGA monitor’s configuration data with I2C and a PocketBeagle

pocketbeagle-vga-600

Ken Shirriff wrote an article showing how to read the monitor’s config data using the I2C protocol and a board with an I2C port:

Have you ever wondered how your computer knows all the characteristics of your monitor— the supported resolutions, the model, and even the serial number? Most monitors use a system called DDC to communicate this information to the computer.1 This information is transmitted using the I2C communication protocol—a protocol also popular for connecting hobbyist devices. In this post, I look inside a VGA monitor cable, use a tiny PocketBeagle (a single-board computer in the BeagleBone family) to read the I2C data from an LCD monitor, and then analyze this data.

More details at Ken Shirriff’s blog.

How to configure I2C sensors with Arduino

sensorpan-600

Edward Mallon writes:

I’ve spent the last year in the ‘uncanny valley’ of the Arduino. That’s the point where you understand the tutorials at Arduino.cc, but still don’t get much from the material on gitHub because trained programmers would never stoop to using the wire.h library when they could just roll their own in native C++ using the avr-g++ compiler.  The problem with establishing sensor communication at the level of the TWI peripheral inside the AVR is that there are so many fiddling details to keep track of that it quickly overruns the 7±2 things this average human can hold in his head at one time: Computers aren’t the only things that crash after a buffer overflow!  So this post is meant to be a chunking exercise for beginner-intermediate level people like myself who want to get a new sensor working using the standard IDE.  I’ve tried to distill it all down to things that I run into frequently, but there’s still a lot of material here:  So pour yourself a cuppa before diving in…

More details at Arduino based underwater sensors blog.

ESP32 (25) – Oled display with U8G2

If you read my post ESP32, Wemos or not to Wemos you know that I brought a development board, that happened to be a D-duino-32 clone, with an ESP-WROOM-32 module and a 0.96″ oled display.

This display, available also as a standalone module on several websites (for example on Banggood), has the following features:

  • size: 0.96 inches
  • resolution: 128×64 pixels
  • controller: SSD1306 with I2C interface
  • power supply: 3.3V – 6V

u8g2-00a u8g2-00b

It’s very easy to interface it with the esp32 chip thanks to the work of olikraus and Neil Kolban. The first wrote the u8g2 library, while the second implemented the specific functions of the u8g2’s hardware abstraction layer (HAL) for the esp32.

u8g2, installation

u8g2 is a fantastic library for monochrome displays: it supports several types of displays and controllers, it’s easy to port to new platforms and offers many methods to draw geometric figures, display images and write strings using different fonts.

Let’s learn how to use it in your project. First download the archive with the content of the library’s Github repository:

u8g2-01

If it doesn’t exist, create the components folder in the main folder of your project. Unzip the archive into that folder and rename the subfolder u8g2-master in u8g2:

u8g2-02

In the u8g2 folder create a file named component.mk with the following content:

u8g2-03

Now download the files u8g2_esp32_hal.c and u8g2_esp32_hal.h from nkolban’s repository:

u8g2-04

You can copy the two files in the main folder of your project:

u8g2-05

u8g2, configuration

To use the u8g2 library in your program, first include the header file:

#include "u8g2_esp32_hal.h"

The HAL configuration is stored in the u8g2_esp32_hal_t struct:

typedef struct {
  gpio_num_t clk;
  gpio_num_t mosi;
  gpio_num_t sda;
  gpio_num_t scl;
  gpio_num_t cs;
  gpio_num_t reset;
  gpio_num_t dc;
} u8g2_esp32_hal_t;

The library supports both I2C and SPI displays: this is the reason why you see in the struct fields related to the two buses.

You can define and initialize the struct with default values with:

u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;

The oled display of the D-duino-32 board has an I2C interface connected to esp32’s pin 4 (SCL) and 5 (SDA):

u8g2_esp32_hal.sda = 5;
u8g2_esp32_hal.scl = 4;

When the configuration of the struct is complete, you can use the u8g2_esp32_hal_init() method:

u8g2_esp32_hal_init(u8g2_esp32_hal);

Now move to the configuration of the u8g2 library. Define an u8g2_t variable:

u8g2_t u8g2;

Based on the display you’re using, you have to choose the correct setup function. The parameters for the function are:

  • the pointer to the u8g2_t variable you defined before
  • a constant value that specifies the display rotation
  • two HAL functions to send data on the bus and to delay

The constants for the display roation are:

u8g2-06

and the two HAL functions developed by Kolban are:

  • u8g2_esp32_msg_i2c_cb
  • u8g2_esp32_msg_i2c_and_delay_cb

Here I’m using the setup function for the ssd1306 “noname” controller with the _f prefix that means full framebuffer:

u8g2_Setup_ssd1306_128x64_noname_f(
  &u8g2, U8G2_R0,
  u8g2_esp32_msg_i2c_cb,
  u8g2_esp32_msg_i2c_and_delay_cb);

framebuffer

full framebuffer means that all the data to send to the display are stored in the RAM memory of the microcontroller. This makes easier and faster to work with the display but consumes more RAM. The u8g2 library also supports a page buffer mode to save some memory. A comparison between the different modes, with pros and cons, is available on the library’s wiki.

Finally – if you’re using an I2C display – you have to specify its address (here 0x78):

u8x8_SetI2CAddress(&u8g2.u8x8,0x78);

u8g2, use

After the setup, initialize the display with:

u8g2_InitDisplay(&u8g2);

The display is now in power save mode, to “turn it on” you have to disable this mode with:

u8g2_SetPowerSave(&u8g2, 0);

Now you can call the different methods available to display text, images, shapes. If you’re using the full framebuffer mode, first prepare the buffer in memory and then send it to the display.

Prepare an empty buffer with ClearBuffer():

u8g2_ClearBuffer(&u8g2);

Now for example use the SetFont() and DrawStr() methods to write a string into the buffer with a specific font:

u8g2_SetFont(&u8g2, u8g2_font_timR14_tf);
u8g2_DrawStr(&u8g2, 2,17,"Hello World!");

and finally display the buffer’s content on the display with:

u8g2_SendBuffer(&u8g2);

u8g2-00c

Demo

In the following video I quickly explain how to install the library, how to prepare an image to be displayed and in the end you can see the execution, on my D-duino-32, of the example program available on Github.

ESP32 (24) – I2C a pratical example with HTU21D sensor

In my previous tutorial I explained how to use the I2C driver included in the esp-idf framework to communicate with I2C devices. Today I’ll show you a pratical example: the use of a temperature/humidity sensor.

The sensor

For this tutorial I chose the HTU21D sensor by Te Connectivity. This sensor offers a good level of accuracy and it’s available, already soldered on a breakboard, for few euros:

htu21d-00a htu21d-00b

htu21d-00c

Sparkfun was the first manufacturer to sell a board based on this sensor… even if now this board is retired, other websites (for example Banggood) sell similar products.

To connect the sensor to your esp32 development board, first you have to choose which pins will be used for SDA and SCL signals. Besides these, you have also to connect VDD (3.3V) and GND:

htu21d-00d htu21d-00e

htu21d-00f htu21d-00g

Datasheet and commands

After having completed the physical connection between the devboard and the sensor, you have to understand how to write a program that communicates with it. The first thing to do is certainly to read the datasheet of the sensor (here in PDF format). Do not be scared if you realize that it contains 21 pages of technical details, in the following paragraphs I’ll explain what are the information you absolutely have to know!

On page 10, it starts the chapter about the communication protocol (COMMUNICATION PROTOCOL WITH HTU21D(F) SENSOR). Immediately you read that the sensor offers an I2C slave interface with address 0x40; just below you can also find the table with the supported commands:

htu21d-001

The sensor offers two different modes when measuring temperature and humidity:

  • hold master
  • no hold master

In the first mode, the sensor blocks the clock signal (SCK) during measurement process: this means that the master can send the read command only when the measure is completed. In the second mode instead the master can operate on the bus (for example send commands to other devices) during the measurement process.

In no hold mode the master, after having sent the “trigger maesurement” command, must wait for the sensor to complete the measurement before reading the value. You can verify if the measuring process is complete sending to the sensor a read command and wait for the ACK: if you receive it, it means that the value is ready.

The sensor returns a raw value of 16bits (= 2 bytes). In addition to the value, it also returns a checksum byte; this byte allows the master to verify that no errors occurred when data was transmitted on the bus.

In the previous post you’ve already learned how to query a slave device; therefore the instructions to read the temperature value from a HTU21D sensor in no hold mode are:

// constants
#define HTU21D_ADDR			0x40
#define TRIGGER_TEMP_MEASURE_NOHOLD  	0xF3
 
// send the command
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, TRIGGER_TEMP_MEASURE_NOHOLD, true);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
 
// wait for the sensor (50ms)
vTaskDelay(50 / portTICK_RATE_MS);
 
// receive the answer
uint8_t msb, lsb, crc;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (HTU21D_ADDR << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &msb, 0x00);
i2c_master_read_byte(cmd, &lsb, 0x00);
i2c_master_read_byte(cmd, &crc, 0x01);
i2c_master_stop(cmd);
ret = i2c_master_cmd_begin(_port, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);

The code above waits 50ms before fetching the data; from the datasheet you can indeed read that the maximum measuring time is exactly 50ms for the temperature value at the maximum resolution:

htu21d-002

To convert the raw value into the “real” temperature (in °C) or humidity (in %) you can use the formulas in the datasheet:

uint16_t raw_value = ((uint16_t) msb << 8) | (uint16_t) lsb;
float temperature = (raw_value * 175.72 / 65536.0) - 46.85;
float humidity = (raw_value * 125.0 / 65536.0) - 6.0;

Resolution

The sensor offers 4 different resolution combinations for temperature and humidity:

  • humidity 12bit, temperature 14bit
  • humidity 8bit, temperature 12bit
  • humidity 10bit, temperature 13bit
  • humidity 11bit, temperature 11bit

You can select the resolution changing the value of a configuration register with the command write user register. Specifically, the registry bits to be modified are bit 0 and bit 7:

htu21d-003

CRC

The algorithm for verifying the CRC value is explained in the datasheet.

The polynomial used is x^8 + x^5 + x^4 + 1, in binary:

htu21d-004

First add to the polynomial as many zeroes as the bit length of the CRC:

htu21d-005

The resulting value, in hexadecimal, is 0x98800.

Add, at the right of the value you want to verify, the CRC value received from the sensor as third byte:

uint32_t row = (uint32_t)value << 8;
row |= crc;

Then check the input bit above the leftmost divisor bit: if the bit is 1, the input value is XORed to the divisor. Finally, the divisor is shifted one bit right:

for (int i = 0 ; i < 16 ; i++) {
 if (row & (uint32_t)1 << (23 - i)) row ^= divisor;  divisor >>= 1;
}

If, at the end of the process, the row value equals zero, the CRC is verified.

Component

I developed a component for the esp-idf framework that implements what explained in this post. The component is available on Github and the documentation about its use is published in a dedicated page.

Here’s a video about it:

ESP32 (23) – I2C basic

In today’s tutorial I’ll explain you how to interface the esp32 chip to external devices (sensors, displays…) using a very widespread bus: the I2C bus.

I2C

I2C (pronounced i-squared-c) is a serial communication bus – invented by Philips in 1982 – that allows two or more devices to communicate. The devices connected to the bus can act as masters (devices that “control” the bus) or slaves. Usually, a bus has only one master and one or more slaves, but the standard allows also more complex topologies. Each slave device must have an unique address.

Two different transmission speeds are available: standard (100Kbit/s) and fast (400Kbit/s).

The I2C bus requires only two communication lines that connect the devices:

  • SDA, Serial DAta – where data transit
  • SCLSerial CLock – where the master generates the clock signal

The two lines must be connected to a reference voltage (Vdd) via pull-up resistors:

i2c-01

If you want to learn more about the I2C bus, I strongly suggest the website www.i2c-bus.org.

esp32

The esp32 chip offers two I2C controllers, that can act both as master and slave and communicate in standard and fast speed.

The I2C controllers are internally connected to the IO_MUX matrix, so – as I explained in a previous post – you can assign different pins (with some exceptions) to them in your program.

the esp-idf framework includes a driver which makes it easy to work with those controllers, without worring about how the different registers have to be configured. To use the driver in your program, you only need to include its header file:

#include "driver/i2c.h"

First you have to configure the controller (port) you want to use. The configuration is performed with the i2c_param_config() method, which accepts as parameter an i2c_config_t struct that contains the settings for the controller:

esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t* i2c_conf);

The two available ports are listed in an enum variable in the i2c.h file:

i2c-02

The i2c_config_t struct is also defined in the same header file:

typedef struct{
  i2c_mode_t mode
  gpio_num_t sda_io_num;
  gpio_pullup_t sda_pullup_en;
  gpio_num_t scl_io_num;
  gpio_pullup_t scl_pullup_en;
  union {
    struct {
      uint32_t clk_speed;
    } master;
    struct {
      uint8_t addr_10bit_en;
      uint16_t slave_addr;
    } slave;
  };
} i2c_config_t;

Here’s the meaning of the different parameters:

  • mode is the working mode of the controller (it can be I2C_MODE_SLAVE or I2C_MODE_MASTER)
  • sda_io_num and scl_io_num configure the pins connected to SDA and SCL signals
  • sda_pullup_en and scl_pullup_en allow to enable or disable the internal pull-up resistors (possible values: GPIO_PULLUP_DISABLE or GPIO_PULLUP_ENABLE)
  • master.clk_speed is the speed in hertz of the clock if in master mode (100000 for standard and 400000 for fast speed)
  • slave.slave_addr is the device address if in slave mode
  • slave.addr_10bit_en tells the controller to use an extended (10 instead of 7bits) address (if value is 0, the 10bit address mode is disabled)

pin choice

The esp32 chip allows, using the IO_MUX matrix, to assign “almost” all the pins to the two I2C controllers. The I2C driver is able to verify if the pins specified in the configuration can be used; if not, it stops the program with an error.

For example, if you want to configure the first I2C controller in master mode, with standard speed and use pins 18 and 19 without enabling the internal pull-up resistors, this is your code:

i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = 18;
conf.scl_io_num = 19;
conf.sda_pullup_en = GPIO_PULLUP_DISABLE;
conf.scl_pullup_en = GPIO_PULLUP_DISABLE;
conf.master.clk_speed = 100000;
i2c_param_config(I2C_NUM_0, &conf);

Once configured the controller, you can install the driver with i2c_driver_install():

esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, 
 size_t slv_rx_buf_len, size_t slv_tx_buf_len, int intr_alloc_flags)

Besides the controller number and its mode, you have to specify the size for the transmitting and receiving buffers (only in slave mode) and additional flags used to allocate the interrupt (usually this parameter is 0).

For the controller configured in the example above, the driver is installed as it follows:

i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0)

Master

Let’s now learn how to use the controller in master mode, to send commands and receive data from a slave.

First, you have to create a command link, that is a “logical” element that will contain the list of actions to be performed to interact with the slave device. The command link is created by the i2c_cmd_link_create() method which returns a pointer to the command link handler:

i2c_cmd_handle_t cmd = i2c_cmd_link_create();

You can now use some methods to add actions to the command link:

  • i2c_master_start and i2c_master_stop
  • i2c_master_write and i2c_master_write_byte
  • i2c_master_read and i2c_master_read_byte

To understand their meaning, first you have to learn how a master device communicate with slaves. First, the master sends on the bus the START signal, followed by the address (7bits) of the slave device and a bit that specifies the requested operation (0 for WRITE, 1 for READ). After every byte sent by the master (including the one that contains the address and the operation bit) the slave answers with an ACK bit:

i2c-04

In your program this translates into:

i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);

The i2c_master_start() method adds to the cmd handler the action to send the START signal, while the i2c_master_write_byte() method sends one byte on the bus. This byte is composed of 7bits for the slave address (slave_addr) left-shifted to 1 bit (<< 1) and of the bit 0 (= IC2_MASTER_WRITE). If you want to perform a READ, you can instead use the I2C_MASTER_READ constant.

The last parameter, set to true, tells the master to wait for the slave to send the ACK bit.

If the requested operation is write, now the master can send n bytes to the slave. At the end, the master sends the STOP signal:

i2c_master_write(cmd, data_array, data_size, true);
i2c_master_stop(cmd);

I used the i2c_master_write method that allows to send an array (uint8_t*) of data. The data_size parameter is the size of the array. Alternatively, I could have used more times the i2c_master_write_byte used previously.

To “execute” the command link, you have to call the i2c_master_cmd_begin() method:

i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_RATE_MS);

its parameters are the number of the I2C controller, the command link handler and the maximum number of ticks it can wait (this method is indeed blocking; in the example below it waits for a maximum of 1 second).

Finally you can free the resources used by the command link with i2c_cmd_link_delete(cmd).

The read operation is slightly more complex. First you have to send to the slave device the command for the data you want to read. In the following tutorial I’ll show you a real application, for now let’s assume that the slave device is a temperature sensor with address 0x40 and that the command measure the actual temperature corresponds to byte 0xF3.

You’ve already learned how to send the command:

i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (0x40 << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, (0xF3, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);

After having successfully sent the command (and eventually after having waited for the sensor to execute it) you can read the output of the sensor creating a new command link with the sensor address (but this time with the READ mode) and adding one or mor read actions (based on the number of bytes the sensor sends):

uint8_t first_byte, second_byte;
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (0x40 << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(cmd, &first_byte, ACK_VAL);
i2c_master_read_byte(cmd, &second_byte, NACK_VAL);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);

The main difference is that, after having read the last byte, the master sends the NACK signal. With this signal, it tells that it cannot receive more bytes and therefore the slave must stop transmitting.

ACK and NACK constants are defined as follows:

#define ACK_VAL    0x0
#define NACK_VAL   0x1

Demo

At the end of this post, I want to show you a classic example that uses the master mode: an I2C scanner. Goal of the program is to analyze the bus looking for any slave devices and print their address on screen.

It’s a very simple program, up to you to understand its source code in my Github repository. In the following post you’ll learn how to interface with a real sensor to get its data.