Dissipating the heat generated by the processor and its peripherals is a critical aspect to consider when designing embedded systems. Natural convection cooling is ideal but may not always be sufficient. In such cases, a forced cooling system will complement it and ensure the proper functioning of the equipment. The project focuses on implementing an embedded system under Linux with a specific emphasis on kernel programming, device drivers, and multitasking applications. The goal is to simulate fan speed control based on CPU temperature using an embedded Linux platform. Here is what the system looks like:
For this project, the NanoPi NEO Plus2 was used with it’s NanoHat OLED display. This board is based on an Alwinner ARM SOC with following features:
And here is the board’s pinout:
Since the target platform lacks a physical fan, and the PWM output pin is already used by the system’s consol, the target uses the blinking of a status LED to represent the fan’s speed, controlled via a timer for frequency modulation.
The display comes from FriendlyElec and has several features:
This project is separate in 3 different levels. Each of those levels has specific tasks:
To facilitate the module’s access to various kernel features, here is some informations. First, the Linux kernel’s linux/thermal.h module provides services for reading the temperature of various zones within the microprocessor. With the following two methods, it is possible to obtain the temperature of μP zones in thousandths of a degree:
struct thermal_zone_device* thermal_zone_get_zone_by_name(const char* name);
int thermal_zone_get_temp(struct thermal_zone_device*, unsigned long* temp);
In it’s current configuration, the CPU only implements one zone called cpu_thermal.
Then we need to access the GPIOs. The linux/gpio.h (deprecated) interface in the Linux kernel provides the necessary services for controlling an input/output pin:
int gpio_request(unsigned gpio, const char* label);
void gpio_free(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
void gpio_set_value(unsigned gpio, int value);
Finally, as the PWM can’t be used, a timer can be utilized to control the LED blinking frequency. To do so, linux/timer.h provides services for timer management:
#define timer_setup(timer, callback, flags)
int del_timer(struct timer_list *timer);
int mod_timer(struct timer_list *timer, unsigned long expires);
The kernel module developed for this project manages the LED blinking speed in two modes: manual and automatic. Three files were created in sysfs for this purpose, accessible for reading and writing:
The GPIO used by the status LED is number 10, managed using the linux/gpio.h library. As recommended, the linux/timer.h module was used to configure timers:
Specific functions allow those functionnality:
The module can be inserted with either insmod or modprobe (module’s makefile already copy the module in the right directory). For more informations, here is the module’s code: https://github.com/mekiisupertramp/fanless_fancontrol/blob/main/module/skeleton.c
A daemon is a background process that runs independently of any user interface and performs tasks or waits to provide specific services to the system or user applications. Daemons are typically started during the boot process (init process ppid=1) and remain running to handle requests (e.g., system services like networking, logging, or scheduled jobs) or to perform periodic background tasks. They are identified by the “d” suffix (e.g., httpd for web server or sshd for SSH daemon) and are often managed by system initialization like systemd.
Contrary to kernel modules, daemons don’t need specific interfaces or modules to be created. Any program could be a daemon if it follows these steps:
Starting a daemon on Linux is an essential part of system administration, as daemons are responsible for background tasks and essential services. Over time, methods for managing and starting daemons have evolved to improve control, reliability, and integration with system boot processes.
Originally, daemons were launched through simple startup scripts in directories like /etc/init.d/, which provided basic start, stop, and restart controls. However, these scripts required manual setup and were limited in monitoring capabilities. With the introduction of systemd on modern Linux systems, managing daemons became more streamlined and reliable. Systemd offers advanced features like automatic restarts, dependency management, and precise control over the daemon’s runtime environment. Here, just launching the daemon as a regular program is fine as the goal is mostly to use IPCs.
The daemon can communicate with the module through sysfs and with the client application via FIFOs. It also manages the various GPIOs exporting them . An epoll syscall has been implemented to monitor the three buttons and the input FIFO. To ensure the OLED display functions properly, it is necessary to enable the I2C bus in the device tree. The daemon can be terminated using SIGKILL, SIGQUIT, SIGTERM, or through the application. To summerize, here is the components used by the daemon:
When launched, the daemon displays the PIDs for the parent and child processes. Below is an example of execution:
This example confirms that the GPIOs and FIFOs are properly instantiated and then correctly destroyed upon termination. For more information, please see the source code here: https://github.com/mekiisupertramp/fanless_fancontrol/blob/main/daemon/my_deamon.c
The application is quite simple. When started, it displays the command menu and wait for user to enter a command. Commands are then sent to the daemon via the FIFOs. Here is the launch screen:
And here is some tests:
The timestamped logs come from the kernel module. kill command doesn’t provide any output, but it’s possible to see if daemon has stoped with top. The buttons, and the display have also been tested; however, unfortunately, no additional pictures or screenshots are available. For more details, please see sources here: https://github.com/mekiisupertramp/fanless_fancontrol/blob/main/app/app.c
This project highlights various aspects of embedded Linux platform development. It is particularly fascinating to see how the different components interact with one another and the numerous approaches available for doing so. As it’s often the case in Linux development, there is no single solution, but rather a multitude of possibilities. Given that Linux is open-source, a wealth of resources is accessible online, and the Linux community is an invaluable source of knowledge. It is important to note that this project does not cover certain aspects such as kernel compilation, root filesystem creation, or configuring U-Boot to boot Linux. For more information, please see the source code here: https://github.com/mekiisupertramp/fanless_fancontrol/tree/main