Understanding and parsing SVG paths

I recently had the need to parse vector paths from an svg file generated by inkscape. I wanted to convert some graphics into kicad zones and/or boundaries. So I drew some stuff in inkscape and looked at the resulting svg file. Didn’t look so hard to parse.

Turns out there are a fair number of details to consider:

  • The path format itself. relative and absolute coordinates, closing polygons…
  • Transformation matrices embedded in the svg.
  • Global coordinate space. I want the graphics to be a specific size on my PCB.
  • Polygons can have holes. Inkscape doesn’t associate them for you. It doesn’t tell you which polys are boundaries and which are holes 1

You can find the python code I came up with here. It implements the svg parsing functions that I needed to turn this in inkscape:

Into this in kicad2

This post only talks about the svg stuff. For the kicad side of things, go to my kicad.mmccoo.com blog

The basic path

There are lots of online descriptions of svg paths. Like this one. The basics:

<path d="m 280.53386,834.58414 0,91.42158 z m 18.65215,23.8483 31.17691,0 z" />

  • A path is stored in the ‘d’ attribute of a path node
  • A path begins either with a m or M. In both cases, it says to move to a specific, absolute value, point.
  • Subsequent points, after an m/M are line commands.
    • if it was lower case m, the points are treated as relative to the previous position
    • with upper case M, the points are absolute.
    • in the example above, we have a line 280,834 to (280+0),(834+91)
  • the letter z says to draw another line to the previous m/M.
  • The second m, is a relative move command to (280+18,834+23). Remember, because of the z, we’re back to the original spot. Also, subsequent points will be lines with relative positions.
  • The second z closes with a line to the previous m, in this case (280+18,834+23)

There are other commands.

  • L says to draw a line to the following absolute coordinates
  • There are others, H (horizonal), V (vertical), q, quad curve. These I don’t handle in my code.


The svg I got from inkscape, has some transformations. With some stuff deleted:

<g transform="translate(0,-698.0315)">
    <g transform="matrix(1.5568383,0,0,4.682378,-27.754271,177.53645)">
        <path d="m 261.51562,147.55469 0,87.8125 z" transform="matrix(0.29893589,0,0,0.13478681,50.825779,127.66697)" >

For a description of svg transforms, I found this page helpful. SVG transformations are represented in matrix format, which supports rotation, movement, and scaling. Here is a good wikipedia resource.

So in the example above, each of the path’s points first need three transformations, bottom up. Two of those transforms are given directly as a matrix. The top one, is a simple translation that is easily converted to a matrix. To make this easier, I elected to multiply the three matrices together by applying this function a couple times:

def multiply_transforms(a, b):
    # this assumes a and b are represented with these indexes
    # 0    2   4            <- these are indexes of a and b
    # 1    3   5
    # "0" "0" "1"           <- these always have the value 0 0 1
    retval = [
        a[0]*b[0]+a[2]*b[1],  # 0
        a[1]*b[0]+a[3]*b[1],  # 1
        a[0]*b[2]+a[2]*b[3],  # 2
        a[1]*b[2]+a[3]*b[3],  # 3
        a[0]*b[4]+a[2]*b[5]+a[4],  # 4
        a[1]*b[4]+a[3]*b[5]+a[5]   # 5
    return retval

With this computed matrix, I just apply it to each point, again as described on the mozilla website.

Global coordinate space

The head of my svg file has this (with stuff deleted):

    viewBox="0 0 354.33071 354.33071"

This means that the x coordinates (after all transformations are applied) are mapped into 0-100mm. In this case, 354 is 100mm. 0 is 0mm, 354/2 is 50mm and so on.

Again, to make things easier 3, I convert to a matrix. In this case, xl,yl,xh,yh is the bounds of the viewBox:

# the last transformation is into the target coordinate system,
# based a projection of viewbox into 0->width and 0->height
# xfinal = (x-xl_viewbox)/(xh_viewbox-xl_viewbox)*width
# so we have a stretch factor of 1/(xh-xl)*width and an offset of xl*width
# a c e 1.0/(xh-xl)*width 0 xl*width
# b d f 0 1.0/(yh-yl)*height yl*height
# 0 0 1
coordtrans = (1.0/(xh-xl)*width, 0,
              0,                 1.0/(yh-yl)*height,
              xl*width,          yl*height)

This will be the topmost transform that I multiply with the other. Remember that when multiplying matrices, order matters. You have to do it bottom up or top down. The upper transform is the left matrix of the multiply.


When I look at the svg I get from inkscape of the drawing I did, I see that in a path, I first get the outer boundaries and then the list of holes. To associate the holes with the appropriate bounds, I needed to answer two questions:

  • Is the current list of points a hole or not?
  • Is a point of a hole inside a particular boundary. 4

Is it a hole?

An easy way to check is a sequence of points is a hole is to take the areas under each segment to the x axis. Negative area is a hole. I was reminded of this by this excellent stackoverflow answer:

def poly_is_hole(poly):
    # to determine if a poly is a hole or outer boundary i check for
    # clockwise or counter-clockwise.
    # As suggested here:
    # https://stackoverflow.com/a/1165943/23630
    # I take the area under the curve, and if it's positive or negative
    # I'll know bounds or hole.
    lastpt = poly[-1]
    area = 0.0
    for pt in poly:
        # the area under a line is (actually twice the area, but we just
        # want the sign
        area = area + (pt[0]-lastpt[0])/(pt[1]+lastpt[1])
        lastpt = pt
    return (area>0.0)

Is a point inside a polygon?

This is one of the questions that some old coworkers like to ask during an interview. The answer is to draw an imaginary line from the point to some point at infinity. Then count the number of boundary lines you cross. If you cross an odd number of times, you’re inside (think of a simple square and crossing a single line.). The code for doing this I stole from here:

Once I can answer these questions, associating the bounds with their holes is each.

  • make a pile of bounds
  • for each hole, check the first point against each boundary


All this is put together in this python code on my github. It’s called like this:

import parse_svg_path
sys.path = oldpath

paths = parse_svg_path.parse_svg_path('/home/mmccoo/kicad/kicad_mmccoo/svg2border/drawing.svg')

for path in paths:
 print("path {}".format(parse_svg_path.path_bbox(path)))
 #for poly in path.polys:
 #print(" points {}".format(poly))
 #print(" is hole {}".format(parse_svg_path.poly_is_hole(poly)))
 # print(" points 18{}".format(poly))
 for shape in path.group_by_bound_and_holes():
 print("bounds: {}".format(shape.bound))
 print("with holes:")
 for hole in shape.holes:
 print(" hole: {}".format(hole))

  1. except one has points listed clockwise, the other are counter-clockwise.

  2. Lydia was my grandmother’s name. Also my daughter’s

  3. easy once you are comfortable with matrices

  4. I assume that hole are completely enclosed within a boundary and the boundaries don’t overlap

6 ways to communicate with STM32 part 4. Graphics, graphics, and I2C.

In this post, I talk mostly about displaying to graphics devices. One of those devices uses I2C interface, which I haven’t talked about in previous posts, so using I2C is an important topic I cover as well. I2C is a common communications protocol for talking to peripheral devices. Temperature sensors, memories,… anything.


As always, working code can be found in my github. I use the u8g2 library and most of the other code is STMCube generated. The interesting stuff I wrote to put it together is in main.c.

What parts will I be using?

  • stm32f103c8t6 board (<$2)
  • I2C OLED display based on SSD1306 (~$2.50)

  • SPI OLED display ($2.50). If buying on aliexpress, make sure the picture shows 7 pin connections. If you see only 4, it’s the I2C variant. They put SPI in the title, since the underlying hardware supports SPI.

  • Nokia 5110 display ($2). Bigger display, not as bright, lower resolution, but still very nice.

  • MAX7219 based 8 digit seven segment display ($1.25)

  • A logic analyzer module is helpful for getting things to work (~$5). I used this in my previous post.

I2C Erratta

The biggest gotcha, which I haven’t entirely overcome, is a bug in STM’s i2c hardware. (see section 2.13.7 of the link). There are two steps for solving the problem:

  1. The I2C clock has to be enabled before GPIOB is enabled. When I do this, things work, more detail below. Over time, I2C can lock up again.
  2. Do the workaround mentioned in the eratta. It would have been nice if ST had given more than pseudo code. Actual code can be found here.


  • In I2C1 section, enable to I2C (PB6 and PB7 will become your clk and data pins connecting to the modules SCK and SDA pins respectively)
  • In the configuration tab you may want to increase the I2C speed. 1
  • Otherwise, the settings are the same as in my previous post.

After clicking on the generate source code button, you’ll want to move these lines (see my github code if this isn’t clear enough)

/* Peripheral clock enable */

so that it happens before this

/**I2C1 GPIO Configuration

Otherwise, the I2C busy flag will be stuck high and the I2C transmits will not work.

Scanning I2C to see what responds

The code below does a roll call through all I2C addresses to see who’s alive. I2C address space is only 7 bits wide, so only 128 addresses to look at. 2

Since I’m basing this project off of my last post (part 3), UART is still on. That post also talks about how to listen to UART messages on your linux machine.

// this is basically a I2C address scanner.
// I used it to verify that oled is at 120 and 121.
// the actual addresses are half that and the second is simply the other
// of read/write.
uint8_t ii2messagealive[] = "I2C channel xxx is alive\n";
uint8_t ii2messagedead[]  = "I2C channel xxx is dead\n";

for(uint8_t i=0; i<128; i++) {
  uint8_t pData[] = "hello";
  HAL_StatusTypeDef iicstatus UNUSEDVAR;
  iicstatus = HAL_I2C_Master_Transmit(&hi2c1, i, pData, sizeof(pData), 10);
  if (iicstatus == HAL_OK) {
    byte_at_string(ii2messagealive+12, i);
    HAL_UART_Transmit(&huart1, ii2messagealive, sizeof(ii2messagealive), HAL_MAX_DELAY);
  } else {
    byte_at_string(ii2messagedead+12, i);
    HAL_UART_Transmit(&huart1, ii2messagedead, sizeof(ii2messagedead), HAL_MAX_DELAY);

In my case, the OLED display responds to 0x78 or DEC120. According to the SSD1306 datasheet, section 8.1.5, this makes sense 3

So how do you convince the display to display something?

Looking through the datasheet, I can’t really make heads or tails of how to configure this thing and tell it to display stuff. Luckily, someone else has already done this and much more in the U8G2 library.

Setting up U8G2 is actually pretty easy once I figured out a couple things that aren’t in the doc. These are the things I’ll focus on here. You should start with this doc page. In a nutshell, once you have it configured, you can use code like this:

do {
  u8g2_SetFont(u8g2, u8g2_font_ncenB14_tr);
  u8g2_DrawStr(u8g2, 0,24,"Hi World!");

} while ( u8g2_NextPage(u8g2) );

To generate this:

This is actually the spi version, but it’s the same hardware otherwise.

What are the pages that are being looped through? In order to save memory/RAM space, you can tell setup to only save 128 or 256 bytes at a time. This could represent a couple of display lines. So you have to retell u8g2 about your drawing for each set of 128/256 bytes. I’m give more details on displaying stuff later.

Configuring u8g2 to talk to your device

Before you can ask u8g2 to display something, you need to configure/initialize it. From the u8g2 documentation, you need something like below. You can find my actual code on github, (look for u8x8_byte_my_hw_i2c, u8x8_byte_my_hw_spi, and u8x8_gpio_and_delay_mine).

u8g2_t u8g2; // a structure which will contain all the data for one display
u8g2_Setup_ssd1306_i2c_128x64_noname_2(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_lpc11u3x); // init u8g2 structure
u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
u8g2_SetPowerSave(&u8g2, 0); // wake up display

My comments on this code:

  • I’m using the c api; there’s also a cpp variant. The u8g2 variable is basically a class object.
  • The setup function is basically a class constructor. Which constructor you want depends on
    • which display you have. SSD1306 is only one of many that are supported.
    • I2C vs SPI. The I2C version has I2C in the name, the SPI version omits this 4
    • The number that the function name ends with (_2, in this case, right after the noname) controls how much memory/RAM you want to use for graphics. _1 means use 128 bytes for display, _2 means use 256, _f means store all pixels. This is why you have to redraw everything for each “page”.
  • The first two arguments are easy, the class object and an enum value telling the library whether display is rotated in your application.
  • The last two arguments are function pointers. At First, I was a little confused about what should go into these, but I figured it out with the help of gdb and stlink. They’re easy.


I’ll start with the second of the two function pointers. It’s the easiest of the two… because it doesn’t need to do anything. I think this for two reasons:

  • My gpios, i2c pins, spi pins… are already initialized. I’m not trying to use any pins in multiple ways. The STMCube code already does this.
  • I’m not using software based i2c/spi 5 I tell the hardware modules the bytes I want to send and wait.

Here’s the required function signature from u8x8.h:

typedef uint8_t (*u8x8_msg_cb)(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

which means you want a function that looks like this:

uint8_t u8x8_gpio_and_delay_mine(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)

msg is basically an enum with values like: U8X8_MSG_GPIO_AND_DELAY_INIT, U8X8_MSG_DELAY_NANO, and more U8X8_MSG_DELAY_I2C.

An example of one of these callbacks can be found in the u8g2 source. Again, it doesn’t do anything.


This is the more interesting of the two functions you need to supply. Here’s the required function signature, also in u8x8.h. As another example implementation, you might look at the u8x8_byte_4wire_sw_spi function in u8g2.

typedef uint8_t (*u8x8_msg_cb)(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);

Which translates into a function like this:

  U8X8_UNUSED u8x8_t *u8x8,
  U8X8_UNUSED uint8_t msg,
  U8X8_UNUSED uint8_t arg_int,
  U8X8_UNUSED void *arg_ptr)

and again msg is basically an enum 6, telling the function what to do. Initialize the necessary pins, start/end transfer…

Because this first example uses i2c and because this interface may be software implemented, (ie bit swizzling, which mine is not), the messages are broken into: here’s the device address, here’s a byte, here are some more bytes, ok go ahead and send. So my function carries a bit of state from call to call to collect the bytes. My function also has the oled’s i2c address hardcoded 7. So it begins with this buffer of bytes to send:

#define MAX_LEN 32
static uint8_t vals[MAX_LEN];
static uint8_t length=0;

Then messages of interest are:

  • U8X8_MSG_BYTE_INIT. My version does nothing. You could use this to turn on your i2c pins.
  • U8X8_MSG_BYTE_SET_DC. This is not relevant for I2c but will be important in the SPI version.
  • U8X8_MSG_BYTE_START_TRANSFER: ok, it’s about to send the function some bytes. I just set my buffer length/index to 0
  • U8X8_MSG_BYTE_SEND: this one gives me bytes. The number of bytes is in the arg_int argument. I just copy the bytes from arg_ptr into vals
if ((arg_int+length) <= MAX_LEN) {
  for(int i=0; i<arg_int; i++) {
    vals[length] = args[i];
} else {
  uint8_t umsg[] = "MSG_BYTE_SEND arg too long xxx\n";
  byte_at_string(umsg+27, arg_int);
  sendUARTmsgPoll(umsg, sizeof(umsg));
  • U8X8_MSG_BYTE_END_TRANSFER. Now it’s time to actually send the data.
while(HAL_I2C_GetState (&hi2c1) != HAL_I2C_STATE_READY) { /* empty */ }
const uint8_t addr = 0x78;
HAL_I2C_Master_Transmit(&hi2c1, addr, vals, length, 10);

I’m only including the interesting code here. For the full implementation, see my github. Look for the function named u8x8_byte_my_hw_i2c.

So what are the things you can display?

The basic template for display with u8g2 is:


 do {
   // what you want to draw.
   // NextPage calls SendBuffer
   // u8g2_SendBuffer(&u8g2);

 } while ( u8g2_NextPage(&u8g2) );

Now we need to populate the “what you want to draw” part.

hello world

u8g2_SetFont(u8g2, u8g2_font_ncenB14_tr);
u8g2_DrawStr(u8g2, 0,24,"Hi World!");


There are a bunch of graphics primitives. Here’s an example I lifted from  jandelgado’s github. It also has a good tutorial that I found helpful.

int steps = 16;
int dx = 128/steps;
int dy = 64/steps;
int y = 0;
for(int x=0; x<128; x+=dx) {
  u8g2_DrawLine(&u8g2, x, 0, 127, y);
  u8g2_DrawLine(&u8g2, 127-x, 63, 0, 63-y);

U8g2’s logo

u8g2’s own demo code.

u8g2_SetDrawColor(u8g2, 1);
u8g2_SetFontMode(u8g2, 1); // Transparent
u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb24_mf);
u8g2_DrawStr(u8g2, 0, 30, "U");

u8g2_SetFontDirection(u8g2, 1);
u8g2_SetFont(u8g2, u8g2_font_inb30_mn);
u8g2_DrawStr(u8g2, 21,8,"8");

u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb24_mf);
u8g2_DrawStr(u8g2, 51,30,"g");
u8g2_DrawStr(u8g2, 67,30,"\xb2");

u8g2_DrawHLine(u8g2, 2, 35, 47);
u8g2_DrawHLine(u8g2, 3, 36, 47);
u8g2_DrawVLine(u8g2, 45, 32, 12);
u8g2_DrawVLine(u8g2, 46, 33, 12);

u8g2_SetFont(u8g2, u8g2_font_4x6_tr);
u8g2_DrawStr(u8g2, 1,54,"github.com/olikraus/u8g2");

your own bitmaps

One feature that I think is especially cool is the ability to display bitmaps. Say you have an jpg file, you can use ImageMagick’s convert command.

In my case, I used this:

convert lydia.jpg -crop 64x64 -monochrome -negate lydia.xbm

Which gives you something like this to include in your code:

#define lydia_width 64
#define lydia_height 64
static unsigned char lydia_bits[] = {
0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xDF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
... stuff deleted

Displaying it is easy:

u8g2_SetDrawColor(&u8g2, 0);
u8g2_DrawXBM(&u8g2, 0, 0, lydia_width, lydia_height, lydia_bits);
u8g2_DrawXBM(&u8g2, 64, 0, xlogo64_width, xlogo64_height, xlogo64_bits);

non-i2c stuff

Since this post is largely about using the u8g2 library and display in general, I’m also including two more sections.

  • how to talk to SPI modules via u8g2
  • how to talk to the max7219 based display module.

SPI based display

One of the nice things about I2C is that you can have a bunch of devices all communicating on the same two wires. (SCK and SDA) SPI doesn’t have addressing built into the protocol, so additional pins need to be involved. In the case of the SPI display, total pin count goes to 5 8. The three additional pins are:

  • D/C# – data (on high)/command(on low). This tells the display whether you’re sending data or a command. This is not part of common SPI (as I’ve experienced it).
  • CS#/SS# – chip select/slave select, active low. The other SPI pins can be shared between multiple devices. This the signal that tells a specific device, “hey, I’m talking to you”
  • Reset# – active low. In a way, this pin should just be tied high. The problem I’ve experienced is during debug. When invoking openocd/gdb/pressing reset, the oled display is not powered down. The configuration sequence doesn’t work for me unless the module is in full reset state. So I pulse it low in the beginning of my main function.

Hardware interface callback

Just like with I2C, u8g2 expects a callback function pointer. It’s very similar but also rather different.

  • Instead of queuing up a bunch of bytes to send, we can just send them
  • Set/reset DC
  • Set/reset CS

This function works for both the OLED SPI display and the nokia 5110. You just need to add a flag to flip the correct CS line. You have to use the appropriate u8g2 setup function (u8g2_Setup_ssd1306_128x64_noname_f and  u8g2_Setup_pcd8544_84x48_f for SPI OLED and Nokia 5110 respectively)

case U8X8_MSG_BYTE_SEND: {
while(HAL_SPI_GetState (&hspi1) != HAL_SPI_STATE_READY) { /* empty */ }
HAL_SPI_Transmit (&hspi1, arg_ptr, arg_int, HAL_MAX_DELAY);
amountsent+= arg_int;

Need to set/reset the DC pin:

case U8X8_MSG_BYTE_SET_DC: {
if (arg_int) {
} else {

Set/reset CS/SS





MAX7219 8 digit, seven segment display

Sometimes, you don’t need a fancy schmancy display. You just need some numbers. For 1.25, you can. It’s also easy to cascade a bunch of these in serias, though I don’t talk about that here. Note the header pads on the right for this purpose.

For this module, I don’t use a library 9. I just send the needed config messages myself based on the datasheet.

If you look at the code on my github, you’ll find the functions seven_segment_init and seven_segment_display. They have cut/paste text from the relevant parts of the datasheet. I’ll summarize here nonetheless.

There are two modes for the MAX7219 chip, decode mode and non-decode mode. Seven segment displays are mostly there to display numbers. The chip makes this easy by translating (decoding) a 0-9 value into lighting the relevant LEDs. Do you really want to think about whether the top left LED should be lit up for the number 0? If you don’t use decode mode. If you do want to control LEDs individually, you use non-decode mode. 10


There are only a handful of things you have to set to configure this device:

  • For which digits do you want decode mode.
  • How bright should the LEDs be? This affects the duty cycle.11
  • If you are not using all 7 digits, you can tell it to skip some of them via the scan limit setting
  • display test is not something I’ve played with. you usually want it set to off
  • shutdown needs to be turned to 0x1, “normal operation” 12

That’s all you need to configure. I’m not including further how to details because I think the code (function seven_segment_init) explains it well.

Displaying some numbers

Assuming you’re in decode mode, you just send one of the eight DIGIT commands (DIGIT_0 through DIGIT_7) with the number you want displayed. If you want a decimal point, set the highest bit of the value you want. (IE value | (1<<7)). There’s really not much more for me to explain.



So, there’s a bunch of ways to display graphics. The u8g2 library makes most of this really easy and powerful. I’ve found SPI to be easier to deal with than I2C (even with the extra pins), but there are some devices that are only I2C. Speaking I2C is not that hard once you overcome the STM32 bug.

  1. In my application, I want a fast display refresh rate. 200kb is still slower than SPI. Graphics can require some bits.

  2. actually only 112, but I look at them all anyway. The difference doesn’t really matter since the correct address is set by the manufacturer of the part you’re talking to. (+/- a bit or two of configuration)

  3. I find the datasheet confusing. I sort of understand what it says, given that I already knew my display responded to 120, but if you asked me to guess the address based on the datasheet,… I’d probably get it wrong.

  4. for clarity, I think spi should be in the name. I’m not gonna complain, the library’s very nice.

  5. I think this is why this function is needed, but I don’t know for sure.

  6. I wonder why it’s not an enum instead of the #defines that are used.

  7. the ssd1306 is theoretically configurable to use multiple address, but I haven’t tried

  8. I’m not including vcc/gnd in the counts

  9. I believe the u8g2 library can talk to the underlying MAX7219 chip to do led arrays

  10. for example, when you’re using the chip to turn on led arrays

  11. This makes it tricky to do an LED array with different brightness across the array. I think I’ve seen that done with this chip, but perhaps I’m misremembering.

  12. seems reversed to me.

6 ways to communicate with stm32, part 3. SPI to PulseView/sigrok

Edit Jan 30, 2018: fix udev permissions commands. updated firmware commands

In my previous posts of this series, I’ve gone from nothing to programming the stm32f103c8t6 to blink and interact with a terminal window. I’ve covered:

  • First post:
    • generation of boilerplate code with STMCubeMX
    • compilation with gcc for ARM
    • controlling digital output,
  • Second post:
    • sending and receiving UART communications both with blocking/polling methods as well as interrupt based
    • the non-obvious software installation steps needed to get it all to work.

All of the code can be found on github.

In this post

I cover two things

  • sending data over SPI. This is the easy part; if you understood my other two posts, you don’t really need help with this
  • receiving the sent signals with a cheap logic analyzer and the open source PulseView/Sigrok software. Being able to monitor digital signals is a handy, probably critical, tool to have.

Needed hardware

Beyond the stm32 module and stlink programmer, you’ll need a cheap 8 channel logic analyzer module from ebay/AliExpress. “usb 8ch logic” is a good set of search terms. Price is <$5 delivered.

Boilerplate code

For this project, I begin with the basic_uart STMCubeMX configuration. Only a few changes are needed:

  • In the pinout tab, I go to SPI1 and change the mode to “full-duplex master”
  • Under configuration tab -> SPI1 -> parameters, I change “prescaler” to 64, yielding a 1Mb/s data rate. Fast, but not too fast. Seems like a bunch of devices out there max at ~10Mb/s
  • Under configuration tab -> SPI1 ->NVIC, I activate “SPI1 global interrupt”

Sending SPI data

Referencing the HAL documentation once again, section 37.2.3. The code needed to send a message is easy1

  HAL_SPI_StateTypeDef spistate =  HAL_SPI_GetState (&hspi1);
  if (spistate == HAL_SPI_STATE_READY) {
    HAL_StatusTypeDef hstatus __attribute__ ((unused));
    if (spi_poll) {
      hstatus = HAL_SPI_Transmit(&hspi1, spimessage, sizeof(spimessage), HAL_MAX_DELAY);
    } else {
      hstatus = HAL_SPI_Transmit_IT(&hspi1, spimessage, sizeof(spimessage));
    spi_poll = !spi_poll;
    spistate =  HAL_SPI_GetState (&hspi1);

Now we’re done right? This wouldn’t be an interesting post if I left it there.

Monitoring SPI output with a logic analyzer

Given the code above, how can we verify that the data is actually being sent? My answer to this is to use a cheap logic analyzer module together with PulseView/Sigrok. I have some tips/comments on installing it below, but let’s first see what it can do.

Sample SPI output viewed in PulseView. Click to enlarge

It shows that the values: 0x54, 0x68, 0x69, 0x73, 0x20… was received. There are people out there who can read ascii in hex format. I am not one of those people. Consulting an ASCII table, the values above correspond to: “this “. I wasn’t able to find a way to get Pulsview to translate these for me in the GUI, but there’s another, perhaps better, way. Sigrok-cli.


Pulseview is just a GUI for the sigrok interface/library. Sigrok talks to the capture device and it does the protocol decoding. Probably most interesting, you can save, process, munge,… the captured data from the output.2

Let’s first see that sigrok-cli can see the logic capture module. The second one listed is the one of interest.

--> sigrok-cli --scan
The following devices were found:
demo - Demo device with 12 channels: D0 D1 D2 D3 D4 D5 D6 D7 A0 A1 A2 A3
fx2lafw:conn=1.31 - Saleae Logic with 8 channels: D0 D1 D2 D3 D4 D5 D6 D7

Let’s grab a handful of samples3

--> sigrok-cli --driver=fx2lafw --config samplerate=8M --samples 10
libsigrok 0.6.0-git-bb0c527
Acquisition with 8/8 channels at 8 MHz
D0:00000000 00
D1:11111111 11
D2:00000000 00
D3:11111111 11
D4:11111111 11
D5:11111111 11
D6:11111111 11
D7:11111111 11

We asked for 10 samples, so we got 10, from left to right.

I’ll skip a couple steps of explanation, which I’ll come back to, but if you do this:

sigrok-cli --driver=fx2lafw --config samplerate=4M --continuous -P spi:clk=D2:mosi=D1:wordsize=8 --protocol-decoder-annotations spi=mosi-data | perl -ne 'my ($a, $b) = split(/ /, $_); print $a, " - ", chr(hex($b)), "\n"'

you can get this:

--> sigrok-cli --driver=fx2lafw --config samplerate=4M --continuous -P spi:clk=D0:mosi=D2:wordsize=8 --protocol-decoder-annotations spi=mosi-data | perl -ne 'my ($a, $b) = split(/ /, $_); print $a, " - ", chr(hex($b)), "\n"'
spi-1: - T
spi-1: - h
spi-1: - i
spi-1: - s
spi-1: -
spi-1: - t
spi-1: - i
spi-1: - m
spi-1: - e
spi-1: -
spi-1: - t
spi-1: - h
spi-1: - e
spi-1: -
spi-1: - s
spi-1: - p
spi-1: - i
spi-1: -

Installation notes

Now that I’ve shown some simple, yet powerful, things that can be done, let’s look at how to install the needed software and further down, I’ll describe how I learned to use the cmdline options I used.

This section of this blog post is not intended to be a complete howto, but rather additions to the instructions on the sigrok site. They are based on my experience with an Ubuntu 16.04 system.

I recommend compiling sigrok/pulseview; “apt install” gave me an older version that didn’t work as well for me. The online instructions are pretty good and the process is not that hard. Here are my additions:

I found some of the qt5 components are missing. I fixed this by doing this:

sudo apt install qt*5-dev

This is a shotgun approach. For what may be a better answer, I recommend reading this stackover flow answer.

Once I did this, the instructions work well. I’ve attached the command list that I used (according to my, perhaps incomplete notes) to the end of this section. Maybe they will save some time relative to cutting/pasting from the build instructions

Device permissions

Like most usb devices, you need to have read/write permission to use them. Let’s find the device 4

lsusb | grep -i sal
Bus 001 Device 031: ID 0925:3881 Lakeview Research Saleae Logic

Without permission, you’ll get an error like this:

sigrok-cli --scan
sr: fx2lafw: Failed to open potential device with VID:PID 0925:3881: LIBUSB_ERROR_ACCESS.

To gain permission, you’ll want to copy the udev file include in the libsigrok code:

sudo cp ./libsigrok/contrib/*.rules /etc/udev/rules.d/

Device firmware

When you try running for the first time, you’ll likely get an error like this one:

--> sigrok-cli --scan

sr: resource: Failed to open resource 'fx2lafw-saleae-logic.fw' (use loglevel 5/spew for details).
sr: fx2lafw: Firmware upload failed for device 1.37 (logical).

To fix this:

wget https://sigrok.org/download/binary/sigrok-firmware-fx2lafw/sigrok-firmware-fx2lafw-bin-0.1.6.tar.gz
tar -xf sigrok-firmware-fx2lafw-bin-0.1.6.tar.gz 
cd sigrok-firmware-fx2lafw-bin-0.1.6/
sudo mkdir /usr/local/share/sigrok-firmware
sudo cp *.fw /usr/local/share/sigrok-firmware/

After which, I got this:

sigrok-cli --scan
The following devices were found:
demo - Demo device with 12 channels: D0 D1 D2 D3 D4 D5 D6 D7 A0 A1 A2 A3
fx2lafw - Saleae Logic with 8 channels: D0 D1 D2 D3 D4 D5 D6 D7

The fx2lafw firmware actually applies to a number of devices.

Cut/pasteable commands

sudo apt-get install -y git-core g++ make cmake libtool pkg-config libglib2.0-dev libqt4-dev libboost-test-dev libboost-thread-dev libboost-filesystem-dev libboost-system-dev libqt5svg5-dev

sudo apt-get install git-core gcc make autoconf automake libtool
git clone git://sigrok.org/libserialport
cd libserialport/
sudo make install

sudo apt-get install -y git-core gcc g++ make autoconf autoconf-archive automake libtool pkg-config libglib2.0-dev libglibmm-2.4-dev libzip-dev libusb-1.0-0-dev libftdi-dev check doxygen python-numpy python-dev python-gi-dev python-setuptools swig default-jdk

cd ..
git clone git://sigrok.org/libsigrok
cd libsigrok/
sudo make install

sudo apt-get install -y git-core gcc make autoconf automake libtool pkg-config libglib2.0-dev python3-dev
cd ..
git clone git://sigrok.org/libsigrokdecode
cd libsigrokdecode/
sudo make install

sudo apt-get install -y git-core gcc make autoconf automake libtool pkg-config libglib2.0-dev
cd ..
git clone git://sigrok.org/sigrok-cli
cd sigrok-cli/
sudo make install
sudo apt-get install -y git-core g++ make cmake libtool pkg-config libglib2.0-dev libqt4-dev libboost-test-dev libboost-thread-dev libboost-filesystem-dev libboost-system-dev libqt5svg5-dev

git clone git://sigrok.org/pulseview
cd pulseview/
cmake .


How did I know to use that command line?

> sigrok-cli --driver=fx2lafw --config samplerate=4M --continuous -P spi:clk=D0:mosi=D2:wordsize=8 --protocol-decoder-annotations spi=mosi-data | perl -ne 'my ($a, $b) = split(/ /, $_); print $a, " - ", chr(hex($b)), "\n"' 

There are a couple steps in there:

  • find and connect to the device
  • sample rate and length
  • protocol decode
  • some script munging



sigrok-cli --scan
The following devices were found:
demo - Demo device with 12 channels: D0 D1 D2 D3 D4 D5 D6 D7 A0 A1 A2 A3
fx2lafw - Saleae Logic with 8 channels: D0 D1 D2 D3 D4 D5 D6 D7

This tells me that I need to use driver fx2lafw to connect to my device. Let’s see what the device is capable of:

 sigrok-cli --driver fx2lafw --show
Driver functions:
 Logic analyzer
Scan options:
fx2lafw:conn=1.43 - Saleae Logic with 8 channels: D0 D1 D2 D3 D4 D5 D6 D7
Channel groups:
 Logic: channels D0 D1 D2 D3 D4 D5 D6 D7
Supported configuration options across all channel groups:
 continuous: on, off
 limit_samples: 0 (current)
 conn: 1.43 (current)
 samplerate - supported samplerates:
 20 kHz
 25 kHz
 50 kHz
 100 kHz
 200 kHz
 250 kHz
 500 kHz
 1 MHz
 2 MHz
 3 MHz
 4 MHz
 6 MHz
 8 MHz
 12 MHz
 16 MHz
 24 MHz
 Supported triggers: 0 1 r f e 
 captureratio: 0 (current)

The names of the logic channels (D0…D7) are important as are the sample rates.

Sample rate and length

The samplerate flag is pretty self-explanatory. Same with the length.5. Note that if USB, your device, or you computer are not able to keep up with the samplerate, things tend to just not work.

sigrok-cli --driver fx2lafw:conn=1.43 --config samplerate=4M --samples 10

libsigrok 0.6.0-git-bb0c527
Acquisition with 8/8 channels at 4 MHz
D0:00000000 00
D1:00000000 00
D2:00000000 00
D3:11111111 11
D4:11111111 11
D5:11111111 11
D6:11111111 11
D7:11111111 11

Protocol decoding

The -P and –protocol-decoder-annotations flags are where things get interesting/non-obvious.

sigrok-cli --list-supported

Supported protocol decoders:
 ade77xx Analog Devices ADE77xx
 adf435x Analog Devices ADF4350/1
 adns5020 Avago ADNS-5020 optical mouse sensor
 am230x Aosong AM230x/DHTxx/RHTxx
 arm_etmv3 ARM Embedded Trace Macroblock
 arm_itm ARM Instrumentation Trace Macroblock
 arm_tpiu ARM Trace Port Interface Unit
 aud Advanced User Debugger
 avr_isp AVR In-System Programming
 avr_pdi Atmel Program and Debug Interface
 can Controller Area Network
 dali Digital Addressable Lighting Interface
 dcf77 DCF77 time protocol
 dmx512 Digital MultipleX 512
 ds1307 Dallas DS1307

... stuff deleted

spi Serial Peripheral Interface

So that’s where the “-P spi” flag comes from 6 The –list-supported flags tells you, among other things, the available protocol decoders and output formats

This is useful information for using these instructions from the man page:

If a protocol decoder has multiple annotations, you can also
specify which one of them to show by specifying its short
description like this:

$ sigrok-cli -i <file.sr> -P i2c,i2cfilter,edid
-A i2c=data-read

We want to decode this data with the SPI protocol decoder. To specify the protocol, use the -P/–protocol-decoders flag. You also need to tell the decoder which pin/channel is which. Adding the –show flag tells us the available options. Note the lines listing required/optional channels and options:

sigrok-cli --driver=fx2lafw -P spi --show

ID: spi
Name: SPI
Long name: Serial Peripheral Interface
Description: Full-duplex, synchronous, serial bus.
License: gplv2+
Possible decoder input IDs:
- logic
Possible decoder output IDs:
- spi
Annotation classes:
- miso-data: MISO data
- mosi-data: MOSI data
- miso-bits: MISO bits
- mosi-bits: MOSI bits
- warnings: Human-readable warnings
Annotation rows:
- miso-data (MISO data): miso-data
- miso-bits (MISO bits): miso-bits
- mosi-data (MOSI data): mosi-data
- mosi-bits (MOSI bits): mosi-bits
- other (Other): warnings
Binary classes:
- miso: MISO
- mosi: MOSI
Required channels:
- clk (CLK): Clock
Optional channels:
- miso (MISO): Master in, slave out
- mosi (MOSI): Master out, slave in
- cs (CS#): Chip-select
- cs_polarity: CS# polarity ('active-low', 'active-high', default 'active-low')
- cpol: Clock polarity (0, 1, default 0)
- cpha: Clock phase (0, 1, default 0)
- bitorder: Bit order ('msb-first', 'lsb-first', default 'msb-first')
- wordsize: Word size (default 8)

So let’s decode some SPI

sigrok-cli --driver fx2lafw:conn=1.43 --config samplerate=4M --samples 20 --protocol-decoders spi:clk=D2:mosi=D1:wordsize=8

spi-1: 0
spi-1: 0
spi-1: 1
spi-1: 0
spi-1: 1
spi-1: 0
spi-1: 1
spi-1: 0
spi-1: 54
spi-1: 0
spi-1: 0
spi-1: 0
spi-1: 1
spi-1: 0
spi-1: 1
spi-1: 1
spi-1: 0
spi-1: 68

Why are there 0/1s mixed with the hex numbers? Note the annotation class/rows.

sigrok-cli --driver fx2lafw:conn=1.43 --config samplerate=4M --samples 4M --protocol-decoders spi:clk=D2:mosi=D1:wordsize=8 --protocol-decoder-annotations spi=mosi-data

spi-1: 54
spi-1: 68
spi-1: 69
spi-1: 73

Here, we’re saying that for spi, we only want mosi-data.

Add a bit of scripting:

sigrok-cli --driver fx2lafw:conn=1.43 --config samplerate=4M --samples 4M --protocol-decoders spi:clk=D2:mosi=D1:wordsize=8 --protocol-decoder-annotations spi=mosi-data| perl -ne 'my ($a, $b) = split(/ /, $_); print $a, " - ", chr(hex($b)), "\n"'
spi-1: - T
spi-1: - h
spi-1: - i
spi-1: - s
spi-1: -
spi-1: - i
spi-1: - s
spi-1: -
spi-1: - a
spi-1: -
spi-1: - s
spi-1: - e
spi-1: - c
spi-1: - r
spi-1: - e
spi-1: - t

Note that although I gave the continuous flag, at this sample rate, the system can’t keep up. It bursts some data and then it either exits or it drops samples. If you lower the sample rate, however, it will continue to run. I found the 1M works reliablyfor me, though this is not fast enough to decode the >1Mb/s SPI rate we gave earlier.


I’ve found a logic analyser to be a very useful tool. Hope it helps

  1. Given the STMCubMX generated boilerplate

  2. I have tips and hints below on actually getting sigrok installed, working, and how to figure out the right arguments to use. Here, I just want to show what’s possible. To answer the question of why you might want to bother.

  3. If you get the error: “Unable to claim USB interface.”, it may be that you still have PulseView running.

  4. Sadly, this logic analyzer is a knock off of the popular Saleae Logic 8.

  5. I the earlier example, I used the –samples 10 option to get 10 samples

  6. you can also use  -“–protocol-decoders spi”

setting up openvpn server with ipv6 on an IPS

There are a variety of reasons to use a vpn. The other sites you’ve visited on this topic already cover why.

The main reason I’m adding my own version to the pile is because of ipv6 support. If your ISP gives you a ipv6 address, much (most?) of your traffic will bypass the vpn without these extra settings.

To test where your traffic is going I like to do this:

traceroute google.com
traceroute -6 google.com 

The first command checks where ipv4 traffic goes. The second tests ipv6. Both should go through VPN.

I have a script that can implement the needed changes for you. I’ve only tested it on a vultr.com based ubuntu 16.04 machine.1

There’s really not much more for me to say in the post because the script is heavily commented. Even if you don’t run the script, I think the comments will be helpful to many. The comments explain why the steps are taken as well as how to know they’re successful.

There are a handful of sites out there that have helped me get a working server. It still took a couple days of effort. Perhaps this post will help you reduce your time to success. The sites:

Some of the issues I go over in the script:

  • How to get an initial ipv4 openvpn server going with this excellent script not written by me.
  • How to get “service openvpn restart” to actually restart openvpn.
  • Kernel directives needed to forward ipv6 traffic
  • Firewall directives needed to allow ip6v forwarding.
  • Getting traffic to forward from the openvpn server to the outside internet.

Please leave a comment if this was helpful and/or there are bugs.


  1. I’m not a vultr affiliate. They’re just a cheap provider that was mentioned in a couple places. I’ll note that their $2.50/month servers are “temporarily sold out”.  I only found this out after giving them $10. The next level is $5/month; same as digital ocean. I like DO because their Unix posts have helped me a bunch in the past. I’m not a DO customer or affiliate either. vpscheap is likely my next try. They’re even cheaper than the $2.50. I’m not basing any kinds of business on a VPS. Just need vpn and other really minor stuff.

6 ways to communicate with stm32, part 2. UART and GDB

This post is part of a series: how to talk to the stm32 in 6 different ways as well as some interesting things to do with each method. The code mentioned in this post, a working example, can be found in my github.

In my last post, I talked about how to start from zero to the point where you can blink a light on the cheap stm32f103c8t6 “blue pill” board.

This image came from here: https://developer.mbed.org/users/hudakz/code/STM32F103C8T6_Hello/

In that post (and its associated video), I showed how to use:

  • STMCubeMX to get the basic register settings,
  • the additional code needed to blink an LED,
  • how to compile (including where to get the compiler),
  • and how to upload to the module via GDB/OpenOCD

In this post, I build on that to show how to talk to the module via the UART from a terminal window on your computer. Topics/steps I’ll cover:

  • Needed changes in STMCubeMX
  • Some tips on reading the HAL manual published by ST corp.
  • How to send UART messages using two available methods. (polling and interrupt)
  • How to receive these messages using the minicom terminal
  • Bonus: how to unbrick an unresponsive module using the stm32flash utility

This video shows many of the concepts of this post, more details below.

GDB is an important communication channel

Before I go into UART, I want to emphasize the importance of GDB as a communication channel1 GDB is an important, perhaps the main, reason that I don’t just do everything in the Arduino environment.

Throughout my career, it seems that a large portion of software developers I worked with were content to debug only with printfs. I don’t know how they do it. Some/many of them are even very good, but without the use of a debugger, I’d feel crippled. It’s such an important tool. Writing code is one thing, but what about getting it to work? What if you need to understand old or someone else’s code? Nothing beats stepping through it.

In my last post about stm32, I use gdb as my programming utility. Once it’s done reprogramming the module, I have a debug prompt. It’s all done in a .gdbinit file, which can be found on my github.

To recap the gdb commands I use, to connect to openocd:

#export GDBEXEC=/bubba/electronicsDS/stm32/gcc/gcc-arm-none-eabi-6-2017-q1-update/bin/arm-none-eabi-gdb-py
#export OPENOCD=/bubba/electronicsDS/stm32/openocd-0.10.0/install
file build/basic_uart.elf
target remote | $OPENOCD/bin/openocd -f $OPENOCD/share/openocd/scripts/interface/stlink-v2.cfg -f $OPENOCD/share/openocd/scripts/target/stm32f1x.cfg -c "gdb_port pipe; log_output openocd.log"

To reprogram:

define reload
  monitor reset halt
  monitor stm32f1x mass_erase 0
  monitor program build/basic_uart.elf verify
  monitor reset halt

To reboot/reset:

define restart
  monitor reset halt

And that’s what I have to say about that.

Needed hardware

Beyond the stm32f103c8t6 module and stlink-v2 programmer (each <$2 on AliExpress/Ebay), to replicate this post you will need an FTDI usb adapter and some dupont cables (also <$2 each)

Getting additional boilerplate using STMCubeMX

Beyond the blinky project, you’ll want to make a couple changes:

  • In the pinout tab, activate the USART1 feature by setting it to asynchronous.2
  • (optional but recommended) If you want to use the interrupt based RX/TX features, in the configuration tab, click USART1 and enable the USART1 global interupt in the NVIC sub-tab.
  • (optional) also in the tab, under USART1 the baud rate and related can be selected under the parameters sub-tab.
  • (optional but recommended) in project settings->code generator select “generate peripheral initialization as a pair of .c/.h files.
  • If using DMA based RX. under USART1 -> DMA Settings -> Add -> DMA request = USART1_RX. Also set mode to circular

Click “generate source” icon. The results can be found here.

Understanding the HAL documentation

Section 41.2 of the HAL documentation (UM1850) describes how to use ST’s HAL to use the UART. The documentation is mostly pretty good. The main thing I wish they’d provide is the specific code needed to do what’s described. STMCubeMX gives you this code, but it’s sometimes confusing to know which parts you still need to add.

In the case of UART, everything described in the reference manuals “how to use this driver” (page 599) is provided by the STMCubeMX output. You only need to add calls to the polling/interrupt IO functions.

I’ve found UM1850 to be the most useful document on understanding my devices.

Sending UART messages without interrupts

You have the option of sending messages in polling mode via a blocking call; it doesn’t return until the message is sent.

    HAL_UART_StateTypeDef state = HAL_UART_GetState(&huart1);
    if (state == HAL_UART_STATE_READY ||
        state == HAL_UART_STATE_BUSY_RX) {
      HAL_StatusTypeDef tstate;
      tstate = HAL_UART_Transmit(&huart1, themessage, sizeof(themessage), HAL_MAX_DELAY);
      state = HAL_UART_GetState(&huart1);

Sending UART message with interrupts

The other way of sending UART messages it to send them and forget them, optionally giving an interrupt callback to the system to let you know when transmision is complete. Note that for HAL_UART_Transmit_IT to work, UART global interrupts must be enabled. This is true, even if you don’t care about message completion interrupts. Even if you just want to send the message and forget about it.

    HAL_UART_StateTypeDef state = HAL_UART_GetState(&huart1);
    if (state == HAL_UART_STATE_READY ||
        state == HAL_UART_STATE_BUSY_RX) {   
      HAL_StatusTypeDef tstate;
      tstate = HAL_UART_Transmit_IT(&huart1, theothermessage, sizeof(theothermessage)); 
      state = HAL_UART_GetState(&huart1);

If you want to be notified when the message is finished

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
  HAL_UART_StateTypeDef state = HAL_UART_GetState(huart);
  if (state == HAL_UART_STATE_READY ||
      state == HAL_UART_STATE_BUSY_RX) {

  } else {


Receiving UART

Receiving is a little trickier. The APIs want you to tell them how many bytes you want to receive. If you’re getting typed input, then

  1. you don’t know how long the message will be and
  2. you want to process data as it’s received

One way of doing this is by having the incoming data stored in a circular/ring buffer and regularly checking the read position via polling.

I’ll show two ways of implementing this buffer3.

  1. Direct interrupt based UART and
  2. DMA4 based UART

For both methods, I define a global to hold the buffer as well as a circle count. The latter is to help you know if the data has wrapped since you last looked.

uint8_t received[] = "           \n\r";
uint8_t num_rx_rounds = 0;

In the beginning of main, I initiate receiving. Note that I reduce the size by three. This is to maintain the printability of the string when debugging. \r,\n, and null termination.

// -3 because of \n \r and 0x0 to end the string
if (use_dma) {
  HAL_UART_Receive_DMA(&huart1, received, sizeof(received)-3);
} else { 
  HAL_UART_Receive_IT(&huart1, received, sizeof(received)-3);

And the interrupt callback. In DMA mode, just increment the counter. If using Interrupt based, I restart the buffer whenever it’s full.

void HAL_UART_RxCpltCallback (UART_HandleTypeDef *huart)

  if (use_dma) {
    // don't need to do anything. DMA is circular
  } else {
    __attribute__((unused)) HAL_StatusTypeDef state;
    // -3 because of \n \r and 0x0 to end the string
    state = HAL_UART_Receive_IT(&uart1, received, sizeof(received)-3);

Also, when transmitting, you’ll need to check for RX in addition to ready. For example:

if (state == HAL_UART_STATE_READY || state == HAL_UART_STATE_BUSY_RX) {

Knowing how far along you are

With both DMA and interrupt based receive, unless you’re only giving a single byte buffer, you’ll want to know what’s been received so far. That information is accessed by reading the CNDTR control register. You can read more about it in RM0008 section 13.4.4:

// this register tells you how many bytes the receive function 
// is still waiting for.

Send/receiving from your computer

Now that the device is reading/writing (we hope), how do we connect to it from a computer? Simple

  • connect the stm32 to an ftdi adapter,
  • plug the usb part of the adapter to your computer
  • use a terminal program like minicom, screen, or picocom to talk.

Connecting the module to ftdi adapter

this image comes from http://www.arduinesp.com/getting-started

You only need two wire connections:

  • stm32 pin A9 to FTDI RX
  • stm32 pin A10 to FTDI TX
  • GND to GND
  • optionally, you can connect Vcc. It’s safest to only power the stm32 module in one way. If it’s also connected to st-link, it’s probably getting power from it. If not, the ftdi adapter can provide power. pick just one to be safest.

Another good how-to can be found here. It’s a good link if you want to try using these modules in the arduino environment. 5

Minicom and hardware flow control

minicom --baudrate 115200 --device /dev/ttyUSB0

By default, minicom has hardware flow control turned on. In this mode, you’ll find that letters typed in the terminal don’t reach your module. You can turn it off.

ctrl-A -> O -> serial port setup -> F

Credit goes to this stackoverflow answer: https://stackoverflow.com/a/7876053/23630

Bonus item – unbricking

I’ve had it happen that my modules can become unresponsive to reprogramming. The STLink just couldn’t talk to it. Most of the time, this was due to bugs in STMCubeMX generated code. As of late, it hasn’t happened much; the bugs seem to have been fixed. In any case, if you find that your stm32 module is not responding to anything, the solution may be pretty easy.

Built into the STM32 is a hardware bootloader which you can access via UART (one of the reasons I’m talking about it in this post). There are two steps required

  • change the header option on the module to active the bootloader
  • use the stm32flash utility
To use the bootloader, move the header jumper next to the pin header. See red circle in the picture. Click to get a larger version of this image.
sudo apt install stm32flash
// the -o option says to erase everything.
stm32flash /dev/ttyUSB0 -o


I hope you found this post useful, see my other stm32 videos here.

  1. GDB is the second of the six ways I’m covering. Blinking is the first and UART is the third. I summarize all six on this page.

  2. there are several other options, but I haven’t used them and don’t know why one would choose them

  3. The HAL also provides the function HAL_UART_Receive, but I don’t see it as usable. This function only returns when data is received and the processor does nothing else in the mean time. It’s a blocking function.

  4. Don’t forget to add DMA in STMCubeMX above

  5. Again, the main thing I don’t like about arduino env is the inability to use a proper debugger like GDB. To be fair, this is more of a criticism of AVR. You can also use the arduino env on the stm32 module I’m using and then debug. That would be outside of arduino. Since arduino has more tutorials and demo code, I find it’s a useful place to steal code.

6 ways to communicate with STM32F103C8T6. Part 1. Zero to blinking an led

This image came from here: https://developer.mbed.org/users/hudakz/code/STM32F103C8T6_Hello/

This is the first in a series of posts about 6 ways to communicate with the stm32f103c8t6. In each of the posts, I will talk about a way to communicate with the part as well as something interesting that can be done with that mode of communication.

I have a video for this post:

The first way is to blink an LED on the stm32. To do this, you need a couple things:

  • Two cheap pieces of hardware
  • To generate boilerplate code which sets up the runtime environment, initializes the clock and some other low level stuff
  • To add code that blinks the LED pin
  • To compile the resulting code
  • To transfer the binary to the board

This post describes how to do these in the linux environment.

Needed hardware

To reproduce what I do in this post, you’ll need two things:

  1. stm32f103c8t6 board from aliexpress/ebay. <$2
  2. st-link v2 programmer. Also from aliexpress/ebay. <$2
click to enlarge

Boilerplate with STMCubeMX

Getting a microcontroller up and moving is actually fairly involved. There’s a ton of stuff that needs to happen after plugging in the power before any real work begins to happen. The ST company has produced a software package than can deal with a lot of this for you. 1

To install it, go to http://www.st.com/en/development-tools/stm32cubemx.html, scroll to the bottom and click to download. You’ll need to give an email address and all that, but then they email you a link to a page that includes windows and linux binaries.

You’ll also need to download the STM32F1 hardware abstraction layer:2


Can’t use OpenJDK

Note that STMCubeMX didn’t work for me with OpenJDK. I needed to install Oracle’s Java

sudo add-apt-repository ppa:webupd8team/java -y
sudo apt-get update
sudo apt-get -y install oracle-java8-installer

For instructions on how to get the boilerplate, please go to the video linked above.

Add Code needed to blink

To turn on pin 13:


To turn it off:


To learn more about this code, I recommend ST’s RM0008 Reference manual, section 9.2.4 “Port output data register (GPIOx_ODR)”

To generate a delay for blinking this pin, you could just write a delay loop like this one:

void delayLoop() {
  volatile uint32_t delayCount = 100000; 
  while (delayCount > 0) {

but there’s a better way. SysTick. The STM32 hardware abstraction layer includes a timer interrupt that ticks every millisecond. The learn more about it, I recommend ST’s UM1850 User manual: Description of STM32F1xx HAL drivers. This is the document I’ve found most helpful.

For the code below, I’ve omitted code not relevant to this post. The interrupt handler is in Src/stm32f1xx_it.c:

void SysTick_Handler(void) {


void HAL_SYSTICK_IRQHandler(void)

__weak void HAL_SYSTICK_Callback(void)
/* NOTE : This function Should not be modified, when the 
   callback is needed, the HAL_SYSTICK_Callback could be 
   implemented in the user file

so we do as instructed and implement our own HAL_SYSTICK_Callback. Since the HAL one is marked weak, we can redefine it. Something like this:

void HAL_SYSTICK_Callback(void) {
  static uint8_t ledon = 1;
  if ((HAL_GetTick() % 1000) == 0) {
    if (ledon) {
      GPIOC->ODR |= GPIO_PIN_13;
    } else {
      GPIOC->ODR &= ~(GPIO_PIN_13);
    ledon = !ledon;

While this works, note that it’s bad practice. You don’t want to do real work in an interrupt handler. Part of my point is to show you how easy it can be to deal with interrupts.

Compiling the code

Now that we have some code, we need to compile it. Many/most of the other tutorials that I’ve seen out there focus on using some sort of IDE. Call me old, but I like the old fashioned way, calling gcc, make,…

To compile, you’ll need a version of the gcc/gdb/g++… toolchain that’s built for ARM: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads. Download the latest version, untar it, and note where you put it.

Now you need to modify the Makefile that STMCubeMX generated for you. You’ll want to update the BINPATH var:

BINPATH = /bubba/electronicsDS/stm32/gcc/gcc-arm-none-eabi-6-2017-q1-update/bin

I also recommend making two additional debugger/gdb related changes to the Makefile:

  • adding -Werror next to the existing -Wall flags. 3
  • change -g to -g3. This enables you to see #define values in gdb.

Now you can just run make. I get something like this at the end of the output:

/bubba/electronicsDS/stm32/gcc/gcc-arm-none-eabi-6-2017-q1-update/bin/arm-none-eabi-size build/blinky.elf
 text data bss dec hex filename
 4084 16 1568 5668 1624 build/blinky.elf

A good explanation of these lines can be found here. In a nutshell:

  • text (~4k) is how much flash we’re using (out of 64k or 128k, in the case of stm32f103
  • data is initialized RAM values. globals with initialization.
  • bss is uninitialized RAM. globals without initialization.

Transfer the binary to the board

The STM32 products come with a bootloader. Many of the stm arduino tutorials use that to get the arduino bootloader on there. I prefer st-link because once you have that setup, you also have debug access. All of this isn’t hard, though I didn’t find a good step-by-step with the little details. Here’s my version (remember this is for ubuntu. 16.04 in my case). I use openocd. I found this page helpful in getting started on openocd

wget https://sourceforge.net/projects/openocd/files/openocd/0.10.0/openocd-0.10.0.tar.gz
tar -xf openocd-0.10.0.tar.gz
cd openocd-0.10.0/
mkdir install
sudo apt-get install -y libtool automake libusb-1.0.0-dev texinfo libusb-dev texlive-base libftdi-dev
# the stlink and usb blaster flags are not required, openocd will automatically include these if you
# have the needed includes in your system. Adding the flags forces it to include and will trigger
# an error if the includes aren't there.
./configure --prefix=`realpath install` --enable-stlink --enable-usb-blaster
make install

this will put openocd into openocd-0.10.0/install

before we can check it, we need to connect the stm board to the st-link. See the picture at the beginning of this post. Both devices are labeled.

  • st-link 3.3  – stm 3.3
  • st-link gnd – stm gnd
  • st-link swclk – stm swclk
  • st-link swdio – stm swo

linux permissions to st-link v2

Once you plug in the programmer (which will also power the stm32; no need to plug in both) 4, linux probably won’t let you access the programmer yet.

First let’s see that the programmer is recognized:

--> lsusb
Bus 001 Device 013: ID 0483:3748 STMicroelectronics ST-LINK/V2

--> ls -l /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0 Jul 5 10:43 /dev/ttyUSB0
crw-rw-rw- 1 root dialout 188, 1 Jul 5 10:38 /dev/ttyUSB1

Note that the USB tty devices belong to the dialout group. Add yourself to this group and you’ll be able to proceed (after logging out and then back in)

sudo usermod -a -G dialout $USER

If all goes well, you can do this:

export OPENOCD=/bubba/electronicsDS/stm32/openocd-0.10.0/install
$OPENOCD/bin/openocd -f $OPENOCD/share/openocd/scripts/interface/stlink-v2.cfg -f $OPENOCD/share/openocd/scripts/target/stm32f1x.cfg

This is what I get:

Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.227913
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints

If you get this instead, you have a permissions error:

Error: libusb_open() failed with LIBUSB_ERROR_ACCESS
Error: open failed

An alternative to adding yourself to dialout group is to add these lines to /etc/udev/rules.d/49-stlinkv2.rules

# stm32 discovery boards, with onboard st/linkv2
# ie, STM32L, STM32F4.
# STM32VL has st/linkv1, which is quite different

SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", \
MODE:="0666", SYMLINK+="stlinkv2_%n"

Finally, we can transfer the code. I like to have gdb upload the code every time I invoke it so I know it’s always uptodate. I do this with a local .gdbinit file. This file is mostly reusable from project to project. Just update the two mentions of blinky.elf. Also note the two env variables mentioned in comments.

#export GDBEXEC=/bubba/electronicsDS/stm32/gcc/gcc-arm-none-eabi-6-2017-q1-update/bin/arm-none-eabi-gdb-py
#export OPENOCD=/bubba/electronicsDS/stm32/openocd-0.10.0/install

file build/blinky.elf
target remote | $OPENOCD/bin/openocd -f $OPENOCD/share/openocd/scripts/interface/stlink-v2.cfg -f $OPENOCD/share/openocd/scripts/target/stm32f1x.cfg -c "gdb_port pipe; log_output openocd.log"

define restart
monitor reset halt

define reload
monitor reset halt
monitor stm32f1x mass_erase 0
monitor program build/blinky.elf verify
monitor reset halt

# add your breakpoints.
break main


Now I run gdb

export GDBEXEC=/bubba/electronicsDS/stm32/gcc/gcc-arm-none-eabi-6-2017-q1-update/bin/arm-none-eabi-gdb-py
export OPENOCD=/bubba/electronicsDS/stm32/openocd-0.10.0/install

target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000178 msp: 0x20005000
stm32x mass erase complete
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
** Programming Started **
auto erase enabled
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000003a msp: 0xfffffffc
wrote 5120 bytes from file build/blinky.elf in 0.365343s (13.686 KiB/s)
** Programming Finished **
** Verify Started **
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000002e msp: 0xfffffffc
verified 4100 bytes in 0.102377s (39.109 KiB/s)
** Verified OK **
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000f18 msp: 0x20005000
Breakpoint 1 at 0x8000286: file Src/main.c, line 86.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at Src/main.c:86
86 HAL_Init();
(gdb) c

You should now have a blinking light on your board.

  1. Some months ago, I had some clocking issues using the generated code. Thankfully, they fixed it. Also a new feature is Makefile generation. There is still at least one major bug I’ve had to work around. (I2C stability)

  2. In theory, STMCube will download this for you, but when I did it, the application crashed

  3. the main reason I do this it to get an error on non-void functions that don’t return a value. I forget to do this with small functions all the time.

  4. most tutorials advise not to plug in both, or if you do, to disconnect the power wire on all but one. This is good conservative advice. I have not had issues when all are connected to various usb ports on the same computer.

Automatically aligning multiple video/audio clips in kdenlive

I’ve recently begun trying to produce some youtube videos on programming topics that I find interesting. In that interest, I’ve come to use the kdenlive linux video editor 1. Kdenlive is pretty nice and will serve my needs just fine.

One thing I’ve aspired to do is some multi-camera videos. My wife practices a couple kinds of martial arts and I’d love to record some of her test katas from several angles.

Aligning a handful of clips really isn’t that hard to do manually, but since I’m a nerd, I decided to program a utility to do this. It’s a problem that I wanted to implement for years and I’ve finally gotten to it.

Similar to the product PluralEyes, my utility looks at the audio portions of your clips and aligns them based on that. In a nutshell, I compute the cross correlation of all pairs of clips using FFTs and then write a timeline based on the strongest matches.

The end of this post will have more implementation details.

Here is a quick demo


The steps to use it are pretty simple

Step 1 – create an unaligned kdenlive project

First, create a kdenlive project that contains all of the desired clips in the desired tracks as below (click for larger image). Note that the clock in the multiple views show different times. This demo project has three different cameras (an S5, HTCM8, and motorola something) in addition to a cheap Sony voice recorder and a Sony PCM-M10

Here’s the fancy rig I used. It’s for creating some 360 style VR videos. I have some scripting for merging stuff together using Hugin, but I haven’t been satisfied with the results yet.

Step 2 – run the utility

A binary of the utility can be found on my github. You can also find the source code for it.

To run it is easy. In the example below, my project is called easydump, because it’s just an easy dump of all the clips:

./sync_kdenlive video_and_voice_recorders/easydump.kdenlive

It will create the file easydump_aligned.kdenlive. It also creates easydump_aligned_aligned0.wav which is a wav file with as many channels as clips.

The demo is about 4.5 minutes and the utility runs in ~10seconds on my Skylake with 16GB. 2 Run time will scale linearly in the length of your longest clip and quadratic in the number of clips. So far, I’ve made little/no attempt to optimize it beyond using FFTs.

This utility is for my own benefit/enjoyment/curiosity. If you have any interest in it, please comment. If you tried to run it and it didn’t work, please comment. If you tried it and it did work, please comment. If I get no comments, I’ll assume no one cares and will make no effort to make it better.

Of course, the easydump part isn’t hardcoded. It’s just the file name that I chose for this experiment.

Step 3 – load the updated project file into kdenlive

Step 2 generated a easydump_aligned.kdenlive. Load it into kdenlive. In the picture below, note that the clocks show the same time.

Here’s the result. From listening to the audio of it, I’d say it worked pretty well:


So how’s it work?

The core idea is something that I learned many moons ago, when I took 6.003 – signals and systems. A playlist of the 2011 lectures can be found here.

In particular, this lecture about convolution:

Convolution or the variant of it cross-correlation that I use in the utility is basically that act of sliding one signal along another, multiply the two, and taking the area of the result. A large area means lots of similarity. Smaller area means less.

If you were to just compute the cross correlation directly, you’d have a lot of computation to do. Thankfully, there was a guy named Fourier who found a way to compute this in the frequency domain. This coupled with the Fast Fourier Transform enables me to to cross correlations on WAV files pretty quickly.

Here are the required steps:

  1. compute the discrete fourier transform of each of your wav files. The length of each result should be equal to the max of the lengths of the two wavs.
  2. piecewise,  multiply the first by the complex conjugate of the second
  3. compute the inverse transform of the result of those multiplies.

Here is what you get with you take two sine waves, shifted relative to each other. The two two waveforms are the two input sines. The bottom two are the real and imaginary components of the cross correlation. The source code for this experiment can be found here on my github.

Note that this technique works for any kind of data. Spatial/visual works too. I’ve had thoughts of implementing a jigsaw puzzle solver this way.

Try it with your own waves

I have a compiled (for ubuntu linux) binary of a utility that takes two waves and writes a wave showing the cross correlation. It can be found here. To invoke it:

./sync_wavs file2.wav file1.wav true

If you omit the “true” argument, you’ll get just the aligned channels. In this case, I added true to tell sync_wavs to include the cross-correlation as the third and fourth channels. Note the peak location corresponds to the beginning of the second channel.

Attribution of libraries I used

Very few real programming projects are implemented entirely from scratch and this one is no different. The external libraries I used are:

FFTW This library implements the fourier transform parts. In addition to its excellent tutorial, I have a couple simple programs that I used in the lead up to this project.

TinyXML2 This library implements the xml read/write functions. I have a lot of experience using LibXML2’s perl interface. LibXML2 is excellent but there are some areas where it makes me do more of the work than I care for. TinyXML2 is easier to use in most ways. The output xml is nicely formatted by default. Adding text and new elements is a bit quicker to implement. The main thing missing TinyXML2 is XPATH support. Using the visitor methods sort of makes up for this.

BOOST. It’s hard for me not to use boost these days. In particular, I use:

ffmpeg I don’t use this in the code directly. The code does call ffmpeg to extract wav files of the same samplerate from each of the clips.

Some additional implementation details

I have found that downsampling the audio of each clip to 5k samples/sec works fine. This is important because the longer the fourier transform, the longer the runtime.

When taking the ffts and iffts, you need to double the length of the input data but padding with zeros (ie, the first half is your wav, the second is silence). This is important in knowing the relative order and offset of any pair of clips. The way the cross-correlation works, if you have a negative offset, the peaks will show up in the second half of the transform data. At the same time, what happens if one clip begins near the end (past halfway) of the other? To sidestep this, I simply doubled the data lengths. 3

In the utility, I compute the cross correlation for all pairs of audios. Some of those pairs will correlate better than other and some don’t correlate at all (since they don’t overlap). Here’s the method I’ve found to work for me:

  • compute the mean/average and standard deviation of the cross-correlation result.
  • find the peak value
  • order the peaks of all the pairs by ordering by the number of standard deviations above the mean/average. For pairs that should correlate (they overlap), the peaks tend to be at least 15 standard deviations above the mean.

  1. I actually have a version of Movie Studio Platinum, which I paid for some years ago. The problem is that I use windows less and less

  2. I imagine that anyone doing video editing will need/want/have a machine that it also pretty beefy. I’m using the graphics stuff built into the Skylake.

  3. I imagine there’s a way to do this without doubling but I don’t know the math well enough. If you do know, please let me know

Using a phony C struct as a function selector

Generic programming, as used by the std and boost packages, depends heavily on template tricks to extract data from specific data structures in a generic way. When I first tried using some of the boost libraries, I felt pretty clueless in getting them to do what I wanted. Hence this post.

Say you have a C struct like this one:

struct Foo {
  int a;
  int b;

Generic programming accesses the a and b members using a global get function and a property map:

template <typename T, typename PMAP>
int get(T &t, PMAP pmap);

What’s in the property map? In this case nothing. The magic happens in the templated specialization of get:

struct get_a {};

int get(Foo &foo, get_a)
  return foo.a;

Which you call like this:

Foo f1;
f1.a = 10;
std::cout << "a is " << get(f1, get_a()) << std::endl;

How’s it work and what’s the compiled result?

The get_a struct is empty; it contains no data members. When you call get, the compiler selects the get_a version since it’s the most specific specialization of get. From there, things get interesting. Constructing get_a() is a noop. Pushing it as a function argument is a noop. After inlining, get simply becomes f1.a.


How to quickly compute the Euclidean MST of a large number of points (and why you might want to)

When I was in college, like most CS type majors, 1 I took the algorithms class. Sorting, binary trees, O(n) notation… all of that.

One of the things I didn’t understand is why such a big deal was made about some algorithms like the graph algorithms. Why would I want a minimum spanning tree of a million nodes?

A bunch of years ago, I did need to compute the MST of a couple hundred thousand nodes for my job. Later on, I needed to do it three dimensionally. Something made me think of it recently, and I have a hankering to write about it.

I’ve also recorded a series of videos to explain the programming aspects:

What problem was I trying to solve?

At the time, I was working on code to generate a clock buffer tree for large seas of logic gates. Say you have a million gates in your logic design and that 10% of those are clocked elements.2. So we have 100k logic gates that need a clock signal.

So, clock buffers can’t drive the capacitance of a 100k gates, at least not if you want the design to run at more than 1 cycle per second. The solution is a clock tree. The input clock drives 5 buffers, which each drive 5 more buffers,… until everyone has their clock.

How do you decide which buffer drives which cell? That’s where the MST comes in. Once I had computed the MST of all of the clocked elements, I picked a random end (of the MST) node 3 and collapsed it into it’s adjacent node. When a collapsed node passes a capacitance threshold (also need to include wiring capacitance), that’s where I add a buffer. Keep doing this until you have your first level of buffers, then recursively do the same up the tree.

But some cells are more timing critical than others.

In a design of 1M gates and 100k clocked elements, most of those gates will easily meet timing, no matter how badly you design things. Most. If memory serves me, we’re talking 90% (99%?) of the design meets timing, easy peasy.

So in those 100k gates, some are timing critical, many are not. An easy solution is to have two subtrees. One for the easy stuff and the other for the hard. The problem there is that you can have one little cell surrounded by critical stuff. Do I really need to segregate it? How can I allow the user to control the thresholds for such things?

The solution I wanted to implement, but never did (though I’m convinced this will work) is to turn the problem into a 3D MST problem. The X and Y axes are the normal X/Y of the design. The third axis represents timing. I simply need to devise a timing to distance coefficient.

MST basics

First off, the MST algorithm is pretty easy. To quote wikipedia.org:

Initially, T contains an arbitrary vertex. In each step, T is augmented with a least-weight edge (x,y) such that x is in T and y is not yet in T. By the Cut property, all edges added to T are in the MST. Its run-time is either O(m log n) or O(m + n log n), depending on the data-structures used.

Sounds pretty easy. The problem is you need a graph to operate on. I only had a set of points. The easy solution is to create a complete graph. All nodes are connected to all other nodes. O(n^2) in the number of nodes. 100k*100k can take up some space.

Triangulation to the rescue

Again, I refer to wikipedia.org:

every edge not in a Delaunay triangulation is also not in any EMST

I don’t really understand the math behind it but first compute a triangulation of your points and run MST on that.

The CGAL library will give you the Delaunay triangulation.

The BOOST Graph Library will give you the MST.

The problem is that these libraries were difficult for me to really understand. Now that I’ve spent some time with them, they’re actually pretty simple. I’ve created some videos that perhaps will help you also see them as simple.

Visualization with paraview

First, it’s important to be able to see that data. I’ve found that paraview works well and is easy to use (once you know how to use it)

CGAL how to

Boost Graph library how to

  1. My major was basically computer science except it was in the math department. Instead of the normal 150 students per year, there were 18 of us doing the Math with CS variant.

  2. The design size was actually smaller. 100k-500k. The percentage of clocked elements was higher. I’m going by memory, but I think the final number of nodes is in the same ballpark.

  3. I think I actually picked the bottom right one, but it doesn’t really make a difference. The main benefit to the bottom right is that you get the same or similar results from one run to the next.