19 Oct 2020

Writing Software for your Embedded Device with OpenEmbedded

In my last post we built our own, very up-to-date, image for our target device (a RaspberryPi3 fitted with Pimoroni's Automation-HAT) using Yocto/OE and poky. This image included the latest revisions of Pimoroni's Python libraries for driving the Automation-HAT, thanks to the enormous amount of help devtool provides for creating new recipes for existing code available in repositories.

If there exists a repository of code you'd like to add to your image, you need a recipe that will fetch, build, deploy, etc the software. If you're lucky a recipe will already exist in the layer index. If you have to write your own recipe, devtool is a powerful and invaluable tool. Simply invoke devtool, choose a recipe name, point devtool at the repository, and it does much of the work for you (including identifying dependencies)! The tooling understands dozens of fetchers for common repository types (git, cvs, clearcase, perforce, wget, bzr, etc…), and it provides dozens of handlers for dealing with various build systems (autotools, make, cmake, meson, npm, setuptools, etc…). As you can imagine, this exercise becomes more challenging if the software isn't using a common build system, or isn't using it correctly.

But what if the code doesn't already exist? If you're creating your own product and writing your own software, there will not be any pre-existing repository at which you can point devtool. This is the scenario we're going to focus on in this post. The assumptions are that you're writing code that needs to be compiled (i.e. not interpreted), and that you're using a much more powerful build host for development therefore you will need to cross-compile your code. Since the hardware we're using has some "toys" to "play" with (i.e. LEDs, ADCs, relays, inputs, and outputs), the goal of our code will be to "play" with some of the "toys" found on the Automation-HAT. Also, since my goal is to focus on cross-compiling and embedded development, I'm going to ignore the pre-exisiting python libraries that are available for this hardware.

There are two broad scenarios:

  1. you are comfortable using OpenEmbedded and would like to create an image and write the software entirely on your own host machine
  2. you are working as part of a team and have no interest in the overall image or knowing how the images are created, you only want to focus on writing the software

In either case all developers need access to the cross-development tools that will be used to build the final image. This will help alleviate many of the "gotchas" that crop up late in the development cycle when code gets integrated into the final image. If developers are using different versions of a given compiler, it's not uncommon for newer versions of the same tool to find more problems/errors with the code than older versions, or for newer versions to compile the code differently or make different assumptions. Having all developers use the same versions of all the same tools and libraries throughout the development process goes a long way towards improving your release process and improving the quality of your testing.

As I mentioned in my previous post, I like to start small and make incremental changes as I go. Which means I like to build my code often and test it often (preferably on the device itself). So let's start with a "Hello, world!". In any "good" software project, the code is but one component of the overall effort. Therefore I like to put my code in a src subdirectory of my project (…in anticipation of a doc directory for documentation, a test subdirectory for tests, etc).

1
2
3
$ mkdir src
$ cd src
$ vi i2ctest.c

…and for the code…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/*
 * Copyright (C) 2020  Trevor Woerner <twoerner@gmail.com>
 */

#include <stdio.h>

int
main (void)
{
        printf("Hello, world!\n");
        return 0;
}

As good developers we should be thinking about using a standard build system. There are many from which to choose; for this example I'm going to select the autotools. In order for our code to be built using the autotools we need to put project-specific metadata in various files which the autotools process so we can then use regular make to build.

In the top-level directory we need to create:

  1. the project's configure.ac (which eventually gets transformed into the familiar ./configure script)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    dnl Copyright (C) 2020  Trevor Woerner <twoerner@gmail.com>
    
    AC_PREREQ(2.57)
    AC_INIT([automationhat-doodles], 0.1.0, twoerner@gmail.com, automationhat-doodles)
    AC_CONFIG_SRCDIR(src/i2ctest.c)
    AC_CONFIG_AUX_DIR(cfg)
    AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 1.9])
    AM_CONFIG_HEADER(cfg/config.h)
    
    SUBDIRS="src"
    
    dnl **********************************
    dnl checks for programs
    dnl **********************************
    AC_PROG_CC
    AC_PROG_CPP
    AC_PROG_MAKE_SET
    AC_PROG_INSTALL
    AC_PROG_LN_S
    
    dnl **********************************
    dnl checks for header files
    dnl **********************************
    AC_HEADER_STDC
    AC_CHECK_HEADERS(stdio.h stdlib.h string.h unistd.h fcntl.h errno.h getopt.h)
    AC_CHECK_HEADERS(sys/types.h sys/stat.h sys/ioctl.h linux/i2c.h linux/i2c-dev.h)
    
    dnl **********************************
    dnl checks for typedefs, structs, and
    dnl compiler characteristics
    dnl **********************************
    AC_TYPE_SIZE_T
    
    dnl **********************************
    dnl other stuff
    dnl **********************************
    AC_SUBST(SUBDIRS)
    
    dnl **********************************
    dnl output
    dnl **********************************
    AC_OUTPUT(Makefile
    cfg/Makefile
    src/Makefile)
    

  2. the top-level Makefile.am
    1
    2
    3
    4
    5
    6
    7
    ## Copyright (C) 2020  Trevor Woerner <twoerner@gmail.com>
    
    ########################
    ## top-level Makefile.am
    ########################
    SUBDIRS = @SUBDIRS@
    DIST_SUBDIRS = cfg @SUBDIRS@
    

I prefer to put the configuration-related things into a separate cfg directory, so I create that directory and create a Makefile.am in there too:

