Monday, September 29, 2025

0

Exploring Automation Possibilities with the ADALM1K Learning Kit

Summary:

During my search for a low cost electronics learning module, I came accross the ADALM1K which has interesting features for the price point (approx. 70$). It incorporates a source measure unit (SMU), an oscilloscope and a function generator. On top of that the hardware and software is open-source which is a learning experience in itself to undestand how the kit works.

My goal was first to test how the kit works overall. Once I had some confidence in its usage functions, I dived deeper into its evaluation / application software to look for automation oppurtinites with Python, where one could automate custom workflows for measurement and learning purposes.

I was able to integrate the ADALM1K with my Raspberry Pi setup and automate its functionality using the provided libsmu/pysmu Python package from Analog. I ended up creating a small Python library (pytest-analog) around libsmu so I could write some automated tested for my projects usning the ADALM1K

As an example, I created automated test cases via Python to measure the power consumption of  a DUT (ESP32 Dev board). This could be extended to create more complex test cases for your system under test using very low cost equipment such as the ADALM1K

Hardware: 

The ADALM1K features two analog channels that support source and measure functionality for different waveforms in voltage or current mode. To offer this functionality, it uses a number of building blocks to take the fixed supply and digital interface of USB and offer voltage and current operation.

The main hardware sepcs are as follows:

  • Two channels supporting measure and source function simultaneously: voltage (0 to 5V) or current output (- 200 to +200mA)
  • Two fixed power supplies (2.5V and 5V) supprting up to 200 mA current draw
  • 16-bit (0.05%) basic measure accuracy with 4 digit resolution
  • Four digital signals (GPIOs 3.3V CMOS)

ADALM1K Block diagram per SMU Channel (wiki.analog.com)

As per the above the diagram, an analog channel on the ADALM1K combines a function generator and an oscilliscope instrument on the same pin. In Rev F of the board, the analog ouput and input functions could be seperated with the two provided addiotnal split pins such that the oscillscope function is brought out along with 1 MΩ from the function generator function. Therefore, each analog channel could be configured to one of the following options:

  • Source Voltage and Measure Current (with / wihtout Split IOs)
  • Source Current and Measure Voltage (with / wihtout Split IOs)
  • High Impedance (with / wihtout Split IOs)

If you would like to learn more about the ADALM1K hardware and its design, the following resources are good to read through:

ADALM1000 Board Upside View


 
ADALM1000 Board Underside View

Software:

The software landscape of the ADALM1K comprises the device firmware and the host software which can run on different platforms (Winodws / Linx / OS-X).

The ADALM1K firmware runs on an Atmel based microcontroller. The host software includes a C++ library (libsmu) containing the abstractions for streaming data to and from ADALM1K via USB. In addition, the Pixelpulse 2 and ALICE GUI-based tools are available to control the ADALM1K and make measurements with it. 

Testing with Analog Discovery:

In order to test the opertion of the ADALM1K via the provided GUI software Pixelpulse 2 and ALICE, I did some basic checks to test the ADALM1K analog and digital inputs/outptus via my Analog Discovery 3 (another great electronics learning tool). In these tests, I connected the two instruments to my PC and started their host softwares simultaneously to feed inputs and read outputs.

Test 1: Checking analog outputs of the ADALM1K via the Analog Discovery scope channels: 

As shown in the image below, I connected the ADALM1K source channels A, B to the Analog Discovery scope channels 1, 2. Then using ALICE software I configured an output voltage on channels A, B and then read measured voltages on the scope channels via the WaveForms software of Analog Discovery 3. I configured channels A, B to output 3.6, 1.2 V. On the Analog Discovery, I read approximately similar values on channels 1, 2. There is a slight 200 mV deviations which also seem to occur when I set the ADALM1K to ouput 0 V. Therfore, the ADALM1K requires some calibration before doing any real testing.


 Test 2: Checking GPIOs state of the ADALM1K via the Analog Discovery digital channels:

In this test, I connected the ADALM1K GPIO pins (0-3) to the Analog Discovery Digital IOs (0-3). Using ALICE software, I have set the ADALM1K GPIOs to a defined high / low state and then checked if the same state is read on the Analog Discovery side using its Static IO instrument in the WaveForms software. I was able to confirm the GPIOs state (high for pins 0,1 and low for pins 2,3)

 

Integration with Raspberry Pi: 

