Add enhanced fan logic

This commit is contained in:
KenwoodFox
2026-06-18 14:09:14 -04:00
parent 49618f0c0a
commit bbe5fd5452
7 changed files with 66 additions and 67 deletions

View File

@@ -137,7 +137,6 @@ TOWERD_RENOGY_TIMEOUT_MS=1000
``` ```
Thank you to [ESP32ArduinoRenogy](https://github.com/wrybread/ESP32ArduinoRenogy) for doing all the hard work! Thank you to [ESP32ArduinoRenogy](https://github.com/wrybread/ESP32ArduinoRenogy) for doing all the hard work!
## systemd ## systemd
`towerd` loads `/etc/towerd/env` via `EnvironmentFile`. If using Docker for InfluxDB, ensure Docker starts before towerd: `towerd` loads `/etc/towerd/env` via `EnvironmentFile`. If using Docker for InfluxDB, ensure Docker starts before towerd:
@@ -149,3 +148,4 @@ Wants=docker.service
``` ```
Add those lines to `/etc/systemd/system/towerd.service` under `[Unit]`. Add those lines to `/etc/systemd/system/towerd.service` under `[Unit]`.

View File

@@ -1,18 +0,0 @@
services:
influxdb:
image: influxdb:2.7
restart: unless-stopped
ports:
- "127.0.0.1:8086:8086"
volumes:
- influx-data:/var/lib/influxdb2
environment:
DOCKER_INFLUXDB_INIT_MODE: setup
DOCKER_INFLUXDB_INIT_USERNAME: admin
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUXDB_INIT_PASSWORD}
DOCKER_INFLUXDB_INIT_ORG: ${INFLUXDB_INIT_ORG:-tower}
DOCKER_INFLUXDB_INIT_BUCKET: ${INFLUXDB_INIT_BUCKET:-tower}
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${INFLUXDB_INIT_ADMIN_TOKEN}
volumes:
influx-data:

View File

@@ -34,8 +34,8 @@ impl Default for Config {
Self { Self {
status_pin: 4, status_pin: 4,
fan_pin: 17, fan_pin: 17,
fan_on_temp_c: 40.0, fan_on_temp_c: 35.0,
fan_off_temp_c: 35.0, fan_off_temp_c: 30.0,
poll_interval_s: 2.0, poll_interval_s: 2.0,
thermal_alarm_temp_c: 80.0, thermal_alarm_temp_c: 80.0,
influx: InfluxConfig::from_env(), influx: InfluxConfig::from_env(),

View File

@@ -31,7 +31,7 @@ pub async fn run(config: Config) -> anyhow::Result<()> {
result = tasks::status::run(config.status_pin, status_rx) => { result = tasks::status::run(config.status_pin, status_rx) => {
result?; result?;
} }
result = tasks::thermal::run(config.clone(), fan.clone(), alarms.clone()) => { result = tasks::thermal::run(config.clone(), alarms.clone()) => {
result?; result?;
} }
result = async { result = async {
@@ -44,6 +44,9 @@ pub async fn run(config: Config) -> anyhow::Result<()> {
} }
result = tasks::renogy::run( result = tasks::renogy::run(
config.renogy.clone(), config.renogy.clone(),
config.fan_on_temp_c,
config.fan_off_temp_c,
fan.clone(),
config.influx.clone(), config.influx.clone(),
alarms.clone(), alarms.clone(),
) => { ) => {

View File

@@ -8,12 +8,16 @@ use tracing::{debug, info, warn};
use crate::alarm::SharedAlarms; use crate::alarm::SharedAlarms;
use crate::config::{InfluxConfig, RenogyConfig}; use crate::config::{InfluxConfig, RenogyConfig};
use crate::gpio::{Fan, SharedFan};
use crate::renogy::{self, Client as RenogyClient, ControllerData, ControllerInfo}; use crate::renogy::{self, Client as RenogyClient, ControllerData, ControllerInfo};
const RECONNECT_INTERVAL_S: f64 = 15.0; const RECONNECT_INTERVAL_S: f64 = 15.0;
pub async fn run( pub async fn run(
config: RenogyConfig, config: RenogyConfig,
fan_on_temp_c: f64,
fan_off_temp_c: f64,
fan: SharedFan,
influx: Option<InfluxConfig>, influx: Option<InfluxConfig>,
alarms: SharedAlarms, alarms: SharedAlarms,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -43,7 +47,16 @@ pub async fn run(
continue; continue;
}; };
match connect_and_poll(&port, &config, influx.as_ref(), &alarms).await { match connect_and_poll(
&port,
&config,
fan_on_temp_c,
fan_off_temp_c,
&fan,
influx.as_ref(),
&alarms,
)
.await {
Ok(()) => return Ok(()), Ok(()) => return Ok(()),
Err(e) => { Err(e) => {
alarms.set_fault("renogy", true).await; alarms.set_fault("renogy", true).await;
@@ -57,6 +70,9 @@ pub async fn run(
async fn connect_and_poll( async fn connect_and_poll(
port: &str, port: &str,
config: &RenogyConfig, config: &RenogyConfig,
fan_on_temp_c: f64,
fan_off_temp_c: f64,
fan: &SharedFan,
influx: Option<&InfluxConfig>, influx: Option<&InfluxConfig>,
alarms: &SharedAlarms, alarms: &SharedAlarms,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
@@ -104,13 +120,23 @@ async fn connect_and_poll(
// Clear fault // Clear fault
alarms.set_fault("renogy", false).await; alarms.set_fault("renogy", false).await;
// Debug log {
debug!( let mut fan = fan.lock().await;
battery_v = data.battery_voltage, update_fan(
battery_soc = data.battery_soc, &mut fan,
solar_w = data.solar_watts, fan_on_temp_c,
"Renogy poll ok" fan_off_temp_c,
); data.controller_temperature_c,
);
debug!(
battery_v = data.battery_voltage,
battery_soc = data.battery_soc,
controller_temp_c = data.controller_temperature_c,
solar_w = data.solar_watts,
fan = if fan.on() { "on" } else { "off" },
"Renogy poll ok"
);
}
// Publish data // Publish data
if let (Some(client), Some(influx)) = (&influx_client, influx) { if let (Some(client), Some(influx)) = (&influx_client, influx) {
@@ -124,6 +150,25 @@ async fn connect_and_poll(
} }
} }
fn update_fan(fan: &mut Fan, on_temp_c: f64, off_temp_c: f64, controller_temp_c: u8) {
let temp_c = f64::from(controller_temp_c);
if !fan.on() && temp_c > on_temp_c {
fan.set_on(true);
info!(
controller_temp_c = temp_c,
threshold = on_temp_c,
"Fan on"
);
} else if fan.on() && temp_c <= off_temp_c {
fan.set_on(false);
info!(
controller_temp_c = temp_c,
threshold = off_temp_c,
"Fan off"
);
}
}
// Publish data to InfluxDB // Publish data to InfluxDB
async fn publish( async fn publish(
client: &InfluxClient, client: &InfluxClient,

View File

@@ -1,57 +1,26 @@
use std::time::Duration; use std::time::Duration;
use tokio::time::MissedTickBehavior; use tokio::time::MissedTickBehavior;
use tracing::{debug, info}; use tracing::debug;
use crate::alarm::SharedAlarms; use crate::alarm::SharedAlarms;
use crate::config::Config; use crate::config::Config;
use crate::gpio::{Fan, SharedFan};
use crate::thermal; use crate::thermal;
pub async fn run(config: Config, fan: SharedFan, alarms: SharedAlarms) -> anyhow::Result<()> { pub async fn run(config: Config, alarms: SharedAlarms) -> anyhow::Result<()> {
// Setup the interval for the thermal task
let mut interval = let mut interval =
tokio::time::interval(Duration::from_secs_f64(config.poll_interval_s)); tokio::time::interval(Duration::from_secs_f64(config.poll_interval_s));
interval.set_missed_tick_behavior(MissedTickBehavior::Skip); interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
loop { // Main loop loop {
interval.tick().await; interval.tick().await;
// Read the temperature
let temp_c = tokio::task::spawn_blocking(thermal::read_cpu_temp_c) let temp_c = tokio::task::spawn_blocking(thermal::read_cpu_temp_c)
.await??; .await??;
// Check if the temperature is over the alarm threshold
let over_temp = temp_c >= config.thermal_alarm_temp_c; let over_temp = temp_c >= config.thermal_alarm_temp_c;
alarms.set_fault("thermal", over_temp).await; alarms.set_fault("thermal", over_temp).await;
// Update the fan debug!(temp_c, alarm = ?alarms.status());
let mut fan = fan.lock().await;
update_fan(&mut fan, &config, temp_c);
// Debug logging
debug!(
temp_c,
fan = if fan.on() { "on" } else { "off" },
alarm = ?alarms.status(),
);
}
}
fn update_fan(fan: &mut Fan, config: &Config, temp_c: f64) {
if !fan.on() && temp_c >= config.fan_on_temp_c {
fan.set_on(true);
info!(
temp_c,
threshold = config.fan_on_temp_c,
"Fan on"
);
} else if fan.on() && temp_c <= config.fan_off_temp_c {
fan.set_on(false);
info!(
temp_c,
threshold = config.fan_off_temp_c,
"Fan off"
);
} }
} }

View File

@@ -1,7 +1,7 @@
[Unit] [Unit]
Description=Tower Daemon Description=Tower Daemon
After=multi-user.target docker.service After=multi-user.target influxdb.service
Wants=docker.service Wants=influxdb.service
[Service] [Service]
Type=simple Type=simple