1
2
$ mkdir cfg
$ vi cfg/Makefile.am

1
2
3
4
5
6
## Copyright (C) 2020  Trevor Woerner <twoerner@gmail.com>

########################
# cfg/Makefile.am
########################
SUBDIRS =

Finally, we need a Makefile.am in the src directory as well:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
## Copyright (C) 2020  Trevor Woerner <twoerner@gmail.com>

########################
## src/Makefile.am
########################
SUBDIRS =
AM_CFLAGS = -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes

bin_PROGRAMS = i2ctest
i2ctest_SOURCES = i2ctest.c

Our code is all ready to go, our target device is standing by, now how do we cross-compile this code with the tools we've used to create the rest of our image, and how can we test the code to verify it's working correctly? Earlier I outlined two scenarios: you're either going to be both writing some code and building the images, or you're only interested in writing code. Similarly there is a solution for each scenario:

  1. use devtool to help write a recipe for the code you're writing (which is very similar to how we used it in my previous blog post) the difference is that instead of pointing devtool to a repository "out there" on the internet, you point it at the top-level directory of wherever you're writing your code
  2. the person who is creating the images and running bitbake generates an SDK and hands it to the developer who uses it like any other SDK

Image And Code

As before, in this case we can use devtool to create a recipe for us and simply build our code like any other package:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ devtool add automationhat-doodles /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/
NOTE: Starting bitbake server...
INFO: Creating workspace layer in /z/build-master/6951/automation-hat/build/workspace
NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
NOTE: Reconnecting to bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
NOTE: Retrying server connection (#1)...
NOTE: Starting bitbake server...
INFO: Recipe /z/build-master/6951/automation-hat/poky/build/workspace/recipes/automationhat-doodles/automationhat-doodles_0.1.0.bb has been automatically created; further editing may be required to make it fully functional

Looking at the workspace:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ tree workspace
workspace
├── README
├── appends
│   └── automationhat-doodles_0.1.0.bbappend
├── conf
│   └── layer.conf
└── recipes
    └── automationhat-doodles
        └── automationhat-doodles_0.1.0.bb

4 directories, 4 files…

…the appends:

1
2
inherit externalsrc
EXTERNALSRC = "/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles"

…and the raw recipe devtool created for us:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Recipe created by recipetool
# This is the basis of a recipe and may need further editing in order to be fully functional.
# (Feel free to remove these comments when editing.)

# Unable to find any files that looked like license statements. Check the accompanying
# documentation and source headers and set LICENSE and LIC_FILES_CHKSUM accordingly.
#
# NOTE: LICENSE is being set to "CLOSED" to allow you to at least start building - if
# this is not accurate with respect to the licensing of the software being built (it
# will not be in most cases) you must specify the correct value before using this
# recipe for anything other than initial testing/development!
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = ""

# No information for SRC_URI yet (only an external source tree was specified)
SRC_URI = ""

# NOTE: if this software is not capable of being built in a separate build directory
# from the source, you should replace autotools with autotools-brokensep in the
# inherit line
inherit autotools

# Specify any options you want to pass to the configure script using EXTRA_OECONF:
EXTRA_OECONF = ""

Everything we have is enough to build our software, no changes required:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ devtool build automationhat-doodles
NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
Loading cache: 100% |                                                                                                                                                                          | ETA:  --:--:--
Loaded 0 entries from dependency cache.
Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:13
Parsing of 2044 .bb files complete (0 cached, 2044 parsed). 3215 targets, 123 skipped, 0 masked, 0 errors.
Removing 1 recipes from the cortexa53 sysroot: 100% |###########################################################################################################################################| Time: 0:00:00
Removing 1 recipes from the raspberrypi3_64 sysroot: 100% |#####################################################################################################################################| Time: 0:00:00
Loading cache: 100% |###########################################################################################################################################################################| Time: 0:00:03
Loaded 3214 entries from dependency cache.
Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:00
Parsing of 2044 .bb files complete (2043 cached, 1 parsed). 3215 targets, 123 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION           = "1.47.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "universal"
TARGET_SYS           = "aarch64-poky-linux"
MACHINE              = "raspberrypi3-64"
DISTRO               = "poky"
DISTRO_VERSION       = "3.1+snapshot-20201018"
TUNE_FEATURES        = "aarch64 armv8a crc cortexa53"
TARGET_FPU           = ""
meta                 
meta-poky            
meta-yocto-bsp       = "master:7cad26d585f67fa6bf873b8be361c6335a7db376"
meta-raspberrypi     = "master:6f85611576b7ccbfb6012631f741bd1daeffc9c9"
workspace            = "master:b1a0414a6df77674a860c365825a4500e6cd698b"
meta-oe              
meta-python          = "master:86a7820b7964ff91d7a26ac5c506e83292e347a3"
devtool-additions    = "master:b1a0414a6df77674a860c365825a4500e6cd698b"

Initialising tasks: 100% |######################################################################################################################################################################| Time: 0:00:00
Sstate summary: Wanted 0 Found 0 Missed 0 Current 116 (0% match, 100% complete)
NOTE: Executing Tasks
NOTE: automationhat-doodles: compiling from external source tree /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles
NOTE: Tasks Summary: Attempted 495 tasks of which 487 didn't need to be rerun and all succeeded.
NOTE: Writing buildhistory
NOTE: Writing buildhistory took: 1 seconds
NOTE: Build completion summary:
NOTE:   do_populate_sysroot: 0.0% sstate reuse(0 setscene, 1 scratch)
NOTE:   do_deploy_source_date_epoch: 0.0% sstate reuse(0 setscene, 1 scratch)
NOTE:   do_package: 0.0% sstate reuse(0 setscene, 1 scratch)
NOTE:   do_packagedata: 0.0% sstate reuse(0 setscene, 1 scratch)

Success! The exact same build system that OE created to generate our entire image has now been used in exactly the same way to build the code we just wrote. As a developer I don't have to know anything about having to find or build an appropriate cross-compiler, or worry about whether I've passed the correct flags to the cross-compiler to ensure my build is using the correct headers (for example).

Now we need to get our code over to the target device so we can test it. In theory we could generate a new image with each build and test our code that way. That would create quite a long development cycle! Provided we have networking working on the target and an ssh server running, we could just scp the file over and run it. With a simple program like this one, the proposed solution would work fine. But as the code gets more complicated (e.g. multiple pieces, libraries, etc) copying everything over could increase the possibilities of errors. Also, once we're done testing a given build, it would be nice of having a way to clear everything off the target device between uploads.

Once again, devtool is very helpful in this situation! It includes not only a devtool deploy-target command, but also a devtool undeploy-target command!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ devtool deploy-target automationhat-doodles root@10.0.0.50
NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
NOTE: Reconnecting to bitbake server...
NOTE: Previous bitbake instance shutting down?, waiting to retry...
NOTE: Retrying server connection (#2)...
Loading cache: 100% |###########################################################################################################################################################################| Time: 0:00:00
Loaded 3209 entries from dependency cache.
Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:01
Parsing of 2040 .bb files complete (2039 cached, 1 parsed). 3210 targets, 165 skipped, 0 masked, 0 errors.
tar: ./usr/bin/i2ctest: time stamp 2020-10-09 07:07:44.128603897 is 274194.69420039 s in the future
tar: ./usr/bin: time stamp 2020-10-09 07:07:44.128603897 is 274194.693851432 s in the future
tar: ./usr: time stamp 2020-10-09 07:07:44.124603877 is 274194.689302922 s in the future
tar: .: time stamp 2020-10-09 07:07:44.124603877 is 274194.689052297 s in the future
INFO: Successfully deployed /z/build-master/6951/automation-hat/build/tmp-glibc/work/cortexa53-oe-linux/automationhat-doodles/0.1.0-r0/image

Once our code has been uploaded to the target we simply would run it on the target like any other piece of software:

1
2
root@raspberrypi3-64:~# i2ctest 
Hello, world!

Once we're done with our tests we clean up the target like so:

1
2
3
$ devtool undeploy-target automationhat-doodles root@10.0.0.50
NOTE: Starting bitbake server...
INFO: Successfully undeployed automationhat-doodles

NOTE: you don't need to undeploy-target after each test. If you're compiling and testing in a tight loop you can simply devtool deploy-target repeatedly. The first thing a devtool deploy-target does is to check for and devtool undeploy-target any previous deploys of the same package. It keeps track of all the files that were copied over on each deploy, so that they can all be removed either when you undeploy-target explicitly, or when your next deploy-target implicitly removes the previous.

Code Only

If you have a team of developers, it is possible to extract the cross-development tools (and all the other relevant pieces) from your build and bundle everything together into an SDK. Using this SDK, the developers will be using the same tools as the image build, and all your developers will be using the same tools. Additionally, the SDK you create will have all the correct header files for all of the components of the software that forms your specific image!

An off-the-shelf SDK you find randomly on the internet will advertise the version of the compiler it's using, but what versions of which other packages are included? Which C library is it using, and what version? Which kernel headers were used? And what if your application sits on top of a bunch of high-level libraries (i.e. a GUI application)? With many off-the-shelf SDKs, getting the flags right to ensure your host system isn't contaminating your cross-build is often left as an exercise for the user! The SDK you generate from your OE image takes care of all those details for you.

To generate an SDK from any of your specific images, build the image with bitbake as usual, but indicate you want the populate_sdk task run specifically:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
$ bitbake core-image-full-cmdline -c populate_sdk
Loading cache: 100% |###########################################################################################################################################################################| Time: 0:00:00
Loaded 3214 entries from dependency cache.
Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:00
Parsing of 2044 .bb files complete (2043 cached, 1 parsed). 3215 targets, 123 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION           = "1.47.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "universal"
TARGET_SYS           = "aarch64-poky-linux"
MACHINE              = "raspberrypi3-64"
DISTRO               = "poky"
DISTRO_VERSION       = "3.1+snapshot-20201018"
TUNE_FEATURES        = "aarch64 armv8a crc cortexa53"
TARGET_FPU           = ""
meta                 
meta-poky            
meta-yocto-bsp       = "master:7cad26d585f67fa6bf873b8be361c6335a7db376"
meta-raspberrypi     = "master:6f85611576b7ccbfb6012631f741bd1daeffc9c9"
workspace            = "master:b1a0414a6df77674a860c365825a4500e6cd698b"
meta-oe              
meta-python          = "master:86a7820b7964ff91d7a26ac5c506e83292e347a3"
devtool-additions    = "master:b1a0414a6df77674a860c365825a4500e6cd698b"

Initialising tasks: 100% |######################################################################################################################################################################| Time: 0:00:02
Sstate summary: Wanted 464 Found 0 Missed 464 Current 982 (0% match, 67% complete)
NOTE: Executing Tasks
WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-src went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards]
WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-dbg went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards]
WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-staticdev went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards]
WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-dev went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards]
WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-doc went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards]
WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-locale went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards]
WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218 went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards]
WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-src went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards]
WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-dbg went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards]
WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-staticdev went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards]
WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-dev went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards]
WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-doc went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards]
WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-locale went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards]
WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards]
NOTE: Tasks Summary: Attempted 4171 tasks of which 3016 didn't need to be rerun and all succeeded.
NOTE: Writing buildhistory
NOTE: Writing buildhistory took: 15 seconds
NOTE: Build completion summary:
NOTE:   do_populate_sysroot: 0.0% sstate reuse(0 setscene, 67 scratch)
NOTE:   do_deploy_source_date_epoch: 0.0% sstate reuse(0 setscene, 98 scratch)
NOTE:   do_package: 0.0% sstate reuse(0 setscene, 99 scratch)
NOTE:   do_packagedata: 0.0% sstate reuse(0 setscene, 99 scratch)
NOTE:   do_package_write_ipk: 0.0% sstate reuse(0 setscene, 99 scratch)

