Despite great technological strides over the last few years, remotely updating embedded systems can still be hard. That’s why maximizing the code quality and robustness of a device is even more important than in other branches of software creation.
Careful considerations should be put into code creation to make it possible to add multiple quality assurance tools. Read on to learn how embedded specialists are effectively developing and maintaining high-quality code that can scale to dynamically changing product goals and market forces.
Tools that maintain code quality in embedded systems
Nowadays, the need to develop hardware solutions for the automated flashing and testing of products is of high importance, which is why the ability to automatically generate executables, or binaries, which are ready and able to flash all the time directly into devices is crucial. (More information about hardware-in-the-loop testing). Continuous delivery (CD) tools that meet industry standards and which have proven adept at achieving this goal include GitHub, GitLab, Jenkins and Azure DevOps.
Embedded teams also need to repeatedly build, test and package applications in a way that streamlines code modifications and which speeds up the delivery of software to different environments.
This involves running tests after every code change and making sure that new code cannot be merged into production-ready code without passing multiple verification gates. Additionally, it means verifying that code meets static analysis standards, which can be done by employing static analysis on a codebase and using appropriate tools to catch bugs and errors that are not easily detectable during coding. Continuous integration (CI) tools GitHub Actions, GitLab Pipelines, Jenkins and Azure DevOps support a correct setup, ensure every code merge is flashed directly onto a device and check basic functionality.
Embedded engineering teams and containerized work environments
One of the most effective ways to achieve consistency is through the implementation of containerized work environments, which make it easier for new developers to start working with a product.
The biggest benefit of containerized environments is that everybody works on the same machine, so there is no issue of something working on one machine, but not all (eliminating the ‘it works on mine’ defense). Moreover, introducing new employees is simple and straightforward and changes to a machine are propagated to all employees at the same time. In this way, disparate teams can effortlessly stay up to date with modifications so that time and resources are not wasted on older versions.
But what should be incorporated into a container? While certain projects may have specific requirements, generally teams should create a container that has everything they need to develop a product and troubleshoot issues. Therefore, essentials include a compilation tool chain, static analysis tools and debugging tools, as well as all necessary libraires that can help with building.
Despite the demonstrable advantages of containers, there are some obstacles that come with using them. The main problem is that sometimes, if a product relies on some sort of licensing, then it could be quite difficult, or too expensive, to provide licenses for every container that you have. This is where Software Mind can help – we can strip away most licensing requirements by replacing paid solutions with open-source alternatives. It can also take a lot of time to create a machine that handles everything. Though this investment may pay off in the long run, for companies looking to get to market faster than their competition, this time may not be possible. Teaming up with an external embedded engineering team that can manage development speeds up delivery cycles and enables a company to focus on its core business.
An alternative to container work is to create a virtual machine that has everything required for building. Though a heavier, separate piece of equipment, a virtual machine delivers the same benefits as a container. While a virtual machine is easier to set up since there is no scripting language – just a virtualized computer – a disadvantage is that it is resource-heavy, so a company needs to have significant computing power to run a virtual system.
Automated tests provide vital support for embedded software development
Automated tests are a proven way to improve and verify whether a product is passing internal quality standards (based on a client’s needs). The practical value of automated tests is flexibility – teams can smoothly add tests to reflect changes in a product or if quality does not need to be very robust, some elements can be skipped.
There are a few key types of testing embedded teams should use:
Smoke testing: ensures that the most critical aspects of a device work as intended – minimal functionality that verifies the bare minimum works – and do not catch on fire.
Integration testing: verifies that a codebase as a whole works after implementing new changes (for the entire device – no division as regards certain aspects or parts). Integration tests rely on the same environment as smoke testing but are more thorough.
Unit testing: automated checks that operate on the smallest units of codebase, these are useful when implementing Test Driven Development (TDD). Unit testing splits code into the smallest possible units (minimal portions of the code base that require a proper framework to be carried out effectively). For example, Gtest (Google’s) Catch2 or Unity for C projects (unlike the others, which are in C++, Unity is in C). Since embedded projects rely heavily on hardware, you need to have a mocking framework as well, to isolate all the accesses directly to the hardware. Some good examples include Gmock, FakeIT and Cmock – a mocking framework from Unity.
It’s worth noting that, based on the requirements of a given device, each of these testing methods can be expanded further by adding fuzz testing, creating hardware verification tools or by using third party calibrators.
Maintaining code reusability
Crafting code in a manner that enables it to be repurposed in various contexts without having to make significant changes saves time, eliminates repetitive tasks, decreases the size of codebases and improves maintainability. For embedded engineers in particular, this also requires thinking about firmware as a set of logical modules that later on might be used in other products. It’s important to remember that each module should propagate fixes to all devices that use it. Furthermore, it’s essential to make use of many great build systems. Using various options as opposed to a vendor-specific IDE can be cheaper and enables you to organize a building process for your firmware.
Depending on the use case, splitting the firmware into logical units can be very difficult, especially when accessing the hardware directly. That’s why employing a good build-system such as CMake, Meson, Bazel, will take care of compilation intricacies with a scripting language. Additionally, applying unit tests to these modules, is a sure way to verify that your module can be adapted to multiple architectures and products.
Software Mind embedded specialists are ready to help
With years of experience supporting companies across industries, the embedded systems experts at Software Mind know how to efficiently understand a product’s business context, onboard into a company’s organization, formulate a strategy and launch cooperation. Whether transferring the pass/fail evaluation from the level of a test fixture firmware to an application that runs on a single board computer, updating and maintaining applications, reducing the time needed for programming and testing, shortening time to market of new device versions or minimizing the risk of delays for the entire device development process, Software Mind’s embedded team is a trusted partner.
Companies around the world turn to Software Mind for embedded development, AI, cloud and data engineering support – agile methodologies and cross-functional teams mean Software Mind can easily scale up or down to meet your needs. Get in touch with one of our experts and learn more about our embedded services offer by filling out this form.
About the authorRafał Jasiński
Senior Embedded Software Engineer
A senior embedded software engineer with over five years' experience, Rafał has supported global organizations across sectors. An avid follower of embedded-related trends, he's keen to integrate emerging solutions into software development life cycles. A passion for innovation is matched by a commitment to quality and attention to detail, which has made Rafał a dependable technology partner for his fellow embedded specialists and clients.