• ...
  • SDK
  • Yocto
  • ...
  • ...
  • your first application
  • tools
  • ...

Getting started with package manager usage#

If you want to create a SimpleSwitch™ application creating the rootfs installing the packages you want, please follow this guide to get you easily started.

Example Project 1 - Using moquitto library#

The mosquitto library is not shipped in the SDK. If you want to compile an application which use that library, assuming it is provided by the repository you fetch, you need to:

  • create a sysroot with the moquitto headers and library.

  • use it to compile your application.

  • deploy both your application and the mosquitto library in the final rootfs.

As an example, we will use a piece of the source code coming from the mosquitto repository itself and we just have to adapt the IP address of the MQTT broker, here a basic-1.c:

// SPDX-FileCopyrightText: (C) 2007, Eclipse Foundation, Inc. and its licensors
// SPDX-License-Identifier: BSD-3-Clause
/*
 * This example shows how to publish messages from outside of the Mosquitto network loop.
 */

#include <mosquitto.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define IP_ADDR "192.168.0.69"

/* Callback called when the client receives a CONNACK message from the broker. */
void on_connect(struct mosquitto *mosq, void *obj, int reason_code)
{
	/* Print out the connection result. mosquitto_connack_string() produces an
	 * appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0
	 * clients is mosquitto_reason_string().
	 */
	printf("on_connect: %s\n", mosquitto_connack_string(reason_code));
	if(reason_code != 0){
		/* If the connection fails for any reason, we don't want to keep on
		 * retrying in this example, so disconnect. Without this, the client
		 * will attempt to reconnect. */
		mosquitto_disconnect(mosq);
	}

	/* You may wish to set a flag here to indicate to your application that the
	 * client is now connected. */
}


/* Callback called when the client knows to the best of its abilities that a
 * PUBLISH has been successfully sent. For QoS 0 this means the message has
 * been completely written to the operating system. For QoS 1 this means we
 * have received a PUBACK from the broker. For QoS 2 this means we have
 * received a PUBCOMP from the broker. */
void on_publish(struct mosquitto *mosq, void *obj, int mid)
{
	printf("Message with mid %d has been published.\n", mid);
}


int get_temperature(void)
{
	sleep(1); /* Prevent a storm of messages - this pretend sensor works at 1Hz */
	return random()%100;
}

/* This function pretends to read some data from a sensor and publish it.*/
void publish_sensor_data(struct mosquitto *mosq)
{
	char payload[20];
	int temp;
	int rc;

	/* Get our pretend data */
	temp = get_temperature();
	/* Print it to a string for easy human reading - payload format is highly
	 * application dependent. */
	snprintf(payload, sizeof(payload), "%d", temp);

	/* Publish the message
	 * mosq - our client instance
	 * *mid = NULL - we don't want to know what the message id for this message is
	 * topic = "example/temperature" - the topic on which this message will be published
	 * payloadlen = strlen(payload) - the length of our payload in bytes
	 * payload - the actual payload
	 * qos = 2 - publish with QoS 2 for this example
	 * retain = false - do not use the retained message feature for this message
	 */
	rc = mosquitto_publish(mosq, NULL, "example/temperature", strlen(payload), payload, 2, false);
	if(rc != MOSQ_ERR_SUCCESS){
		fprintf(stderr, "Error publishing: %s\n", mosquitto_strerror(rc));
	}
}


int main(int argc, char *argv[])
{
	struct mosquitto *mosq;
	int rc;

	/* Required before calling other mosquitto functions */
	mosquitto_lib_init();

	/* Create a new client instance.
	 * id = NULL -> ask the broker to generate a client id for us
	 * clean session = true -> the broker should remove old sessions when we connect
	 * obj = NULL -> we aren't passing any of our private data for callbacks
	 */
	mosq = mosquitto_new(NULL, true, NULL);
	if(mosq == NULL){
		fprintf(stderr, "Error: Out of memory.\n");
		return 1;
	}

	/* Configure callbacks. This should be done before connecting ideally. */
	mosquitto_connect_callback_set(mosq, on_connect);
	mosquitto_publish_callback_set(mosq, on_publish);

	/* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds.
	 * This call makes the socket connection only, it does not complete the MQTT
	 * CONNECT/CONNACK flow, you should use mosquitto_loop_start() or
	 * mosquitto_loop_forever() for processing net traffic. */
	rc = mosquitto_connect(mosq, IP_ADDR, 1883, 60);
	if(rc != MOSQ_ERR_SUCCESS){
		mosquitto_destroy(mosq);
		fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
		return 1;
	}

	/* Run the network loop in a background thread, this call returns quickly. */
	rc = mosquitto_loop_start(mosq);
	if(rc != MOSQ_ERR_SUCCESS){
		mosquitto_destroy(mosq);
		fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
		return 1;
	}

	/* At this point the client is connected to the network socket, but may not
	 * have completed CONNECT/CONNACK.
	 * It is fairly safe to start queuing messages at this point, but if you
	 * want to be really sure you should wait until after a successful call to
	 * the connect callback.
	 * In this case we know it is 1 second before we start publishing.
	 */
	while(1){
		publish_sensor_data(mosq);
	}

	mosquitto_lib_cleanup();
	return 0;
}