Summary: There were 14 WARNING messages shown.

The resulting SDK is found in ${TMPDIR}/deploy/sdk:

1
2
3
4
5
6
$ ls -lh tmp/deploy/sdk/
total 254M
-rw-r--r-- 2 trevor users 9.4K Oct 18 00:33 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.host.manifest
-rwxr-xr-x 2 trevor users 253M Oct 18 00:35 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh
-rw-r--r-- 2 trevor users 160K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.target.manifest
-rw-r--r-- 2 trevor users 339K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.testdata.json

The SDK is bundled as a self-extracting shell script. Give the script to your developers (in this case it's the poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh file) and have them simply execute the file. It will ask the person installing the file for a target location. In the following example, I've decided to install the SDK into the directory /opt/toolchains/automation-hat/poky/sdk:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ ./poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh 
Poky (Yocto Project Reference Distro) SDK installer version 3.1+snapshot
========================================================================
Enter target directory for SDK (default: /opt/poky/3.1+snapshot): /opt/toolchains/automation-hat/poky
You are about to install the SDK to "/opt/toolchains/automation-hat/poky". Proceed [Y/n]? Y
Extracting SDK..................................................................................done
Setting it up...done
SDK has been successfully set up and is ready to be used.
Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g.
 $ . /opt/toolchains/automation-hat/poky/environment-setup-cortexa53-poky-linux

As the installer so helpfully reminds us, anytime you want to use the SDK you just installed, in a fresh shell environment you'll have to source the environment file in order to correctly use the cross-compiler. In the following example I'm going to use the SDK I just installed in order to build my code. I'll start a new shell for this demonstration, but this isn't shown.

NOTE: in the following example my SDK is installed to /opt/toolchains/automation-hat/poky/sdk my source code is found in /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles and I will be building my code "out of tree" at /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
$ cd /opt/toolchains/automation-hat/poky/sdk/
$ . ./environment-setup-cortexa53-poky-linux
$ export PS1="${PS1}sdk> "
$ sdk> cd /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/

$ sdk> autoreconf -i
configure.ac:15: installing 'cfg/compile'
configure.ac:7: installing 'cfg/install-sh'
configure.ac:7: installing 'cfg/missing'
src/Makefile.am: installing 'cfg/depcomp'

$ sdk> mkdir _build
$ sdk> cd _build
$ sdk> ../configure --host=x86_64
configure: loading site script /opt/toolchains/automation-hat/poky/sdk/site-config-cortexa53-poky-linux
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for x86_64-strip... aarch64-poky-linux-strip
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for x86_64-gcc... aarch64-poky-linux-gcc  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... yes
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether aarch64-poky-linux-gcc  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux accepts -g... yes
checking for aarch64-poky-linux-gcc  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux option to accept ISO C89... none needed
checking whether aarch64-poky-linux-gcc  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux understands -c and -o together... yes
checking whether make supports the include directive... yes (GNU style)
checking dependency style of aarch64-poky-linux-gcc  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux... gcc3
checking how to run the C preprocessor... aarch64-poky-linux-gcc -E  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux
checking whether make sets $(MAKE)... (cached) yes
checking whether ln -s works... yes
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking stdio.h usability... yes
checking stdio.h presence... yes
checking for stdio.h... yes
checking for stdlib.h... (cached) yes
checking for string.h... (cached) yes
checking for unistd.h... (cached) yes
checking fcntl.h usability... yes
checking fcntl.h presence... yes
checking for fcntl.h... yes
checking errno.h usability... yes
checking errno.h presence... yes
checking for errno.h... yes
checking getopt.h usability... yes
checking getopt.h presence... yes
checking for getopt.h... yes
checking for sys/types.h... (cached) yes
checking for sys/stat.h... (cached) yes
checking sys/ioctl.h usability... yes
checking sys/ioctl.h presence... yes
checking for sys/ioctl.h... yes
checking linux/i2c.h usability... yes
checking linux/i2c.h presence... yes
checking for linux/i2c.h... yes
checking linux/i2c-dev.h usability... yes
checking linux/i2c-dev.h presence... yes
checking for linux/i2c-dev.h... yes
checking for size_t... yes
configure: creating ./config.status
config.status: creating Makefile
config.status: creating cfg/Makefile
config.status: creating src/Makefile
config.status: creating cfg/config.h
config.status: executing depfiles commands

$ sdk> make
Making all in src
make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
make[2]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
aarch64-poky-linux-gcc  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux -DHAVE_CONFIG_H -I. -I../../src -I../cfg    -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -O2 -pipe -g -feliminate-unused-debug-types  -MT i2ctest.o -MD -MP -MF .deps/i2ctest.Tpo -c -o i2ctest.o ../../src/i2ctest.c
mv -f .deps/i2ctest.Tpo .deps/i2ctest.Po
aarch64-poky-linux-gcc  -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong  -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -O2 -pipe -g -feliminate-unused-debug-types   -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -Wl,-z,relro,-z,now -o i2ctest i2ctest.o  
make[2]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build'
make[1]: Nothing to be done for 'all-am'.
make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build'

$ sdk> file src/i2ctest
src/i2ctest: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, BuildID[sha1]=47845aa25dff34453e7b1cb367c9b59883f8292c, for GNU/Linux 3.14.0, with debug_info, not stripped

Yay! We've demonstrated generating an SDK tuned specifically for our image, giving this SDK to an independent developer, and that developer using the SDK on their own, separate machine, to cross-compile their code (in which they've used the autotools build system). This developer doesn't have OE installed on their system, isn't using OE to generate images, and is only focused on writing and cross-compiling their code for the target device.

