MaixPy #3: Maixduino Analog-To-Digital Converter (ADC)

An ADC converts a continuous-time and continuous-amplitude analog signal to a discrete-time and discrete-amplitude digital signal. The conversion involves quantization of the input, so it necessarily introduces a small amount of error or noise. Furthermore, instead of continuously performing the conversion, an ADC does the conversion periodically, sampling the input, limiting the allowable bandwidth of the input signal. [1]

In this tutorial, I am going to talk about the ADC function of the Maixduino and do one experiment: using a LDR (light dependent resistor) to light up an LED based on the brightness conditions.

What Will We Need

 

Maixduino ADC

Introduction

The AI module does not have the ability to process analog signals, so we have to implement communication between the Sipeed M1 AI module and the ESP32 that is available on the board. There are 6 pins available for ADC operations on the board, and the communication is done via SPI. This interface is also used for WIFI functions.

Here’s a link to the bigger schematic here (Sipped official site).

Reading an analog value with the ESP32 from the Maixduino board means you can measure varying voltage levels between 0 V and 3.3 V. The voltage measured is then assigned a value between 0 and 4095, in which 0 V corresponds to 0, and 3.3 V corresponds to 4095. [4]

The pins have a 12 bit resolution in normal mode (without attenuation). The 4095 value comes from 2^12=4096, but it starts at zero, so we have the 0-4095 range.

In theory, the range of the ADC pins should be linear, but it isn’t. It is non-linear. That is what we are going to see next.

ESP32 ADC Problems

I have to say that the Esp32 ADC is not perfect. There are a lot of forums discussing these details, and almost everyone has negative things to say about them. The basic fact is that there are two aspects to the problem: the noise and the calibration. These are things that should be minimized when the boards are being made and these are one of them. It makes no sense for me to have to connect a bypass capacitor and calibrate the pins using a reference voltage. All of this can be seen on the official Espressif site. Next, I will state the main problems:

  • Noise

  • ADC Calibration: This is a very pretty official calibration graph from Espressif.

  • ADC behaviour in practice: As we can see, it’s not so pretty. The ESP32 can’t distinguish 3.3 V from 3.2 V. You’ll get the same value for both voltages: 4095. And in the lower range, the same for 0 V and 0.1 V, you’ll get the same value: 0.

Source: https://github.com/espressif/arduino-esp32/issues/92

Maixduino ADC Pins

Of the 6 available pins, some of them have special characteristics. The schematic of the Maixduino Esp32 doesn’t specify if all the functions of the original ESP32 board are available. My research led me to a site where people like Rui Santos (randomnerdtutorials.com) have studied the esp32 extensivly. Assuming that the ADC pins of the Maixduino have all the functions, we can say that:

  • IO36 (AI Module) <–>A5 (Esp32) = Sensor_VP (Special pins)   [Input Only]
  • IO39 (AI Module) <–>A4 (Esp32) = Sensor_VN (Special pins)   [Input Only]
  • IO34 (AI Module)< –>A3 (Esp32) = Normal pin   [Input Only]
  • IO35 (AI Module) <–>A2 (Esp32) = Normal pin   [Input Only]
  • IO32 (AI Module) <–>A1 (Esp32) = Capacitive touch GPIOs [Input/Output]
  • IO33 (AI Module) <–>A0 (Esp32) = Capacitive touch GPIOs [Input/Output]

Maixduino original schematic link (Github)

In depth, the caracteristics of the pins in here (Technical Reference Manual for SP32)

SPI Comunication

The Serial Peripheral Interface (SPI) is a synchronous serial communication interface specification used for short-distance communication, primarily in embedded systems. The interface was developed by Motorola in the mid-1980s and has become a de facto standard. Typical applications include Secure Digital cards and liquid crystal displays.[2]

The SPI bus specifies four logic signals:

  • SCLK: Serial Clock (output from master)
  • MOSI: Master Out Slave In (data output from master)
  • MISO: Master In Slave Out (data output from slave)
  • CS /SS: Chip/Slave Select (often active low, output from master to indicate that data is being sent)

In the communication between the AI Module (Sipeed M1) and the ESP32, we have the usual four pins and two extra.

  • SCLK: IO27 (AI Module)<–>Esp32 GPIO18
  • MOSI: IO28 (AI Module)<–>Esp32 GPIO14
  • MISO: IO26 (AI Module)<–>Esp32 GPIO23
  • CS: IO25 (AI Module)<–>Esp32 GPIO5
  • ESP32_EN: IO8 (AI Module)<–>Esp32 CHP_PU (PIN3)
  • ESP32_READY: IO9 (AI Module)<–>Esp32 GPIO25

Maixduino ADC Code (Original)

Import the modules:

import time, network
from Maix import GPIO
from fpioa_manager import fm

Make the pin connections for the SPI communication, creating a class wifi:

class wifi():
    # IO map for ESP32 on Maixduino
    fm.register(25,fm.fpioa.GPIOHS10)#cs
    fm.register(8,fm.fpioa.GPIOHS11)#rst
    fm.register(9,fm.fpioa.GPIOHS12)#rdy
    print("Use Hareware SPI for other maixduino")
    fm.register(28,fm.fpioa.SPI1_D0, force=True)#mosi
    fm.register(26,fm.fpioa.SPI1_D1, force=True)#miso
    fm.register(27,fm.fpioa.SPI1_SCLK, force=True)#sclk
    nic = network.ESP32_SPI(cs=fm.fpioa.GPIOHS10, rst=fm.fpioa.GPIOHS11, rdy=fm.fpioa.GPIOHS12, spi=1)

print("ESP32_SPI firmware version:", wifi.nic.version())

Create a loop to display all the pin values and the ADC on the serial terminal:

# get ADC0 ADC1 ADC2
adc = wifi.nic.adc((0,1,2))
print(adc)

while True:
    try:
        # get ADC0~5
        adc = wifi.nic.adc()
    except Exception as e:
        print(e)
        continue
    for v in adc:
        print("%04d" %(v), end=" ")
    print(': adc')

Maixduino ADC Code (My Code)

I almost broke my brain trying to figure out how to access just one pin of the ADC. To do that, we have to add a comma in the code ((0,)). This is my code to access the A0 pin of the Maixduino:

I made a little change, converted the raw values from the range 0-4095 to the voltage range of 0-3.3 Volt witch i think is better for visualization in the serial terminal. We just have to divide the voltage (3.3 V) for 4096 and it will give us 0.000806.

import time, network, utime
from Maix import GPIO
from fpioa_manager import fm

class wifi():
    # IO map for ESP32 on Maixduino
    fm.register(8,fm.fpioa.GPIOHS11)# ESP32_EN
    fm.register(9,fm.fpioa.GPIOHS12)# ESP32_READY
    fm.register(28,fm.fpioa.SPI1_D0, force=True)# MOSI
    fm.register(26,fm.fpioa.SPI1_D1, force=True)# MISO
    fm.register(27,fm.fpioa.SPI1_SCLK, force=True)# SCLK
    fm.register(25,fm.fpioa.GPIOHS10)# CS
    nic = network.ESP32_SPI(cs=fm.fpioa.GPIOHS10, rst=fm.fpioa.GPIOHS11, rdy=fm.fpioa.GPIOHS12, spi=1)

while True:
    adc = wifi.nic.adc((0,))  #Accessing the A0 pin of the maixduino
    adc0=adc[0]*0.000806   #Convertion to volt
    #print(adc)
    print(adc0)  #Print the voltage values on the serial terminal
    utime.sleep_ms(150)

 

A Tuple in Python (Extra):

  • Tuples are used to store multiple items in a single variable.
  • Tuple is one of 4 built-in data types in Python used to store collections of data, the other 3 are List, Set, and Dictionary, all with different qualities and usage.
  • A tuple is a collection which is ordered and unchangeable.
  • Tuples are written with round brackets. [5]

To access just one item, we have to use the [,]:

thistuple = ("apple",) print(type(thistuple))
 #NOT a tuple
thistuple = ("apple") print(type(thistuple))

 

Maixduino ADC Experiment With LDR and LED

Introduction

For this experience, we are going to use an LDR (Light-Dependent Resistor) and a LED. The objective is to light up the LED when we cover the LDR.

An L.D.R. is an electrical component whose resistance varies according to the luminosity of the place where it is inserted. When the component is covered, its resistance increases, staying at that of the MegaOhm (MΩ) range, and when the opposite level occurs, that is, when the brightness of the place is high, its resistance decreases.

Maixduino LDR Experiment

Calculations

I am going to use the raw values of the ESP32 ADC without any modification in terms of noise or calibration procedures.

The calculations are straight-forward because I’m lazy. In the LDR, we use the principle of voltage divider, and one of the characteristics is that if we have two resistences of the same value, the voltage output is half the value. That is what I am going to use here. The steps are as follows:

  1. Measure with a multimeter the resistance of the LDR in normal brightness conditions. For me, it gave approximately 10KΩ.
  2. So, the other resistance will also be 10KΩ.
  3. In the end, for Vin = 3.3 V we should have a Vout = 1.65 V approximately.

Calculations:

So in this connection, as LDR resistance increases (meaning light decreases, the voltage increases, and vice versa).

Circuit

Final Code

import time, network, utime
from Maix import GPIO,
from fpioa_manager import fm

#IO map of the PIN2
PIN2 = 21
fm.register(PIN2, fm.fpioa.GPIO0)
pin2=GPIO(GPIO.GPIO0, GPIO.OUT)

class wifi():
    # IO map for ESP32 on Maixduino
    fm.register(8,fm.fpioa.GPIOHS11)#en
    fm.register(9,fm.fpioa.GPIOHS12)#rdy
    fm.register(28,fm.fpioa.SPI1_D0, force=True)#mosi
    fm.register(26,fm.fpioa.SPI1_D1, force=True)#miso
    fm.register(27,fm.fpioa.SPI1_SCLK, force=True)#sclk
    fm.register(25,fm.fpioa.GPIOHS10)#cs
    nic = network.ESP32_SPI(cs=fm.fpioa.GPIOHS10, rst=fm.fpioa.GPIOHS11, rdy=fm.fpioa.GPIOHS12, spi=1)

while True :
    adc = wifi.nic.adc((0,))
    adc0=adc[0]*0.000806 #convertion to volt
    #print(adc)    values in the 0-4095 range
    print(adc0)    #values in the 0-3.3V range
    utime.sleep_ms(50)
    #if adc[0] > 3000:   if statement in the 0-4095 range
    if adc0 > 2.45:      #if statement in the 0-3.3V range
        pin2.value(1)
    else:
        pin2.value(0)

 

Maixduino Experiment Materials

References

[1] https://en.wikipedia.org/wiki/Analog-to-digital_converter

[2] https://en.wikipedia.org/wiki/Serial_Peripheral_Interface

[3] https://wiki.sipeed.com/soft/maixpy/en/

[4] https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/

[5] https://www.w3schools.com/python/python_tuples.asp

[6] https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html