前置き
久々の投稿がこれになったけど、仕方ないんや。
MinisforumのUM690に後継機(というか機能改善)のUM690Sが出たのが悪いんや
Minisforumは、割と性能の良いミニPCをお手頃価格で出している会社です。
で、前々からセカンドPCほしいなーと思ってて、数ヶ月前にUM690という製品を購入したのですが、この製品 M.2(SSD)が裏面にあるため、CPUファンの恩恵を受けれないんですよ。
ちゃんとヒートシンクはあるんで普段遣いでは困らないんですが、ちょっと負荷の高いゲームなどを実行すると熱暴走でPCが落ちたりします(CPU側冷却は問題なくて、M.2側の冷却が能力不足な感じです)
なので、物理的にDCファンつければ直るだろうけどちょっとめんどくさいなーと思って今まで放置していました。
でも、公式から空冷対応版がでちゃって悔しい(悲しい)ので自力でUM690に空冷機能を追加して、ついでにoLEDで温度表示もつけちゃおうというのをやりました。
作り方
Fusion360で下につけるケースをデザインする。
stlファイルはこちら。
Amazonで良さげなDCファンを探しつつ、裏面取付のSSDもそのまま使えるようにしつつ、ちょこちょとっとデザインを修正しつつ
※ DCファンは、Amazonのブロアファン2個入りを使用。
あと、温度も取得したかったので、サーミスタもAmazonでポチっておきました。
DCファンの回転数制御と温度管理はいえに転がってた Seeed XIAO nRF52840を使用(arduinoで小さくて収まればなんでもいいと思う)
書き忘れてた。
OLED液晶はamazonで こちらのを使ってます。
サーミスタもamazonで こちらを使ってます(100Kの方)
で、3Dプリンタで召喚して、その間に制御回路づくり(というほどのものでもないけど)
KiCADで回路図をカキカキ。
ショットキーとか各種抵抗やコンデンサなどはざっくり適当です。(ネットから情報拾って書き直しただけなので保証できません)
BSS138とかは秋月で25個パックとかで売ってるので入手しやすいと思うけど、リミット300mAなので、もうちょい余裕あったほうがいいのかもです(DCファンが0.1Aを2台なのでいけるっちゃいける?)
で、あとはユニバーサル基板を適当に切ってハンダして回路作ったら3Dプリンタで作ったガワに組み込み。
あとは、蓋をして制御プログラムの作成。
Arduino IDEでコードカキカキ(設定などは割愛。ぐぐってくだせぇ)
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
const int fanPin = A0;
const int tempPin = A1;
const float Vref = 3.3; // 基準電圧 (3.3V と仮定)
const float Rfixed = 100000; // 分圧抵抗の値 (100KΩ)
const float B = 3950; // B 定数
const float Tnominal = 298.15; // ノミナル温度 25℃ をケルビンで表したもの
const float Rnominal = 100000; // ノミナル抵抗値 (100KΩ)
const int fanSpeedMin = 190;
const int fanSpeedMax = 255;
const float tempMin = 35.0;
const float tempMax = 55.0;
const float tempThresholdOn = tempMin + 8; // ファンがオンになる温度
const float tempThresholdOff = tempMin; // ファンがオフになる温度
bool fanOn = false; // ファンの状態を追跡するフラグ
int fanSpeed = fanSpeedMin;
void setup() {
Serial.begin(9600);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Clear the buffer
display.clearDisplay();
display.display();
pinMode(fanPin, OUTPUT);
pinMode(tempPin, INPUT);
analogWrite(fanPin, fanSpeedMax);
delay(2000); // Pause for 2 seconds
}
void loop() {
float temperature = readTemperature(); // 温度を読み取る
fanSpeed = calculateFanSpeed(temperature);
analogWrite(fanPin, fanSpeed);
drawOLED(temperature, fanSpeed);
delay(1000);
}
float readTemperature() {
int analogValue = analogRead(tempPin); // アナログピンから値を読み取る
float Vout = (analogValue / 1023.0) * Vref; // 電圧へ変換
float Rthermistor = Rfixed * (Vref / Vout - 1); // サーミスタの抵抗値を計算
float lnR = log(Rthermistor / Rnominal); // サーミスタの抵抗値の自然対数を計算
float T = (1 / (1/Tnominal + lnR/B)); // 温度を計算 (ケルビン単位)
float temperature = T - 273.15; // ケルビンを摂氏に変換
return temperature;
}
void drawOLED(float temperature, int fanSpeed) {
display.clearDisplay();
display.setTextSize(2); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.cp437(true); // Use full 256 char 'Code Page 437' font
display.print(F("Temp:"));
display.print(temperature, 1);
display.println(F("C"));
display.print(F("Fan :"));
if (fanSpeed == 0) {
display.println(F("OFF"));
}else{
int fanPersent = map(fanSpeed, 190, 255, 10, 100);
display.print(fanPersent);
display.println(F("%"));
}
display.display();
}
// 温度に基づいてファンスピードを指数関数的に計算する
int calculateFanSpeed(int temperature) {
if (temperature >= tempThresholdOn) {
fanOn = true;
} else if (temperature <= tempThresholdOff) {
fanOn = false;
}
if (fanOn) {
// 温度が55度以上の場合は255を返す
if (temperature > tempMax) {
return fanSpeedMax;
}
// 温度が40度から55度の間の場合、指数関数的な増加を計算する
float normalizedTemp = float(temperature - tempMin) / (tempMax - tempMin);
return fanSpeedMin + (fanSpeedMax - fanSpeedMin) * pow(normalizedTemp, 2); // 2乗で指数的に増加
}else{
return 0;
}
}
で、XIAOに書き込みして動作チェックして完成!
完成したのがこちら
うん、満足!