Electronics

How to use the LDAC pin on DACs (Digital-to-Analog converters)

The \overline{\text{LDAC}} pin on Digital-to-Analog converters (DACs) provides a convenient way to tell the DAC when an output.

For example, if you have an 8-channel I²C DAC, you need to set the DAC registers for the channels one after another using the I²C protocol, but often you want all channels to change their analog outputs at the same time (after writing all the values to the corresponding registers).  The \overline{\text{LDAC}} pin allows you to do that by driving it high (e.g. 3.3V).

Using it is simple:

  • If you don’t want to use the \overline{\text{LDAC}} pin, tie it to GND. This will cause the DAC’s outputs to be updated immediately whenever the registers or corresponding inputs are changed.
  • Don’t leave the \overline{\text{LDAC}} pin floating ! Always connect it to an output of e.g. a microcontroller or tie it to GND
  • If you want to use the pin, connect it to a GPIO output of a microcontroller. By default, drive it high (e.g. to 3.3V). Whenever you are finished updating the DAC register, drive it low for at least 10 microseconds (typical value that will work for most DACs). The DAC will then update its analog outputs, all at once.
Posted by Uli Köhler in Electronics

How to fix wifi blocked on boot on Raspberry Pi 4

After migrating a fresh Raspbian install using the official 2020-04 Raspbian light image from my Raspberry Pi Model 2 to my new Raspberry Model 4, the Wifi was disabled at boot.

I tried configuring the Wifi using raspi-config but that didn’t change anything.

First, try rfkill unblock all and then reboot in order to check if the Wifi adapter is still unblocked after the reboot. In my case, this fixed the issue permanently and wifi worked immediately.

If that doesn’t help, check if country=... is set in /etc/wpa_supplicant/wpa_supplicant.conf. You need to set it to your correct country code to comply with regulatory limits. For example, use country=DE to set the regulatory domain to Germany.

Posted by Uli Köhler in Linux, Raspberry Pi

Minimal ESP-IDF UART transmit example for ESP32 & PlatformIO

The following example writes the string CAFE to UART continously, waiting for 100ms in between:

#include <driver/gpio.h>
#include <driver/uart.h>
// Include FreeRTOS for delay
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

int app_main() {
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
    };
    uart_driver_install(UART_NUM_1, 2048, 0, 0, NULL, 0);
    uart_param_config(UART_NUM_1, &uart_config);
    uart_set_pin(UART_NUM_1, 10, 9, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    // Main loop
    while(true) {
        uart_write_bytes(UART_NUM_1, "CAFE", 5);
        vTaskDelay(100 / portTICK_RATE_MS);
    }
}

 

Posted by Uli Köhler in C/C++, Embedded, PlatformIO

ESP-IDF equivalent to Arduino delay()

You can use the FreeRTOS API to provide a delay similar to the Arduino delay() function in the ESP-IDF framework. FreeRTOS is included in the PlatformIO ESP-IDF default configuration.

First, include the FreeRTOS headers

// Include FreeRTOS for delay
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

After that, you can use vTaskDelay(...) to perform the delay. This example delays by 500ms:

vTaskDelay(500 / portTICK_RATE_MS);

You can use vTaskDelay() even if not using FreeRTOS tasks.

For a full example, refer to PlatformIO ESP-IDF ESP32 blink example

Posted by Uli Köhler in C/C++, Embedded, PlatformIO

How to set pin to output mode using ESP-IDF

Use this snippet to define a pin as output using the ESP-IDF framework (e.g. using PlatformIO):

gpio_config_t io_conf;
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
// Define the pin here (e.g. (1ULL << 2) for GPIO9)
io_conf.pin_bit_mask = (1ULL << 2);
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);

The example above configures GPIO2 as an output. Use (1ULL << 3) to configure GPIO3 as an output

Posted by Uli Köhler in C/C++, Embedded, PlatformIO

How to fix ESP-IDF ‘undefined reference to app_main’

Problem:

You are trying to compile your C/C++ ESP8266/ESP32 firmware using the ESP-IDF framework. Your source code looks like this:

int main() {
    // ...
}

but you only see an error message like this:

.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\esp-idf\esp32\libesp32.a(cpu_start.c.o):(.literal.main_task+0x18): undefined reference to `app_main'
.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\nodemcu-32s\esp-idf\esp32\libesp32.a(cpu_start.c.o): in function `main_task':
.platformio\packages\framework-espidf\components\esp32/cpu_start.c:540: undefined reference to `app_main'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\nodemcu-32s\firmware.elf] Error 1

Solution:

For the ESP-IDF framework, the main() function needs to be named app_main():

int app_main() {
    // ...
}

See PlatformIO ESP-IDF ESP32 blink example for a complete example.

Posted by Uli Köhler in C/C++, Embedded, PlatformIO

PlatformIO ESP-IDF ESP32 blink example

This example is the equivalent of the simple Arduino blink example for ESP32 boards when using the ESP-IDF framework:

#include <driver/gpio.h>
// Include FreeRTOS for delay
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

#define LED 2 // LED connected to GPIO2

int app_main() {
    // Configure pin
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask = (1ULL << LED);
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);

    // Main loop
    while(true) {
        gpio_set_level(LED, 0);
        vTaskDelay(500 / portTICK_RATE_MS);
        gpio_set_level(LED, 1);
        vTaskDelay(500 / portTICK_RATE_MS);
    }
}

 

Posted by Uli Köhler in C/C++, Embedded, PlatformIO

Computing the CRC8-ATM CRC in Python

The 8-bit CRC8-ATM polynomial is used in many embedded applications, including Trinamic UART-controlled stepper motor drivers like the TMC2209:

\text{CRC} = x^8 + x^2 + x^1 + x^0

The following code provides an example on how to compute this type of CRC in Python:

def compute_crc8_atm(datagram, initial_value=0):
    crc = initial_value
    # Iterate bytes in data
    for byte in datagram:
        # Iterate bits in byte
        for _ in range(0, 8):
            if (crc >> 7) ^ (byte & 0x01):
                crc = ((crc << 1) ^ 0x07) & 0xFF
            else:
                crc = (crc << 1) & 0xFF
            # Shift to next bit
            byte = byte >> 1
    return crc

This code has been field-verified for the TMC2209.

Posted by Uli Köhler in Algorithms, Embedded, MicroPython, Python

MicroPython ESP32 minimal UART example

This example shows how to use UART on the ESP32 using MicroPython. In this example, we use UART1 which is mapped to pins GPIO9 (RX) and GPIO10 (TX).

from machine import UART
uart = UART(1, 115200) # 1st argument: UART number: Hardware UART #1

# Write
uart.write("test")

# Read
print(uart.read()) # Read as much as possible using

Don’t know how to upload the file to MicroPython so it is automatically run on boot?

Posted by Uli Köhler in Embedded, MicroPython, Python

How to fix ESP32 MicroPython ‘ValueError: pin can only be input’

Problem:

You are trying to initialize an ESP32 pin in MicroPython using

import machine
machine.Pin(34, machine.Pin.OUT)

but you see an error message like

>>> machine.Pin(34, machine.Pin.OUT)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: pin can only be input

Solution:

On the ESP32, pins with numbers >= 34 are input-only pins!

You need to use other pins < 34 if you need output capability!

For reference, see the relevant MicroPython source code section:

// configure mode
if (args[ARG_mode].u_obj != mp_const_none) {
    mp_int_t pin_io_mode = mp_obj_get_int(args[ARG_mode].u_obj);
    if (self->id >= 34 && (pin_io_mode & GPIO_MODE_DEF_OUTPUT)) {
        mp_raise_ValueError("pin can only be input");
    } else {
        gpio_set_direction(self->id, pin_io_mode);
    }
}
Posted by Uli Köhler in Embedded, MicroPython, Python

How to upload files to MicroPython over USB/serial

In this post we will investigate how to connect to a wireless network on boot

First, install ampy – a tool to modify the MicroPython filesystem over a serial connection.

sudo pip3 install adafruit-ampy

Now prepare your script – we’ll use main.py in this example.

Upload the file to the board:

ampy -p /dev/ttyUSB* put main.py

This only takes about 2-4 seconds. In case ampy is still running after 10 seconds, you might need to

  • Stop ampy (Ctrl+C), reset the board using the RESET button and retry the command
  • Stop ampy (Ctrl+C). Detach USB and ensure the board is powered off (and not powered externally). Re-Attach USB and retry the command.
  • In case that doesn’t help, try re-flashing your board with the most recent version of MicroPython. See How to flash MicroPython to your ESP32 board in 30 seconds. This will also clear the internal filesystem and hence remove any file that might cause failure to boot properly.
Posted by Uli Köhler in Embedded, MicroPython, Python

MicroPython ESP32 blink example

This MicroPython code blinks GPIO2 which is connected to the LED on most ESP32 boards.

import machine
import time
led = machine.Pin(2, machine.Pin.OUT)
while True:
    led.value(1)
    time.sleep(1)
    led.value(0)
    time.sleep(1)

Don’t know how to upload the file to MicroPython so it is automatically run on boot?

Posted by Uli Köhler in Embedded, MicroPython, Python

How to upload files to MicroPython using WebREPL using webrepl_cli.py

First, clone the webrepl repository:

git clone https://github.com/micropython/webrepl.git

Now use ampy to initially setup your wifi connection and setup both wifi and WebREPL on on boot (see How to autoconnect to Wifi using MicroPython on your ESP32 board):

import network
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect("MyWifi", "MyWifiPassword")

# Start webrepl
import webrepl
webrepl.start(password="Rua8ohje")

Upload via the serial port using

ampy -p /dev/ttyUSB0 put main.py

You should only have to do this once (except if you break you main.py). If the upload doesn’t work

Go to the directory where webrepl_cli.py is located:

cd webrepl

Now you can upload main.py using

./webrepl_cli.py -p Rua8ohje ../main.py espressif.local:/main.py

You might need to use a different host, but espressif.local seems to work out-of-the-box in many configurations.

The output should look like this:

op:put, host:espressif.local, port:8266, passwd:Rua8ohje.
../main.py -> /main.py
Remote WebREPL version: (1, 12, 0)
Sent 329 of 329 bytes

Now, reset your board using the reset button, so your updated main.py will be executed.

Posted by Uli Köhler in Embedded, MicroPython

How to run WebREPL without webrepl_setup in MicroPython

Problem:

You want to enable WebREPL on your MicroPython board using

import webrepl
webrepl.start()

but it is only showing this error message:

WebREPL is not configured, run 'import webrepl_setup'

However, you want to configure WebREPL programmatically instead of manually running it on every single board.

Solution:

Use

import webrepl
webrepl.start(password="Rua8ohje")

This will circumvent webrepl_setup completely and is compatible with an automated setup process.

Note: At the time of writing this you can only use passwords with 8 characters max! (see How to fix MicroPython WebREPL ValueError in File &#8222;webrepl.py&#8220;, line 72, in start )

Posted by Uli Köhler in Embedded, MicroPython, Python

How to fix MicroPython WebREPL ValueError in File “webrepl.py”, line 72, in start

Problem:

You want to configure your MicroPython WebREPL programmatically using webrepl.start(password="...") but you see a stacktrace like

>>> webrepl.start(password="Rua8ohjedo")
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "webrepl.py", line 72, in start
ValueError:

Solution:

Use a shorter password with 8 characters max:

webrepl.start(password="Rua8ohje")

 

Posted by Uli Köhler in Embedded, MicroPython, Python

How to autoconnect to Wifi using MicroPython on your ESP32 board

In this post we will investigate how to connect to a wireless network on boot

First, install ampy – a tool to modify the MicroPython filesystem over a serial connection.

sudo pip3 install adafruit-ampy

Now download main.py and save it in your current working directory and insert your wifi credentials:

import network
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect("YourWifiName", "EnterYourWifiPasswordHere")

Upload the file to the board:

ampy -p /dev/ttyUSB* put main.py

In case ampy shows no output within 5 seconds, try resetting the board, waiting for 5-10 seconds and retrying the upload using ampy

Note: You can list the files on the board’s filesystem using

ampy -p /dev/ttyUSB0 ls

You can verify the content of main.py using

ampy -p /dev/ttyUSB0 get main.py
Posted by Uli Köhler in Embedded, MicroPython, Python

How to connect your ESP32 MicroPython board to your Wifi in 20 seconds

Didn’t flash MicroPython on your ESP32 board yet? See How to flash MicroPython to your ESP32 board in 30 seconds

Copy this script and enter your Wifi credentials:

import network
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect("YourWifiName", "EnterYourWifiPasswordHere")

Now you need to open a REPL to MicroPython via USB.

If your Wifi is within range of the board and the password is correct, your should see output like

>>> import network
>>> station = network.WLAN(network.STA_IF)
I (457700) wifi: wifi driver task: 3ffd2a80, prio:23, stack:3584, core=0
I (459702) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (459712) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (459832) wifi: wifi firmware version: 10f4364
I (459832) wifi: config NVS flash: enabled
I (459832) wifi: config nano formating: disabled
I (459832) wifi: Init dynamic tx buffer num: 32
I (459842) wifi: Init data frame dynamic rx buffer num: 32
I (459842) wifi: Init management frame dynamic rx buffer num: 32
I (459852) wifi: Init management short buffer num: 32
I (459852) wifi: Init static rx buffer size: 1600
I (459862) wifi: Init static rx buffer num: 10
I (459862) wifi: Init dynamic rx buffer num: 32
>>> station.active(True)
W (459872) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (460012) phy: phy_version: 4102, 2fa7a43, Jul 15 2019, 13:06:06, 0, 2
I (460062) wifi: mode : sta (24:6f:28:b0:28:b4)
True
I (460062) wifi: STA_START
>>> station.connect("MyWifi", "ThisIsMyWifiPassword")
>>> I (461782) wifi: new:<5,0>, old:<1,0>, ap:<255,255>, sta:<5,0>, prof:1
I (462632) wifi: state: init -> auth (b0)
I (462632) wifi: state: auth -> assoc (0)
I (462642) wifi: state: assoc -> run (10)
I (462672) wifi: connected with MyWifi, channel 5, BW20, bssid = 9a:c7:44:33:22:11
I (462672) wifi: pm start, type: 1

I (462672) network: CONNECTED
I (463672) tcpip_adapter: sta ip: 192.168.178.42, mask: 255.255.255.0, gw: 192.168.178.1
I (463672) network: GOT_IP
>>>

Note that the output might take a few seconds to appear since it might take some time to connect to the access point.

Note the IP address (192.168.178.42 in this example)

Posted by Uli Köhler in Embedded, MicroPython

How to get into a MicroPython REPL using USB in 10 seconds

On Linux, use picocom:

picocom /dev/ttyUSB0 -b115200

and press enter to get a REPL (or press the RESET button on the board if that does not work).

You should now see the

>>>

prompt.

Don’t have picocom installed?

sudo apt -y install picocom

Got permission denied errors?

sudo usermod -a -G dialout $USER

then logout and log back in or reboot!

Posted by Uli Köhler in MicroPython

How to flash MicroPython to your ESP32 board in 30 seconds

This script does all the neccessary steps to flash MicroPython to your ESP32 board on Linux. Run this script with your board plugged in via USB:

#!/bin/sh
# Call without arguments
# Download esptool
git clone https://github.com/espressif/esptool.git -b v2.8
cd esptool
# Erase flash. Press the reset button while Connecting.
python3 esptool.py --chip esp32 --port /dev/ttyUSB* erase_flash
# Download firmware
wget -qO micropython.bin https://micropython.org/resources/firmware/esp32-idf4-20191220-v1.12.bin
# Upload firmware to board
python3 esptool.py --chip esp32 --port /dev/ttyUSB* --baud 460800 write_flash -z 0x1000 micropython.bin

Remember to press the RESET button on the board if the script is telling you Connecting.... Not sure which button it is? Just press either one, and if the output doesn’t show Chip is ESP32 within 2 seconds, press the other one.

This script was built for my ESP-WROOM-32 board.

Now you can try to get into a REPL using USB.

Posted by Uli Köhler in Embedded, MicroPython