Здравствуйте, дорогие друзья!
Купил я тут себе компутер. Выбирал, как водится, долго и мучительно — хотел мини-ПК, потому что давно проникся этим форм-фактором: компактный, экономичный, да и по цене приятнее, чем здоровенные системники.
В момент выбора, конечно, руководствовался главным критерием — ценой.
Мой взгляд пал на чудо инженерной мысли шэньжэньского производства — Gmtec mini pc K-6. За свои деньги он предлагает вполне бодрые характеристики, особенно если руки не из коробки, а из плеч.

Брал я его без ОЗУ и ПЗУ, с мыслью: «Сам поставлю — сам виноват».
В итоге воткнул 64 ГБ оперативки и 1 ТБ SSD, и понеслось: начал пробовать локальные модели ИИ. Забегая вперёд скажу: оно работает!
Но если бы сейчас выбирал снова — взял бы обычный стационарник. Потому что заставить миник крутить нейронки — это пытаться родить что-то крупное такими средствами.

Кулеры, паяльник и немного магии
После пары дней нагрузочного ада железо взвыло и запросило дополнительного охлаждения. Не буду углубляться в тему — половина Хабра уже резала и пилила свои мини-ПК, — просто скажу, что вдохновлялся вот этим постом:
? 4PDA — Gmtec K-6 (тема модификаций)


Коротко суть:
— Пилил, сверлил, подгонял.
— В итоге поставил башенный кулер на процессор.
— Второй кулер (охлаждение памяти) заменил на 92 мм зверюгу.

Плюс 5 В → вход повышайки → выход 12 В на кулер.
У миника — 5 В, у кулеров — 12 В и 4 пина.
Решение: DC-DC повышайка в «разрыв» питания.
Подключение:
Плюс 5 В → вход повышайки → выход 12 В на кулер.
GND общий.
PWM (управление скоростью, подтяжка к плюсу).
Tach (датчик Холла, считает обороты).
Всё заработало! Кулеры крутятся, миник дышит.
Новый корпус и графическая станция мечты
Следующий квест — корпус. Тут выручили добрые люди с 4PDA: автор выложил STL-модели, а я немного модифицировал и распечатал новый корпус. Теперь это не мини-ПК, а мини-монстр.
Кроме того, прикупил графическую станцию и переходник M.2 → OCuLink, ведь у K-6 два гнезда под память. В планах — докинуть видеокарту и пустить её в бой за ИИ-величие.
Корпус в итоге получился почти размером с “Алису” — большую колонку от Яндекса.
И тут в голову прилетела идея:
А что, если добавить немного графической магии?
Анимация огня на матрице WS2812B
В освободившемся месте я поставил 8×8-матрицу WS2812B, а управляет всем ESP8266.
Он считывает температуру с DS18B20, установленного на радиаторе. Чем выше температура — тем ярче горит анимация пламени.
Комп думает → греется → загорается → выглядит круто.
Логично? Логично!
Самое забавное — код анимации я написал при помощи тех самых локальных моделей ИИ, что крутятся на этом минике. Вдохновлялся легендарной “Лампой Гайвера”.

Также имеется веб интерфейс, при первом включении контроллер разворачивает точку доступа, далее можно подклюситься к wifi и отрегулировать минимальные и максимальные значения температуры датчика для эффекта пламени
В результате я получил:
? Незабываемые впечатления (особенно от работы болгаркой в туалете, выпиливая радиатор нужного размера);
? Ценный опыт;
?️ И главное — снижение температуры градусов на 15 (по ощущениям, конечно, но кулеры шуршат довольные).
Вывод простой:
Если вы опытный инженер, который знает себе цену — не повторяйте это дома.
А если вы мечтатель-фантазёр, то... ну, вы уже и так знаете, что делать. ?
Хочу сказать спасибо сообществу, которое делится опытом, и всем, кто не боится «пилить» в прямом и переносном смысле.
А я пошёл допиливать подсветку и думать, как прикрутить туда ещё что-нибудь умное.
P.S. Ниже выкладываю код огня выполнен в среде ардуино (может кому пригодится)
#include <FastLED.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// ================== НАСТРОЙКИ СВЕТОДИОДОВ ==================
#define DATA_PIN D3
#define NUM_COLS 8
#define NUM_ROWS 8
#define NUM_LEDS (NUM_COLS * NUM_ROWS)
#define MATRIX_ZIGZAG true
CRGB leds[NUM_LEDS];
// ================== НАСТРОЙКИ ЭФФЕКТА ОГНЯ ==================
#define SPARKLES 1 // вылетающие угольки вкл/выкл
uint8_t matrixValue[NUM_ROWS][NUM_COLS];
uint8_t *line = NULL;
uint8_t pcnt = 0;
// Маска значений для формы огня (растянуто до 8x8)
const uint8_t valueMask[NUM_ROWS][NUM_COLS] PROGMEM = {
{ 32 , 0 , 0 , 0 , 0 , 0 , 0 , 32 },
{ 64 , 0 , 0 , 0 , 0 , 0 , 0 , 64 },
{ 96 , 32 , 0 , 0 , 0 , 0 , 32 , 96 },
{128 , 64 , 32 , 0 , 0 , 32 , 64 ,128 },
{160 , 96 , 64 , 32 , 32 , 64 , 96 ,160 },
{192 ,128 , 96 , 64 , 64 , 96 ,128 ,192 },
{224 ,160 ,128 , 96 , 96 ,128 ,160 ,224 },
{255 ,192 ,160 ,128 ,128 ,160 ,192 ,255 }
};
// Маска оттенков для огня (8x8)
const uint8_t hueMask[NUM_ROWS][NUM_COLS] PROGMEM = {
{ 1 , 11 , 19 , 25 , 25 , 19 , 11 , 1 },
{ 1 , 8 , 13 , 19 , 19 , 13 , 8 , 1 },
{ 1 , 8 , 13 , 16 , 16 , 13 , 8 , 1 },
{ 1 , 5 , 11 , 13 , 13 , 11 , 5 , 1 },
{ 0 , 1 , 5 , 8 , 8 , 5 , 1 , 0 },
{ 0 , 0 , 1 , 5 , 5 , 1 , 0 , 0 },
{ 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 },
{ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }
};
// ================== DS18B20 ==================
#define ONE_WIRE_BUS D6
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
float currentTemp = 30.0; // начальная температура
unsigned long lastTempMillis = 0;
unsigned long lastRequestMillis = 0;
const unsigned long TEMP_INTERVAL = 2000; // обновление раз в 2 секунды
bool tempRequested = false;
// ================== ФУНКЦИЯ ДЛЯ ЗИГЗАГ-МАТРИЦЫ С ПЕРЕВОРОТОМ ==================
uint16_t getPixelNumber(uint8_t x, uint8_t y) {
if (x >= NUM_COLS || y >= NUM_ROWS) return -1;
uint8_t y_flipped = NUM_ROWS - 1 - y; // переворот по вертикали
if (MATRIX_ZIGZAG) {
if (x % 2 == 0) {
return x * NUM_ROWS + y_flipped;
} else {
return x * NUM_ROWS + (NUM_ROWS - 1 - y_flipped);
}
} else {
return x * NUM_ROWS + y_flipped;
}
}
// ================== ФУНКЦИИ ЭФФЕКТА ОГНЯ ==================
void fireInit() {
FastLED.clear();
if (line == NULL) {
line = (uint8_t*)malloc(NUM_COLS);
}
generateLine();
memset(matrixValue, 0, sizeof(matrixValue));
pcnt = 0;
}
void fireRelease() {
if (line != NULL) {
free(line);
line = NULL;
}
}
// Случайная генерация следующей линии
void generateLine() {
for (uint8_t x = 0; x < NUM_COLS; x++) {
line[x] = random8(64, 255);
}
}
// Сдвиг всех значений в матрице на одну строку вверх
void shiftFireUp() {
for (uint8_t y = NUM_ROWS - 1; y > 0; y--) {
for (uint8_t x = 0; x < NUM_COLS; x++) {
matrixValue[y][x] = matrixValue[y - 1][x];
}
}
for (uint8_t x = 0; x < NUM_COLS; x++) {
matrixValue[0][x] = line[x];
}
}
// Отрисовка кадра с интерполяцией
void drawFireFrame(uint8_t pcnt) {
int nextv;
uint8_t effectBrightness = 255; // максимальная яркость
// коэффициент яркости в зависимости от температуры (25–35)
float tempFactor = (currentTemp - 25.0) / 10.0; // 0.0–1.0
tempFactor = constrain(tempFactor, 0.0, 1.0);
for (uint8_t y = NUM_ROWS - 1; y > 0; y--) {
for (uint8_t x = 0; x < NUM_COLS; x++) {
nextv = (((100 - pcnt) * matrixValue[y][x] + pcnt * matrixValue[y - 1][x]) / 100) - pgm_read_byte(&(valueMask[y][x]));
uint8_t brightness = uint8_t(max(0, nextv) * tempFactor);
CRGB color = CHSV(
20 + pgm_read_byte(&(hueMask[y][x])), // оттенок огня
255,
brightness
);
CRGB color2 = color.nscale8_video(effectBrightness);
int idx = getPixelNumber(x, y);
if (idx >= 0) leds[idx] = color2;
}
}
for (uint8_t x = 0; x < NUM_COLS; x++) {
uint8_t brightness = uint8_t(((100 - pcnt) * matrixValue[0][x] + pcnt * line[x]) / 100 * tempFactor);
CRGB color = CHSV(
20 + pgm_read_byte(&(hueMask[0][x])),
255,
brightness
);
CRGB color2 = color.nscale8_video(effectBrightness);
int idx = getPixelNumber(x, 0);
if (idx >= 0) leds[idx] = color2;
}
}
// Основная функция эффекта огня
void fireRoutine() {
if (pcnt >= 90) {
shiftFireUp();
generateLine();
pcnt = 0;
}
drawFireFrame(pcnt);
pcnt += 30;
}
// ================== НЕБЛОКИРУЮЩИЙ ОПРОС ТЕМПЕРАТУРЫ ==================
void updateTemperatureNonBlocking() {
unsigned long currentMillis = millis();
if (!tempRequested) {
if (currentMillis - lastTempMillis >= TEMP_INTERVAL) {
oneWire.reset();
oneWire.skip();
oneWire.write(0x44, 1);
tempRequested = true;
lastRequestMillis = currentMillis;
}
} else {
if (currentMillis - lastRequestMillis >= 750) {
uint8_t data[12];
oneWire.reset();
oneWire.skip();
oneWire.write(0xBE);
for (uint8_t i = 0; i < 9; i++) {
data[i] = oneWire.read();
}
int16_t raw = (data[1] << 8) | data[0];
currentTemp = (float)raw / 16.0;
currentTemp = constrain(currentTemp, 25.0, 35.0);
tempRequested = false;
lastTempMillis = currentMillis;
Serial.print("Температура: ");
Serial.println(currentTemp);
}
}
}
// ================== ЭФФЕКТ ЗАГРУЗКИ "ОГНЕННАЯ КОМЕТА" ==================
void loadingEffect(uint8_t rounds = 5, uint16_t speed = 40, uint8_t tail = 12) {
const uint8_t pathLen = (NUM_COLS * 2 + (NUM_ROWS - 2) * 2);
uint8_t pathX[pathLen];
uint8_t pathY[pathLen];
uint8_t idx = 0;
// === путь по периметру против часовой ===
for (uint8_t x = 0; x < NUM_COLS; x++) { // верхняя сторона (слева направо)
pathX[idx] = x; pathY[idx] = 0; idx++;
}
for (uint8_t y = 1; y < NUM_ROWS; y++) { // правая сторона (сверху вниз)
pathX[idx] = NUM_COLS - 1; pathY[idx] = y; idx++;
}
for (int8_t x = NUM_COLS - 2; x >= 0; x--) { // нижняя сторона (справа налево)
pathX[idx] = x; pathY[idx] = NUM_ROWS - 1; idx++;
}
for (int8_t y = NUM_ROWS - 2; y > 0; y--) { // левая сторона (снизу вверх)
pathX[idx] = 0; pathY[idx] = y; idx++;
}
// === анимация ===
for (uint8_t r = 0; r < rounds; r++) {
for (int i = pathLen - 1; i >= 0; i--) { // против часовой
FastLED.clear();
// голова + хвост
for (uint8_t t = 0; t < tail; t++) {
int posIndex = (i + t) % pathLen;
int ledIndex = getPixelNumber(pathX[posIndex], pathY[posIndex]);
if (ledIndex >= 0) {
// плавный градиент: белый → жёлтый → оранжевый → красный
uint8_t fade = map(t, 0, tail - 1, 0, 255);
CRGB color;
if (t == 0) {
color = CRGB::White; // голова белая
} else if (fade < 85) {
color = blend(CRGB::White, CRGB::Yellow, fade * 3); // белый → жёлтый
} else if (fade < 170) {
color = blend(CRGB::Yellow, CRGB::Orange, (fade - 85) * 3); // жёлтый → оранжевый
} else {
color = blend(CRGB::Orange, CRGB::Red, (fade - 170) * 3); // оранжевый → красный
}
leds[ledIndex] = color.nscale8(255 - (t * (200 / tail))); // постепенное затухание
}
}
FastLED.show();
delay(speed);
}
}
FastLED.clear();
FastLED.show();
}
// ================== НАСТРОЙКА И ОСНОВНОЙ ЦИКЛ ==================
void setup() {
Serial.begin(115200);
delay(1000);
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(255);
sensors.begin();
// Эффект загрузки перед стартом огня
loadingEffect(5, 40, 12); // 5 кругов, скорость 40 мс, хвост 12 пикселей
fireInit();
Serial.println("Инициализация завершена. Температура опрашивается без блокировки!");
}
void loop() {
updateTemperatureNonBlocking();
fireRoutine();
FastLED.show();
delay(25);
}
Комментарии (4)
melodictsk
05.10.2025 05:00Взял мини пк как домашний сервер взамен устаревшему мини пк. Размер 11х11 см. Проц 6800hs с тдп до 45 Вт, но охлаждение вывозит только 35. Лпддр6400 гораздо быстрее ддр5. 2х2,5гб провод и вайфай 6е. 4 слота м2 под магнитной крышкой. Стоит себе за телевизором, не видно не слышно. Дочь решила на нем играть и тут его мелкий вентилятор начинает работать как пылесос. Пришлось подкинуть 3070м через м2 псие4. И теперь это не микро коробочка, а микро коробочка стоящая на здоровенный док станции с большим бп и видеокартой.
VictorDmitriev
Понравился мини ПК с "доработкой напильником".
Понравился вывод про овчинку и выделку " если бы сейчас выбирал снова — взял бы обычный стационарник".
Прошёл тот же путь, когда захотел собрать лёгкий портативный и мощный переносный системник. Для графики и видео. :)))
Начал со слим-корпуса, в который не входит ничего, кроме мат-платы и помпы жидкостного охлаждения, основной блок которой прикрепил на корпус СНАРУЖИ.
Закончил "просто мини-башней", в которую теперь тоже ничего не втиснуть, но полноразмерная двухкулерная RTX5060ti всё же вошла.