And here is a Makefile able to build the application:

# SPDX-FileCopyrightText: (C) 2023 Avnet Embedded GmbH
# SPDX-License-Identifier: LicenseRef-Avnet-OSS-1.0
BIN = basic-1
OBJS = basic-1.o

all: $(BIN)

$(BIN): $(OBJS)
	$(CC) -o $@ $< -lmosquitto

clean:
	rm -f $(BIN) $(OBJS)

install: $(BIN)
	install -m 0755 -d "$(PREFIX)/opt/$(BIN)"
	install -m 0755 $(BIN) "$(PREFIX)/opt/$(BIN)"

.PHONY: clean install

Build the project#

Even with a makefile aware of cross-compilation, building the binary will fail because the mosquitto library is not part of the SDK. As said previously you need a sysroot which embed the mosquitto headers and library. It is possible to run some commands to create that sysroot (see TODO), but the command simpleswitch-generate-package could do it for you. Here the only command to run is from the sourced SDK (see Source the SDK)

  $ simpleswitch-generate-package -n helloworld-mosquitto -i mosquitto \
  -m . -s "/opt/basic-1/basic-1"

The parameter -i <package> (you can repeat it multiple times) make you able to choose the packages you want to install in the rootfs, which is implicitly created using the template packages-install. The sysroot to build the application is created by the previous command installing the -dev version of the packages indicated by the parameter -i <package> (so here installing mosquitto-dev).

Example Project 2 - Using Qt library#

In this example we will reuse the source from Getting started with Qt

The good point of this approach is that the final rootfs, will just contained the Qt libraries you need.

Using Qt with a Makefile#

First of all source the environment and go in the root directory of the project

$ cd helloworld

Then we must create the sysroot. To do that we will use the command simpleswitch-install-packages, which allow us to install some packages from the repository in a directory, with the parameters:

  • -w let us set the working directory, in which we will create the sysroot directory and some other files.

  • -n let us choose the name of the directory in which the packages will be installed.

  • -i let us choose the packages to install.

$ simpleswitch-install-packages -w workdir-sysroot -n sysroot -i qtbase-dev -i qtdeclarative-dev

Then we need to create the environment variable to use that new sysroot. It is done thanks to the command simpleswitch-create-sysroot-env with the parameters:

  • -s to indicate the path to the sysroot.

  • -o the output directory where the files will be created.

$ simpleswitch-create-sysroot-env -s workdir-sysroot/sysroot -o workdir-sysroot

An environment file have been created. It is possible to use it in order to use it instead of the one coming from the SDK

$ source workdir-sysroot/envfile

In that environment the variable SIMPLESWITCH_QT_CONF_PATH contains the path to the qt.conf file to make you able to use the new sysroot. So we can create the Makefile

$ mkdir -p build
$ qmake -qtconf ${SIMPLESWITCH_QT_CONF_PATH} -o build helloworld

Finally it is possible to start the creation of the SimpleSwitch™ packages thanks to

$ simpleswitch-generate-package -n helloworld-qt -w simpleswitch -u -i qtbase -i qtdeclarative -i qtwayland -r workdir-sysroot/sysroot -m build -s "/opt/helloworld/bin/helloworld"

Deploy to the target#

Now it is time to deploy the generated SimpleSwitch™ container to the device. For this please see Deploy a SimpleSwitch™ package.

Starting a packages repository#

By default in the SDK the packages-install template comes with a predefined repository which is the localhost. So the repository should be started on your computer. So in your Yocto working directory, after you build all the packages you need, you can create the repository data thanks to

$ scotty command bitbake package-index

And then start an HTTP server with for example

$ cd build/build/tmp/deploy/rpm
$ sudo python3 -m http.server 80

Changing packages repository URL#

When you recompile the SDK, it is possible to change the default URL of the packages repository. The URL is set by the SIMPLECORE_REPO_URL variable which is defined in the simpleswitch-packages-install-template recipe. By default it is set to http://localhost, so you can override it thanks to a bbappend of the recipe (or eventually setting that variable in your local.conf).