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
Загрузка...
Поиск...
Не найдено
thingspeak_client.cpp
См. документацию.
1#include <WiFiClient.h>
2#include "thingspeak_client.h"
3#include "modbus_sensor.h"
4#include "wifi_manager.h"
5#include <ThingSpeak.h>
6#include "jxct_device_info.h"
7#include "jxct_config_vars.h"
8#include "jxct_format_utils.h"
9#include "logger.h"
10#include <NTPClient.h>
11#include <ctype.h>
12extern NTPClient* timeClient;
13
14// URL для отправки данных в ThingSpeak
15const char* THINGSPEAK_API_URL = "https://api.thingspeak.com/update";
16
17static unsigned long lastTsPublish = 0;
18static int consecutiveFailCount = 0; // счётчик подряд неудач
19
20// Утилита для обрезки пробелов в начале/конце строки C
21static void trim(char* s)
22{
23 if (!s) return;
24 // Trim leading
25 char* p = s;
26 while (*p && isspace((unsigned char)*p)) ++p;
27 if (p != s) memmove(s, p, strlen(p) + 1);
28
29 // Trim trailing
30 size_t len = strlen(s);
31 while (len > 0 && isspace((unsigned char)s[len - 1]))
32 s[--len] = '\0';
33}
34
35// ✅ Заменяем String на статические буферы
36static char thingSpeakLastPublishBuffer[32] = "0";
37static char thingSpeakLastErrorBuffer[64] = "";
38
39// Геттеры для совместимости с внешним кодом
45{
47}
48
49void setupThingSpeak(WiFiClient& client)
50{
51 ThingSpeak.begin(client);
52}
53
55{
56 // Проверки
57 if (!config.flags.thingSpeakEnabled) return false;
58 if (!wifiConnected) return false;
59 if (!sensorData.valid) return false;
60
61 unsigned long now = millis();
62 if (now - lastTsPublish < config.thingSpeakInterval) // too frequent
63 return false;
64
65 char apiKeyBuf[25];
66 char channelBuf[16];
67 strlcpy(apiKeyBuf, config.thingSpeakApiKey, sizeof(apiKeyBuf));
68 strlcpy(channelBuf, config.thingSpeakChannelId, sizeof(channelBuf));
69 trim(apiKeyBuf);
70 trim(channelBuf);
71
72 unsigned long channelId = strtoul(channelBuf, nullptr, 10);
73
74 // Проверяем корректность ID и API ключа - если неверные, молча пропускаем
75 if (channelId == 0 || strlen(apiKeyBuf) < 16)
76 {
77 // Не логируем ошибку каждый раз, просто пропускаем отправку
78 if (strlen(thingSpeakLastErrorBuffer) == 0) // логируем только первый раз
79 {
80 logWarn("ThingSpeak: настройки не заданы (Channel ID: %s, API Key: %d символов)",
81 channelBuf, strlen(apiKeyBuf));
82 strlcpy(thingSpeakLastErrorBuffer, "Настройки не заданы", sizeof(thingSpeakLastErrorBuffer));
83 }
84 return false;
85 }
86
87 // Отправка данных
88 ThingSpeak.setField(1, format_temperature(sensorData.temperature).c_str());
89 ThingSpeak.setField(2, format_moisture(sensorData.humidity).c_str());
90 ThingSpeak.setField(3, format_ec(sensorData.ec).c_str());
91 ThingSpeak.setField(4, format_ph(sensorData.ph).c_str());
92 ThingSpeak.setField(5, format_npk(sensorData.nitrogen).c_str());
93 ThingSpeak.setField(6, format_npk(sensorData.phosphorus).c_str());
94 ThingSpeak.setField(7, format_npk(sensorData.potassium).c_str());
95
96 logData("Отправка в ThingSpeak: T=%.1f°C, H=%.1f%%, PH=%.2f",
97 sensorData.temperature, sensorData.humidity, sensorData.ph);
98
99 int res = ThingSpeak.writeFields(channelId, apiKeyBuf);
100
101 if (res == 200)
102 {
103 logSuccess("ThingSpeak: данные отправлены");
104 lastTsPublish = millis();
106 thingSpeakLastErrorBuffer[0] = '\0'; // Очистка ошибки
107 consecutiveFailCount = 0; // обнуляем при успехе
108 return true;
109 }
110 else if (res == -301)
111 {
112 logWarn("ThingSpeak: таймаут (-301), повторим позже");
113 strlcpy(thingSpeakLastErrorBuffer, "Timeout -301", sizeof(thingSpeakLastErrorBuffer));
114 }
115 else if (res == -401)
116 {
117 logDebug("ThingSpeak: превышен лимит публикаций");
118 strlcpy(thingSpeakLastErrorBuffer, "Превышен лимит публикаций (15 сек)", sizeof(thingSpeakLastErrorBuffer));
119 }
120 else if (res == -302)
121 {
122 logError("ThingSpeak: неверный API ключ");
123 strlcpy(thingSpeakLastErrorBuffer, "Неверный API ключ", sizeof(thingSpeakLastErrorBuffer));
124 }
125 else if (res == -304)
126 {
127 logError("ThingSpeak: неверный Channel ID");
128 strlcpy(thingSpeakLastErrorBuffer, "Неверный Channel ID", sizeof(thingSpeakLastErrorBuffer));
129 }
130 else if (res == 0)
131 {
132 logError("ThingSpeak: ошибка подключения");
133 strlcpy(thingSpeakLastErrorBuffer, "Ошибка подключения", sizeof(thingSpeakLastErrorBuffer));
134 }
135 else if (res == 400)
136 {
137 logError("ThingSpeak: HTTP 400 – неверный запрос (проверьте API Key/Channel)");
138 strlcpy(thingSpeakLastErrorBuffer, "HTTP 400 – неверный запрос (API/Channel)", sizeof(thingSpeakLastErrorBuffer));
139 }
140 else
141 {
142 logError("ThingSpeak: ошибка %d", res);
143 snprintf(thingSpeakLastErrorBuffer, sizeof(thingSpeakLastErrorBuffer), "Ошибка %d", res);
144 }
145
147
148 // Если слишком много ошибок подряд, временно отключаем на 1 час
149 if (consecutiveFailCount >= 10)
150 {
151 logWarn("ThingSpeak: %d ошибок подряд, отключаем на 1 час", consecutiveFailCount);
152 lastTsPublish = millis(); // устанавливаем время последней попытки
153 consecutiveFailCount = 0; // сбрасываем счётчик
154 strlcpy(thingSpeakLastErrorBuffer, "Отключён на 1 час (много ошибок)", sizeof(thingSpeakLastErrorBuffer));
155 }
156
157 return false;
158}
Config config
Определения config.cpp:34
std::string format_ec(float value)
Определения jxct_format_utils.cpp:18
std::string format_moisture(float value)
Определения jxct_format_utils.cpp:4
std::string format_ph(float value)
Определения jxct_format_utils.cpp:25
std::string format_temperature(float value)
Определения jxct_format_utils.cpp:11
std::string format_npk(float value)
Определения jxct_format_utils.cpp:32
void logDebug(const char *format,...)
Определения logger.cpp:112
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 logData(const char *format,...)
Определения logger.cpp:196
Система логгирования с красивым форматированием
NTPClient * timeClient
Определения main.cpp:43
SensorData sensorData
Определения modbus_sensor.cpp:18
static unsigned long lastTsPublish
Определения thingspeak_client.cpp:17
static void trim(char *s)
Определения thingspeak_client.cpp:21
static char thingSpeakLastErrorBuffer[64]
Определения thingspeak_client.cpp:37
bool sendDataToThingSpeak()
Определения thingspeak_client.cpp:54
static char thingSpeakLastPublishBuffer[32]
Определения thingspeak_client.cpp:36
const char * getThingSpeakLastPublish()
Определения thingspeak_client.cpp:40
const char * getThingSpeakLastError()
Определения thingspeak_client.cpp:44
static int consecutiveFailCount
Определения thingspeak_client.cpp:18
void setupThingSpeak(WiFiClient &client)
Определения thingspeak_client.cpp:49
const char * THINGSPEAK_API_URL
Определения thingspeak_client.cpp:15
bool wifiConnected
Определения wifi_manager.cpp:25