---

Showing posts with label pico. Show all posts
Showing posts with label pico. Show all posts

Wednesday, January 24, 2024

Lies, Damn Lies and Analog Inputs (comparing ADCs on ESP32, Pico and Arduino)

After some inconsistent and unreliable results reading an analog input from an ESP32 board, I decided to get all scientific and do some experimenting with an ESP32, a Raspberry Pi Pico and an Arduino Uno R3.

Method

My test setup was a bench power supply providing the reference voltage to be measured by the test board. The output of the bench PSU had a dummy load of a 470Ω resistor and a 100nF capacitor in parallel (the latter largely for superstitions reasons) as the voltage output looks extremely stable on a DMM voltmeter.

This output from the bench power supply was then applied directly to an analog input and GND of the board being tested.

I was particularly interested in three things:

* finding any dead-zones at each end of the analog input voltage range

* measuring the reproducibility of the readings

* linearity through the range

Another time, I'd like to look at the input impedance of the ADC (analog to digital convertor) and the effects of how rapidly you sample. But I'll leave that for another day.

To measure the reproducibility of the readings, each time the test voltage was changed, 100 readings would be taken, and the mean and standard deviation of that set recorded. That way, when it came to plotting the readings from the boards, I could add some error bars.

For the ESP32 and Pico, I used MicroPython and for the Arduino Uno, I used Arduino C. The Arduino readings were scaled up to 16bit unsigned values (max value 65536) to be consistent with the MicroPython version. In all cases, the default ADC settings were used.

ESP32

For this experiment, I used an ESP32 Lite (sometimes also called LOLIN32 Lite). These are a ubiquitous low-cost ESP32 board, with built-in WiFi and Bluetooth.

Analog input maximum voltage 1.0V



Here's the plot

The red error bars show +- 3 standard deviations (SDs) from 100 samples. Nearly all of the sample values would fall within 3 SDs and 60% would fall within 1 SD.

There is a sizeable dead zone until the voltage rises to about 0.05V and a lot of noise around the readings, evidenced by the large error bars. But it retains pretty good linearity once you get past that up to the 1V upper limit.

Raspberry Pi Pico

The Raspberry Pi Pico uses Raspberry Pi's RP2040 chip. It'a another popular, good value board.

It's maximum analog input voltage is the full 3.3V supply range.


Here's the plot for the Pico - 


This is considerably better than the ESP32, with smaller 3 x SD error bars, a small dead zone at the low voltage end and some slight tail-off in linearity at the 3.3V end.

Arduino Uno R3

Despite its age, the Arduino Uno R3 (not the fancy new one) is still my go-to board for any experimentation or early stage project work that doesn't need a specific microcontroller. I'll admit, it's partly out of familiarity and inertia on my part.



And here are the results.


And there we have it. Very little deviation between readings and great linearity across the whole range, right up to 5V. The Uno with it's ancient Atmega328 is streets ahead of the other two boards.

Conclusion

On looking at the documentation in MicroPython and learning that the analog readings for a Pico and ESP32 come at a massive 16 bit precision (a number between 0 and 65536) it's easy think that their analog inputs are much better than the paltry 10 bits of an Arduino (0 to 1023 reading range). But this is to confuse precision with accuracy. It's why pure megapixels is not the best way to judge a camera. So much depends on the lens.

So, if you are trying to get decent accuracy and reproducibility from your analog readings, then you probably want to take a set of readings and average them -- or use an Arduino Uno R3!

Test Programs

ESP32

from machine import ADC, Pin
from time import sleep
from math import sqrt

analog = ADC(12)

p = 0.05
n = 100

while True:
    readings = []
    for i in range(0, n):
        readings.append(analog.read_u16())
        sleep(p)
    total = 0
    for i in range(0, n):
        total += readings[i]
    mean = total / n
    dist_tot = 0
    for i in range(0, n):
        dist = readings[i] - mean
        dist_tot += dist * dist
    
    print(mean, sqrt(dist_tot / n))
    
    input('Press enter to read again')

Pico

from machine import ADC, Pin
from time import sleep
from math import sqrt

analog = ADC(28)

p = 0.05
n = 100