Although this is a very small example, it does scale. If the code being written was, say, a GUI app that was using (for example) boost and GTK or Qt, simply add those libraries to your image and when you generate the SDK from this image, the SDK will include everything the developer needs to write their application (i.e. all the appropriate headers and cross-libraries).

On-target testing, though, is up to the developer. For a small example such as this, it wouldn't be too hard to copy the one file over and test it. But anything more complex could get messy. If only there was some way to create an SDK that also contained devtool …

Code Only… extended

Turns out there is a way:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$ bitbake core-image-full-cmdline -c populate_sdk_ext
Loading cache: 100% |                                                                                                                                                                          | ETA:  --:--:--
Loaded 0 entries from dependency cache.
Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:14
Parsing of 2042 .bb files complete (0 cached, 2042 parsed). 3213 targets, 123 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION           = "1.47.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "universal"
TARGET_SYS           = "aarch64-poky-linux"
MACHINE              = "raspberrypi3-64"
DISTRO               = "poky"
DISTRO_VERSION       = "3.1+snapshot-20201018"
TUNE_FEATURES        = "aarch64 armv8a crc cortexa53"
TARGET_FPU           = ""
meta-raspberrypi     = "master:6f85611576b7ccbfb6012631f741bd1daeffc9c9"
devtool-additions    = "master:b1a0414a6df77674a860c365825a4500e6cd698b"
meta                 
meta-poky            
meta-yocto-bsp       = "master:7cad26d585f67fa6bf873b8be361c6335a7db376"
meta-oe              
meta-python          = "master:86a7820b7964ff91d7a26ac5c506e83292e347a3"

Initialising tasks: 100% |######################################################################################################################################################################| Time: 0:00:03
Sstate summary: Wanted 2 Found 0 Missed 2 Current 1952 (0% match, 99% complete)
NOTE: Executing Tasks
NOTE: Tasks Summary: Attempted 5092 tasks of which 5079 didn't need to be rerun and all succeeded.
NOTE: Writing buildhistory
NOTE: Writing buildhistory took: 1 seconds

Note, however, that there is a significant size difference between an SDK and an eSDK:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ ls -lh tmp/deploy/sdk/
total 1.3G
-rw-r--r-- 2 trevor users 9.4K Oct 18 00:33 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.host.manifest
-rwxr-xr-x 2 trevor users 253M Oct 18 00:35 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh
-rw-r--r-- 2 trevor users 160K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.target.manifest
-rw-r--r-- 2 trevor users 339K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.testdata.json
-rw-r--r-- 1 trevor users 9.6K Oct 18 11:53 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.host.manifest
-rwxr-xr-x 2 trevor users 999M Oct 18 11:53 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.sh
-rw-r--r-- 1 trevor users 6.6K Oct 18 11:53 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.target.manifest
-rw-r--r-- 2 trevor users 335K Oct 18 11:49 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.testdata.json
-rw-r--r-- 2 trevor users 6.6K Oct 18 02:06 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.host.manifest
-rwxr-xr-x 2 trevor users  26M Oct 18 02:07 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.sh
-rw-r--r-- 2 trevor users    0 Oct 18 02:05 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.target.manifest
-rw-r--r-- 2 trevor users 306K Oct 18 02:05 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.testdata.json

You could install and use an eSDK exactly the same way you would use the regular SDK:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
$ /z/build-master/6951/automation-hat/poky/build/tmp/deploy/sdk/poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.sh 
Poky (Yocto Project Reference Distro) Extensible SDK installer version 3.1+snapshot
===================================================================================
Enter target directory for SDK (default: ~/poky_sdk): /opt/toolchains/automation-hat/poky/esdk
You are about to install the SDK to "/opt/toolchains/automation-hat/poky/esdk". Proceed [Y/n]? Y
Extracting SDK................................................done
Setting it up...
Extracting buildtools...
Preparing build system...
Loading cache: 100% |                                                                | ETA:  --:--:--
Parsing recipes: 100% |###############################################################| Time: 0:00:21
Initialising tasks: 100% |############################################################| Time: 0:00:02
Checking sstate mirror object availability: 100% |####################################| Time: 0:00:00
Loading cache: 100% |#################################################################| Time: 0:00:00
Initialising tasks: 100% |############################################################| Time: 0:00:00
done
SDK has been successfully set up and is ready to be used.
Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g.
 $ . /opt/toolchains/automation-hat/poky/esdk/environment-setup-cortexa53-poky-linux

$ export PS1="${PS1}esdk> "
$ esdk>

$ esdk> cd /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/
$ esdk> autoreconf -i
configure.ac:15: installing 'cfg/compile'
configure.ac:7: installing 'cfg/install-sh'
configure.ac:7: installing 'cfg/missing'
src/Makefile.am: installing 'cfg/depcomp'

$ esdk> mkdir _build
$ esdk> cd _build

$ esdk> ../configure --host=x86_64
configure: loading site script /usr/share/site/x86_64-unknown-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for x86_64-strip... no
checking for strip... strip
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for x86_64-gcc... no
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking how to run the C preprocessor... gcc -E
checking whether make sets $(MAKE)... (cached) yes
checking whether ln -s works... yes
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking stdio.h usability... yes
checking stdio.h presence... yes
checking for stdio.h... yes
checking for stdlib.h... (cached) yes
checking for string.h... (cached) yes
checking for unistd.h... (cached) yes
checking fcntl.h usability... yes
checking fcntl.h presence... yes
checking for fcntl.h... yes
checking errno.h usability... yes
checking errno.h presence... yes
checking for errno.h... yes
checking getopt.h usability... yes
checking getopt.h presence... yes
checking for getopt.h... yes
checking for sys/types.h... (cached) yes
checking for sys/stat.h... (cached) yes
checking sys/ioctl.h usability... yes
checking sys/ioctl.h presence... yes
checking for sys/ioctl.h... yes
checking linux/i2c.h usability... yes
checking linux/i2c.h presence... yes
checking for linux/i2c.h... yes
checking linux/i2c-dev.h usability... yes
checking linux/i2c-dev.h presence... yes
checking for linux/i2c-dev.h... yes
checking for size_t... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating cfg/Makefile
config.status: creating src/Makefile
config.status: creating cfg/config.h
config.status: executing depfiles commands

$ esdk> make
Making all in src
make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
make[2]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
gcc -DHAVE_CONFIG_H -I. -I../../src -I../cfg    -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -g -O2 -MT i2ctest.o -MD -MP -MF .deps/i2ctest.Tpo -c -o i2ctest.o ../../src/i2ctest.c
mv -f .deps/i2ctest.Tpo .deps/i2ctest.Po
gcc -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -g -O2   -o i2ctest i2ctest.o  
make[2]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src'
make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build'
make[1]: Nothing to be done for 'all-am'.
make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build'

$ esdk> file src/i2ctest
src/i2ctest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, BuildID[sha1]=4199ed513fc41043d24d05b43075b73fc9037e87, for GNU/Linux 3.2.0, with debug_info, not stripped

However, note that in order to take full advantage of devtool (e.g. devtool deploy-target) the independent developer needs to create a recipe for the code they're writing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ esdk> devtool add automationhat-doodles /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles
NOTE: Starting bitbake server...
NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
NOTE: Reconnecting to bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
NOTE: Retrying server connection (#1)...
NOTE: Starting bitbake server...
INFO: Recipe /opt/toolchains/automation-hat/poky/esdk/workspace/recipes/automationhat-doodles/automationhat-doodles_0.1.0.bb has been automatically created; further editing may be required to make it fully functional

$ esdk> devtool build automationhat-doodles
NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
Loading cache: 100% |#################################################################| Time: 0:00:00
Loaded 3213 entries from dependency cache.
Parsing recipes: 100% |###############################################################| Time: 0:00:00
Parsing of 2043 .bb files complete (2042 cached, 1 parsed). 3214 targets, 123 skipped, 0 masked, 0 errors.
Loading cache: 100% |#################################################################| Time: 0:00:01
Loaded 3213 entries from dependency cache.
Parsing recipes: 100% |###############################################################| Time: 0:00:00
Parsing of 2043 .bb files complete (2042 cached, 1 parsed). 3214 targets, 123 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies
Initialising tasks: 100% |############################################################| Time: 0:00:00
Sstate summary: Wanted 8 Found 1 Missed 7 Current 108 (12% match, 93% complete)
NOTE: Executing Tasks
NOTE: automationhat-doodles: compiling from external source tree /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles
NOTE: Tasks Summary: Attempted 495 tasks of which 487 didn't need to be rerun and all succeeded.
NOTE: Build completion summary:
NOTE:   do_populate_sysroot: 0.0% sstate reuse(0 setscene, 1 scratch)
NOTE:   do_deploy_source_date_epoch: 0.0% sstate reuse(0 setscene, 1 scratch)
NOTE:   do_package: 0.0% sstate reuse(0 setscene, 1 scratch)
NOTE:   do_packagedata: 0.0% sstate reuse(0 setscene, 1 scratch)

$ esdk> devtool deploy-target automationhat-doodles root@10.0.0.50
NOTE: Starting bitbake server...
NOTE: Reconnecting to bitbake server...
NOTE: Retrying server connection (#1)...
Loading cache: 100% |#################################################################| Time: 0:00:00
Loaded 3213 entries from dependency cache.
Parsing recipes: 100% |###############################################################| Time: 0:00:00
Parsing of 2043 .bb files complete (2042 cached, 1 parsed). 3214 targets, 123 skipped, 0 masked, 0 errors.
tar: ./usr/bin/i2ctest: time stamp 2020-10-18 18:16:41 is 82401236.305274304 s in the future
tar: ./usr/bin: time stamp 2020-10-18 18:16:41 is 82401236.304915762 s in the future
tar: ./usr: time stamp 2020-10-18 18:16:41 is 82401236.304415189 s in the future
tar: .: time stamp 2020-10-18 18:16:41 is 82401236.304187064 s in the future
INFO: Successfully deployed /opt/toolchains/automation-hat/poky/esdk/tmp/work/cortexa53-poky-linux/automationhat-doodles/0.1.0-r0/image


Turning On An LED

We now have the mechanics in place for writing, cross-compiling, and on-device testing of your code as it is being written, all using the same bleeding-edge, state-of-the-art tools being used to build your image.

Working through the code to get an LED lit we end up with:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * Copyright (C) 2020  Trevor Woerner <twoerner@gmail.com>
 */

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <i2c/smbus.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <getopt.h>

static char *i2cDevice_pG = "/dev/i2c-1";

static void print_funcs(unsigned long);
static void process_cmdline_args(int,char**);
static void usage(const char*);

int
main (int argc, char *argv[])
{
        int ret;
        int i2cFd;
        unsigned long funcs;

        /* process cmdline - get i2c device */
        process_cmdline_args(argc, argv);
        if (optind == argc)
                ;
        else if ((optind+1) == argc) {
                i2cDevice_pG = strdup(argv[optind]);
                if (i2cDevice_pG == NULL) {
                        perror("strdup()");
                        return 1;
                }
        }
        else {
                fprintf(stderr, "bad cmdline args\n");
                usage(argv[0]);
                return 1;
        }
        printf("device: %s\n", i2cDevice_pG);

        /* open i2c device */
        i2cFd = open(i2cDevice_pG, O_RDWR);
        if (i2cFd < 0) {
                perror("open()");
                return 1;
        }

        /* figure out and show i2c capabilities */
        ret = ioctl(i2cFd, I2C_FUNCS, &funcs);
        if (ret < 0) {
                perror("ioctl()");
                close(i2cFd);
                return 1;
        }
        printf("funcs: 0x%08lx\n", funcs);
        print_funcs(funcs);

        /* interract with device */
        ret = ioctl(i2cFd, I2C_SLAVE, 0x54);
        if (ret < 0) {
                perror("I2C_SLAVE");
                return 1;
        }
        i2c_smbus_write_byte_data(i2cFd, 0x17, 0xff); // reset
        i2c_smbus_write_byte_data(i2cFd, 0x00, 0x01); // enable
        i2c_smbus_write_byte_data(i2cFd, 0x12, 0x10); // some pwm on channel 0x12
        i2c_smbus_write_byte_data(i2cFd, 0x15, 0x20); // turn on channel 0x12
        i2c_smbus_write_byte_data(i2cFd, 0x16, 0xff); // update

        close(i2cFd);
        return 0;
}

static struct {
        unsigned long flag;
        const char *str_p;
} I2CFlags_G[] = {
        {I2C_FUNC_I2C, "I2C_FUNC_I2C"},
        {I2C_FUNC_10BIT_ADDR, "I2C_FUNC_10BIT_ADDR"},
        {I2C_FUNC_PROTOCOL_MANGLING, "I2C_FUNC_PROTOCOL_MANGLING"},
        {I2C_FUNC_SMBUS_PEC, "I2C_FUNC_SMBUS_PEC"},
        {I2C_FUNC_NOSTART, "I2C_FUNC_NOSTART"},
        {I2C_FUNC_SLAVE, "I2C_FUNC_SLAVE"},
        {I2C_FUNC_SMBUS_BLOCK_PROC_CALL, "I2C_FUNC_SMBUS_BLOCK_PROC_CALL"},
        {I2C_FUNC_SMBUS_QUICK, "I2C_FUNC_SMBUS_QUICK"},
        {I2C_FUNC_SMBUS_READ_BYTE, "I2C_FUNC_SMBUS_READ_BYTE"},
        {I2C_FUNC_SMBUS_WRITE_BYTE, "I2C_FUNC_SMBUS_WRITE_BYTE"},
        {I2C_FUNC_SMBUS_READ_BYTE_DATA, "I2C_FUNC_SMBUS_READ_BYTE_DATA"},
        {I2C_FUNC_SMBUS_WRITE_BYTE_DATA, "I2C_FUNC_SMBUS_WRITE_BYTE_DATA"},
        {I2C_FUNC_SMBUS_READ_WORD_DATA, "I2C_FUNC_SMBUS_READ_WORD_DATA"},
        {I2C_FUNC_SMBUS_WRITE_WORD_DATA, "I2C_FUNC_SMBUS_WRITE_WORD_DATA"},
        {I2C_FUNC_SMBUS_PROC_CALL, "I2C_FUNC_SMBUS_PROC_CALL"},
        {I2C_FUNC_SMBUS_READ_BLOCK_DATA, "I2C_FUNC_SMBUS_READ_BLOCK_DATA"},
        {I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, "I2C_FUNC_SMBUS_WRITE_BLOCK_DATA"},
        {I2C_FUNC_SMBUS_READ_I2C_BLOCK, "I2C_FUNC_SMBUS_READ_I2C_BLOCK"},
        {I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, "I2C_FUNC_SMBUS_WRITE_I2C_BLOCK"},
        {I2C_FUNC_SMBUS_HOST_NOTIFY, "I2C_FUNC_SMBUS_HOST_NOTIFY"},
};
#define I2CFLAGS_SZ (sizeof(I2CFlags_G) / sizeof(I2CFlags_G[0]))

static void
print_funcs (unsigned long funcs)
{
        size_t i;

        for (i=0; i<I2CFLAGS_SZ; ++i)
                if (funcs & I2CFlags_G[i].flag)
                        printf("- %s\n", I2CFlags_G[i].str_p);
}

static void
usage (const char *pgm_p)
{
        fprintf(stderr, "%s\n", PACKAGE_STRING);

        if (pgm_p != NULL)
                fprintf(stderr, "\nusage: %s [opts] [device]\n", pgm_p);
        fprintf(stderr, "  where [device]:\n");
        fprintf(stderr, "    optionally provide the i2c device node to use\n");
        fprintf(stderr, "    (default: %s)\n", i2cDevice_pG);
        fprintf(stderr, "  where [opts]:\n");
        fprintf(stderr, "    -h|--help    print this help and exit successfully\n");
}

static void
process_cmdline_args (int argc, char *argv[])
{
        int c;
        struct option longOpts[] = {
                {"help", no_argument, NULL, 'h'},
                {NULL, 0, NULL, 0},
        };

        while (1) {
                c = getopt_long(argc, argv, "h", longOpts, NULL);
                if (c == -1)
                        break;

                switch (c) {
                        case 'h':
                                usage(argv[0]);
                                exit(0);
                                break;

                        default:
                                fprintf(stderr, "unknown getopt: %d (0x%02x)\n", c, c);
                                break;
                }
        }
}

This C code for i2c has a dependency on i2c-tools, so the following updates are required.

configure.ac:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
dnl Copyright (C) 2020  Trevor Woerner <twoerner@gmail.com>

AC_PREREQ(2.57)
AC_INIT([automationhat-doodles], 0.1.0, twoerner@gmail.com, automationhat-doodles)
AC_CONFIG_SRCDIR(src/i2ctest.c)
AC_CONFIG_AUX_DIR(cfg)
AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 1.9])
AM_CONFIG_HEADER(cfg/config.h)

