300x250 AD TOP

Search This Blog

Pages

Featured Post

Unit Testing with ESP32 and PlatformIO

Writing Quality Code and Tests usually goes hand in hand, some find it more productive to write code and then write tests for it and others,...

Paling Dilihat

Powered by Blogger.

Feature Label Area

Sunday, May 22, 2022

Unit Testing with ESP32 and PlatformIO

Writing Quality Code and Tests usually goes hand in hand, some find it more productive to write code and then write tests for it and others, following TDD/BDD write tests before code. The benefits of each is out of scope for this article, what is in scope is how to reach the nirvana of software development by using tests.

Lets start with the basics.

PlatformIO tests can be run on:

  • Locally - on the development machine, it requires that gcc is installed and in the path. for windows this can be done with mingw gcc.
  • On Device - PlatformIO automates this by building the tests, uploading them and resetting the device in order for them to execute and wait for 2 lines, one stating how many tests were executed and another stating if any of the tests failed.
  • Remotely - PlatformIO automates this by building the code on the development machine, copying the firmware to the remote agent, uploading to the device connected to the remote agent and waiting for the test results.

To reach that nirvana we can write tests that execute on the ESP32, but no matter how fast your machine is, compiling, uploading and executing tests on ESP32 can take anywhere from 1 to 10 minutes and that will reduce the effectiveness of the TDD/BDD cycles, one of the big enemies of workflow is slow workflow where the mind wanders off.

Another option would be to do quick development cycles on the development machine and every once in a while running the tests on the device and lastly ensuring code quality by allowing the CI on the build machine to make sure the code is built and tested on an actual device.

The basic Tenet

All testing should be written in the same way, we prepare code and data for the unit under test, we execute the operation and we should verify the outcome is the one we expected, this is called the AAA Pattern, or Arrange-Act-Assert.

Arrange - prepare objects and data for operation

Act  - execute operation

Assert - check results/side effects

If you have the same arrange for many tests, consider the "setup" phase most testing frameworks have, but to maintain a coherent style one should strive to keep the generic parts in the setup phase and the test specific parts in the test itself.

If you elected to use the "setup" phase, just note that you also have the "tear down" phase to do the cleanup from the setup phase - keeping them out of the test is usually a good practice.

Another tenet is repeatability, tests which are not repeatable cannot be trusted and are soon ignored. avoid testing random values in unit tests.

Unit Tests

Unit tests are the most basic kind of testing but are sometimes misunderstood, the purpose of the unit tests are to verify a needed functionality works in the way it was designed to work during the life of the application, there is a broad interpretation of what constitutes a 'unit' but most agree that a unit is a small chunk of code (or function) that can be accessed from outside the module its in.

Note: while testing every individual functionality manually can also verify the code "works", its not doing it throughout the life of the application, a manual test can lead to a waste of time and will not test the same functionality every time. 

PlatformIO provides a way to run tests and split them into groups, each group can be set to either run or not run on each platform (ESP32, Native and more), we can split by library, header or other significant group.

Project Tests Structure

For very simple projects you can put a single main file in the test folder and run all tests from it, keep in mind if it gets too big it might not fit in ESP32. 
For more complex projects, its possible to create multiple executables in the test folder by using subfolders, each subfolder starting with test_ will create a separate executable.

Like in all other unit test demos, we'll start with a simple calculator, it has addition, subtraction, multiplication and division.

int Calculator::add(int a, int b)
{
    return a + b;
}

int Calculator::sub(int a, int b)
{
    return a - b;
}

int Calculator::mul(int a, int b)
{
    return a * b;
}

int Calculator::div(int a, int b)
{
    return a / b;
}

Testing Frameworks

PlatformIO supports out of the box a few testing frameworks, the more popular ones in the embedded world are unity, cpputest and doctest.

Now we'll write some tests

Unity

Unity is popular due to its low overhead and simple use, it was the first framework supported on PlatformIO.

#include <calculator.h>
#include <unity.h> //Unity Testing Framework
#include <runner.h> //Simplifies main()

Calculator calc;

void test_function_calculator_addition(void) {
    TEST_ASSERT_EQUAL(32, calc.add(25, 7));
}

void test_function_calculator_subtraction(void) {
    TEST_ASSERT_EQUAL(20, calc.sub(23, 3));
}

void test_function_calculator_multiplication(void) {
    TEST_ASSERT_EQUAL(50, calc.mul(25, 2));
}

void test_function_calculator_division(void) {
    TEST_ASSERT_EQUAL(32, calc.div(96, 3));
}

void process() {
    UNITY_BEGIN();
    RUN_TEST(test_function_calculator_addition);
    RUN_TEST(test_function_calculator_subtraction);
    RUN_TEST(test_function_calculator_multiplication);
    RUN_TEST(test_function_calculator_division);
    UNITY_END();
}

MAIN(){
    process();
}

doctest

doctest is a similar framework to Catch, except for the slow compilation time, some developers really like Catch's way of doing things but really hate the slow compilation time, doctest while natively supported by PlatformIO still have issues when compiling for ESP32, you can find a modified doctest in the examples that works fine with both ESP32 and natively.


#define DOCTEST_CONFIG_IMPLEMENT
#define DOCTEST_THREAD_LOCAL
#include <doctest/doctest.h> //doctest testing framework
#include <runner.h> //Simplifies main()

MAIN(){
    const int argc_ = 3;
    const char *argv_[] = {
        "exe",
        "-d",
        "-s"};
    return doctest::Context(argc_, argv_).run();
}

#include <calculator.h>

Calculator calc;


TEST_CASE("calculator addition"){
    CHECK(32== calc.add(25, 7));
}

TEST_CASE("calculator subtraction"){
    CHECK(20 == calc.sub(23, 3));
}

TEST_CASE("calculator multiplication"){
    CHECK(50 ==  calc.mul(25, 2));
}

TEST_CASE("calculator division"){
    CHECK(32 == calc.div(96, 3));
}

CppUTest

#include <runner.h> //Simplifies main()

#define CPPUTEST_USE_LONG_LONG 1 //mandatory for cpputest to work with esp32
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTest/TestPlugin.h"
#include "CppUTest/TestRegistry.h"
#include "CppUTestExt/IEEE754ExceptionsPlugin.h"
#include "CppUTestExt/MockSupportPlugin.h"

MAIN(){
    const char * argv_[] = {
        ""
        "",
        "-v",
        "-c",
        "-o",
        "eclipse"
        //"teamcity"//"eclipse"//"junit"
    };
    return CommandLineTestRunner::RunAllTests(5, argv_);
}

#include <calculator.h>

Calculator calc;

TEST_GROUP(Calculator){ };


TEST(Calculator, Addition){
    CHECK(32== calc.add(25, 7));
}

TEST(Calculator, Subtraction){
    CHECK(20 == calc.sub(23, 3));
}

TEST(Calculator, Multiplication){
    CHECK(50 ==  calc.mul(25, 2));
}

TEST(Calculator, Division){
    CHECK(32 == calc.div(96, 3));
}

If you're migrating to PlatformIO and already use a different framework which is not on the supported list, you might be interested to know how to setup PlatformIO to work with custom framework

Environments

To use PlatformIO effectively, we'll need to tell it which setups its going to work with, such as Platforms, Boards and Frameworks, the combination of these can be grouped into environments in platformio.ini, however, environments can contain more than that

Lets define two environments, one for ESP32 and one for natively running on the development machine.

[env:esp32]
platform = espressif32
board = esp32doit-devkit-v1
framework = espidf

[env:native]
platform = native

Running Tests

Running tests in PlatformIO is pretty simple, this command will run all tests in all environments, so if we have a native and ESP32 environment defined, it will executes all tests against them.
pio test

But lets say we want to run only native environment:
pio test -e native

Lastly, some labs have the device connected to a remote agent and shared by multiple developers or even a CI agent

pio remote test

Limiting Tests to Specific Environments

We can always use ifdef guards to build tests for specific platforms but for the sake of order we're probably going to want to group common, embedded and desktop tests. I propose that we'll have 3 separate folders for our sample tests and tell PlatformIO to ignore the incompatible tests on the incompatible platforms.

[env:esp32]
platform = espressif32
board = esp32doit-devkit-v1
framework = espidf
test_ignore = test_desktop

[env:native]
platform = native
test_ignore = test_embedded

Now that we have our tests working, lets see how the results look like

>pio test -e native
Verbosity level can be increased via `-v, -vv, or -vvv` option
Collected 3 tests

Processing test_common in native environment
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Building...
Testing...
test\test_common\test_calculator.cpp:49: test_function_calculator_addition      [PASSED]
test\test_common\test_calculator.cpp:50: test_function_calculator_subtraction   [PASSED]
test\test_common\test_calculator.cpp:51: test_function_calculator_multiplication        [PASSED]
test\test_common\test_calculator.cpp:52: test_function_calculator_division      [PASSED]
------------------------------------------------------------------------------------ native:test_common [PASSED] Took 2.75 seconds ------------------------------------------------------------------------------------

Processing test_desktop in native environment
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Building...
Testing...
test\test_desktop\test_calculator.cpp:49: test_function_calculator_addition     [PASSED]
test\test_desktop\test_calculator.cpp:50: test_function_calculator_subtraction  [PASSED]
test\test_desktop\test_calculator.cpp:51: test_function_calculator_multiplication       [PASSED]
test\test_desktop\test_calculator.cpp:44: test_function_calculator_division: Expected 32 Was 33 [FAILED]
------------------------------------------------------------------------------------ native:test_desktop [FAILED] Took 2.42 seconds ------------------------------------------------------------------------------------

======================================================================================================= SUMMARY =======================================================================================================
Environment    Test          Status    Duration
-------------  ------------  --------  ------------
native         test_common   PASSED    00:00:02.753
native         test_desktop  FAILED    00:00:02.421

_________________________________________________________________________________________________ native:test_desktop _________________________________________________________________________________________________
test\test_desktop\test_calculator.cpp:44:test_function_calculator_division:FAIL: Expected 32 Was 33

Emulators

PlatformIO has integrated a few emulators, unfortunately none of them is for ESP32, however, Espressif has worked on QEMU, which might make it into the supported emulators one day.

Multithreading

Testing multithreaded code does not technically constitutes a unit test, however, sometimes we want to verify the integration between components bridged by concurrency patterns.

More over, unit testing multithreaded code can lead to issues, such as irreproducible or sparse failures with no certain way to check why and in turn lead to tests being ignored or deleted.

In general, when developing multithreaded code the main issues raise when threads share memory and data, depending on architecture and word size, accessing unaligned variables and structs usually compiles to multiple instructions that access and dissect the aligned memory into smaller chunks which will cause issues if multiple threads access that data since the context switch can occur between instructions.

With that being said, if you must test multithreaded code, you'll need to control the timing or wait for something to happen and not depend on sleeps and delays since it will make your code non-deterministic.

If you must share data between threads, do it with the appropriate concurrency pattern, such as messages, queues and lists.

You may find more interesting patterns with etl.

RTOS

RTOS use on a microcontroller can enable higher quality code by separating responsibilities to individual tasks and functions, allowing them to interact safely, the downside of that is the added complexity of working with RTOS. The esp-idf has integrated FreeRTOS already for you, saving some of the learning curve but I do recommend reading the FreeRTOS documentation and even going into some of its source code to understand exactly what's going on, if you really need it, FreeRTOS has been ported to Windows/Linux so you test a part of your code on your development machine.



Tags: , , ,

Monday, November 15, 2021

USART with DMA on STM32

 I've been working with many projects that use the USART and not one was like the other alghough hardware resources were pretty similar. 

So I've sat down and decided to make a boilerplate for USART with DMA implementation that uses binary semaphores to notify when data arrives and buffers the output to create as little delay as possible as well as leave as much CPU as possible for the rest of the system.

For this demo I'll be using the STM32F446 Nucleo-64.



By default, it has the USART2 pins connected to the on board ST-Link so its possible to just open a terminal, watch logs and send commands to the MCU with as little effort as possible.


Once we have the basics setup in the IDE and the USART2 Enabled as Asynchronous, We'll go ahead and add DMA Channels:



One for read, one for write and set them both to Normal mode.

Enable global interrupts:



We then go ahead and add FreeRTOS, so we can demo a general application:


And go ahead and USE_NEWLIB_REENTRANT so we can use printf:


And lastly we'll go to project manager and mark the Generate peripheral initialization as pair of '.c/.h' files per peripheral for just to keep our application a bit cleaner:


A known bug (1,2,3,4) in HAL generated projects is that the DMA is not initialized in order, a simple solution will be to duplicate the DMA initialization call to the 'USER CODE BEGIN SysInit' section in main.c so whenever the project is regenerated, the change won't get lost.

1
2
3
/* USER CODE BEGIN SysInit */
  MX_DMA_Init();
/* USER CODE END SysInit */


Once our project is generated, we'll add a circular buffer of choice, in this case I've chosen to use Tilen Majerle's lwrb - Lightweight ring buffer manager.

Next in our usart.c, we'll add 2 semaphores for the tx and rx buffers, 2 aligned buffers for the DMA and 2 buffers for rx and tx, we'll use our "USER  CODE BEGIN 0" for that so we'll keep them when the project is regenerated through STM32CubeMX/IDE. 

Feel free to change the buffer sizes, though for my needs I didn't see a reason to go higher.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* USER CODE BEGIN 0 */

#include <cmsis_os.h>
#include "lwrb/lwrb.h"

static SemaphoreHandle_t readSemaphore;
static osSemaphoreId writeSemaphore;

#define TX_DMA_BUFFER_SIZE 16
__aligned(32) uint8_t TX_DMA_buffer[TX_DMA_BUFFER_SIZE];

#define RX_DMA_BUFFER_SIZE 16
__aligned(32) uint8_t RX_DMA_buffer[RX_DMA_BUFFER_SIZE];

lwrb_t rx_buffer;
uint8_t rx_buffer_container[255];

lwrb_t tx_buffer;
uint8_t tx_buffer_container[255];

void initialize_buffers(void) {
	osSemaphoreDef(WRITESEM);
	writeSemaphore = osSemaphoreCreate(osSemaphore(WRITESEM), 1);

	vSemaphoreCreateBinary(readSemaphore);
	if (readSemaphore == NULL) {
		Error_Handler();
	}

	if (lwrb_init(&rx_buffer, rx_buffer_container, sizeof(rx_buffer_container)) != 1){
		Error_Handler();
	}
	if (lwrb_init(&tx_buffer, tx_buffer_container, sizeof(tx_buffer_container)) != 1){
		Error_Handler();
	}
}

/* USER CODE END 0 */

Note we included also our buffer initialization routine in the header.

Next we'll add the DMA start in our MX_USART2_UART_Init function in usart.c:

1
2
3
4
  /* USER CODE BEGIN USART2_Init 2 */
  HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RX_DMA_buffer, RX_DMA_BUFFER_SIZE);
  __HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);
  /* USER CODE END USART2_Init 2 */

Thanks for the tip about DMA_IT_HT from ControllersTech.

Next we'll add our USART tx/rx functions in usart.c. If you're wondering about the xSemaphoreGiveFromISR at line 23, its used to notify the waiting thread about new data rather than continuous polling that will either waste CPU time or cause a delay between received bytes until the thread realizes it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/* USER CODE BEGIN 1 */

static int tx_next_chunk(void) {
	int number_of_items_in_tx_buffer = lwrb_read(&tx_buffer, TX_DMA_buffer, TX_DMA_BUFFER_SIZE);
	if (number_of_items_in_tx_buffer > 0) {
		if (HAL_UART_Transmit_DMA(&huart2, TX_DMA_buffer,
				number_of_items_in_tx_buffer) != HAL_OK) {
			assert(0);
		}
		__HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);
	}
	return number_of_items_in_tx_buffer;
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
	if (huart->Instance == USART2) {
		if (lwrb_write(&rx_buffer,  RX_DMA_buffer, Size) != Size ){
			//buffer overrun
		}

		HAL_UARTEx_ReceiveToIdle_DMA(huart, RX_DMA_buffer, RX_DMA_BUFFER_SIZE);
		BaseType_t xHigherPriorityTaskWoken;
		xSemaphoreGiveFromISR(readSemaphore,&xHigherPriorityTaskWoken);
	}
}

int get_rx_data(uint8_t *buffer, size_t buffer_length, uint32_t timeout) {
	xSemaphoreTake(readSemaphore,pdMS_TO_TICKS(timeout ));
	return lwrb_read(&rx_buffer, buffer, buffer_length);
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
	if (huart->Instance == USART2) {
		tx_next_chunk();
	}
}

void put_tx_data_with_wait(uint8_t *buffer, size_t buffer_length) {
	int retries = 1000;
	while (retries > 0) {
		int pushed_bytes = put_tx_data(buffer, buffer_length);
		buffer_length -= pushed_bytes;
		buffer += pushed_bytes;
		if (buffer_length <= 0) {
			break;
		}
		osDelay(1);
		retries--;
	}
}

int put_tx_data(uint8_t *buffer, size_t buffer_length) {
	int ret = 0;
	if (osSemaphoreWait(writeSemaphore, osWaitForever) == osOK) {
		ret = lwrb_write(&tx_buffer, buffer, buffer_length);
		osSemaphoreRelease(writeSemaphore);
	}

	if (huart2.gState == HAL_UART_STATE_READY) {
		tx_next_chunk();
	}
	return ret;
}

/* USER CODE END 1 */

And our function prototypes in usart.h:

1
2
3
4
5
/* USER CODE BEGIN Prototypes */
void put_tx_data_with_wait(uint8_t *buffer, size_t buffer_length);
int put_tx_data(uint8_t *buffer, size_t buffer_length);
int get_rx_data(uint8_t *buffer, size_t buffer_length, uint32_t timeout);
/* USER CODE END Prototypes */

And lastly we'll create our echo demo in StartDefaultTask in our freertos.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
	while (1){
		uint8_t temp_buffer[64];
		size_t read_bytes;
		read_bytes =get_rx_data(temp_buffer, sizeof(temp_buffer), 100);
		put_tx_data_with_wait(temp_buffer,read_bytes);
	}
  /* USER CODE END StartDefaultTask */
}

What the demo does is essentially waiting for up to 64 bytes or 100ms and transmitting back what it got. so this thread is waiting most of the time, the DMA does most of the work and the ring buffer is just there to make sure everything plays together nicely.

The demo project can be found here:

https://github.com/drorgl/usart-boilerplate




Tags: , , ,

Tuesday, November 5, 2019

Introduction to ESP32 Debugging

Note: This article is a prelude to a talk I'm having about ESP32 Unit Testing and Debugging on November 27th 2019.

If you read any of my previous articles you could probably guess I'm not a big fan of debugging. I truly believe that production should not be debugged (with exceptions) and therefore, its better to change one's thought process and build beneficial logging abilities.

But probably the most widely used debugging 'technology' is the printf way, by locating the crash or stack trace, the magical printf can tell us the current state which lead to the bug or crash and we can fix it. If we're thorough, we'll probably add a unit test to avoid that bug in the future.

As much as I would like it to be, in embedded systems, logging is not always realistic, it can affect timing, occupy UARTS and kill eMMC.

So what are our other options?

Sometimes the we only need an indication something is not happening, an "if" statement we're not sure is actually happening, how about using a GPIO to turn a led on?

Two more relatively fast options are I²C and SPI, we can use a very simple program that dumps the values being sent.

To further improve the logging abilities of these facilities, you can encode only the values, rather than a text log message.


Debugging Port


JTAG has been around for quite some time (1990!), these days most MCUs have a debugging port, be it JTAG, SWD or debugWire (AVR).
JTAG in particular is very capable, its designed to be chained across all the chips, processors and DSPs on the board, so a single port can be used to debug many components.

Logging

While printf debugging can provide a short term or a localized debugging option, as developers, we need to consider longer term and production problem solving and these solutions either keep state changes in a log file by either saving rolling logs or by having some sort of circular buffer of logging messages.

While logging is pretty straight forward implementation, the ESP32 logging facilities provides a few interesting points:
1. logs are divided by TAGs
2. logs can be turned off/on/set logging level by each tag
3. internal esp-idf components also have a log tag
4. printf is always sent to UART0
5. logs can be captured, this is one of the more interesting features since it allows you, as a developer to have a device in the field that is misbehaving and you can turn on logging remotely and ask for the log files.

While not directly related to logging, the ESP32 and FreeRTOS provides a few more interesting mechanisms for debugging problems:
1. get reset reason, this is very important, think of it as extra information you can write to your logs when the device starts, did it reboot because of power failure? brownout? watchdog?
2. Core Dump, when the device in the field, who is going to monitor the stack trace? it cannot be written to the log file, nothing is usually monitoring the UARTs, so where does it go? you can configure a core dump to place it on the flash, so next time you're asking for logs, you can also retrieve the core dump and analyze it.
3. FreeRTOS Memory Analysis, heap corruption, maximum stack use, maximum heap use and even memory tracing similar to crtdbg.
4. FreeRTOS CPU Utilization


PlatformIO Unified Debugging

PlatformIO became my favorite development platform, its simplistic, near zero configuration and simple extensibility gives one ability to do almost anything with very little effort.
In ESP32 case, the openocd-esp32 and esp-idf are integrated with its unified debugger, making it so simple, I just had to add one line to platformio.ini:
debug_tool = esp-prog
or
debug_tool = jlink

But that will only get you so far, if you start the debugger, the esp32 debugging configuration is missing, so you'll need to add a debug env as well with build flags to add debug symbols to the firmware:
build_flags = -ggdb 

JTAG

JTAG is a standard debugging port, its common with most of the modern systems, it can help you to physically test a board using Boundary Scan, Stop and Start CPU cores, read and write variables and memory, add breakpoints, read and write registers, execute code and commands and even write firmware.

Unfortunately ESP32 does not provide boundary scan capabilities, but you can achieve that and more if you have your test fixture flash MicroPython, script logic analyzer commands and analyze the results.

source


The rest of the features JTAG is enabling are great and on top of that the Tensilica TRAX module enhances debugging facilities by adding real-time log tracing and even FreeRTOS event tracing.

So what is TRAX?

TRAX is TRace Analyzer for Xtensa, is a module that the CPU and JTAG share to transfer data between the host and Tensilica Processor.

With that in mind, we can use that data for almost anything, Espressif provided us with two interesting examples, trace logs and FreeRTOS events, but the sky is the limit.


Getting Started

This is the ESP32-DevKitC, it's one of the most popular ESP32 development kits, its a low-footprint board with the essentials, it comes with either WROOM or WROVER modules. Its drawback is the lack of JTAG connectors, but you can add it by wiring directly to the pins.

source
Get yours here

On the other hand, this adapter exposes the JTAG pins in both 10pin esp-prog format and 20pin standard JTAG / Segger J-Link format, it can stack between the DevKit and your breadboard or development PCB, it made me a lot less lazy connecting the debugger, is it a positive or a negative thing, you decide.

In the end of this article you can find other options from Espressif.

As a side note, I've experienced different problems with different debuggers, the Segger J-Link would freeze every once in a while, needing a complete disconnect and power down of both the debugger and devkit, the FT2232 based debuggers would succeed to upload the sketch through the J-Link interface but it was an inconsistent experience.

So how to debug?

1. compile and upload the firmware using -ggdb flag.
2. in VSCode, go to Debug View, click PIO Debug (skip Pre-Debug), wait about 10-20 seconds and your first breakpoint will be caught.


We have a few interesting points here.
1. The top left PIO Debug will start the debugger, you should switch to the lower right debug console tab to see progress and execute debugger commands.
2. Debugger specific sidebar where Variables, Watch, Call Stack, Breakpoints etc' are visible.
3. The gutter in the editor can set a breakpoint or conditional breakpoint, please note that since conditional breakpoint is implemented in the debugger, the execution will be paused each time the breakpoint is hit and evaluated, this affects timing and performance.
4. Top right bar shows debugger controls, Continue, Step Over, Step In, etc', Note that they might not work if no hardware breakpoint is available.


The Debug Console view in VSCode exposes GDB, I'm saving it for my next article, its going to be about ESP32 log tracing and event tracing abilities, exciting!

Debugging Supported ESP32 Development Kits

1. ESP-WROVER-KIT - JTAG on board (using FT2232HL chip)
ESP-WROVER-KIT-VB is a highly integrated ultra-low-power development board which includes Flash and PSRAM with dual-core 240 MHz CPU.
Create Internet cameras, smart displays or Internet radios by connecting LCDs, microphones and codecs to it.

source

2. ESP32-LyraTD-MSC - JTAG connector
Designed for smart speakers and AI applications. Supports Acoustic Echo Cancellation (AEC), Automatic Speech Recognition (ASR), Wake-up Interrupt and Voice Interaction.

source
3. ESP32-Ethernet-Kit - JTAG on board (using FT2232HL chip)
Consists of two development boards, the Ethernet board A and the PoE board B
source





Tags:

Wednesday, July 17, 2019

Is ESP32 Ready for some AI?

IoT is a passion of mine for quite some time, so imagine how happy I was to receive a gift from Semix, the all new ESP-EYE v2.1!

Semix specializes in representation and distribution of world leading manufacturers of Electronic Components, Modules and Integrated solutions in Israel, in this case Espressif and Manica.



While the board looks like it was pretty thought out (ferrite beads all over it!), it does lack GPIO connections, looking like it was directly made to demonstrate the ESP32 capabilities rather than a maker Swiss army knife. In the end of this article there are some other options if you're curious about combining these capabilities with your other crazy ideas. :-)

I've started by looking up some information, videos, design reference and anything I can find on that module and eventually I've cloned the esp-who project.

I've followed a few getting started examples but I really love what PlatformIO did with Visual Studio Code so I had to set it up to compile in PlatformIO. BTW, PlatformIO already has the esp-idf framework, which makes it very easy to use with ESP32!

You'd be surprised how much faster a good IDE can help you understand a project structure!

Eventually I got curious enough to see how they did it so I've begun to dig up a bit. the face recognition part seems to be based on MTCNN, where it's actually 3 separate networks integrated with algorithm glue.

source
The audio keyword recognition seems to be very similar to TensorFlow demo I've seen, but during my research I've seen a few other examples that gave me the impression Espressif did not use TensorFlow.

However, TensorFlow lite could be used for another project I was doing research for, so I've decided to take the plunge and see if I can compile it for ESP32.

The getting started is pretty straight forward, download, compile, run and of-curse learn. but to really get started you need to get your feet wet and test the hardware compatibility since TensorFlow lite was not specifically ported to ESP32. So what do you do? you run the suite tests on the ESP32.

Let me assure you, all the tests passed, some did take some time to complete but that's because it wasn't optimized for tensilica yet.

I've also run the micro speech demo, but since it wasn't optimized, it took 360ms to process 100ms of audio. I did find some optimizations for the inference engine and boom, this thing is fast! (80ms for FFT + inference!!)

If you'd like you can find more pretrained models and examples in TensorFlow website.

To me this little exploration opened a whole new world and ideas of AI on ESP32, if you had any doubts, you should definitely check out TensorFlow lite on ESP32!

source

Please note that there are other variations of the ESP-EYE (or ESP-CAM) with different capabilities:

1. ESP32-CAM
The ESP32-CAM also has 4MB of external PSRAM, it exposes some GPIOs for extensibility and even has an SD-CARD slot but no Mic.
Notice there's no USB plug, so you'll need an external USB-TTL adapter to program this device.

2. M5Stack Official ESP32 Camera Development Board


Almost looks like a copy,I couldn't find any reference of external PSRAM, so if anyone knows, leave your comments please.

There are empty footprints for MPU6050, BME280, Mic and lithium battery connection, so it can be easily used for your wearable projects.

Notice it has USB-C connector.

3. TTGO T-Camera Plus


That thing is sweet!
8MB of PSRAM(!!!)
1.3 inch LCD
Microphone
SD card slot
Battery connection/charger
and a USB connection.

4. TTGO T-Camera ESP32-WROVER-B
Another notable module, very similar to the T-Camera Plus.
We all know that video/imaging takes power, how are we expected to write low power applications when our MCU takes most of our power? well, if your particular application doesn't require you to always scan your camera, you can put your MCU to sleep and wake it up only when there's movement with a simple PIR sensor.



Tags: , ,

Friday, March 8, 2019

A Million Times

A while ago someone at work approached me with an idea to build a replica of  "A Million Times" by Humans Since 1982, while the project did eventually die off as far as I know, the idea looked very interesting, many clocks, synchronized to display animation, text and time, what can be bad about it? or as the original creator wrote:

"Metaphorically speaking, we liberated the clock from its sole function of measuring and reporting the time by taking the clock hands out of their 'administrative' roles and turning them into dancers." – Humans since 1982

A Million Times at Changi, 2014-2018
While they did not expose much of the design for their work, David Cox, who is the engineer of this project, shared a few hints in his facebook:

Source



From what I could deduce, the project is probably using 2 types of MCUs, one to control each of the motors (such as ATtiny85) and another to control the whole block (ATmega of sort) and then connected via USB to a PC to control the entire assembly.

This article has been collecting links and paragraphs for quite some time (since July 2017!), I've decided to finish it after I've started to learn more about PCB design and actually took it off the breadboard.

Assuming we would like to design our own, my first thought was that I'll need to use pipes, gears, Plexiglas and plenty of patience, just like Cornelius Franz tried:

source
He actually implemented what I thought to make, I've had a single motor with the same driver, I've had to add a step up (since the driver needed a minimum of 8v and used an ATmega328 instead of the STM32F103), I've had to use a a higher voltage than actually needed since the motor would miss steps and the maximum speed wasn't great since I've used AccelStepper which isn't very efficient. I've also thought about using the micro-switches for addressing but i don't think its a good use of available pins, even if used with resistor ladder.

I've thought about the following options for controllers:
- ATmega328 for board MCU, connected to a driver, hall effect sensor and canbus, which can be used without a transceiver on short distances.
- ATmega328 for board MCU, connected to a driver, hall effect sensor and i2c/spi bus
- ATmega32u2/4 for USB connectivity to i2c/spi

My colleague, Allan Schwartz from whatimade.today suggested the i2c route, I had doubts it will work over long distance, but as it turns out there have been uses for i2c over long distances with repeaters (such as PCA9515), I still didn't get around to test the long distance repeater solution, but the datasheet does specify you may not use more than one repeater, but does it also include parallel repeaters?...) Using the i2c as a bus for the entire assembly makes things simpler over communicating with 20 or more USB virtual com ports.

Different layouts, either a PCB per motor or a PCB for 4 motors, which makes things a lot simpler on one hand but won't work if I wanted to add some more visual effects such as addressable Leds.

For drivers, the following options:
- no driver, these motors do not consume too much power (about 20ma VERIFY), so in theory the Arduino can power it, however, when I tried it, the motor produced inconsistent movement for various speeds, I suspect due to the fact I didn't implement micro-stepping in my source code. I did find out that Wojtek Kosak did make it work without any driver, so it might have been my fault it did not work.
- 2 x ULN2003 ($0.2) or DRV8836 ($1.5) (there are many alternatives, just an h-bridge) per motor
- 2 x A3967SLB ($2) per motor
DRV8821 ($4.5), minimum 8v which might complicate things
- L298N ($1) might work
X12.017 stepper driver / VID6606BY8920 /  AX1201728SG ($1), should be able to control 4 motors (or two dual shaft motors) - source, I've tested the AX1201728SG  and it was very stable even in high speeds as long as acceleration control is implemented.

After testing a few drivers, the one that worked best is the AX1201728SG.

And for motors it turns out there are dual shaft stepper motors, the following look pretty promising:
- X40 8798 Stepper Motor ($6.8) - datasheet
- Sonceboz 6407 (27€)
- vid28-05 Stepper Motor
- BKA30D-R5 ($3.8) with a stop, but it turns out the manufacturer already realized that people would like to use them without a stop, so they started manufacturing them without a stop!!

Last but not least is to use a real clock and modify the circuit, the motor is Lavet-type stepping motor and someone made a crazy clock with it, I love it!



All in all, I think Cornelius Franz did a some amazing work, it seems like he's on a good path to have a working replica!


source

I've also found out that Wojtek Kosak Główczewski actually completed a replica, you might want to look at his schematic and his project:

source

Eventually I wanted to build my own, so I went with the parts I could source from Aliexpress as it was available and didn't cost $50 to ship unlike packages from DigiKey or Mouser.

I got a recommendation to check the VID28-05, however it was harder to find, it seems like they are either no longer manufactured or perhaps I didn't look very hard after finding the replica BKA30D-R5, it's also a plus that the manufacturer is on Aliexpress, its a drop in replacement anyway.

source
The original BKA30D-R5 had a hard stop, which should be removed if you want to rotate it 360 degrees


However, the manufacturer took it upon themselves to supply motors without stops!

The vid28 series comes with a thorough datasheet, explaining how to drive the motors, the pinout, measurements and a lot more, if you're planning to use these motors, its definitely worth to read!

I've made a small breadboard with the motor, an arduino and DRV8825 and it kinda worked, I had to jack up the voltage to 12v (outside the specs) so it won't miss any steps and I've even tried the A4988, but it produced a high pitched noise.


So Allan attempted the same thing using shift register (74HC595), from a video he sent me, I saw it was missing some steps and made a-lot of noise, I suspect its due to the lack of micro-stepping.

To zero the hands, I chose a hall effect sensor + 2mm magnets, I've attempted to use the SS49E but it turned out to be not sensitive enough (1.8mV/G), so I'm now attempting to use the SS495 and while its a bit more sensitive (3.3mV/G), its a lot more expensive, so perhaps using a larger magnet or a a sort of magnetic flux concentrator will be a better solution.

It did work properly on one side, I'm not sure if its the N or P, so I'm thinking about building a 3D magnetic sensor (using MLX90393) to diagnose the problem more precisely.

Alternative methods can be to use a reflective optical sensor or a reed switch, but the magnet needed for the reed is too big and heavy to fit on the hands.

I think the research we did on this project makes it relatively simple to implement hardware wise, you may find software other developers wrote in the end of this article. in my opinion,probably the easiest build will be a combination of the original PCB form factor, X12.017 driver, a hall sensor and atmega328p, wire a few assemblies with i2c and to a PC via USB.

I've started to design a more modular PCB, you can shape it into cubes, spheres, towers, what ever your imagination can conjure.


The first revision was a partial success, the motor fits perfectly, the lights work, the homing more or less works.

A few design issues were discovered during the first test, the brownout trips when either the leds turn on or the motor driver resets, and although I covered most of the power requirements by adjusting the trace widths, you can guess what the problem was, decoupling capacitors for example and a main capacitor.

I've also used relatively heavy acrylic hands with magnets on them, so the motors missed steps here and there, I've rewrote AccelStepper to use interrupts and s-curve instead of linear acceleration, which was a lot of fun and should probably affect the overall life of the motors but no noticeable difference except for top speed.

I've also added the famous WS2812B addressable Leds to see what else can this board do.

Eventually I've decided to learn KiCad and the redesign was done from the ground up while learning, at first, I've hated it, but now I'm really enjoying myself designing PCBs!

I'm not sure where this little project is going, but my key take away are the interesting research, the world of stepper motors is not a complete stranger, but this is not NEMA, magnetism and of-curse KiCad!

References:
Humans since 1982 are Bastian Bischoff (b. 1982, Germany) and Per Emanuelsson (b. 1982, Sweden). Since meeting as postgraduate students at HDK Göteborg in 2008, the duo have produced works that defy easy categorisation, situated between visual art and product design. Creating objects and experiential installations, their work manages to be analytical with a healthy dose of escapism.
Facebook: https://www.facebook.com/HumansSince1982/

If you want to buy one and not interested in the engineering part, just head to MoMA Design and you can also get a black one!

Another interesting idea which is somewhat related is Clock by Christiaan Postma, not sure if they are related in any way, still worth a look.

If you're looking only for source code:
Conor Hunt was also inspired by Humans since 1982 and wrote a javascript demo and shared the source code.
Jos Fabre was also inspired and made this demo.
Dmitry Yakimenko wrote an iOS app and published the source code and a demo.
Carlos Cabo wrote a webGL demo and shared the source code.
Takahashi Yuto wrote a demo in elm and shared the source code.
Nicolas Daniel wrote a demo and shared the source code.
Malte Wessel wrote a demo and shared the source code.
Ubayd Rahmonzoda wrote a demo and shared the source code.


There are already existing projects for trying to build a replica:
https://hackaday.com/2014/09/21/ask-hackaday-how-would-you-build-a-clock-clock/
https://hackaday.io/project/7872-clock-clock-clone
https://hackaday.io/project/4164-clock-clock

You might want to take a look at a discussion from mikrocontroller.net, its pretty old but there is some progress there and people are sharing their experiences.

Also, there's a single motor with breakout that someone sells on tindie.

Tags: