STM32 UART Continuous Receive with Interrupt

My last post is quite some time ago, due to vacations and high workload. But now I encountered some problem within an embedded project, I want to share the solution with you. Continuously receive data using interrupts on UART is complicated (or even impossible) in HAL. Most approaches I found crawling the internet are using the LL library to achieve this and many discussions around HAL do not end in satisfaction. Some work around the problems with dirty approaches (e.g. changing the HAL code itself), other step back from interrupt and use a polling approach.

To be honest, the high levels of HAL do not offer such a solutions. Instead, it offers functions to receive a special amount of data using a non-blocking interrupt approach, handling all the difficulties with tracking the state in the instance stucture (huartX) and entering a callback for the diverse states of the reception/transmission, e.g.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) or
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)

Using HAL_UART_Receive_IT (not recommended)

A nearby approach without touching HAL code itself is, to call HAL_UART_Receive_IT(&huart3, &rxbuf, 1) once after initalization and at the end of the RxCpltCallback, to retrigger the reception, but this leads to some undesired lock (possibly a HAL-Bug), when transmitting data using HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size), which could also not be the desired behaviour and the beginning of endless debug sessions and frustration.

Simply Enable the IRQ

The best solution in my opinion instead is really simple. Don’t use the high level receive functions at all for the continuous RX behaviour, since you do not want to receive a special amount of data but be called at each reception. So, configure the UART with interrupt in CubeMX and after it’s initalization, enable the interrupt itself, never calling the HAL_UART_Receive_IT or any other UART receive function (it will disable the IT after finishing).

In the section of the appropriate instance in void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle), add the following line of code:

__HAL_UART_ENABLE_IT(&huartX, UART_IT_RXNE);

In stm32xxx_it.c do:

void USART3_IRQHandler(void)  {    
    /* USER CODE BEGIN USART3_IRQn 0 */
    CallMyCodeHere();
    return;  // To avoid calling the handler at all 
             // (in case you want to save the time)
    /* USER CODE END USART3_IRQn 0 */
    HAL_UART_IRQHandler(&huart3);
    /* USER CODE BEGIN USART3_IRQn 1 */
    /* USER CODE END USART3_IRQn 1 */ 
}

The return statement will avoid calling the HAL IRQ handler. I did not try during transmit, but it seems not disturbing anything. If you plan to use the HAL_UART_Receive_IT functions in parallel, you could try to put your code below the handler. I did not test it, but there is a good chance that it works.

Since this approach only touches the user code functions, none of your code will be destroyed by code re-generation of CubeMX.

This is all you need… Happy UART processing 😉

If Timestamping is Needed

Simple Millisecond Timestamps

If you want to trigger on inactive time durations (some serial protocols use it as a synchronisation condition), save a timestamp (e.g. HAL_GetTick()) within the UART-RX-Interrupt and look at the difference to the previous one (subtract the duration of a byte to get the real inactive time).

High Resolution Timestamps

If sub-milli-second resolution is required, run a timer with a prescaler of desired resolution and take the counter value of the timer instead of the tick counter. (you can get it with __HAL_TIM_GET_COUNTER(&htimX)).

Hope this helps in your next project using UART 🙂

How to build a Smart Home

Since everbody complains about smart homes are vendor lock in, too expensive, giving you data for free to some suspecting cloud provider,… I need to preset my smart home solution to you, that can be completely running inside your „four walls“ and does not need any third party, if you do not want it to. Additionally, there is NO vendor lock in. Everything can be open source and connect to almost any existing smart home appliance.

What I’m talking about? It’s iobroker.

If you already own a Homematic System from EQ-3 (ELV in older ages), iobroker is a must-have. BTW: If you already own a Charly, you can install iobroker on it without addional hardware, but be advised to install a USB drive with enough storage for the data that accumulates when years pass by. I would not suggest to use a flash based drive, since it needs a tradeoff between amount of data lost when power cuts and the endurance of your drive. Write cycles will be quite often…

Welp, where to start? I would first get some low-power and reliable mini PC. The solution I selected is from hardkernel and called ODROID HC-1 (in Germany best bought at Pollin for 60€). This is a little ARM board running ubuntu linux and providing space for a single 2.5″ HDD. I selected a WD Red 1TB drive for that purpose. The linux OS itself needs to be put on a Micro-SD card.