while True:
    readings = []
    for i in range(0, n):
        readings.append(analog.read_u16())
        sleep(p)
    total = 0
    for i in range(0, n):
        total += readings[i]
    mean = total / n
    dist_tot = 0
    for i in range(0, n):
        dist = readings[i] - mean
        dist_tot += dist * dist
    
    print(mean, sqrt(dist_tot / n))
    
    input('Press enter to read again')

Arduino

int p = 50;

const int n = 100;


unsigned int readings[n];


void setup() {

  Serial.begin(9600);

}


void loop() {

  if (Serial.available()) {

    Serial.read();

    Serial.println("measuring");

    for (int i = 0; i < n; i++) {

      readings[i] = analogRead(A0) * 64; // 16 bit not 10

      delay(p);

    }

    float total = 0.0;

    for (int i = 0; i < n; i++) {

      total += float(readings[i]);

    }

    float mean = total / n;

    float dist_total = 0.0;

    for (int i = 0; i < n; i++) {

      float dist = float(readings[i] - mean);

      dist_total += (dist * dist);

    }

    float sd = sqrt(dist_total / n);

    Serial.print(mean); Serial.print(' '); Serial.println(sd);

  }

}



Friday, September 16, 2022

Simple Long Range Radio from Raspberry Pi to raspberry Pi Pico using HC-12 433MHz

I have a project in progress that is the third generation of my Hen House Door project. I want to be able to open and close the door from my home automation controller (Raspberry Pi 2 running NodeRED). Unfortunately, the hen house is at the end of the garden and out of WiFi range. Anyway the door opener is low-power and solar powered, and WiFi uses too much current.

I've used NRF24 and CC1101 modules before and frankly there are a lot of wires to connect for SPI and generally a mess of libraries to try and get working. Wouldn't it be lovely (I mused) if there was a long range wireless module that talked UART serial and hid all the communication layer stuff. 

With a bit of googling, I was delighted to find the HC-12 modules that do exactly that. They take AT commands to configure them for power mode etc. But aside from that they work just like you had a cable between your two devices and talked serial along it.

Eventually, my Raspberry Pi 2 will talk to the hen house controller that is based on an ATTiny1616 at 1MHz and using a minimal amount of electricity except when driving the door motor. But as a first test, I thought it would be good to establish a link between a Pico and a regular Raspberry Pi (in this case, my Pi 400).

The Pico

Here's the Pico end.


The connections are:

  • GND on the Pico to GND on the HC-12
  • 3V on the Pico to 3V on the HC-12
  • Tx on the Pico to Rx on the HC-12
  • Rx on the Pico to Tx on the HC-12
To test it out, I used the program below - making sure its copied onto the Pico itself (use Save a Copy in Thonny) so I can take it mobile with a battery pack.


Here's the code for ease of copying:

from machine import UART
import time

uart = UART(0, 9600, timeout=400)

count = 0

while True:
    count += 1
    print(count)
    uart.write(str(count)+"\n")
    time.sleep(1)


The Raspberry Pi

Here's an HC-12 attached to my Pi400 by way of a GPIO adapter and some jumper wires.


The connections are:

  • GND on the Pi to GND on the HC-12
  • 3V on the Pi to 3V on the HC-12
  • Tx on the Pi (14) to Rx on the HC-12
  • Rx on the Pi ( 15) to Tx on the HC-12
The receiving code is just:

import serial

ser = serial.Serial('/dev/serial0', baudrate=9600)

while True:
    line = ser.readline().decode("utf-8")
    print(line)

Run the program, and it should start receiving numbers from the Pico. Wander around with the Pico powered from a USB battery pack and see how far you can get before you start loosing data. 


pi@pi400:~ $ nano hc_12.py 

pi@pi400:~ $ python3 hc_12.py 

1553


1554


1555


1556


1557


1558


Conclusion

Everything worked without a hitch and no need for any tricky SPI programming or dodgy libraries. Pyserial was all that was required. These are now my wireless modules of choice.

I'm just using the little coiled-wire antenna that came with the module and I'm getting more than enough range to reach the hen house even through a few walls. The modules have a tiny antenna connector, to which an external antenna can be attached for maximum range. The module datasheet claims 1km at maximum power and lowest baud rate. Please check that 433MHz is legal in your area.

Next up for me is using the module's AT commands to balance low power with range and change channel, to avoid interference.