SUBDIRS="src"

dnl **********************************
dnl checks for programs
dnl **********************************
AC_PROG_CC
AC_PROG_CPP
AC_PROG_MAKE_SET
AC_PROG_INSTALL
AC_PROG_LN_S

dnl **********************************
dnl checks for libraries
dnl **********************************
AC_CHECK_LIB(i2c, i2c_smbus_write_block_data, ,AC_MSG_ERROR([Can't find library i2c]), )

dnl **********************************
dnl checks for header files
dnl **********************************
AC_HEADER_STDC
AC_CHECK_HEADERS(stdio.h stdlib.h string.h unistd.h fcntl.h errno.h getopt.h)
AC_CHECK_HEADERS(sys/types.h sys/stat.h sys/ioctl.h linux/i2c.h linux/i2c-dev.h)
AC_CHECK_HEADERS(i2c/smbus.h)

dnl **********************************
dnl checks for typedefs, structs, and
dnl compiler characteristics
dnl **********************************
AC_TYPE_SIZE_T

dnl **********************************
dnl other stuff
dnl **********************************
AC_SUBST(SUBDIRS)

dnl **********************************
dnl output
dnl **********************************
AC_OUTPUT(Makefile
cfg/Makefile
src/Makefile)

and modify the automationhat-doodles recipe:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Recipe created by recipetool
# This is the basis of a recipe and may need further editing in order to be fully functional.
# (Feel free to remove these comments when editing.)

# Unable to find any files that looked like license statements. Check the accompanying
# documentation and source headers and set LICENSE and LIC_FILES_CHKSUM accordingly.
#
# NOTE: LICENSE is being set to "CLOSED" to allow you to at least start building - if
# this is not accurate with respect to the licensing of the software being built (it
# will not be in most cases) you must specify the correct value before using this
# recipe for anything other than initial testing/development!
LICENSE = "CLOSED"
LIC_FILES_CHKSUM = ""

# No information for SRC_URI yet (only an external source tree was specified)
SRC_URI = ""

# NOTE: if this software is not capable of being built in a separate build directory
# from the source, you should replace autotools with autotools-brokensep in the
# inherit line
inherit autotools

# Specify any options you want to pass to the configure script using EXTRA_OECONF:
EXTRA_OECONF = ""

DEPENDS += "i2c-tools"

Running the code on the target:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
root@raspberrypi3-64:~# i2ctest 
device: /dev/i2c-1
funcs: 0x0eff0009
- I2C_FUNC_I2C
- I2C_FUNC_SMBUS_PEC
- I2C_FUNC_SMBUS_QUICK
- I2C_FUNC_SMBUS_READ_BYTE
- I2C_FUNC_SMBUS_WRITE_BYTE
- I2C_FUNC_SMBUS_READ_BYTE_DATA
- I2C_FUNC_SMBUS_WRITE_BYTE_DATA
- I2C_FUNC_SMBUS_READ_WORD_DATA
- I2C_FUNC_SMBUS_WRITE_WORD_DATA
- I2C_FUNC_SMBUS_PROC_CALL
- I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
- I2C_FUNC_SMBUS_READ_I2C_BLOCK
- I2C_FUNC_SMBUS_WRITE_I2C_BLOCK

and looking at the target: