├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── fan-control.layout ├── fan-daemon.cbp ├── fan-daemon.cpp ├── fan-daemon.depend ├── fan-daemon.h ├── fan-daemon.layout ├── fan-daemon.service ├── install.sh └── uninstall.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/Release/* 2 | /obj/Debug/* 3 | /obj/Release/* 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 William Hooper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------# 2 | # This makefile was generated by 'cbp2make' tool rev.147 # 3 | #------------------------------------------------------------------------------# 4 | 5 | 6 | WORKDIR = `pwd` 7 | 8 | CC = gcc 9 | CXX = g++ 10 | AR = ar 11 | LD = g++ 12 | WINDRES = windres 13 | 14 | INC = 15 | CFLAGS = -Wall -fexceptions 16 | RESINC = 17 | LIBDIR = 18 | LIB = 19 | LDFLAGS = 20 | 21 | INC_RELEASE = $(INC) 22 | CFLAGS_RELEASE = $(CFLAGS) -Os -std=c++11 23 | RESINC_RELEASE = $(RESINC) 24 | RCFLAGS_RELEASE = $(RCFLAGS) 25 | LIBDIR_RELEASE = $(LIBDIR) 26 | LIB_RELEASE = $(LIB) 27 | LDFLAGS_RELEASE = $(LDFLAGS) -s 28 | OBJDIR_RELEASE = obj/Release 29 | DEP_RELEASE = 30 | OUT_RELEASE = bin/Release/fan-daemon 31 | 32 | OBJ_RELEASE = $(OBJDIR_RELEASE)/fan-daemon.o 33 | 34 | all: Release 35 | 36 | clean: clean_release 37 | 38 | before_release: 39 | test -d bin/Release || mkdir -p bin/Release 40 | test -d $(OBJDIR_RELEASE) || mkdir -p $(OBJDIR_RELEASE) 41 | 42 | after_release: 43 | 44 | Release: before_release out_release after_release 45 | 46 | out_release: before_release $(OBJ_RELEASE) $(DEP_RELEASE) 47 | $(LD) $(LIBDIR_RELEASE) -o $(OUT_RELEASE) $(OBJ_RELEASE) $(LDFLAGS_RELEASE) $(LIB_RELEASE) 48 | 49 | $(OBJDIR_RELEASE)/fan-daemon.o: fan-daemon.cpp 50 | $(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c fan-daemon.cpp -o $(OBJDIR_RELEASE)/fan-daemon.o 51 | 52 | clean_release: 53 | rm -f $(OBJ_RELEASE) $(OUT_RELEASE) 54 | rm -rf bin/Release 55 | rm -rf $(OBJDIR_RELEASE) 56 | 57 | .PHONY: before_release after_release clean_release 58 | 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jetson Nano fan control daemon 2 | Fan control daemon for the Nvidia Jetson Nano. Written in C++ to reduce the memory usage. 3 | 4 | This project is based off a similar project written in Python and found here 5 | 6 | https://github.com/Pyrestone/jetson-fan-ctl 7 | 8 | ## Requirements: 9 | 10 | ### Hardware 11 | You will need a 5V PWM fan installed for this daemon to work. The 12 | **Noctua nf-a4x20 5V PWM** or **Noctua nf-a4x10 5V PWM** fans work well. 13 | The **Noctua nf-a4x10 5V PWM** is a low profile fan and therefore not 14 | as bulky as the **Noctua nf-a4x20 5V PWM**. 15 | 16 | Additionally, it is recommend you use at a minimum a 4A power supply that supplies 17 | power via the power barrel jack. Remember to also include the power jumper to enable 18 | the external power supply. 19 | 20 | ### Software 21 | Use the standard Linux image on your Jetson Nano. 22 | 23 | ## How to install: 24 | 25 | The code is contained in a Code::Blocks project. You do not need to install 26 | Code::Blocks to build the project. To build without Code::BLocks cd into the 27 | project directory and execute 28 | 29 | make all 30 | 31 | This will build the project and place the executable in the project's 32 | ./bin/Release directory. 33 | 34 | If you have Code:Blocks you can load the project into Code::Blocks,select the 35 | Release build and build the project. 36 | 37 | Once you have built the project from the project directory exceute the install script. 38 | 39 | ./install.sh 40 | 41 | The script will install the fan-daemon as a system service which excecutes at run-time. 42 | It's a set-it-and-forget-it type thing, unless you want to modify the fan speeds. 43 | 44 | ## How to customize: 45 | In the project directory open fan_control.h in Code::Blocks or with your favorite 46 | editor and modify the following defines: 47 | 48 | #define FAN_OFF_TEMP 20 49 | 50 | #define FAN_MAX_TEMP 60 51 | 52 | #define UPDATE_INTERVAL 2 53 | 54 | FAN_OFF_TEMP is the temperature (°C) below which the fan is turned off. 55 | FAN_MAX_TEMP is the temperature (°C) above which the fan is at 100% speed. 56 | 57 | The daemon will interpolate linearly between these two points to determine the 58 | appropriate fan speed. 59 | 60 | UPDATE_INTERVAL tells the daemon how often to update the fan speed (in seconds). 61 | 62 | You can use only integers in each of these fields. The temperature precision of the thermal 63 | sensors is 0.5 (°C), so don't expect the daemon to be too precise. 64 | 65 | After making changes in the daemon, compile the code and run the uninstall script to remove 66 | the old daemon. Then run the install script to install your new daemon. 67 | 68 | You can run 69 | 70 | sudo service fan-daemon restart 71 | 72 | if you wish to ensure that the changes are applied immediately. 73 | 74 | If you stop the fan control daemon with 75 | 76 | sudo service fan-daemon stop 77 | 78 | the fan is set to the fan PWM cap value. 79 | 80 | If you suspect something went wrong, please check: 81 | 82 | sudo service fan-daemon status 83 | -------------------------------------------------------------------------------- /fan-control.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /fan-daemon.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 61 | 62 | -------------------------------------------------------------------------------- /fan-daemon.cpp: -------------------------------------------------------------------------------- 1 | /* Jetson Nano Fan Control Daemon 2 | 3 | * MIT License 4 | * 5 | * Copyright (c) 2019 William Hooper 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | #include "fan-daemon.h" 26 | 27 | using namespace std; 28 | 29 | static unsigned pwmCap; 30 | 31 | /** 32 | * Exit handler. Shutoff the fan before leaving 33 | */ 34 | void exit_handler(int s) 35 | { 36 | writeIntSysFs(TARGET_PWM, pwmCap); 37 | exit(1); 38 | } 39 | 40 | /** 41 | * Initialize the exit handler 42 | */ 43 | void init_exit_handler() 44 | { 45 | struct sigaction sigIntHandler; 46 | 47 | sigIntHandler.sa_handler = exit_handler; 48 | sigemptyset(&sigIntHandler.sa_mask); 49 | sigIntHandler.sa_flags = 0; 50 | 51 | sigaction(SIGINT, &sigIntHandler, NULL); 52 | sigaction(SIGTERM, &sigIntHandler, NULL); 53 | } 54 | 55 | 56 | /** 57 | * Main 58 | */ 59 | int main(int argc, char *argv[]) 60 | { 61 | unsigned temp; 62 | unsigned pwmValue; 63 | unsigned lastPwmValue = 0; 64 | 65 | pwmCap = getPwmCap(); 66 | 67 | init_exit_handler(); 68 | 69 | system(JETSON_CLOCKS); 70 | 71 | while (true) 72 | { 73 | temp = readAverageTemp(); 74 | pwmValue = adjustFanSpeed( temp, pwmCap); 75 | if (pwmValue != lastPwmValue) 76 | { 77 | writeIntSysFs(TARGET_PWM, pwmValue); 78 | lastPwmValue = pwmValue; 79 | } 80 | this_thread::sleep_for(chrono::milliseconds(UPDATE_INTERVAL * MICRO_SECONDS)); 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | /** 87 | * Read the average temperature. The function reads all thermal zones and returns the average. 88 | * 89 | */ 90 | unsigned readAverageTemp() 91 | { 92 | unsigned averageTemp; 93 | glob_t globResult; 94 | 95 | averageTemp = 0; 96 | glob(THERMAL_ZONE_GLOB, GLOB_TILDE, NULL, &globResult); 97 | for(unsigned i = 0; i < globResult.gl_pathc; ++i) 98 | { 99 | averageTemp += readIntSysFs(globResult.gl_pathv[i]); 100 | } 101 | 102 | return (averageTemp / globResult.gl_pathc) / 1000; 103 | } 104 | 105 | /** 106 | * Get the PWM cap. This is maximum value that the fan PWM channel can support 107 | */ 108 | unsigned getPwmCap() 109 | { 110 | return readIntSysFs(PWM_CAP); 111 | } 112 | 113 | /** 114 | * Return the adjusted PWM fan speed. Calculated from the provided temperature and the 115 | * fan PWM cap value. 116 | */ 117 | unsigned adjustFanSpeed(unsigned temp, unsigned pwmCap) 118 | { 119 | unsigned speed = pwmCap * max(0, (int)(temp - FAN_OFF_TEMP)) / (FAN_MAX_TEMP - FAN_OFF_TEMP); 120 | 121 | return min((unsigned)max((unsigned)0, speed), pwmCap); 122 | } 123 | 124 | /** 125 | * Read an unsigned integer value from a sysfs path 126 | */ 127 | unsigned readIntSysFs(string path) 128 | { 129 | unsigned value; 130 | 131 | ifstream infs(path); 132 | infs >> value; 133 | infs.close(); 134 | 135 | return value; 136 | } 137 | 138 | /** 139 | * Write an unsigned integer value to a sysfs path 140 | */ 141 | void writeIntSysFs(string path, unsigned value) 142 | { 143 | ofstream outfs(path); 144 | outfs << value; 145 | outfs.close(); 146 | } 147 | 148 | -------------------------------------------------------------------------------- /fan-daemon.depend: -------------------------------------------------------------------------------- 1 | # depslib dependency file v1.0 2 | 1561247100 source:/home/william/Projects/fan_control/main.cpp 3 | 4 | 5 | 6 | 7 | 8 | 1561255624 source:/home/william/Projects/fan_control/fan_control.cpp 9 | "fan_control.h" 10 | 11 | 1561257220 /home/william/Projects/fan_control/fan_control.h 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1561258894 source:/home/william/Projects/fan_daemon/fan_daemon.cpp 20 | "fan_daemon.h" 21 | 22 | 1561257220 /home/william/Projects/fan_daemon/fan_daemon.h 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 1561263666 source:/home/william/Projects/fan_daemon/fan-daemon.cpp 31 | "fan-daemon.h" 32 | 33 | 1561263559 /home/william/Projects/fan_daemon/fan-daemon.h 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 1561266384 source:/home/william/Projects/fan-daemon/fan-daemon.cpp 46 | "fan-daemon.h" 47 | 48 | 1561266650 /home/william/Projects/fan-daemon/fan-daemon.h 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 1561266384 source:/home/william/GitRepos/fan-daemon/fan-daemon.cpp 58 | "fan-daemon.h" 59 | 60 | 1561266650 /home/william/GitRepos/fan-daemon/fan-daemon.h 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /fan-daemon.h: -------------------------------------------------------------------------------- 1 | #ifndef FAN_CONTROL_H_INCLUDED 2 | #define FAN_CONTROL_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // the following three defines can be modified to change the daemon behaviour 13 | #define FAN_OFF_TEMP 45 14 | #define FAN_MAX_TEMP 60 15 | #define UPDATE_INTERVAL 2 16 | // end of modifiable code 17 | 18 | 19 | #define MICRO_SECONDS 1000 20 | #define JETSON_CLOCKS "/usr/bin/jetson_clocks" 21 | #define THERMAL_ZONE_GLOB "/sys/devices/virtual/thermal/thermal_zone*/temp" 22 | #define PWM_CAP "/sys/devices/pwm-fan/pwm_cap" 23 | #define TARGET_PWM "/sys/devices/pwm-fan/target_pwm" 24 | 25 | unsigned readAverageTemp(); 26 | unsigned adjustFanSpeed(unsigned, unsigned); 27 | unsigned getPwmCap(); 28 | unsigned readIntSysFs(std::string); 29 | void writeIntSysFs(std::string path, unsigned value); 30 | 31 | #endif // FAN_CONTROL_H_INCLUDED 32 | -------------------------------------------------------------------------------- /fan-daemon.layout: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /fan-daemon.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Fan control daemon 3 | 4 | [Service] 5 | Type=simple 6 | ExecStart=/usr/local/bin/fan-daemon/fan-daemon 7 | User=root 8 | StandardOutput=journal+console 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Require sudo 4 | if [ $EUID != 0 ]; then 5 | sudo "$0" "$@" 6 | exit $? 7 | fi 8 | 9 | echo "setting to /usr/local/bin/fan-daemon/..." 10 | rm -rf /usr/bin/fan-daemon/ 2>/dev/null 11 | mkdir /usr/local/bin/fan-daemon 12 | cp ./bin/Release/fan-daemon /usr/local/bin/fan-daemon/ 13 | echo "done" 14 | 15 | echo "adding service to /lib/systemd/system/..." 16 | cp fan-daemon.service /lib/systemd/system/ 17 | chmod 644 /lib/systemd/system/fan-daemon.service 18 | echo "done" 19 | 20 | echo "starting and enabling service..." 21 | systemctl daemon-reload 22 | systemctl start fan-daemon 23 | systemctl enable fan-daemon 24 | echo "done" 25 | 26 | echo "fan-daemon installed sucessfully!" 27 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Require sudo 4 | if [ $EUID != 0 ]; then 5 | sudo "$0" "$@" 6 | exit $? 7 | fi 8 | 9 | echo "removing service..." 10 | systemctl stop fan-daemon 11 | systemctl disable fan-daemon 12 | echo "done" 13 | 14 | 15 | echo "removing /usr/local/bin/fan-daemon/..." 16 | rm -r /usr/local/bin/fan-daemon 17 | rm -r /usr/bin/fan-daemon 2>/dev/null 18 | echo "done" 19 | 20 | echo "removing service from /lib/systemd/system/..." 21 | rm /lib/systemd/system/fan-daemon.service 22 | echo "done" 23 | 24 | echo "reloading services" 25 | systemctl daemon-reload 26 | echo "done" 27 | 28 | echo "fan-daemon uninstalled sucessfully!" 29 | echo "" 30 | echo "If you are find a problem, please create an issue at the repo." 31 | --------------------------------------------------------------------------------