I would suggest to also buy the following:

  • Power supply (or a PoE+ adapter if you own a PoE+-capable switch)
  • Micro-SD card with at least 8GB (best with ubuntu preinstalled)
  • The serial cord from hardkernel
  • The Battery (for RTC)
  • A hard disk for all the data your smart home will collect and
  • A little case against the dust, that definitely will render you appliance as very attractive 😉

Start installing your software. First you need to install the Ubuntu OS, provided by hardkernel (if not already installed).

When everything arrived, connect it to your network, find out the IP address or use the serial cord and start installing everything you need (supposing, you do all under root, if not, prepend a sudo where needed):

Prepare your hard disk

dmesg  # Find out, which is your HDD, assuming /dev/sda
fdisk /dev/sda
# Create 2 Partitions
# 8 GB with swap
# Remainder with ext4 or something else (I prefer btrfs)
# write partition table with entering 'w'
mkswap /dev/sda1
swapon /dev/sda2 
mkfs.ext4 /dev/sda2
mkdir /var/data
blkid /dev/sda2   # Take the UUID-part
echo "UUID=<the-above> /var/data   ext4 errors=remount-ro,noatime 0 1" \
  >> /etc/fstab
mount /var/data
mkdir /var/data/iobroker
mkdir /opt/iobroker /var/lib/influxdb
echo "/opt/iobroker/ /var/data/iobroker  none bind" \
  >> /etc/fstab
echo "/var/data/influxdb/ /var/lib/influxdb none bind" \
  >> /etc/fstab

Installing node.js

sudo apt-get install python build-essential curl
mkdir src
cd src
wget https://nodejs.org/dist/v10.15.3/node-v10.15.3.tar.gz
tar xzvf node-v10.15.3.tar.gz
cd node-v10.15.3.tar.gz
./configure --without-snapshot
make
./node -v  # If version is returned than 'make' was OK 
make install

Installing InfluxDB

curl -sL https://repos.influxdata.com/influxdb.key | \
   sudo apt-key add - source /etc/lsb-release echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list
sudo apt-get update && sudo apt-get install influxdb sudo systemctl unmask influxdb.service sudo systemctl start influxdb 

Installing Go language

apt install git golang-go  # needed to build a newer go
cd /usr/lib
git clone https://go.googlesource.com/go
cd go
git checkout go1.12.5
cd src
./all.bash
cd $HOME
cd /usr/bin
rm go
ln -s ../lib/go-1.12 go
cd $HOME
cat >>.profile <<HERE
export GOROOT=/usr/lib/go
export GOPATH=$HOME/go
[ -d $GOPATH ] || mkdir $GOPATH
[ -d $GOPATH/bin ] || mkdir $GOPATH/bin
export PATH=$GOPATH/bin:$PATH
HERE

Installing yarn

see: https://yarnpkg.com/en/docs/install#debian-stable

Installing Chronograph

For creating your own querys to InfluxDB (e.g. with node red), it is easiest to have some good InfluxDB interface, best to be used in a browser…

With go and yarn, it is as easy as boiling water:

go get github.com/influxdata/chronograf
cd $GOPATH/src/github.com/influxdata/chronograf
make
go install github.com/influxdata/chronograf/cmd/chronograf

Now you can start chronograph

./chronograph

Now visit the chronograph web interface by browsing to http://<host>:8888. A Wizard will welcome you. Enter the appropriate data to access your InfluxDB. Youcan skip the Kapacitor question. After finishing the wizard, you should be able to see the following on chronographs Config tab.

When this is done, issue your first query on the Explore tab (use the datapoint selector on the lower half of the page and select an appropriate date from the date picker in the upper right corner:

Install iobroker

COMING SOON…

How to Build A Private Storage Cluster (with Ceph)

Since my NAS (a QNAP TS-419P II) get more and more buggy, especially with non-working Windows shares and the painfully low processing power of the integrated ARM single core, wished something like a SAN for myself. But SAN is quite expensive, the peripherial hardware (Switches, UPS,…) not included. So I decided to skip a few levels and build up a NAS 2.0 storage cluster based on open source ceph using low-budget ODROID HC2 (Octa-Core 4 x Cortex-A15 + 4 x Cortex-A7) from Hardkernel as the work horse to create storage nodes. To make it even more dense, you can use the ODROID HC1 that is just the same but for 2.5″ disks (be aware of the power supply: HC2 = 12V, HC1 = 5V !!!).

If you don’t need a SATA drive (e.g. for the controlling nodes of the cluster: mgr, metadata, nfs, cifs,…), you can use the MC1, MC1 solo, XU4 or XU4Q.

If you want to go with x86 instead of ARM, the ODROID H2 looks like a great alternative, but it will also be a bit more expensive (e.g. RAM is not included).

In fact, installing ceph will be much less pain, going for 64-bit x86 than going with ARM 32 bit. I decided to go with ARM 32, because I want to build up the most energy efficient cluster, to maximize scale out capabilities also in sense of my private budget.

To build up the cluster, I currently use 4 x ODRID HC2 with WD Red 4 TB drives (WD40EFRX), also installing the ceph non-OSD-services distributed accross this little cluster. The BOM for my test cluster is as follows:

If powering up the cluster in sequence (not all at once), you could reduce the power requirements of the supply component a lot (currently 12V/2A per node, 5V/4A for HC1). I will dive into this topic a bit deeper in future. I think, it can be done in software by delaying the spin up through some bootarg. Nevertheless, an optimum solution would be to have a power distribution unit for switching and measuring the supply current and also providing some UPS capabilities on the low voltage path. Additionally, current measuments could give you the ability to regulate the power through e.g. cpufreq to optimize the efficiency of the cluster and the power supply.

To generate the debian packages for installing ceph on the nodes, follow the instructions here. When you have built the debian packages, move them over to some http(s) server, to be easily accessible by your nodes.

Your Own Debian Package Repository

To be accessible, a Debian Package Repository needs to be placed in a webserver’s directory accessible at least in your own network. It is best practice to secure this repository with SSL, since Debian APT more or less expects this… So first we start with creating a (self signed CA). Later, if needed, you can easily replace the certifciate by an official one or let an authorithy also sign your server certificate.

Generating a CA for SSL

This part is based on the tutorial here. First, we will simply use self signed certificates, since it is much easier and faster than using officially signed certificates. We will then place the CA in the cert storage of our linux OS, to make it trust ourself. 😉

mkdir ~/CA
cd ~/CA
# Generate the CA key
openssl genrsa -out ca.key 4096
# Generate the CA certificate, here, you can leave the CN empty
openssl req -new -x509 -key ca.key -days 366 -out ca.crt
# Make it unaccessible by other users
chmod 700 ca.key
# Generate a certificate configuration
wget https://raw.githubusercontent.com/the78mole/scripts/master/templates/configs/ssl/cert.conf -O example.org.conf
# Edit the configuration
vi example.org.conf
# Create a server certificate key and the signing request (not the yet cert)
openssl req -new -out example.org.csr -config example.org.conf
# Create the public key
openssl rsa -in example.org.key -pubout -out example.org.pubkey
# Sign the CSR with your CA and create the certificate
openssl x509 -req -in example.org.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extensions my_extensions -extfile example.org.conf -days 366 -out example.org.crt

To get the alternative DNS names and IPs added to the certificate, you need to specify the config file as an extensions and point to the config section, where the extensions are located. This is because the extensions in the CSR get ignored by openssl when signing and you need to specify it explicitly.

After generating the certificate, you need to import it, where you need it to be accepted (Browser, APT). For testing, it is best to try with a browser. Some tutorial can be found here (it’s german, so use google translator, to read in english) and here. Use the shell of your desktop Debian system.

scp <CA_HOST>:/<PATH_TO_CA>/ca.crt example_ca.pem
sudo cp example_ca.pem /usr/local/share/ca-certificates/
sudo update-ca-certificates

To add the certificate to your browser, e.g. chromium

sudo apt install libnss3-tools
certutil -A -n "Example Company CA" -t "TCu,Cu,Tu" -i example_ca.pem -d ~/.pki/nssdb

Note: Maybe this does not work correctly… Then, in Chromium, use Settings –> Privacy and Security –> Manage Certificates –> Import –> Select the CA –> Check all boxes.

Now we need the CA’s and the server’s certificate along with the server key for securing webserver traffic.

Install and Configure the Webserver

Welp, we will use nginx as our webserver. Feel free to use any other, it does not really matter. In fact, every further step (e.g. the let’s encrypt tutorial) will be based on nginx.

sudo apt install nginx
cd /etc/nginx
cp snippets/snakeoil.conf snippets/ssl_example.org.conf
mkdir -p ssl/pub
mkdir -p ssl/priv
sudo chown -R root:www-data ssl
sudo chmod -R 0755 ssl/pub
sudo chmod -R 0750 ssl/priv
cp ~/CA/ca.crt ~/CA/example.org.crt
cp ~/CA/example.org.key
# Edit the ssl config file to your needs
vi snippets/ssl_example.org.conf
# Now adjust the nginx configuration to use SSL
vi sites-enabled/default
# Ensure following lines are added and not commented out
# listen 443 ssl default_server
# listen [::]:443 ssl default_server
# include snippets/ssl_example.org.conf
service nginx restart

When everything is OK, use your desktop web browser and point it to the location https://example.org. Your should get the page without an error. Thos means, you have setup a CA you can use to sign server certificates and they get trusted.

If you plan to use the Debian package repository on many of your linux hosts, then you should add your CA certificate to the certificate store on all the machines.

Generating GnuPG Key-Pair

To sign a file, email, hash, debian package, repository,… you often need GnuPG. To be able to sign something, you need to first generate your own key, that get trusted from at least the receiving party. All this works again with asymmetric encraption, like the signing of certificates does. An in depth tutorial with links to even deeper knowledge can be found here.

First we should install a tool to gather some entropy, otherwise gnupg may be not able to generate a key on a headless system (no real user input,… –> very few entropy sources).

apt install rng-tools

IF it can not find a hw-rng, you can still try to get randomsound working (if you have a soundcard…)

apt install randomsound

Run this in a seperate window, when gpg is collecting entropy for too long. It will abort after some time, if it can not generate the key.

arecord -l # Do you have any soundcard?
randomsound -v

If all fails, you can still pipe some data into /dev/random to feed the entropy pool, e.g. with (also in a seperate window when gpg gen-key is running.

sudo dd if=/dev/sda of=/dev/random status=progress

You can watch the entropy-pool with:

watch -n 0.5 cat /proc/sys/kernel/random/entropy_avail

To finally generate a GPG-key, simply follow the instructions below:

apt install gnupg
# Create the .gnupg directory easily and add a secure configuration
gpg --list-keys --fingerprint
wget https://raw.githubusercontent.com/the78mole/scripts/master/templates/configs/gnupg/gpg.conf -O ~/.gnupg/gpg.conf
gpg --full-gen-key
# Select:
# Key type : RSA and RSA
# Keysize : 4096
# Expiration: 1y
# Then enter your name and email, but don't include a comment
# Skipping the password makes CI much easier, but less secure...
# It will take some time (maybe minutes) to generate the key

Creating the debian repository (reprepro)

make-debs already created a debian repository, but we will create one, that is more general, also serving well for other software packages. make-deps

…. to be continued …

Coming soon: To add some real NAS features, we could use just another embedded board with e.g. FreeNAS or NextCloud installed to mount the cluster file system and using the cluster as the storage backend. We already have the nginx SSL configured, so we easily can add reverse proxy targets… (for HTTPS-HTTPS-proxy, see here)

Compile Ceph (master) on ARM (32-Bit)

TODO: Test this all on a virgin armhf system (raspberry, odroid hc1/2/XU4,…) and complete the TODOs for openssl and phantomjs (and the sass-dependency). Maybe with the new master tree, it is not needed to build it outside the ceph repo.

First install prerequisites:

sudo apt install python-pip build-essential libgmp-dev \
libmpfr-dev libmpc-dev reprepro

Install nodejs from nodejs.org

curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash - sudo apt-get install -y nodejs
sudo npm install -g npm

Then prepare a swap partition (you will need it 😉 )

dd if=/dev/zero of=/<some-hdd-path>/swapfile \
bs=1M count=8192 progress=status
mkswap /<some-hdd-path>/swapfile
swapon /<some-hdd-path>/swapfile

Then we should install some dependencies

sudo apt install libgmp-dev libmpfr-dev libmpc-dev ruby

Now install a new GCC that supports C++17.

wget https://ftp.gnu.org/gnu/gcc/gcc-8.2.0/gcc-8.2.0.tar.xz
tar xfJ gcc-8.2.0.tar.xz
cd gcc-8.2.0
./configure # for armhf
# ./configure --disable-multilib # for x86_64/arm64
make

Building ceph with do_cmake, building a debian package with make-debs.sh or simply build packages using another compiler than the debian default one (6.3.0) requires you to change the default compiler e.g. to gcc-8.2.0 for the whole system:

sudo update-alternatives --install /usr/bin/cc cc /usr/local/gcc-8.2/bin/gcc-8.2 50
sudo update-alternatives --install /usr/bin/c++ c++ /usr/local/gcc-8.2/bin/g++-8.2 50

Checkout OpenSSL-1.0.2-stable (seems also necessary for armhf), PhantomJS, compile and install it:

cd /opt/GIT
git clone git@github.com:openssl/openssl.git
cd openssl
git checkout OpenSSL-1_0_2-stable
...TODO...
# Following seems only necessary on arm
# (or all platforms wihtout precompiled binary)
cd /opt/GIT
git clone git@github.com:ariya/phantomjs.git
cd phantomjs
...TODO...
sudo LD_LIBRARY_PATH=/opt/openssl_build_stable/lib/ \
deploy/package.sh --bundle-libs

Add the following to build.py (at L:244, just after PlatformOptions.extend)

phantom_openssl = os.getenv("PHANTOM_OPENSSL_PATH", "")
if phantom_openssl != "":
openssl = os.putenv("OPENSSL_LIBS", "-L" + phantom_openssl + "/lib -lssl -lcrypto")
openssl_include = "-I" + phantom_openssl + "/include"
openssl_lib = "-L" + phantom_openssl + "/lib"
platformOptions.extend([openssl_include, openssl_lib])
print("Using OpenSSL at %s" % phantom_openssl)

Then install it to /opt

Build and compile Ceph

git clone git@github.com:the78mole/ceph.git
cd ceph
git checkout wip-32-bit-arm-fixes
./install-deps.sh
./do_cmake_arm32.sh # for armhf
# ./do_cmake.sh # for x86_64/amd64 or arm64
cd build
make -j4
# if it gets really slow due to swapping, break an do make -j1
# or use the scheduler-script from link below

Here you can find a rudimentary (but working) script that suspends compilers processes based on total compilers memory consumption. Running it through ‚watch‘-tool you can start e.g. 8 tasks and when memory limit is reached, it will suspend (kill -TSPT) the youngest tasks in sense of user space runtime.
https://github.com/the78mole/scripts/blob/master/linux/bash/schedule_compile.sh

Now do…

cd ..   # Back to ceph base dir
./make-debs-arm32.sh # for armhf
# ./make-debs.sh # fox x86_64/amd64 or arm64

If you encounter problems with setuptools (Exception –> TypeError: unsupported operand type(s) for -= ‚Retry‘ and ‚int‘) try to get a more recent version of python pip with the following commands and rerun make-debs-arm32.sh.

apt-get remove python-pip python3-pip
wget https://bootstrap.pypa.io/get-pip.py
python get-pip.py
python3 get-pip.py

If I forgot anything to make it work, feel free to write some comment…

OrangePi 4G-IoT Android 8.1 SDK

Same as with the 2G-IOT, OrangePi also provides a quite inconvenient way (through mega.nz) to get the Android SDKs for the 4G-IOT… Here are the torrents (tar.gz and tar.xz have the same content but xz is much smaller)

Have fun…

OrangePi 2G-IOT Android 4.4 SDK

Getting the Android SDK

Since I just struggled getting the Android SDK for my little OrangePi 2G-IOT, I felt responsible to share it using bittorrent. The main problems have been, to download all that stuff from mega.nz (it raises limits for users not paying a monthly fee) and another one was, to concat all 7 files to a working tar.gz. I think, the wildcard they use in the OrangePi’s user manual does not expand the filesnames of the parts in the right order. But then, wired errors occur.

If you encounter problems with not being able to create symbolic links when unpacking, just use a real file system as the base for your unpacked archive (e.g. ext4, btrfs, …, but NOT NTFS or FAT).

Here you can find the torrent: 

 https://downloads.the78mole.de/OrangePi_2G-IOT.tar.gz.torrent

Getting the toolchain

Another dangling point of OrangePi’s SDK is the lack of a toolchain. They have a large download, but it does not contain a toolchain. So just download the correct one from linaro (take the appropriate one for your system, mine is x86_64), unpack it inside the folder where the empty folder toolchain is (here it’s ~/somewhere/OrangePi in the example) located, remove this toolchain folder (rmdir should succeed with a fresh unpack, when it is already a link, use rm) and set a symbolic link to the fresh linaro one.

$ cd ~/somewhere/OrangePi/
$ wget https://releases.linaro.org/components/toolchain/binaries/latest-5/arm-linux-gnueabi/gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabi.tar.xz
$ rmdir toolchain
$ ln -s
gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabi toolchain

Running the kernel build

Just stick to your User’s guide again and do:

$ ./build.sh

Getting Started Embedded – Part I – The Toolchain

Introduction

For getting started with any embedded development, the most important piece is the toolchain.

Many people suggest to use Keil, IAR, or some other fancy, professional, rocket-sience (and very expensive) IDE. In my opinion, that is just rubbish.

In former days, when tiny embedded controllers just have not been designed with compilers in mind, it was good to have some highly optimized compilers that could transfer a piece of C code to the ASM of these devices.

Nowadays, processors are designed with compilers in mind. Therefore, I would highly recommend to use GCC not only because of its low price tag (0 $), but because of its stunning community and active development. Since the community and many companies still putting so much effort into this piece of software, no single company can compete with an own closed source product.

Installing the ARM toolchain

There are many possible sources, where you can get GCC and all the tools you need to start. You can compile ARM-GCC yourself using your platform GCC, download the one from ARM directly, take a release from GNU MCU Eclipse and many many more…

Don’t know what to do? Just get kickstarted, and give XPM (a node/npm module) a try. Download and install node.js. The version does not matter too much. If you are not developing with node.js itself, better stick to the stable version.

When node.js is installed, install xpm and the ARM GCC toolchain (same for Windows, Linux & macOS):

me@diggerVM:~$ npm install xpm
me@diggerVM:~$ xpm install --global @gnu-mcu-eclipse/arm-none-eabi-gcc
me@diggerVM:~$ arm-none-eabi-gcc -v

OK, that was easy… If this worked, you maybe need some supporting tools. Best practice differs a bit, depending on your platform. For Linux, just install the build-essentials package (Debian dn ubuntu call it like this).

me@diggerVM:~$ sudo apt-get install build-essentials

For Windows (would also work for Linux, but I prefer the OS provided package), you can use xpm again:

C:\Users\me\>xpm install --global @gnu-mcu-eclipse/windows-build-tools

After this has finished, you possibly need to add the build tools to your PATH environment. You will find it in %APPDATA%\xPacks\@gnu-mcu-eclipse\windows-build-tools\2.11.1-1\.content\bin.

Testing your Toolchain

To test your setup, just clone, download,… the STM32 example project. Open a command line prompt, cd to the project directory and fire make:

me@diggerVM:~/GIT/stm32-example$ make

If this worked without errors, you have your toolchain up and running. Congratulations!

What’s next…

The next post will explain, how to setup STM32CubeMX and the Eclipse IDE to start developing own embedded applications effectively. Until now, there is not much difference to commercial IDEs and Toolchains from a workflow point of view. But don’t worry, we still have automation in mind and the goal is to have a CI-Pipeline running soon.