My next step was to connect the ADALM1K to my Raspberry Pi and get it working using the libsmu library provided by Analog. I wanted the setup to be headless so I could simply login remotely to the Pi and then run Python scripts to run different workflows with ADALM1K (i.e. something like a remote testing station). On the hardware side, I could simply connect the ADALM1K to my Raspberry Pi 5 via USB. On the software side, I had to compile and install libsmu from source as there was no ready-to-use package for Raspbian OS platform. 
 
You can refer to the this github repo section for building and installing libsmu on a Linux / Windows based platform. After installing libsmu, a utility executable named:"smu" should be available and you could trigger it to check if the ADALM1K board can be detected. For example: running smu -l command to list available devices as show below.
 
 
 
In addition, I have installed pysmu which contains the Python bindings for libsmu to control ADALM1K via Python. To test the interaction via Python, I started an interactive Python session via Ipython and ran some commands to check if I could configure the ADALM1K and read data from it as shown below.
You can refer to pysmu_examples to get more familiar with it.
 
 

Python Wrapper and Test Automation: 

After integrating the ADALM1K with my Raspberry Pi setup and getting familiar with the libsmu / pysmu libraries, I decided to create a small python library (wrapper class) to ease the control of the instrument functions and also create pytest-fixtures for setting up / tear down of the instrument in an automated testing environment. The library is named pytest-analog and it also support the automation of the Analog Discovery instrument from Digilent.

To demonstrate the usage of ADALM1K with pytest-analog in automated testing context, I created a small python test case where the ADALM1K would measure the power consumption of an ESP32 microcontroller running different sketches.

Given the instrument source and measure capabilites, it can power the ESP32 with a given voltage and measure drawn current simulatenously.

The steps to create an automated test with the ADALM1K via pytest-analog are listed below:

  • Create an empty workspace folder (e.g pytest_analog_tmp) and navigate to it (In bash terminal):
    • mkdir pytest_analog_tmp && cd pytest_analog_tmp
  • Create a Python virtual environment to install pytest_analog and its dependencies in isolation:
    • python3 -m venv venv
    • . venv/bin/activate
    • python3 -m pip install --extra-index-url https://test.pypi.org/simple/ pytest-analog 
    • python3 -m pip install matplotlib 
  • Create an empty "pytest.ini" file to provide the ADALM1K configuration at the start of the test with the content below. 
The first couple of lines are pytest specific to configure its output options and logging format. The last two lines are used by the fixtures in pytest-analog to configure channels A, B output voltage. Channel A is used to power the ESP32 at its 3.3V supply pin.
[pytest]
# pytest options
addopts = -v --capture=tee-sys

# Filtering Warnings
filterwarnings =
    ignore::DeprecationWarning

# Logging Options
log_cli=true
log_level=INFO
log_format = %(asctime)s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S

# ADALM1000 Fixtures Options
# Voltage Source
adalm1k_ch_a_voltage = 3.30
adalm1k_ch_b_voltage = 0.00 
  • Create a test_esp32_current_consumption.py file with the content below: 
import numpy as np
import pytest
import time
import math
import logging
import matplotlib.pyplot as plt
from pytest_analog import ADALM1KWrapper, AnalogChannel
from datetime import datetime

def test_esp32_current_consumption(
adalm1k_voltage_source: ADALM1KWrapper
) -> None:

# 2000 samples collected at base rate 100 kHz with averaging every 1000 samples -> 20 seconds
MEASUREMENTS_COUNT = int(2000)
# Do some averaging over collected data
AVERAGE_RATE = int(1e3) # average every 1 / AVERAGE_RATE, Default sampling rate of ADALM1K is 100 kHz

samples = []
ch_a_avg_voltage = []
ch_a_avg_current = []

# Read incoming samples in a blocking fashion (timeout = -1)
for _ in range(MEASUREMENTS_COUNT):
samples.append(adalm1k_voltage_source.read_all(AVERAGE_RATE, -1))

# Average captured readings
for idx in range(MEASUREMENTS_COUNT):
# voltages in V
ch_a_voltage = [sample[0][0] for sample in samples[idx]]
# currents in mA
ch_a_current = [sample[0][1] * 1000 for sample in samples[idx]]

ch_a_avg_voltage.append(np.mean(ch_a_voltage))
ch_a_avg_current.append(np.mean(ch_a_current))

logging.info(f"Average current consumption: channel A: {np.mean(ch_a_avg_current):.3f} mA")
logging.info(f"Max current consumption: channel A: {np.max(ch_a_avg_current):.3f} mA")

# plot current profile
t = np.arange(0, MEASUREMENTS_COUNT) / (100e3 / AVERAGE_RATE) # Default sampling rate is 100 kHz
fig, ax = plt.subplots()
fig.suptitle(f"ESP32 Blinky current consumption", wrap=True)
fig.supxlabel("Time (s)")

ax.plot(t, ch_a_avg_current, color='red')
ax.set(ylabel= "CH_A I(mA)")
ax.margins()
ax.set_xlim([0, math.ceil(np.max(t))])
ax.grid(True, which='both')
ax.minorticks_on()

plt.savefig(f"I_consumption_esp32_blinky_{time.strftime(datetime.now().strftime('%H%M'))}.png")
  • Run the test using pytest: pytest test_esp32_current_consumption.py  which generates the following logs in the terminal.

Current consumption profile for ESP32 blinking an LED every 2 seconds


Current consumption profile for ESP32 going into deep sleep mode for 5 seconds then waking up shortly

After the test execution, a graph is generated with the measured current consumption profile during the test. The graphs above are for two different sketches running on the ESP32. 

The first one is a basic blinky sketch where one could certainly see an increase in current draw when the LED is on. The duration of those peaks match closely with the LED-On period as expected.

The second sketch showcases the ESP32 deep sleep mode for extreme power saving applications, where the device is consuming a couple of miliamps during sleep mode and then waking up shortly every 5 seconds.

IMPORTANT: The above results can be inaccurate and require verification with a professional equipment to compare how good the ADALM1K is in capturing power characterstics of a given DUT.


Sunday, September 21, 2025

0

Bluetooth 6.0 on Raspberry Pi featuring LE Audio w/ Auracast

Summary:

In a previous blog post, I shared my experience integrating the Intel AX210 WiFi + Bluetooth module into my Raspberry Pi 5 to to experiment with the Bluetooth LE Audio feature. After that I started looking out for addiotnal Bluetooth modules supporting the latest Bluetooth standard, has Lnux support and can interface with the Raspberry Pi over USB or UART.

After doing some reasearch, I came accross the Infenion (CYW55513 CYW55573) chipsets, which according to their specs support Bluetooth 6.0 standards and are LE Audio capable. The CYW55573 in particular supports Auracast which is the next big thing in Bluetooth technology allowing audio sharing / broadcasting among LE Audio capable devices.

After deciding on the chipsets I wanted to use, I ordered them in the form of M.2 embedded modules and started the experiment journey. At the end, I was able to attach the Bluetooth module to the Pi via the dedicated UART interface and control it via BlueZ (Linux Bluetooth protocol stack). I ran some tests to check if the audio sharing is working using a Pixel 8 phone having latest version of Android supporting Auracast

Hardware Setup: 

The following components were used:

 Software Setup:

I flashed the latest Raspbian OS (12 bookworm) on the SD card of the Pi and enabled SSH for remote access. I updated the OS to the latest available software by running the following in the terminal:

  • sudo apt update

  • sudo apt upgrade 

The Linux kernel version used is 6.12.34+rpt-rpi-2712

To have LE Audio support, I installed the latest stable releases of BlueZ 5.83, Pipewire 1.4.6 and WirePlumber 0.5.10 directly from source.

Experimental features need to be enabled in BlueZ "main.conf" file by setting the following options:

  • ControllerMode = le # use low energy mode

  • Experimental = true # enable experimental features

  • KernelExperimental = 6fbaf188-05e0-496a-9885-d6ddfdb4e03e # enable ISO sockets

For WirePlumber, I created a configuration file: "~/.config/wireplumber/wireplumber.conf.d/80-bluez-properties.conf" according to the wiki documentation to set the Bluetooth audio profiles.

Afterwards a reboot of the Pi is required so changes would take effect.

Attaching Bluetooth over UART and flashing firmware:

Bluetooth module UART Interface:

I placed the IF573 M.2 module into the adapter board and referred to the uSD-M2 Adapter LBEE0ZZ1WE-uSD-M2 datasheet to locate the UART interface pins. Furthermore, I need one GND connection and the module could be powered from a 5V USB connection. 

After going through the data sheet, I learned that the UART interface is 4-wire type and so following singals are required for communication: TX, RX, RTS, CTS

The next step is to connect the M.2 adapter board to the Pi through the FTDI UART - USB adapter. One could perhaps connect directly to the UART interface on the Pi. However, you would need to reserve some GPIO pins on the Pi for that.

The connection singals would be as follows:

M.2 Adapter   <---->  FTDI USB adatper

RTS <----> CTS

CTS <----> RTS

TX <----> RX

RX <----> TX

GND <----> GND

Finally I connected the FTDI UART - USB adapter to the Pi via USB and also connected the M.2 adapter board via USB such that the IF573 module could be powered from 5V USB connection.

Flashing the Bluetooth firmware patch to the IF573:

Now that everything is connected, we still need to perform a very important step which is applying the required Bluetooth firmware for the controller so we can enable underlying functionality and also control the HCI interface via BlueZ. To save you sometime looking for the firmware files, you could find them avaialbe under this Github repo. Note that these may not be the latest firmware releases for the CYW55573 and perhaps one could refer to Infenion website to check for latest updates.

Anyway, I downladed the following package for the firmware files: summit-if573-pcie-firmware-12.103.0.5.tar.bz2 and since I am only interested in the Bluetooth functionality, I just needed to locate the .hcd patch file as shown below:

   

 

To apply the firmware patch file above to the device, it was not sufficent to use the btattach utility tool from BlueZ (the one usually used to attach Bluetooth devices over serial port). The CYW55573 according to its datasheet has some sort of secure upload mechanism where the chip needs to be placed in "autobaud" mode. You can read here for more details. Anyhow, What I had to do in the end is to do a reset for the Bluetooth core via a GPIO pin while the upload procedure is started. When the chip is out of reset, the firmware download into the chip RAM wokrs and the Bluetooth device is sucessfully connected and it is up and running !.

To apply the firmware patch, I used the following tool (brcm_patchram)  from Broadcom acquired later by Cypress and later on Infenion :). You simply need to trigger the executable as follows:

./brcm_patchram_plus --no2bytes --autobaud_mode --baudrate 115200 --use_baudrate_for_download --enable_hci --patchram /lib/firmware/cypress/CYW55560A1_v001.002.087.0225.0065.hcd /dev/ttyUSB0                                                             

After few seconds you should see the following in your terminal and you can confirm the Bluetooth device is connected and running via the "hciconfig" command as show below:

 

 

Testing of LE Audio & Auracast:

Now that the IF573 controller is up and running with BlueZ. I performed two experiments, one to check if we could stream audio from the Pi to a LE Audio capable headset and the second was to connect the Pi with an Android phone that supports Auracast audio sharing with the latest Android 16 update. Please note that in both scenraios, the main goal was to check if the interface is working and did not inveset much time looking into audio link quality and latency issues so do not expect something perfect =) 

Bluetooth LE Audio Unicast streaming to an audio headset:

I was able to pair and connect my LE Audio headset device to the Pi using CYW55573 chipset without probelms. After connection I see two LE Audio related endpoints are registerd which means they are also now recognised as media source/sink devices by wireplumber as shown below

Now when I start some audio play from the Pi, I see the related profiles being activated and they come back to idle when I pause / stop the stream so the interface seems to be working.  
 
If you look in parallel to the HCI logs using BlueZ btmon utility you will see alot of LE Audio data packets being sent as the stream is running.
 

Bluetooth LE Audio Sharing (Auracast) with Android phone:

In this test, I configured the Bluetooth controller (i.e CYW55573) to be discoverable and advertising so I could connect to it from my Pixel 8 phone and see if audio sharing is supported. I was able to see two settings enabled for my controller in BlueZ: (iso-broadcaster and sync-receiver)
 
Once the Pi is paired and connected with my Pixel 8 phone, I could see that it supports audio sharing !. Great now it seems I can share audio over Bluetooth using my Pi and Android phone :) 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 
 
 
 
 
 
 
 

Since audio sharing is now enabled. I can pair additional LE Audio devices to share ongoing audio stream from the phone. What I did here is to configure my Intel AX210 controller (also connected to the Pi via USB) to act as a peripheral via BlueZ and enabled discovery so it could be seen by the Pixel phone. Now when refreshing the audio sharing window on Android, I can add my Intel AX210 chip (advertising here under the name "LE_Audio"). 
Amazing!, Now I established a shared audio stream from Pixel phone to two Bluetooth devices running in parallel on the Pi.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

To check whether sharing is working, I played a test sound from Android as shown in the menu above and observed the playing status notifications in BlueZ for my two connected Bluetooth devices

 

Encountered Problems:

Currently the setup has couple of issues that require additional investigation / optimizations:

  • When attaching the IF573 module to the Pi over UART, it only connects when I set the baudrate to 115200. Given that the module supports high speed UART, I want to have a faster UART link. This becomes important also for audio applications, otherwise, the audio quality is really bad. 

  • When attempting to stream audio from Pixel phone to the Pi, the BlueZ stack notifies me that audio play has started. However, no audio is recieved on the Pi. I checked the HCI logs and it shows that the stream disconnect shorty after the play starts. This needs more investigation for sure.