Skip to content

Best Practice for RIOT Programming

Emmanuel Baccelli edited this page Nov 15, 2017 · 12 revisions

Hints for quicker & better RIOT development

Coding "Dos" and "Don'ts":

Dos

  • Use static memory. See also Static vs. Dynamic Memory.
  • Select the priorities carefully.
  • Minimize stack usage with DEVELHELP and CREATE_STACKTEST.
  • Use threads to increase flexibility, modularity, and robustness by leveraging IPC.
  • Use unsigned or signed integer (unsigned, int, size_t or ssize_t) for loop variables wherever possible, but keep in mind that on some platforms an int has a width of only 16-bit. In general, you should avoid types like uint8_t for loop iterators as they will probably make it more expensive on some platforms.

Don'ts

  • Don't use too many threads. Try not to use more than one thread per module. Don't create threads for one-time tasks.
  • Don't use the POSIX wrapper if implementing something from scratch.
  • Don't allocate big chunks of memory (for instance the IPC message queue) on the stack, but use rather static memory for that.
  • Don't use enums for flags, because flags have a width in memory that is in most cases smaller than sizeof(enum) (most bitfields are 16 bits max, on most of our newer platforms, sizeof(enum) is however 32 bits). This results in every assignment needed to be cast to either uint8_t or uint16_t. With macros you don't need to cast since they are typeless. Making the enum packed makes its width unpredictable in terms of alignment issues, when used in struct.

Methodology: emulator first, target IoT hardware last!

The below methodology is recommended, using well-known de facto standard tools from there FLOSS community that are compatible with RIOT. Using the below workflow improves time-to-running-code compared to typical IoT software workflows (which can be as retro as "LED-driven" debugging).

  1. For newbies, preliminaries are typically faster with the provisioned virtual environment setup, e.g. with Vagrant.
  2. To check your code, first use available static analysis as much as possible initially, which means (i) enable all compiler warnings and fix all problems found, then (ii) use a supported linter such as cppcheck to find bad coding patterns (i.e. code smells) and identify misuse of standard APIs.
  3. Next, use available dynamic analysis tools to find further defects while running the code on RIOT native, which means (i) running unit tests and integration tests on RIOT native emulator, and (ii) using Valgrind memcheck, as well as the GCC stack smashing detection, to detect and avoid undefined behavior due to invalid memory access.
  4. In case of networked applications or protocols, test several instances of native communicating via a virtual network mimicking the targeted scenario, which means (i) either using the default virtual full-mesh or other topologies configured via DESvirt, and (ii) using Wireshark to capture and analyze virtual network traffic, e.g. to ensure protocol packets are syntactically correct, and to observe network communication patterns.
  5. In case of incorrect behavior at this stage, analyze the system state for semantic errors on native using the standard debugger gdb, which allows virtually unlimited conditional breakpoints, record and replay, catchpoints, tracepoints and watchpoints.
  6. In case of suspected performance bottleneck, use performance profilers gprof, or else cachegrind, to identify precisely the bottlenecks.
  7. At this stage the implementation has proven bug-free on the native emulator. One can thus finally move on to hardware-in-the-loop, which means (i) flashing the binary on the targeted IoT hardware, typically using standard flasher OpenOCD or edbg, and (ii) using the RIOT shell running on the target IoT device(s) for easier debugging on the target hardware.
  8. In case the hardware is not available on-site, one can consider remotely flashing and testing the binary on supported open-access testbeds, e.g. IoT-LAB hardware is fully supported by RIOT.
  9. In case of failure, after analyzing the failure and attempting to fix the defect, go back to step 1 to make sure the fix did not itself introduce a new defect.
Clone this wiki locally