몇일전 테라마스터 NAS(F4-420)를 이용하여 헤놀 설치 및 셋팅 완료하였습니다
셋팅 끝내고 실사용을 시작하는데 생각 이상으로 팬 소음이 심각합니다;;
정확히는 팬이 멈췄다가 다시 동작하는 작업이 반복적으로 일어나는데
조치를 취하지 않으면 실사용이 불가능할거라 생각이 들어 여러곳을 찾아 본 결과
DSM 내부적으론 해결이 안되고 BIOS에서 팬 속도를 조정하는게 가장 좋다는 결론이 나왔습니다
그걸 위해 12핀 - vga 케이블을 구매 & 설치 하는것도 좀 아닌거같아 조금 더 찾아보니
https://xpenology.com/forum/topic/14007-terramaster-f4-220-fan-control/ 에서
작업 스케줄러를 통한 설정 변경으로 해결가능하다는 정보를 입수했습니다
하지만 한글 설명을 보고 따라하는 실력밖에 없는 입장인지라 번역기를 돌려봐도 셋팅하는 방법을 모르겠네요
fancontrol.cpp 파일을 워드패드로 열어보면
--------------------------------------------------------------------------------------------------------
// MIT License
// Copyright (c) 2019 Eudean Sun
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <assert.h>
#include <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/io.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
// These defaults can be overridden at the CLI
static bool debug = false; // Turn on/off logging
static int setpoint = 37; // Default target hard drive operating temperature
static int pwminit = 128; // Initial PWM value (50%)
static int interval = 10; // How often we poll for temperatures
static int overheat = 50; // Overheat limit where we drive the fans to 100%
static int pwmmin = 80; // Never drive the fans below this PWM value (30%)
static double kp = 1.0;
static double ki = 0.0;
static double imax = 10.0;
static double kd = 0.0;
const static int pwmmax = 255; // Max PWM value, do not change
const static uint8_t port = 0x2e;
const static uint8_t fanspeed = 200;
static uint16_t ecbar = 0x00;
const static char * synostoragedir = "/run/synostorage/disks";
void iowrite(uint8_t reg, uint8_t val) {
outb(reg, port);
outb(val, port + 1);
}
uint8_t ioread(uint8_t reg) {
outb(reg, port);
return inb(port + 1);
}
void ecwrite(uint8_t reg, uint8_t val) {
outb(reg, ecbar + 5);
outb(val, ecbar + 6);
}
uint8_t ecread(uint8_t reg) {
outb(reg, ecbar + 5);
return inb(ecbar + 6);
}
int main(int argc, char * argv[]) {
switch (argc) {
case 11:
kd = atof(argv[10]);
case 10:
imax = atof(argv[9]);
case 9:
ki = atof(argv[8]);
case 8:
kp = atof(argv[7]);
case 7:
pwmmin = atoi(argv[6]);
case 6:
overheat = atoi(argv[5]);
case 5:
interval = atoi(argv[4]);
case 4:
setpoint = atoi(argv[3]);
case 3:
pwminit = atoi(argv[2]);
case 2:
if (strncmp(argv[1], "-h", 3) == 0) {
printf("Usage:\n"
"\n"
" fancontrol\n"
" fancontrol <debug> <setpoint> <pwminit> <interval> <overheat> <pwmmin> <kp> <ki> <imax> <kd>\n"
"\n"
"Arguments must be specified in order. Arguments that are not \n"
"specified will take their default value.\n"
"\n"
"debug Enable (1) or disable (0) debug logs (default: 0)\n"
"setpoint Target maximum hard drive operating temperature in\n"
" degrees Celsius (default: 37)\n"
"pwminit Initial PWM value to write (default: 128)\n"
"interval How often we poll for temperatures in seconds (default: 10)\n"
"overheat Overheat temperature threshold in degrees Celsius above \n"
" which we drive the fans at maximum speed (default: 50)\n"
"pwmmin Never drive the fans below this PWM value (default: 80)\n"
"kp Proportional coefficient (default: 1.0)\n"
"ki Integral coefficient (default: 0.0)\n"
"imax Maximum integral value (default: 10.0)\n"
"kd Derivative coefficient (default: 0.0)\n");
return 0;
} else {
debug = atoi(argv[1]);
}
}
// Obtain access to IO ports
iopl(3);
// Initialize the IT8772E
outb(0x87, port);
outb(0x01, port);
outb(0x55, port);
outb(0x55, port);
// Sanity check that this is the 8772
assert(ioread(0x20) == 0x87);
assert(ioread(0x21) == 0x72);
// Set LDN = 4 to access environment registers
iowrite(0x07, 0x04);
// Activate environment controller (EC)
iowrite(0x30, 0x01);
// Read EC bar
ecbar = (ioread(0x60) << 8) + ioread(0x61);
// Initialize the PWM value
uint8_t pwm = pwminit;
ecwrite(0x6b, pwm);
ecwrite(0x73, pwm);
// Set software operation
ecwrite(0x16, 0x00);
ecwrite(0x17, 0x00);
double integral = 0;
double error = 0;
double prev_error = 0;
double pout = 0;
double iout = 0;
double dout = 0;
double timediff = 0;
int maxtemp = 0;
double pwmtemp = pwm;
struct timespec curtime;
struct timespec lasttime;
clock_gettime(CLOCK_MONOTONIC, &lasttime);
while (true) {
DIR * dir = opendir(synostoragedir);
if (dir == NULL) {
goto endloop;
}
struct dirent * entity;
maxtemp = 0;
char smartcmd[200];
while ((entity = readdir(dir)) != NULL) {
if ((strncmp(entity->d_name, ".", 2) == 0) ||
(strncmp(entity->d_name, "..", 3) == 0)) {
continue;
}
snprintf(smartcmd, 200,
"smartctl -A -d sat /dev/%s | "
"grep Temperature_Celsius | "
"awk '{print $10}'",
entity->d_name);
FILE * pipe = popen(smartcmd, "r");
char tempstring[20];
if (!pipe) {
continue;
}
fgets(tempstring, sizeof(tempstring), pipe);
pclose(pipe);
int temp = atoi(tempstring);
if (temp > maxtemp) {
maxtemp = temp;
}
}
closedir(dir);
// Calculate time since last poll
clock_gettime(CLOCK_MONOTONIC, &curtime);
timediff = ((1000000000 * (curtime.tv_sec - lasttime.tv_sec) +
(curtime.tv_nsec - lasttime.tv_nsec)))/1000000000;
if (timediff == 0) {
goto endloop;
}
lasttime.tv_sec = curtime.tv_sec;
lasttime.tv_nsec = curtime.tv_nsec;
// Calculate PID values
error = maxtemp - setpoint;
pout = kp * error;
iout += ki * error * timediff;
if (iout > imax) {
iout = imax;
} else if (iout < -imax) {
iout = -imax;
}
dout = kd * (error - prev_error) / timediff;
prev_error = error;
// Calculate new PWM value
pwmtemp += pout + iout + dout;
if ((maxtemp > overheat) || (pwmtemp > pwmmax)) {
pwmtemp = pwmmax;
} else if (pwmtemp < pwmmin) {
pwmtemp = pwmmin;
}
pwm = pwmtemp;
if (debug) {
printf("maxtemp = %d, error = %f, pout = %f, iout = %f, dout = %f, "
"pwmtemp = %f, pwm = %d\n",
maxtemp, error, pout, iout, dout, pwmtemp, pwm);
}
clock_gettime(CLOCK_MONOTONIC, &lasttime);
// Write new PWM
ecwrite(0x6b, pwm);
ecwrite(0x73, pwm);
endloop:
sleep(interval);
}
iopl(0);
return 0;
}
--------------------------------------------------------------------------------------------------------
라고 나오는데 리눅스 입문도 못한 제 실력으론 어떻게 셋팅해야 할지 감도 안잡히는 中 입니다
어떻게 설정해야 할까요??
입력하신 링크 주소로 가보니 실행파일도 포함되어 있으니 그거 이용하시면 됩니다. (fancontrol.cpp 위에 있는 fancontrol 파일)
# 모델명이 조금 다르네요. 실행 안 되면 소스 코드 컴파일 후, 사용하셔야 됩니다. 아마 아래 명령어로 실행 파일 만들어 질겁니다.
g++ -o fancontrol fancontrol.cpp
실행 방법
1. fancontrol 파일을 nas에 업로드
2. ssh로 접속 후, 아래 명령어로 fancontrol 실행(관리자 권한이 필요함. 링크 주소에 있는 리플 참조)
sudo ./fancontrol
# 세부적인 팬 설정을 하고 싶으면 명령어 뒤에 옵션 값을 추가하시면 됩니다.
sudo ./fancontrol 1 40 (enable debug logging, setpoint = 40)
sudo ./fancontrol 0 37 255 (use the maximum PWM value initially before entering the PID loop)
sudo ./fancontrol 0 37 150 10 50 80 0 0 0 0 (use PWM value 150 initially and disable PID by setting all coefficients to 0, this would be like hard-coding the PWM to 150 forever)
# fancontrol 프로그램 메뉴얼 표시 방법
sudo ./fancontrol -h
--------------------------------------------------------------------------------------------------------------------------------------------------------
스케줄 작업에 fancontrol 실행 파일 추가 방법(위의 fancontrol 프로그램이 정상적이라는 가정 안에 사용)
1. 스케줄 작업 설정 파일(crontab)에 재부팅 시, fancontrol를 실행하는 명령어를 입력
echo "@reboot sleep 10 && root '경로및실행파일입력' " | sudo tee -a /etc/config/crontab > /dev/null
'경로및실행파일입력' 란에 '/fancontrol파일경로/fancontrol' 입력
2. 스케줄링 서비스 재시작
/etc/init.d/crond.sh restart
참고로 오피셜 프로그램도 아니고, 관리자(root) 권한이 필요한 프로그램이라서 사용 상에 주의가 필요하실거 같습니다.
스케줄 작업전에 알려주신 소스 코드 컴파일 및 실행 시도해보았는데 잘 모르겠네요
우선 실행파일 만들어지는 명령어(g++ -o fancontrol fancontrol.cpp) 입력시
-ash: g++: command not found 라고 나오며
fancontrol 실행 명령어(sudo ./fancontrol) 입력 후 엔터 누르니
이미지처럼 뜨면서 딱히 뭔가 실행되는게 나오지 않았습니다 (아무거나 입력후 엔터 눌러도 딱히 나오는게 없음)
PuTTY를 통해 입력하였는데 따로 입력하는 방법이 있는건지, 아니면 다른 프로그램을 써야하는건지 알 수 있을까요??
fancontrol 실행했을 때 아무 것도 나오지 않는건 프로그램에서 아무것도 출력하지 않아서 그런거고
디버그 옵션에 1(enable)을 주면 아래 같이 출력될거 같네요 (소스 라인 419 참조)
maxtemp = 값, error = 값, pout = 값, iout = 값, dout = 값, "pwmtemp = 값, pwm = 값
debug : 디버깅 로그 출력 여부
setpoint : 하드 디스크의 온도를 특정 온도까지 내려갈 때까지 fan 작동
pwminit : 초기 PWM 값 입력
interval : 온도(하드디스크 온도) 확인 주기
overheat : 특정 온도(하드디스크 온도) 이상일 때 팬 풀 가동
kp, ki, imax, kd는 잘 모르겠음
# fancontrol 프로그램은 cpu 온도를 읽는게 아니고 하드 디스크 온도(smartctl 이용)를 체크해서 fan을 컨트롤하네요
(I don't read the CPU temp (or any other sensors). I think keeping the drives at 40 C or below will likely be a stronger constraint on fan speed than CPU usage, but if you expect heavy CPU usage at times when there isn't heavy storage utilization (unlikely for a NAS) then this may not be what you want )
참고로 fancontrol 프로그램은 PMW 기능을 통해서 fan을 컨트롤하는 프로그램입니다.
PMW 기능을 이용하기 위해서는 fan이 4핀짜리여야 하며, 2핀 또는 3핀은 사용하실 수 없을겁니다.
(PWM 참조 : https://quasarzone.co.kr/bbs/board.php?bo_table=qf_cool&wr_id=9611 )
Agony 님 도움으로 PuTTY 수동실행으로 동작까지 확인(수치 변경은 너무 복잡해서 올라와있는 파일 그대로 씀)하였습니다
몇일 지켜봐야겠지만 굉음과 무음 간격이 3~4초 였던 지옥같은 팬 소음은 아직까진 안들리는거 같네요 ^^
마지막으로 질문드릴게 있습니다
부팅 후 자동실행을 위한 명령어
echo "@reboot sleep 10 && root '/volume1/Public/fan/fancontrol' " | sudo tee -a /etc/config/crontab > /dev/null
를 스케줄에 입력하였는데
이미지와 같은 오류가 뜨면서 실행이 안되고 있습니다 (fancontrol 및 fancontrol.cpp 파일을 /volume1/Public/fan 폴더에 둠)
뭔가 실수한게 있을까요??