Compare commits

...

22 Commits

Author SHA1 Message Date
2d564b2670 I'm trying to learn stm32cube framework :sadge: 2025-12-06 22:56:56 +01:00
5a67e90143 Changed issue 7 as well as some other tweaks
correctly set variable to set influxdb
2025-10-18 08:52:30 +02:00
224fae39f7 checked PC_CONTROL_CODES with mypy 2025-08-09 10:53:17 +02:00
45459ee231 Added constant def and return types 2025-08-09 10:37:35 +02:00
978dd0dc5d Added error catching for KeyError for Influx. 2025-07-16 17:53:18 +02:00
1f96e10f76 Added thread locking instead of variable lock 2025-07-16 17:47:42 +02:00
c5782018b8 Added a special token to avoid usage conflicts. 2025-07-16 17:16:20 +02:00
67874de37a Simple Grafana dashboard 2025-07-05 21:55:21 +02:00
582925b8a1 Finished InfluxDB integration 2025-07-02 22:43:46 +02:00
b6c91e4ac4 working with influxdb implementation 2025-07-02 21:26:06 +02:00
627cc9bc24 Fixed main docker image so that it actually works 2025-07-02 21:25:51 +02:00
2ee9a26abd Added main docker-compose file 2025-07-02 21:25:37 +02:00
7ce3f5efc2 It's working yet again 2025-07-02 14:53:15 +02:00
b45180d50d Added env files and fixed docker-compose 2025-07-02 12:44:49 +02:00
f0c90c831f Changed time interval to 1 second from 5 minutes 2025-07-02 12:39:39 +02:00
69cce7be3e Added a very simple script that just works. 2025-06-10 22:59:21 +02:00
YuruC3
18427298bc Finally got what's the difference between : and =
under environments in docker-compose.
2025-05-10 16:58:18 +02:00
YuruC3
247848285f Some more README changes under docker section 2025-05-10 13:48:58 +02:00
YuruC3
641b5d2f68 small change to README 2025-05-10 13:45:57 +02:00
YuruC3
fa4153d68d Docker is now working 2025-05-10 13:44:21 +02:00
YuruC3
964236b6b5 main.py is fully working. 2025-05-10 11:55:25 +02:00
YuruC3
5dc92e9ca4 Some changes to main README file. 2025-05-10 10:28:18 +02:00
34 changed files with 62058 additions and 103 deletions

18
.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "linux-gcc-x64",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "/usr/bin/gcc",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "linux-gcc-x64",
"compilerArgs": [
""
]
}
],
"version": 4
}

24
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": false,
"cwd": "/hdd/GiTea-REPO/MD1200/PC_CONTROL_CODE/CPP/noInflux",
"program": "/hdd/GiTea-REPO/MD1200/PC_CONTROL_CODE/CPP/noInflux/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

59
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,59 @@
{
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",
"C_Cpp_Runner.debuggerPath": "gdb",
"C_Cpp_Runner.cStandard": "",
"C_Cpp_Runner.cppStandard": "",
"C_Cpp_Runner.msvcBatchPath": "",
"C_Cpp_Runner.useMsvc": false,
"C_Cpp_Runner.warnings": [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wshadow",
"-Wformat=2",
"-Wcast-align",
"-Wconversion",
"-Wsign-conversion",
"-Wnull-dereference"
],
"C_Cpp_Runner.msvcWarnings": [
"/W4",
"/permissive-",
"/w14242",
"/w14287",
"/w14296",
"/w14311",
"/w14826",
"/w44062",
"/w44242",
"/w14905",
"/w14906",
"/w14263",
"/w44265",
"/w14928"
],
"C_Cpp_Runner.enableWarnings": true,
"C_Cpp_Runner.warningsAsError": false,
"C_Cpp_Runner.compilerArgs": [],
"C_Cpp_Runner.linkerArgs": [],
"C_Cpp_Runner.includePaths": [],
"C_Cpp_Runner.includeSearch": [
"*",
"**/*"
],
"C_Cpp_Runner.excludeSearch": [
"**/build",
"**/build/**",
"**/.*",
"**/.*/**",
"**/.vscode",
"**/.vscode/**"
],
"C_Cpp_Runner.useAddressSanitizer": false,
"C_Cpp_Runner.useUndefinedSanitizer": false,
"C_Cpp_Runner.useLeakSanitizer": false,
"C_Cpp_Runner.showCompilationTime": false,
"C_Cpp_Runner.useLinkTimeOptimization": false,
"C_Cpp_Runner.msvcSecureNoWarnings": false
}

BIN
58FO8iY.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1 +1,2 @@
testing testing
cpp

View File

@@ -0,0 +1,2 @@
# Run every minute
* * * * /location/to/bash/simple.sh

View File

@@ -0,0 +1,9 @@
#!/bin/bash
for i in {1..59}
do
stty -F /dev/ttyUSB0 speed 38400 cs8 -ixon raw
echo -ne "_shutup 24\n\r" > /dev/ttyUSB0
sleep 1
done
exit

Binary file not shown.

1
PC_CONTROL_CODE/docker/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.mypy_cache

View File

@@ -0,0 +1,28 @@
FROM alpine:latest
# https://docs.docker.com/reference/dockerfile/#environment-replacement
# ENV MD1200BAUD=38400
# ENV SERIALADAPTER=/dev/ttyUSB0
# ENV EPPYSLEEPY=300
# VOLUME [""]
RUN apk update && \
apk add python3 py3-pip
RUN mkdir /etc/MD1200FAN/
WORKDIR /etc/MD1200FAN/
COPY ./mainDocker.py /etc/MD1200FAN/
# COPY ./requirements.txt /etc/MD1200FAN/
RUN python3 -m venv venv && \
venv/bin/python3 -m pip install --upgrade pip && \
venv/bin/pip3 install PySerial
# venv/bin/pip3 install -r requirements.txt
# VOLUME ["/etc/MD1200FAN/"]
CMD ["venv/bin/python3", "mainDocker.py"]
# CMD ["/etc/MD1200FAN/venv/bin/python3", "/etc/MD1200FAN/mainDocker.py"]

View File

@@ -0,0 +1,18 @@
---
services:
mdfanchanger:
container_name: MD_Fan_Changer
image: yuruc3/md1200_fan_controll:v1.2.1
environment:
# - MD1200BAUD=
- SERIALADAPTER=/dev/ttyUSB0
- TEMP_FACTOR=17
- EPPYSLEEPY=0.25
- MDSERIALTIMEOUT=0.75
# - LOW_FAN_TRSHD=
# - HIGH_FAN_TRSHD=
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
restart: unless-stopped
privileged: false

View File

@@ -0,0 +1,194 @@
import serial, time, os
from typing import Final
# setting consts that can be customized
# baud rate. Prob not needed as 38400 is standard
MD1200BAUD: Final[int] = int(os.getenv("MD1200BAUD", 38400))
# used if you want to run it on multiple JBODs
SERIALADAPTER: Final[str] = os.getenv("SERIALADAPTER", "/dev/ttyUSB0")
# Factor that defines how aggressive the temperature curve is
TEMP_FACTOR: Final[int] = int(os.getenv("TEMP_FACTOR", 16))
# time between sending command to get temp and storing it. It's there to allow JBOD to answer
EPPYSLEEPY: Final[float] = float(os.getenv("EPPYSLEEPY", 1))
LOW_FAN_TRSHD: Final[int] = int(os.getenv("LOW_FAN_TRSHD", 21))
HIGH_FAN_TRSHD: Final[int] = int(os.getenv("HIGH_FAN_TRSHD", 40))
GETTMPCMND: Final[str] = os.getenv("GETTMPCMND", "_temp_rd")
SETFANCMND: Final[str] = os.getenv("SETFANCMND", "set_speed")
DEFOUTPRCNTG: Final[int] = int(os.getenv("DEFOUTPRCNTG", 24))
MDSERIALTIMEOUT: Final[float] = float(os.getenv("MDSERIALTIMEOUT", 1))
TEMPREADINTERVAL: Final[int] = int(os.getenv("TEMPREADINTERVAL", 15))
GETTEMPTIMESLEEP: Final[int] = int(os.getenv("GETTEMPTIMESLEEP", 1))
# init
MDserial = serial.Serial(
port=SERIALADAPTER,\
baudrate=MD1200BAUD,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=MDSERIALTIMEOUT)
lastTempReading = time.time()
MDtempDict = {}
def getTemp() -> dict:
MDserial.write(f"{GETTMPCMND}\n\r".encode())
time.sleep(GETTEMPTIMESLEEP)
MDreturning = MDserial.read_until(" >").decode(errors="ignore")
MDict = {}
# Sanitise output
MDsanit = MDreturning.splitlines()
#if there is smth do smth
if MDreturning:
for line in MDsanit:
if ">" in line or "b'" in line:
continue
matchstm = line[2:6]
match matchstm:
case "BP_1":
MDict["bp1"] = int(line[12:14])
case "BP_2":
MDict["bp2"] = int(line[12:14])
case "SIM0":
MDict["sim0"] = int(line[12:14])
case "SIM1":
MDict["sim1"] = int(line[12:14])
case "EXP0":
MDict["exp0"] = int(line[12:14])
case "EXP1":
MDict["exp1"] = int(line[12:14])
# case "AVG":
# MDict["avg"] = int(line[12:14])
# MDict["avg"] = int(line.strip().split("=")[1].strip().replace("c", ""))
# try:
# # Extract number from e.g. ' AVG = 40c'
# temp = int(line.strip().split("=")[1].strip().replace("c", ""))
# MDict["avg"] = temp
# except Exception as e:
# # print(f"[WARN] Failed to parse AVG line: {line} ({e})", flush=True)
# pass
case _:
# try to catch the AVG line like: " AVG = 40c"
stripped = line.strip()
if stripped.startswith("AVG"):
try:
temp = int(stripped.split("=")[1].strip().replace("c", ""))
MDict["avg"] = temp
except Exception as e:
print(f"Could not parse AVG line: {line} ({e})", flush=True)
continue
# continue
return MDict
else:
return {"error": "unidentified"}
def setSpeed(inSpeeDict: dict) -> int:
bpavrg = 0
# default
outfanprcntg = 0
# get backplanbe average
if "bp1" in inSpeeDict and "bp2" in inSpeeDict:
bpavrg = (inSpeeDict["bp1"] + inSpeeDict["bp2"]) /2
outfanprcntg = int((bpavrg / (HIGH_FAN_TRSHD - LOW_FAN_TRSHD)) * TEMP_FACTOR)
# os.system(f"echo setting {outfanprcntg}%")
return outfanprcntg
# Set fan speed
if outfanprcntg >= 20:
MDserial.write((f"{SETFANCMND} {str(outfanprcntg)} \n\r").encode())
print(f"setting {outfanprcntg}%", flush=True)
return outfanprcntg
else:
# Set default value
MDserial.write((f"{SETFANCMND} {str(DEFOUTPRCNTG)} \n\r").encode())
return DEFOUTPRCNTG
# If something goes super wrong
return 1
# Check if UART is used
# Not neede because when defining MDserial it gets automatically opened
# Will leave it here anyway
# try:
# MDserial.open()
# except serial.serialutil.SerialException:
# # MDserial.close()
# # MDserial.open()
# print("Port allready opened.\nTry closing it first")
# Init
MDtempDict = getTemp()
lastTempReading = time.time()
try:
while True:
# https://stackoverflow.com/questions/52578122/not-able-to-send-the-enter-command-on-pyserial
# get temperature data, wait for MD1200 to answer and store
currentTime = time.time()
if currentTime - lastTempReading >= TEMPREADINTERVAL:
MDtempDict = getTemp()
lastTempReading = currentTime
if MDtempDict:
setSpeedrcode = setSpeed(MDtempDict)
# good
if setSpeedrcode == 0:
pass
# print("Were mint", flush=True)
# time.sleep(EPPYSLEEPY)
# not good
elif setSpeedrcode == 1:
print("Ambigous temperature readings.\nFalling back to safe values.", flush=True)
# time.sleep(EPPYSLEEPY)
# very not good
elif setSpeedrcode == -1:
print("o nyo", flush=True)
exit()
# very very very not good
else:
print("idk", flush=True)
exit()
else:
print(f"temperature not yet pulled.\nFalling back do default fan speed", flush=True)
# os.system(f"echo temperature not yet pulled.\nFalling back do default fan speed")
MDserial.write((f"{SETFANCMND} {str(DEFOUTPRCNTG)} \n\r").encode())
time.sleep(EPPYSLEEPY)
except KeyboardInterrupt:
print("\n[INFO] KeyboardInterrupt detected. Exiting gracefully...")
MDserial.close()
exit()
finally:
print("closing port")
MDserial.close()
print("closing port")
MDserial.close()

View File

@@ -0,0 +1,6 @@
baud_rate=
serial_adapter=
wait_time=
temp_factor=
lower_treshold=
upper_treshold=

View File

@@ -0,0 +1 @@
.mypy_cache

View File

@@ -0,0 +1,28 @@
FROM alpine:latest
# https://docs.docker.com/reference/dockerfile/#environment-replacement
# ENV MD1200BAUD=38400
# ENV SERIALADAPTER=/dev/ttyUSB0
# ENV EPPYSLEEPY=300
# VOLUME [""]
RUN apk update && \
apk add python3 py3-pip
RUN mkdir /etc/MD1200FAN/
WORKDIR /etc/MD1200FAN/
COPY ./mainDocker.py /etc/MD1200FAN/
# COPY ./requirements.txt /etc/MD1200FAN/
RUN python3 -m venv venv && \
venv/bin/python3 -m pip install --upgrade pip && \
venv/bin/pip3 install PySerial influxdb_client
# venv/bin/pip3 install -r requirements.txt
# VOLUME ["/etc/MD1200FAN/"]
CMD ["venv/bin/python3", "mainDocker.py"]
# CMD ["/etc/MD1200FAN/venv/bin/python3", "/etc/MD1200FAN/mainDocker.py"]

View File

@@ -0,0 +1,11 @@
---
services:
mdfanchanger:
container_name: MD_Fan_Changer
image: yuruc3/md1200_fan_controll:v2-flux
env_file: md1200.env
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
restart: unless-stopped
privileged: false

View File

@@ -0,0 +1,55 @@
import time, os, influxdb_client
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions
from datetime import timedelta
from concurrent.futures import ThreadPoolExecutor
from typing import Final
# INFLUXDB config
# token = "apg1gysUeCcxdcRTMmosJTenbEppmUNi9rXlANDB2oNadBdWAu2GVTDc_q_dyo0iyYsckKaOvPRm6ba2NK0y_A=="
INFLUXTOKEN: Final[str] = os.getenv("INFLUX_TOKEN")
# bucket = "JBOD"
# INFLUXBUCKET: Final[str] = os.getenv("INFLUX_BUCKET")
# org = "staging"
INFLUXORG: Final[str] = os.getenv("INFLUX_ORG")
# url = "http://localhost:8086"
INFLUXURL: Final[str] = os.getenv("INFLUX_URL")
# measurement = "MD1200"
measurement = os.getenv("INFLUX_MEASUREMENT")
# MACHINE_TAG = "CHONGUS1200"
MACHINE_TAG = os.getenv("INFLUX_MACHINE_TAG")
# LOCATION = "HQ"
LOCATION = os.getenv("INFLUX_LOCATION")
# INFLX_SEPARATE_POINTS = 0.1
INFLUX_SEPARATE_POINTS = int(os.getenv("INFLUX_SEPARATE_POINTS"))
# Initialize InfluxDB client and influxdb API
inflxdb_client = influxdb_client.InfluxDBClient(url=INFLUXURL, token=INFLUXTOKEN, org=INFLUXORG)
#write_api = inflxdb_client.write_api(write_options=SYNCHRONOUS)
write_api = inflxdb_client.write_api(write_options=WriteOptions(batch_size=500, flush_interval=1000))
# Threaded flow processor
def process_temps(inEntry):
global MDict
# ---LeData---
# {'bp1': 35, 'bp2': 29, 'sim0': 35, 'sim1': 34, 'exp0': 56, 'exp1': 54}
# ---LeData---
# Prep InfluxDB data
inflxdb_Data_To_Send = (
influxdb_client.Point(f"{measurement}-script")
.tag("MACHINE", MACHINE_TAG)
.tag("LOCATION", LOCATION)
.field("Backplane1", inEntry["bp1"])
.field("Backplane2", inEntry["bp2"])
.field("SASIntModule0", inEntry["sim0"])
.field("SASIntModule1", inEntry["sim1"])
.field("Expander0", inEntry["exp0"])
.field("Expander1", inEntry["exp1"])
.field("Average", inEntry["avg"])
)
print("----------------")
return ()

View File

@@ -0,0 +1,350 @@
import time, os, influxdb_client, serial, threading
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS, ASYNCHRONOUS, WriteOptions
from concurrent.futures import ThreadPoolExecutor
from typing import Final
# setting consts that can be customized
# baud rate. Prob not needed as 38400 is standard
MD1200BAUD: Final[int] = int(os.getenv("MD1200BAUD", "38400"))
# used if you want to run it on multiple JBODs
SERIALADAPTER: Final[str] = os.getenv("SERIALADAPTER", "/dev/ttyUSB0")
# Factor that defines how aggressive the temperature curve is
TEMP_FACTOR: Final[float] = float(os.getenv("TEMP_FACTOR", "19"))
# time between sending command to get temp and storing it. It's there to allow JBOD to answer
EPPYSLEEPY: Final[float] = float(os.getenv("EPPYSLEEPY", "1"))
LOW_FAN_TRSHD: Final[float] = float(os.getenv("LOW_FAN_TRSHD", "21"))
HIGH_FAN_TRSHD: Final[float] = float(os.getenv("HIGH_FAN_TRSHD", "40"))
GETTMPCMND: Final[str] = os.getenv("GETTMPCMND", "_temp_rd")
SETFANCMND: Final[str] = os.getenv("SETFANCMND", "set_speed")
DEFOUTPRCNTG: Final[float] = float(os.getenv("DEFOUTPRCNTG", "24"))
MDSERIALTIMEOUT: Final[float] = float(os.getenv("MDSERIALTIMEOUT", "1"))
TEMPREADINTERVAL: Final[float] = float(os.getenv("TEMPREADINTERVAL", "15"))
# If True or yes then we good
TEMPSETING = os.getenv("TEMPSETING", "1").strip().lower() in ("1", "true", "yes", "on")
PROCESSTEMPWAITTIME: Final[float] = float(os.getenv("PROCESSTEMPWAITTIME", "0.75"))
BACKOFFTIME: Final[float] = float(os.getenv("BACKOFFTIME", "5"))
INFLUX_MAX_RETRIES: Final[int] = int(os.getenv("INFLUX_MAX_RETRIES", "3"))
# INFLUXDB config
# should Influx be used?
USEINFLUX: Final[bool] = os.getenv("USEINFLUX", "True").strip().lower() in ("1", "true", "yes", "on")
# token = "apg1gysUeCcxdcRTMmosJTenbEppmUNi9rXlANDB2oNadBdWAu2GVTDc_q_dyo0iyYsckKaOvPRm6ba2NK0y_A=="
INFLUXTOKEN: Final[str] = str(os.getenv("INFLUX_TOKEN", "0"))
# bucket = "JBOD"
INFLUXBUCKET: Final[str] = str(os.getenv("INFLUX_BUCKET", "0"))
# org = "staging"
INFLUXORG: Final[str] = str(os.getenv("INFLUX_ORG", "0"))
# url = "http://localhost:8086"
INFLUXURL: Final[str] = str(os.getenv("INFLUX_URL", "0"))
# measurement = "MD1200"
INFLUXMEASUREMENT: Final[str] = str(os.getenv("INFLUX_MEASUREMENT", "0"))
# MACHINE_TAG = "CHONGUS1200"
MACHINE_TAG: Final[str] = str(os.getenv("INFLUX_MACHINE_TAG", "0"))
# LOCATION = "HQ"
LOCATION: Final[str] = str(os.getenv("INFLUX_LOCATION", "0"))
# INFLX_SEPARATE_POINTS = 0.1
# INFLUX_SEPARATE_POINTS = float(os.getenv("INFLUX_SEPARATE_POINTS"), 0.1)
# init
MDserial = serial.Serial(
port=SERIALADAPTER,\
baudrate=MD1200BAUD,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=MDSERIALTIMEOUT)
# lastTempReading: float = time.time()
setSpeedrcode = 21
MDtempDict: dict = {}
MDict: dict = {}
currentSerialUsage = threading.Lock()
fluxSending: bool = False
currentTime: float = 0
lastTempReading: float = 0
# Initialize InfluxDB client and influxdb API
# ---------------------UNCOMMENT-----------------------
if USEINFLUX:
inflxdb_client = influxdb_client.InfluxDBClient(url=INFLUXURL, token=INFLUXTOKEN, org=INFLUXORG)
write_api = inflxdb_client.write_api(write_options=SYNCHRONOUS)
inflxdb_LeData: list = []
# ---------------------UNCOMMENT-----------------------
# Just a helper function for process_temps to avoid db errors on flapping network
def process_temps_dbsend(inpdatatosend) -> bool:
if not USEINFLUX:
return True
try:
write_api.write(bucket=INFLUXBUCKET, org=INFLUXORG, record=inpdatatosend)
return True
except Exception as e:
print(f"Influx write error {e}", flush=True)
return False
def getTemp() -> dict:
global MDict, fluxSending
with currentSerialUsage:
MDserial.write(f"{GETTMPCMND}\n\r".encode())
time.sleep(1)
MDreturning = MDserial.read_until(" >").decode()
# MDict = {}
# Sanitise output
MDsanit = MDreturning.splitlines()
#if there is smth do smth
if MDreturning:
for line in MDsanit:
if ">" in line or "b'" in line:
continue
matchstm = line[2:6]
match matchstm:
case "BP_1":
MDict["bp1"] = int(line[12:14])
case "BP_2":
MDict["bp2"] = int(line[12:14])
case "SIM0":
MDict["sim0"] = int(line[12:14])
case "SIM1":
MDict["sim1"] = int(line[12:14])
case "EXP0":
MDict["exp0"] = int(line[12:14])
case "EXP1":
MDict["exp1"] = int(line[12:14])
# case "AVG":
# MDict["avg"] = int(line[12:14])
# MDict["avg"] = int(line.strip().split("=")[1].strip().replace("c", ""))
# try:
# # Extract number from e.g. ' AVG = 40c'
# temp = int(line.strip().split("=")[1].strip().replace("c", ""))
# MDict["avg"] = temp
# except Exception as e:
# # print(f"[WARN] Failed to parse AVG line: {line} ({e})", flush=True)
# pass
case _:
# try to catch the AVG line like: " AVG = 40c"
stripped = line.strip()
if stripped.startswith("AVG"):
try:
temp = int(stripped.split("=")[1].strip().replace("c", ""))
MDict["avg"] = temp
except Exception as e:
print(f"[WARN] Could not parse AVG line: {line} ({e})", flush=True)
continue
# continue
# {'bp1': 35, 'bp2': 29, 'sim0': 35, 'sim1': 33, 'exp0': 56, 'exp1': 54, 'avg': 40}
# process_temps(MDict)
fluxSending = True
return MDict
else:
return {"error": "unidentified"}
def setSpeed(inSpeeDict: dict) -> int:
bpavrg = 0
# default
outfanprcntg = 0
# get backplanbe average
if "bp1" in inSpeeDict and "bp2" in inSpeeDict:
bpavrg = (inSpeeDict["bp1"] + inSpeeDict["bp2"]) /2
#outfanprcntg = int((bpavrg / (HIGH_FAN_TRSHD - LOW_FAN_TRSHD)) * TEMP_FACTOR)
outfanprcntg = int((bpavrg / (HIGH_FAN_TRSHD - LOW_FAN_TRSHD)) * TEMP_FACTOR)
# os.system(f"echo setting {outfanprcntg}%")
with currentSerialUsage:
# Set fan speed
if outfanprcntg >= 20:
MDserial.write((f"{SETFANCMND} {str(outfanprcntg)} \n\r").encode())
print(f"setting {outfanprcntg}%", flush=True)
return outfanprcntg
else:
# Set default value
MDserial.write((f"{SETFANCMND} {str(DEFOUTPRCNTG)} \n\r").encode())
return outfanprcntg
# If something goes super wrong
return 1
# Check if UART is used
# Not neede because when defining MDserial it gets automatically opened
# Will leave it here anyway
# try:
# MDserial.open()
# except serial.serialutil.SerialException:
# # MDserial.close()
# # MDserial.open()
# print("Port allready opened.\nTry closing it first")
# Threaded flow processor
def process_temps() -> None:
global MDict, fluxSending, setSpeedrcode
while True:
# ---LeData---
# {'bp1': 35, 'bp2': 29, 'sim0': 35, 'sim1': 34, 'exp0': 56, 'exp1': 54}
# ---LeData---
if fluxSending:
# Prep InfluxDB data
# build local copy in case MDict changes while executing
MDictLocalCopy = MDict.copy()
try:
inflxdb_Data_To_Send = (
influxdb_client.Point(f"{INFLUXMEASUREMENT}-script")
.tag("MACHINE", MACHINE_TAG)
.tag("LOCATION", LOCATION)
.field("Backplane1", MDictLocalCopy["bp1"])
.field("Backplane2", MDictLocalCopy["bp2"])
.field("SASIntModule0", MDictLocalCopy["sim0"])
.field("SASIntModule1", MDictLocalCopy["sim1"])
.field("Expander0", MDictLocalCopy["exp0"])
.field("Expander1", MDictLocalCopy["exp1"])
.field("Average", MDictLocalCopy["avg"])
.field("FanSpeed", setSpeedrcode)
)
except KeyError:
time.sleep(BACKOFFTIME)
continue
# Prep/append data
inflxdb_LeData.append(inflxdb_Data_To_Send)
# Issue YuruC3/MD1200#7 fix
if not process_temps_dbsend(inflxdb_Data_To_Send):
i = 0
while i < INFLUX_MAX_RETRIES:
if process_temps_dbsend(inflxdb_Data_To_Send):
print("Sending data to InfluxDB", flush=True)
break
else:
time.sleep(1)
i += 1
else:
print(f"Failed to send data to InfluxDB after {INFLUX_MAX_RETRIES} retires", flush=True)
else:
print("Sending data to InfluxDB", flush=True)
# Clean up before another lo#op, 0.75
inflxdb_LeData.clear()
fluxSending = False
else:
time.sleep(PROCESSTEMPWAITTIME)
# Init
MDict = getTemp()
lastTempReading = time.time()
def mainCodeHere() -> None:
global setSpeedrcode
while True:
global MDict, fluxSending, currentTime, lastTempReading
# https://stackoverflow.com/questions/52578122/not-able-to-send-the-enter-command-on-pyserial
# get temperature data, wait for MD1200 to answer and store
currentTime = time.time()
if currentTime - lastTempReading >= TEMPREADINTERVAL:
getTemp()
lastTempReading = currentTime
if MDict and TEMPSETING:
setSpeedrcode = setSpeed(MDict)
# good
if setSpeedrcode == 0:
pass
# print("Were mint", flush=True)
# time.sleep(EPPYSLEEPY)
# not good
elif setSpeedrcode == 1:
print("Ambigous temperature readings.\nFalling back to safe values.", flush=True)
# time.sleep(EPPYSLEEPY)
# very not good
elif setSpeedrcode == -1:
print("o nyo", flush=True)
exit()
# very very very not good
else:
print("idk", flush=True)
exit()
elif TEMPSETING == False:
print(f"Waiting to get temp", flush=True)
pass
else:
print(f"temperature not yet pulled.\nFalling back do default fan speed", flush=True)
# os.system(f"echo temperature not yet pulled.\nFalling back do default fan speed")
with currentSerialUsage:
MDserial.write((f"{SETFANCMND} {str(DEFOUTPRCNTG)} \n\r").encode())
time.sleep(EPPYSLEEPY)
# Prepare threads and launch them
# daemon to make docker exit smoother
thread_main = threading.Thread(target=mainCodeHere, daemon=True)
threads = [thread_main]
if USEINFLUX:
thread_flux = threading.Thread(target=process_temps, daemon=True)
threads.append(thread_flux)
for thr in threads:
thr.start()
# Join both (this will block forever, which is fine for a daemon)
for thr in threads:
thr.join()

View File

@@ -0,0 +1,21 @@
# MD1200BAUD=
SERIALADAPTER=/dev/ttyUSB0
# These are (for me) working values
EPPYSLEEPY=1.5
MDSERIALTIMEOUT=2
TEMPREADINTERVAL=30
TEMP_FACTOR=16
# LOW_FAN_TRSHD=21
# HIGH_FAN_TRSHD=40
# Influxdb config
INFLUX_TOKEN=-3rZgq6EprG9i-gKqBDSFCC3hTS3U49fxGkg==
INFLUX_BUCKET=JBOD
INFLUX_ORG=FUBUKUS
INFLUX_URL=http://192.168.1.101:8086
INFLUX_MEASUREMENT=MD1200
INFLUX_MACHINE_TAG=CHONGUS1200
INFLUX_LOCATION=HQ

View File

@@ -0,0 +1,19 @@
MD1200BAUD=38400
SERIALADAPTER=/dev/ttyUSB0
EPPYSLEEPY=1
MDSERIALTIMEOUT=1
TEMPREADINTERVAL=15
TEMP_FACTOR=19
LOW_FAN_TRSHD=21
HIGH_FAN_TRSHD=40
# Influxdb config
INFLUX_TOKEN===
INFLUX_BUCKET=JBOD
INFLUX_ORG=staging
INFLUX_URL=http://localhost:8086
INFLUX_MEASUREMENT=MD1200
INFLUX_MACHINE_TAG=CHONGUS1200
INFLUX_LOCATION=HQ

View File

@@ -1,29 +0,0 @@
import time
import board
import adafruit_dht
# https://randomnerdtutorials.com/raspberry-pi-dht11-dht22-python/
# Sensor data pin is connected to GPIO 4
sensor = adafruit_dht.DHT22(board.D4)
# Uncomment for DHT11
#sensor = adafruit_dht.DHT11(board.D4)
while True:
try:
# Print the values to the serial port
temperature_c = sensor.temperature
temperature_f = temperature_c * (9 / 5) + 32
humidity = sensor.humidity
print("Temp={0:0.1f}ºC, Temp={1:0.1f}ºF, Humidity={2:0.1f}%".format(temperature_c, temperature_f, humidity))
except RuntimeError as error:
# Errors happen fairly often, DHT's are hard to read, just keep going
print(error.args[0])
time.sleep(2.0)
continue
except Exception as error:
sensor.exit()
raise error
time.sleep(3.0)

View File

@@ -2,7 +2,11 @@ import serial, time
# CONST # CONST
MD1200BAUD = 38400 MD1200BAUD = 38400
SERIALADAPTER = "/dev/ttyUSB0" # In Windows it would be something like COM3 SERIALADAPTER = "/dev/ttyUSB0"
GETTEMP = "_temp_rd"
SETFANPRCNT = "set_speed"
EPPYSLEEPY = 1 # 1 second
#EPPYSLEEPY = 150 # 2,5 minutes
# init # init
MDserial = serial.Serial( MDserial = serial.Serial(
@@ -14,8 +18,81 @@ MDserial = serial.Serial(
timeout=1) timeout=1)
def getTemp(inpMDreturning):
MDict = {}
# Sanitise output
MDsanit = inpMDreturning.splitlines()
#if there is smth do smth
if inpMDreturning:
for line in MDsanit:
if ">" in line or "b'" in line:
continue
matchstm = line[2:6]
match matchstm:
case "BP_1":
MDict["bp1"] = int(line[12:14])
case "BP_2":
MDict["bp2"] = int(line[12:14])
case "SIM0":
MDict["sim0"] = int(line[12:14])
case "SIM1":
MDict["sim1"] = int(line[12:14])
case "EXP0":
MDict["exp0"] = int(line[12:14])
case "EXP1":
MDict["exp1"] = int(line[12:14])
case "AVG":
MDict["avg"] = int(line[12:14])
case _:
continue
return MDict
def setSpeed(inSpeeDict: dict):
bpavrg = 0
# Some safe fan speedvalue
defoutprntg = 27
# default
outfanprcntg = 0
# Decide on fan speeds
LOW_FAN_TRSHD = 21
HIGH_FAN_TRSHD = 40
TEMP_FACTOR = 21
# get backplanbe average
if inSpeeDict["bp1"] and inSpeeDict["bp2"]:
bpavrg = (inSpeeDict["bp1"] + inSpeeDict["bp2"]) /2
outfanprcntg = int((bpavrg / (HIGH_FAN_TRSHD - LOW_FAN_TRSHD)) * TEMP_FACTOR)
# Set fan speed
if outfanprcntg >= 20:
MDserial.write(("set_speed " + str(outfanprcntg) + " \n\r").encode())
print(f"setting {outfanprcntg}%")
return 0
else:
# Set default value
MDserial.write(("set_speed " + str(defoutprntg) + " \n\r").encode())
return 1
# If something goes super wrong
return -1
# Check if UART is used # Check if UART is used
# Not neede because when defining MDserial it gets automatically opened # Not neede because when defining MDserial it gets automatically opened
# Will leave it here anyway
# try: # try:
# MDserial.open() # MDserial.open()
# except serial.serialutil.SerialException: # except serial.serialutil.SerialException:
@@ -23,45 +100,32 @@ MDserial = serial.Serial(
# # MDserial.open() # # MDserial.open()
# print("Port allready opened.\nTry closing it first") # print("Port allready opened.\nTry closing it first")
def getTemp():
print()
def setSpeed():
print()
while True: while True:
# https://stackoverflow.com/questions/52578122/not-able-to-send-the-enter-command-on-pyserial
MDserial.write("_temp_rd\n\r".encode())
time.sleep(1)
MDreturning = MDserial.read_until(" >").decode() MDreturning = MDserial.read_until(" >").decode()
# sleep(50) MDtempDict = getTemp(MDreturning)
MDfanspeed = getTemp(MDreturning) setSpeedrcode = setSpeed(MDtempDict)
setSpeedrcode = setSpeed()
# good
if setSpeedrcode == 0: if setSpeedrcode == 0:
continue # print("Were mint")
time.sleep(EPPYSLEEPY)
# not good
elif setSpeedrcode == 1:
print("Ambigous temperature readings.\nFalling back to safe values.")
time.sleep(EPPYSLEEPY)
# very not good
elif setSpeedrcode == -1: elif setSpeedrcode == -1:
continue
else:
print("o nyo") print("o nyo")
exit() exit()
# very very very not good
else:
print("idk")
exit()
print("closing port")
MDserial.close()
# https://stackoverflow.com/questions/52578122/not-able-to-send-the-enter-command-on-pyserial
# MDserial.write("_temp_rd\n\r".encode())
# getTemp()
# print(MDserial.read_until(" >"))
# fanprct = 23
# MDserial.write(f"set_speed {fanprct}\n\r".encode())
# print("closing port")
# MDserial.close()

View File

@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Netflow to InfluxDB script Description=Adjust MD1200/MD1220 fan speeds
After=multi-user.target network.target network-online.target After=multi-user.target
# Place in /etc/systemd/system/ # Place in /etc/systemd/system/
[Service] [Service]
@@ -10,14 +10,13 @@ Type=simple
Restart=on-failure Restart=on-failure
# EnvironmentFile=/etc/NetFlux/netflow.env # EnvironmentFile=/etc/NetFlux/netflow.env
# User=myuser # User=myuser
WorkingDirectory=/etc/NetFlux/HQ/ WorkingDirectory=/etc/MD1200FAN/
ExecStart=/etc/NetFlux/HQ/venv/bin/python3 /etc/NetFlux/HQ/HQnetflow.py --serve-in-foreground ExecStart=/etc/MD1200FAN/venv/bin/python3 /etc/MD1200FAN/main.py --serve-in-foreground
#StandardInput=tty-force #StandardInput=tty-force
# Log file will be create if it doesn't exist # Log file will be create if it doesn't exist
StandardOutput=append:/var/log/HQNetFlowInflux.log StandardOutput=append:/var/log/MD1200FAN.py.log
StandardError=append:/var/log/HQNetFlowInflux.errlog StandardError=append:/var/log/MD1200FAN.py.errlog
# StandardOutput=syslog # StandardOutput=syslog
# StandardError=syslog # StandardError=syslog

View File

@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Netflow to InfluxDB script Description=Adjust MD1200/MD1220 fan speeds
After=multi-user.target network.target network-online.target After=multi-user.target
# Place in /etc/systemd/system/ # Place in /etc/systemd/system/
[Service] [Service]
@@ -11,12 +11,12 @@ Restart=on-failure
# EnvironmentFile=/etc/NetFlux/netflow.env # EnvironmentFile=/etc/NetFlux/netflow.env
# User=myuser # User=myuser
WorkingDirectory=/dir/to/script/ WorkingDirectory=/dir/to/script/
ExecStart=/dir/to/script'sVENV/venv/bin/python3 /dir/to/script/NetFlowCollect.py --serve-in-foreground ExecStart=/dir/to/script'sVENV/venv/bin/python3 /dir/to/script/main.py --serve-in-foreground
#StandardInput=tty-force #StandardInput=tty-force
# Log file will be create if it doesn't exist # Log file will be create if it doesn't exist
StandardOutput=append:/var/log/NetFlowCollect.log StandardOutput=append:/var/log/MD1200FAN.py.log
StandardError=append:/var/log/NetFlowCollect.errlog StandardError=append:/var/log/MD1200FAN.py.errlog
# StandardOutput=syslog # StandardOutput=syslog
# StandardError=syslog # StandardError=syslog

226
PC_CONTROL_CODE/test.py Normal file
View File

@@ -0,0 +1,226 @@
import serial, time
# CONST
MD1200BAUD = 38400
SERIALADAPTER = "/dev/ttyUSB0"
GETTEMP = "_temp_rd"
SETFANPRCNT = "set_speed"
EPYSLEEPY = 300 # 5 minutes
#EPYSLEEPY = 150 # 2,5 minutes
# init
# MDserial = serial.Serial(
# port=SERIALADAPTER,\
# baudrate=MD1200BAUD,\
# parity=serial.PARITY_NONE,\
# stopbits=serial.STOPBITS_ONE,\
# bytesize=serial.EIGHTBITS,\
# timeout=1)
# After running .encode() on output
MDreturn = "b'\r\nBlueDress.105.001 >_temp_rd\r\n\r\n BP_1[2] = 24c\r\n BP_2[3] = 24c\r\n SIM0[0] = 26c\r\n SIM1[1] = 29c\r\n EXP0[4] = 43c\r\n EXP1[5] = 47c\r\n\r\n AVG = 32c\r\n\r\nBlueDress.105.001 >'"
# before running .encode() on output
TrueMDeturn = MDreturn.encode()
def getTemp(inpMDreturning):
# bp1 = 0
# bp2 = 0
# exp0 = 0
# exp1 = 0
# simm0 = 0
# simm1 = 0
# averr = 0
MDict = {}
# print("1")
# Sanitise output
MDsanit = inpMDreturning.splitlines()
#if there is smth do smth
if inpMDreturning:
# print("2")
# print(MDsanit)
for line in MDsanit:
# print(line)
if ">" in line or "b'" in line:
continue
# print(line[2:])
# if "BP_1" in line[2:]:
# print("yeeee")
matchstm = line[2:6]
# print(matchstm)
match matchstm:
case "BP_1":
# print("BP_1 " + line[12:14])
# bp1 = line[12:14]
MDict["bp1"] = int(line[12:14])
case "BP_2":
# print("BP_2 " + line[12:14])
# bp2 = line[12:14]
MDict["bp2"] = int(line[12:14])
case "SIM0":
# print("SIM0 " + line[12:14])
# simm0 = line[12:14]
MDict["sim0"] = int(line[12:14])
case "SIM1":
# print("SIM1 " + line[12:14])
# simm1 = line[12:14]
MDict["sim1"] = int(line[12:14])
case "EXP0":
# print("EXP0 " + line[12:14])
# exp0 = line[12:14]
MDict["exp0"] = int(line[12:14])
case "EXP1":
# print("EXP1 " + line[12:14])
# exp1 = line[12:14]
MDict["exp1"] = int(line[12:14])
case "AVG":
# print("AVG " + line[12:14])
# averr = line[12:14]
MDict["avg"] = int(line[12:14])
case _:
print("ay men")
# continue
# for thing in line.split(" ")[2:]:
# print(thing)
# print(line[12:14])
# print(MDsanit.split("\n"))
return MDict
# print(MDict)
# for key, thing in getTemp(MDreturn).items():
# print(key, thing)
def setSpeed(inSpeeDict: dict):
print("skibidi")
bpavrg = 0
# Some safe fan speedvalue
defoutprntg = 27
# default
outfanprcntg = 0
# Decide on fan speeds
LOW_FAN_TRSHD = 21
HIGH_FAN_TRSHD = 40
TEMP_FACTOR = 21
# DEBUG
# for key, thing in inSpeeDict.items():
# print(key, thing)
# get backplanbe average
if inSpeeDict["bp1"] and inSpeeDict["bp2"]:
bpavrg = (inSpeeDict["bp1"] + inSpeeDict["bp2"]) /2
outfanprcntg = int((bpavrg / (HIGH_FAN_TRSHD - LOW_FAN_TRSHD)) * TEMP_FACTOR)
print(f"outfanprcntg is {outfanprcntg}")
# Set fan speed
if outfanprcntg >= 20:
# MDserial.write(("set_speed " + str(outfanprcntg) + " \n\r").encode())
return 0
else:
# Set default value
# MDserial.write(("set_speed " + str(defoutprntg) + " \n\r").encode())
return 1
# If something goes super wrong
return -1
while True:
# if True:
# MDserial.write("_temp_rd\n\r".encode())
# CHANGE AFTER TESTING
# MDreturning = MDserial.read_until(" >").decode()
MDreturning = MDreturn
# sleep(50)
MDtempDict = getTemp(MDreturning)
# setSpeed(MDtempDict)
setSpeedrcode = setSpeed(MDtempDict)
# good
if setSpeedrcode == 0:
print("Were mint")
time.sleep(EPYSLEEPY)
# not good
elif setSpeedrcode == 1:
print("Ambigous temperature readings.\nFalling back to safe values.")
time.sleep(EPYSLEEPY)
# very not good
elif setSpeedrcode == -1:
print("o nyo")
exit()
# very very very not good
else:
print("idk")
# def getTemp():
# bp1 = 0
# bp2 = 0
# #exp0 = 0
# #exp1 = 0
# #simm0 = 0
# #simm1 = 0
# getMD1200tempReturn = ""
# MDserial.write(GETTEMP.encode())
# print(MDserial.readlines())
# MDreturning = MDserial.readlines().decode()
# #if there is smth do smth
# if len(MDreturning) >= 1:
# print("skibidi")
# return MDreturning
# try:
# MDserial.open()
# except serial.serialutil.SerialException:
# print("Port allready opened.\nTry closing it first")
# # https://stackoverflow.com/questions/52578122/not-able-to-send-the-enter-command-on-pyserial
# MDserial.write("_temp_rd\n\r".encode())
# print(MDserial.read_until(" >"))
# fanprct = 23
# MDserial.write(f"set_speed {fanprct}\n\r".encode())
# MDserial.close()

View File

@@ -1,8 +1,58 @@
# Python scripts for changing fan speed on MD1200 # MD1200 fan noise reduction
This adjusts fan speed "dynamically" based on average backplane temperature reading. A set of scripts that automagically set fan speed on a MD1200 (probably MD1220 as well) based on internal temperature readings.
## PC ## PC
### Docker
## STM32F103C6T6 In .env file change:
```serial_adapter``` which is a serial port you're using.
On linux it is /dev/ttyUSBx and on windows it is COMx
```wait_time``` is the interval in which script is checking temperature. By default it is 300 seconds, which is 5 minutes.
Then run with ```sudo docker-compose up -d```
To see output run ```sudo docker container logs MD_Fan_Changer```
### Systemd
First create virtual enviroment
```
python3 -m venv venv
```
Then install required modules
```
venv/bin/pip3 install PySerial
```
After that you just need to change a few things
```SERIALADAPTER``` to a port you're using.
On linux it is /dev/ttyUSBx and on windows it is COMx
```EPPYSLEEPY``` is the interval in which script is checking temperature. By default it is 300 seconds, which is 5 minutes.
### Proxmox LXC
You can also run it in LXC container on your Proxmox host. Just follow the [systemd](###systemd) instructions.
Here you will also need to add ```/dev/ttyUSBx``` to your LXC container. You do it under Resources -> Add -> Device Passthrough -> ```/dev/ttyUSBx``` as Device Path.
## STM32F103C6T6
I think it needs a MAX2323 between MD1200.
Will look into that.
## Arduino Nano
Same here
### FAQ
dc: yuruc3

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

View File

@@ -8,10 +8,10 @@
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[env:genericSTM32F103C6] [env:bluepill_f103c6]
platform = ststm32 platform = ststm32
board = genericSTM32F103C6 board = bluepill_f103c6
framework = arduino framework = stm32cube
lib_deps = adafruit/DHT sensor library@^1.4.6 lib_deps = adafruit/DHT sensor library@^1.4.6
debug_tool = stlink debug_tool = stlink
upload_protocol = stlink upload_protocol = stlink

130
STM32_CONTROL/src/main.cpp Normal file
View File

@@ -0,0 +1,130 @@
#include <stm32f1xx_hal.h>
UART_HandleTypeDef huart1;
// --- Function prototypes ---
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
// --- Optional RX buffer for interrupt ---
uint8_t rx_byte;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
char msg[] = "UART started at 38400 baud\r\n";
// Send a startup message
HAL_UART_Transmit(&huart1, (uint8_t*)msg, sizeof(msg)-1, HAL_MAX_DELAY);
// Start interrupt-based receive of one byte
HAL_UART_Receive_IT(&huart1, &rx_byte, 1);
while (1)
{
// Send a heartbeat message every second
char heartbeat[] = "Ping\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)heartbeat, sizeof(heartbeat)-1, HAL_MAX_DELAY);
HAL_Delay(1000);
}
}
// -----------------------------------------------------------------------------
// Error handler
// -----------------------------------------------------------------------------
void Error_Handler(void)
{
while (1) { }
}
// -----------------------------------------------------------------------------
// UART1 initialization (38400 baud)
// -----------------------------------------------------------------------------
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 38400;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
// -----------------------------------------------------------------------------
// GPIO for UART1 pins (PA9 TX, PA10 RX)
// -----------------------------------------------------------------------------
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// PA9 -> USART1_TX (Alternate Function Push-Pull)
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// PA10 -> USART1_RX (Input)
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// -----------------------------------------------------------------------------
// Interrupt callback for UART receive
// -----------------------------------------------------------------------------
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) {
// Echo received byte back
HAL_UART_Transmit(&huart1, &rx_byte, 1, HAL_MAX_DELAY);
// Re-enable reception of next byte
HAL_UART_Receive_IT(&huart1, &rx_byte, 1);
}
}
// -----------------------------------------------------------------------------
// Basic system clock (HSE not required for 38400 UART)
// -----------------------------------------------------------------------------
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_PCLK1 |
RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}

View File

@@ -1,22 +0,0 @@
#include <Arduino.h>
// UART2 (PA3 = RX, PA2 = TX)
HardwareSerial mdSerial(PA10, PA9);
// HardwareSerial sigma(PA9, )
void setup() {
mdSerial.begin(38400);
}
void loop() {
mdSerial.write("skibidi");
delay(5000); // wait before next loop
}

View File

@@ -0,0 +1,24 @@
// #include <Arduino.h>
#include <stm32f1xx_hal.h>
#define PIN_SERIAL3_RX PB11
#define PIN_SERIAL3_TX PB10
// UART2 (PA3 = RX, PA2 = TX)
HardwareSerial Serial1(PA10, PA9);
// HardwareSerial sigma(PA9, )
void setup() {
Serial1.begin(38400);
}
void loop() {
Serial1.write("skibidi");
delay(5000); // wait before next loop
}

File diff suppressed because one or more lines are too long

232
dashboard.json Normal file
View File

@@ -0,0 +1,232 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 38,
"links": [],
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 2,
"panels": [],
"title": "MD1200-1",
"type": "row"
},
{
"datasource": {
"type": "influxdb",
"uid": "bdp80jf4cy328f"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
},
"unit": "celsius"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Average {LOCATION=\"HQ\", MACHINE=\"CHONGUS1200\"}"
},
"properties": [
{
"id": "displayName",
"value": "Average"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Backplane1 {LOCATION=\"HQ\", MACHINE=\"CHONGUS1200\"}"
},
"properties": [
{
"id": "displayName",
"value": "Backplane 1"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Backplane2 {LOCATION=\"HQ\", MACHINE=\"CHONGUS1200\"}"
},
"properties": [
{
"id": "displayName",
"value": "Backplane 2"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Expander0 {LOCATION=\"HQ\", MACHINE=\"CHONGUS1200\"}"
},
"properties": [
{
"id": "displayName",
"value": "Expander 0"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Expander1 {LOCATION=\"HQ\", MACHINE=\"CHONGUS1200\"}"
},
"properties": [
{
"id": "displayName",
"value": "Expander 1"
}
]
},
{
"matcher": {
"id": "byName",
"options": "SASIntModule0 {LOCATION=\"HQ\", MACHINE=\"CHONGUS1200\"}"
},
"properties": [
{
"id": "displayName",
"value": "SAS interface module 0"
}
]
},
{
"matcher": {
"id": "byName",
"options": "SASIntModule1 {LOCATION=\"HQ\", MACHINE=\"CHONGUS1200\"}"
},
"properties": [
{
"id": "displayName",
"value": "SAS interface module 1"
}
]
}
]
},
"gridPos": {
"h": 10,
"w": 24,
"x": 0,
"y": 1
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.0.2",
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "bdp80jf4cy328f"
},
"query": "from(bucket: \"JBOD\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"MD1200-script\")\n |> filter(fn: (r) => r[\"LOCATION\"] == \"HQ\")\n |> filter(fn: (r) => r[\"MACHINE\"] == \"CHONGUS1200\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")",
"refId": "A"
}
],
"title": "Temperatures",
"type": "timeseries"
}
],
"preload": false,
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-3h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "JBODs",
"uid": "1ee6cf76-943c-45e0-8d6f-c92db9691459",
"version": 3
}

18
docker-compose.yml Normal file
View File

@@ -0,0 +1,18 @@
---
services:
mdfanchanger:
container_name: MD_Fan_Changer
image: yuruc3/md1200_fan_controll:v1.2.1
environment:
# - MD1200BAUD=
- SERIALADAPTER=/dev/ttyUSB0
- TEMP_FACTOR=17
- EPPYSLEEPY=0.25
- MDSERIALTIMEOUT=0.75
# - LOW_FAN_TRSHD=
# - HIGH_FAN_TRSHD=
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
restart: unless-stopped
privileged: false