JXCT Soil Sensor 7-in-1 v3.4.9 (June 2025)
Professional IoT soil monitoring system with ESP32, Modbus RTU, MQTT, and advanced compensation algorithms
Загрузка...
Поиск...
Не найдено
calibration_manager.cpp
См. документацию.
2#include "logger.h"
4
6{
7 static bool _initialized = false;
8
9 const char* profileToFilename(SoilProfile /*profile*/)
10 {
11 return "/calibration/custom.csv"; // единый файл
12 }
13
14 bool init()
15 {
16 if (_initialized) return true;
17
18 if (!LittleFS.begin(true))
19 {
20 logError("LittleFS не инициализирован");
21 return false;
22 }
23
24 // Создаем каталог /calibration при необходимости
25 if (!LittleFS.exists("/calibration"))
26 {
27 LittleFS.mkdir("/calibration");
28 }
29
30 _initialized = true;
31 logSuccess("LittleFS инициализирован, доступен каталог /calibration");
32 return true;
33 }
34
35 bool saveCsv(SoilProfile profile, Stream& fileStream)
36 {
37 if (!init()) return false;
38 const char* path = profileToFilename(profile);
39
40 File f = LittleFS.open(path, "w");
41 if (!f)
42 {
43 logError("Не удалось открыть файл %s для записи", path);
44 return false;
45 }
46
47 while (fileStream.available())
48 {
49 uint8_t b = fileStream.read();
50 f.write(b);
51 }
52
53 f.close();
54 logSuccess("Калибровочная таблица %s сохранена (%d байт)", path, f.size());
55 return true;
56 }
57
58 bool loadTable(SoilProfile profile, CalibrationEntry* outBuffer, size_t maxEntries, size_t& outCount)
59 {
60 outCount = 0;
61 if (!init()) return false;
62 const char* path = profileToFilename(profile);
63 File f = LittleFS.open(path, "r");
64 if (!f)
65 {
66 logWarn("Нет калибровочной таблицы %s", path);
67 return false;
68 }
69
70 String line;
71 while (f.available() && outCount < maxEntries)
72 {
73 line = f.readStringUntil('\n');
74 line.trim();
75 if (line.length() == 0) continue;
76 if (line[0] == '#') continue; // комментарий
77
78 // Пропускаем строку-заголовок (если обнаружены буквы)
79 if (!isDigit(line[0]) && line[0] != '-') continue;
80
81 int comma = line.indexOf(',');
82 if (comma < 0) continue;
83
84 float raw = line.substring(0, comma).toFloat();
85 float corr = line.substring(comma + 1).toFloat();
86 outBuffer[outCount++] = {raw, corr};
87 }
88
89 f.close();
90 logInfo("Загружено %d записей из %s", outCount, path);
91 return outCount > 0;
92 }
93
94 bool hasTable(SoilProfile profile)
95 {
96 if (!init()) return false;
97 return LittleFS.exists(profileToFilename(profile));
98 }
99
101 {
102 if (!init()) return false;
103 const char* path = profileToFilename(profile);
104 if (LittleFS.exists(path))
105 {
106 return LittleFS.remove(path);
107 }
108 return false;
109 }
110
111 float applyCalibration(float rawValue, SoilProfile profile)
112 {
113 // Если калибровочная таблица не загружена, возвращаем исходное значение
114 if (!hasTable(profile)) {
115 return rawValue;
116 }
117
118 // Загружаем калибровочную таблицу
119 constexpr size_t MAX_ENTRIES = 100;
120 CalibrationEntry entries[MAX_ENTRIES];
121 size_t entryCount;
122
123 if (!loadTable(profile, entries, MAX_ENTRIES, entryCount) || entryCount == 0) {
124 return rawValue;
125 }
126
127 // Ищем ближайшие значения для интерполяции
128 float lowerRaw = entries[0].raw;
129 float lowerCorr = entries[0].corrected;
130 float upperRaw = entries[entryCount - 1].raw;
131 float upperCorr = entries[entryCount - 1].corrected;
132
133 // Ищем точное совпадение или ближайшие значения
134 for (size_t i = 0; i < entryCount; i++) {
135 if (entries[i].raw == rawValue) {
136 // Точное совпадение - применяем коэффициент
137 return rawValue * entries[i].corrected;
138 }
139
140 if (entries[i].raw < rawValue) {
141 lowerRaw = entries[i].raw;
142 lowerCorr = entries[i].corrected;
143 } else if (entries[i].raw > rawValue) {
144 upperRaw = entries[i].raw;
145 upperCorr = entries[i].corrected;
146 break;
147 }
148 }
149
150 // Линейная интерполяция между ближайшими значениями
151 if (upperRaw > lowerRaw) {
152 float ratio = (rawValue - lowerRaw) / (upperRaw - lowerRaw);
153 float interpolatedCoeff = lowerCorr + ratio * (upperCorr - lowerCorr);
154 return rawValue * interpolatedCoeff;
155 } else {
156 // Если нет интервала, используем ближайший коэффициент
157 return rawValue * lowerCorr;
158 }
159 }
160} // namespace CalibrationManager
void logWarn(const char *format,...)
Определения logger.cpp:78
void logSuccess(const char *format,...)
Определения logger.cpp:129
void logError(const char *format,...)
Определения logger.cpp:61
void logInfo(const char *format,...)
Определения logger.cpp:95
Система логгирования с красивым форматированием
bool deleteTable(SoilProfile profile)
Определения calibration_manager.cpp:100
bool loadTable(SoilProfile profile, CalibrationEntry *outBuffer, size_t maxEntries, size_t &outCount)
Определения calibration_manager.cpp:58
bool hasTable(SoilProfile profile)
Определения calibration_manager.cpp:94
const char * profileToFilename(SoilProfile)
Определения calibration_manager.cpp:9
static bool _initialized
Определения calibration_manager.cpp:7
float applyCalibration(float rawValue, SoilProfile profile)
Определения calibration_manager.cpp:111
bool saveCsv(SoilProfile profile, Stream &fileStream)
Определения calibration_manager.cpp:35
bool init()
Определения calibration_manager.cpp:14
Алгоритмы коррекции показаний датчиков
SoilProfile
Определения sensor_compensation.h:14
Определения calibration_manager.h:10
float raw
Определения calibration_manager.h:11
float corrected
Определения calibration_manager.h:12