Arnov Sharma
Published © MIT

Cooling Fan Upgrade for Lattepanda Delta

Little Fan upgrade for my Lattepanda 3 Delta

IntermediateFull instructions provided1 hour23
Cooling Fan Upgrade for Lattepanda Delta

Things used in this project

Hardware components

PCBWay Custom PCB
PCBWay Custom PCB
×1
LattePanda 3 Delta
LattePanda 3 Delta
×1

Software apps and online services

Fusion
Autodesk Fusion

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

SCH

Code

code

C/C++
#include <Arduino.h>
#include <Arduino_GFX_Library.h>

/* ================= DISPLAY ================= */
#define LCD_MOSI 45
#define LCD_SCLK 40
#define LCD_CS 42
#define LCD_DC 41
#define LCD_RST 39
#define LCD_BL 46

Arduino_DataBus *bus = new Arduino_ESP32SPI(
LCD_DC, LCD_CS, LCD_SCLK, LCD_MOSI, GFX_NOT_DEFINED
);

Arduino_GFX *gfx = new Arduino_ST7789(
bus,
LCD_RST,
3, // correct orientation for your panel
true,
172, 320,
34, 0,
34, 0
);

/* ================= SAFE AREA ================= */
#define UI_MARGIN 10
#define UI_X UI_MARGIN
#define UI_Y UI_MARGIN
#define UI_W (320 - UI_MARGIN * 2)
#define UI_H (172 - UI_MARGIN * 2)

/* ================= FAN (YOUR PINS) ================= */
#define FAN_PWM 0
#define FAN_TACH 1

#define PWM_FREQ 25000
#define PWM_RES 8

/* ================= BUTTONS (YOUR PINS) ================= */
#define BTN_PLUS 2
#define BTN_MINUS 3

/* ================= RPM ================= */
volatile uint32_t pulseCount = 0;
uint32_t lastRPMMillis = 0;
uint32_t rpm = 0;

/* ================= MODE ================= */
int mode = 0; // 0=LOW,1=MED,2=HIGH,3=OFF
#define MODE_COUNT 4

/* ================= PER-MODE RPM SCALE ================= */
const uint16_t MODE_MAX_RPM[MODE_COUNT] = {
1200, // LOW
2200, // MED
3500, // HIGH
1 // OFF (dummy, not used)
};

/* ================= BAR GRAPH ================= */
#define BAR_W 30
#define BAR_H 110
#define BAR_X (UI_X + UI_W - BAR_W - 10)
#define BAR_Y (UI_Y + 35)

/* ================= BUTTON STATE ================= */
bool lastPlusState = HIGH;
bool lastMinusState = HIGH;
uint32_t lastDebounce = 0;

/* ================= ISR ================= */
void IRAM_ATTR tachISR() {
pulseCount++;
}

/* ================= FAN MODE ================= */
void setFanMode(int m) {
mode = (m + MODE_COUNT) % MODE_COUNT;

switch (mode) {
case 0: ledcWrite(FAN_PWM, 80); break; // LOW
case 1: ledcWrite(FAN_PWM, 150); break; // MED
case 2: ledcWrite(FAN_PWM, 255); break; // HIGH
case 3: ledcWrite(FAN_PWM, 0); break; // OFF
}
}

/* ================= BAR COLOR ================= */
uint16_t barColorForMode() {
switch (mode) {
case 0: return GREEN; // LOW
case 1: return YELLOW; // MED
case 2: return RED; // HIGH
default: return BLACK; // OFF
}
}

/* ================= STATIC UI ================= */
void drawStaticUI() {
gfx->fillScreen(BLACK);

gfx->drawRect(UI_X, UI_Y, UI_W, UI_H, WHITE);

gfx->setTextColor(WHITE);
gfx->setTextSize(2);
gfx->setCursor(UI_X + 10, UI_Y + 8);
gfx->println("FAN MONITOR");

gfx->setCursor(UI_X + 10, UI_Y + 40);
gfx->println("RPM");

gfx->drawRect(UI_X + 150, UI_Y + 35, 90, 60, WHITE);
gfx->setCursor(UI_X + 160, UI_Y + 40);
gfx->println("MODE");

gfx->drawRect(BAR_X, BAR_Y, BAR_W, BAR_H, WHITE);
}

/* ================= DYNAMIC UI ================= */
void updateRPMNumber(uint32_t value) {
gfx->fillRect(UI_X + 10, UI_Y + 65, 140, 50, BLACK);
gfx->setTextSize(4);
gfx->setCursor(UI_X + 10, UI_Y + 65);
gfx->println(value);
}

void updateModeText() {
gfx->fillRect(UI_X + 160, UI_Y + 65, 70, 25, BLACK);
gfx->setTextSize(2);
gfx->setCursor(UI_X + 165, UI_Y + 65);

switch (mode) {
case 0: gfx->println("LOW"); break;
case 1: gfx->println("MED"); break;
case 2: gfx->println("HIGH"); break;
case 3: gfx->println("OFF"); break;
}
}

void updateRPMBar(uint32_t rpmValue) {
gfx->fillRect(BAR_X + 1, BAR_Y + 1, BAR_W - 2, BAR_H - 2, BLACK);

if (mode == 3) return; // OFF

uint16_t maxRPM = MODE_MAX_RPM[mode];
if (rpmValue > maxRPM) rpmValue = maxRPM;

int filled = map(rpmValue, 0, maxRPM, 0, BAR_H);

if (filled > 0) {
gfx->fillRect(
BAR_X + 2,
BAR_Y + BAR_H - filled + 2,
BAR_W - 4,
filled - 4,
barColorForMode()
);
}
}

/* ================= SETUP ================= */
void setup() {
pinMode(LCD_BL, OUTPUT);
digitalWrite(LCD_BL, HIGH);

gfx->begin();
drawStaticUI();
updateRPMNumber(0);
updateRPMBar(0);
updateModeText();

ledcAttach(FAN_PWM, PWM_FREQ, PWM_RES);
setFanMode(0);

pinMode(FAN_TACH, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(FAN_TACH), tachISR, FALLING);

pinMode(BTN_PLUS, INPUT_PULLUP);
pinMode(BTN_MINUS, INPUT_PULLUP);
}

/* ================= LOOP ================= */
void loop() {
uint32_t now = millis();

if (now - lastRPMMillis >= 1000) {
noInterrupts();
uint32_t pulses = pulseCount;
pulseCount = 0;
interrupts();

rpm = (pulses / 2) * 60;

updateRPMNumber(rpm);
updateRPMBar(rpm);
lastRPMMillis = now;
}

bool plusState = digitalRead(BTN_PLUS);
bool minusState = digitalRead(BTN_MINUS);

if (now - lastDebounce > 200) {

if (plusState == LOW && lastPlusState == HIGH) {
setFanMode(mode + 1);
updateModeText();
updateRPMBar(rpm);
lastDebounce = now;
}

if (minusState == LOW && lastMinusState == HIGH) {
setFanMode(mode - 1);
updateModeText();
updateRPMBar(rpm);
lastDebounce = now;
}
}

lastPlusState = plusState;
lastMinusState = minusState;
}

Credits

Arnov Sharma
366 projects • 376 followers
I'm Arnov. I build, design, and experiment with tech—3D printing, PCB design, and retro consoles are my jam.

Comments