396 lines
14 KiB
C
396 lines
14 KiB
C
|
|
/* sensor.c - 485 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD><C9BC><EFBFBD><EFBFBD>ɼ<EFBFBD><C9BC>ն˺<D5B6><CBBA><EFBFBD>ģ<EFBFBD>飩
|
|||
|
|
*
|
|||
|
|
* Ӳ<EFBFBD><EFBFBD>: USART2 <EFBFBD><EFBFBD> 485 <EFBFBD><EFBFBD><EFBFBD>ߣ<EFBFBD>PA2(TX), PA3(RX)<EFBFBD><EFBFBD>9600-8N1<EFBFBD><EFBFBD>DMA+IDLE <EFBFBD><EFBFBD>֡<EFBFBD><EFBFBD><EFBFBD>ա<EFBFBD>
|
|||
|
|
* Э<EFBFBD><EFBFBD>: Modbus RTU <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ּĴ<EFBFBD><EFBFBD><EFBFBD>(0x03)<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ/<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> sensor.h <EFBFBD><EFBFBD>ͷע<EFBFBD>͡<EFBFBD>
|
|||
|
|
* ҵ<EFBFBD><EFBFBD>: ÿ 10s <EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD>ѯһ<EFBFBD>鴫<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˢ<EFBFBD>»<EFBFBD><EFBFBD>棨<EFBFBD><EFBFBD> OLED ʵʱ<EFBFBD><EFBFBD>ʾ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
* <EFBFBD>ϴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ê<EFBFBD><EFBFBD><EFBFBD><EFBFBD>"OTA <20><>ȷ<EFBFBD><C8B7>"<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD>̣<EFBFBD>+5s/+30s/+60s <EFBFBD><EFBFBD>һ<EFBFBD>Σ<EFBFBD>֮<EFBFBD><EFBFBD>ÿ 5 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>Ρ<EFBFBD>
|
|||
|
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include "sensor.h"
|
|||
|
|
#include "adc.h"
|
|||
|
|
#include "uart.h"
|
|||
|
|
#include "log.h"
|
|||
|
|
#include "network.h"
|
|||
|
|
#include "FreeRTOS.h"
|
|||
|
|
#include "task.h"
|
|||
|
|
#include <string.h>
|
|||
|
|
#include <stdio.h>
|
|||
|
|
#include <math.h>
|
|||
|
|
|
|||
|
|
sensor_data_t g_sensor;
|
|||
|
|
uint16_t g_sensor_online = 0;
|
|||
|
|
|
|||
|
|
/*==== Modbus <20>ײ<EFBFBD> ====*/
|
|||
|
|
|
|||
|
|
static uint16_t crc16_modbus(const uint8_t *data, uint16_t len)
|
|||
|
|
{
|
|||
|
|
uint16_t crc = 0xFFFF;
|
|||
|
|
for (uint16_t i = 0; i < len; i++) {
|
|||
|
|
crc ^= data[i];
|
|||
|
|
for (uint8_t j = 0; j < 8; j++) {
|
|||
|
|
crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : (crc >> 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return crc;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* <20><> 8 <20>ֽڲ<D6BD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD>һ֡Ӧ<D6A1><D3A6><EFBFBD><EFBFBD>IDLE <20><>֡<EFBFBD><D6A1><EFBFBD><EFBFBD>
|
|||
|
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD>֡<EFBFBD><EFBFBD>(>0)<EFBFBD><EFBFBD>0=<EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD>֡<EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Լ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȥ<EFBFBD><EFBFBD> 485 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>֡<EFBFBD><EFBFBD> */
|
|||
|
|
static int sensor_query(const uint8_t *req, uint8_t *resp, uint16_t max_len, uint32_t timeout_ms)
|
|||
|
|
{
|
|||
|
|
USART2_FlushRxBuf(); /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӳ<EFBFBD><D3B2><EFBFBD><EFBFBD><EFBFBD>ճ<EFBFBD><D5B3><EFBFBD> */
|
|||
|
|
HAL_UART_Transmit(&huart2, (uint8_t *)req, 8, 100);
|
|||
|
|
|
|||
|
|
uint32_t t0 = HAL_GetTick();
|
|||
|
|
while ((HAL_GetTick() - t0) < timeout_ms) {
|
|||
|
|
int n = USART2_PollFrame(resp, max_len);
|
|||
|
|
if (n > 0) {
|
|||
|
|
if (n == 8 && memcmp(resp, req, 8) == 0) continue; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6> */
|
|||
|
|
return n;
|
|||
|
|
}
|
|||
|
|
vTaskDelay(2);
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void dump_frame(const char *tag, const uint8_t *buf, int len)
|
|||
|
|
{
|
|||
|
|
char hex[3 * 32 + 1] = {0};
|
|||
|
|
int n = len > 32 ? 32 : len;
|
|||
|
|
for (int i = 0; i < n; i++) snprintf(hex + i * 3, 4, "%02X ", buf[i]);
|
|||
|
|
log_info("> SENSOR: %s <20><> %d <20>ֽ<EFBFBD>: %s", tag, len, hex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* <20><><EFBFBD><EFBFBD><EFBFBD>ּĴ<D6BC><C4B4><EFBFBD><EFBFBD><EFBFBD>addr <20>ӻ<EFBFBD><D3BB><EFBFBD>ַ<EFBFBD><D6B7>start <20><>ʼ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD>num <20>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|||
|
|
* <EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0 <EFBFBD><EFBFBD> resp Ϊ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD>֡/<EFBFBD><EFBFBD><EFBFBD><EFBFBD> resp_len<EFBFBD><EFBFBD>ʧ<EFBFBD>ܷ<EFBFBD><EFBFBD>ط<EFBFBD> 0<EFBFBD><EFBFBD>1=<EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD> 2=CRC<EFBFBD><EFBFBD> 3=֡<EFBFBD>쳣<EFBFBD><EFBFBD> */
|
|||
|
|
static int modbus_read(uint8_t addr, uint16_t start, uint8_t num,
|
|||
|
|
uint8_t *resp, uint16_t *resp_len, const char *tag)
|
|||
|
|
{
|
|||
|
|
uint8_t req[8] = { addr, 0x03,
|
|||
|
|
(uint8_t)(start >> 8), (uint8_t)(start & 0xFF),
|
|||
|
|
0x00, num, 0x00, 0x00 };
|
|||
|
|
uint16_t crc = crc16_modbus(req, 6);
|
|||
|
|
req[6] = (uint8_t)(crc & 0xFF);
|
|||
|
|
req[7] = (uint8_t)(crc >> 8);
|
|||
|
|
|
|||
|
|
uint16_t expect = (uint16_t)(5 + num * 2);
|
|||
|
|
int n = sensor_query(req, resp, 40, 400);
|
|||
|
|
if (n <= 0) {
|
|||
|
|
log_warn("> SENSOR: %s <20><>Ӧ<EFBFBD><D3A6>", tag);
|
|||
|
|
return 1;
|
|||
|
|
}
|
|||
|
|
dump_frame(tag, resp, n);
|
|||
|
|
if (n != (int)expect || resp[1] != 0x03) {
|
|||
|
|
log_warn("> SENSOR: %s ֡<>쳣(<28><>%d <20><><EFBFBD><EFBFBD>%d)", tag, n, (int)expect);
|
|||
|
|
return 3;
|
|||
|
|
}
|
|||
|
|
uint16_t calc = crc16_modbus(resp, (uint16_t)(n - 2));
|
|||
|
|
uint16_t recv = (uint16_t)resp[n - 1] << 8 | resp[n - 2];
|
|||
|
|
if (calc != recv) {
|
|||
|
|
log_warn("> SENSOR: %s CRC<52><43>(calc=%04X recv=%04X)", tag, calc, recv);
|
|||
|
|
return 2;
|
|||
|
|
}
|
|||
|
|
*resp_len = (uint16_t)n;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* <20>з<EFBFBD><D0B7><EFBFBD><EFBFBD>¶Ƚ<C2B6><C8BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֽ<EFBFBD> 0xFF Ϊ<><CEAA><EFBFBD><EFBFBD><EFBFBD>ο<EFBFBD><CEBF><EFBFBD><EFBFBD>̰<EFBFBD><CCB0><EFBFBD><EFBFBD>봦<EFBFBD><EBB4A6><EFBFBD><EFBFBD> */
|
|||
|
|
static float parse_temp(uint8_t hi, uint8_t lo)
|
|||
|
|
{
|
|||
|
|
if (hi == 0xFF) {
|
|||
|
|
lo = (uint8_t)(~lo + 1);
|
|||
|
|
return -(float)(lo / 10.0f);
|
|||
|
|
}
|
|||
|
|
return (float)((hi * 256 + lo) / 10.0f);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*==== <20>ܽ<EFBFBD><DCBD><EFBFBD> DO59 ר<>ã<EFBFBD>UNESCO <20><>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD>Ͷ<EFBFBD>%<25><><EFBFBD><EFBFBD> mg/L<><4C><EFBFBD>ζ<EFBFBD>0<EFBFBD><30>====*/
|
|||
|
|
static float do59_mg_l(float per, float temp, float pressure)
|
|||
|
|
{
|
|||
|
|
float T = 273.15f + temp;
|
|||
|
|
float lnX1 = -173.4292f + 249.6339f * (100.0f / T) + 143.3483f * logf(T / 100.0f) + (-21.8492f) * (T / 100.0f);
|
|||
|
|
float X1 = expf(lnX1);
|
|||
|
|
float U = powf(10.0f, 8.10765f - 1750.286f / (235.0f + temp));
|
|||
|
|
float Phmg = pressure * 760.0f / 101.325f;
|
|||
|
|
float X2 = (Phmg - U) / (760.0f - U);
|
|||
|
|
return per * X1 * X2 * 1.4276f / 100.0f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* DO59 <20><><EFBFBD>Σ<D7B6><CEA3><EFBFBD>"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"(0x2500)<29><><EFBFBD>´<EFBFBD><C2B4><EFBFBD>"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"(0x2600 <20>¶<EFBFBD>+<2B><><EFBFBD>Ͷ<EFBFBD>)<29><>
|
|||
|
|
* <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 0=<EFBFBD><EFBFBD><EFBFBD>ζ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD>1=<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD>δ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD>/У<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
|
static int do59_read(void)
|
|||
|
|
{
|
|||
|
|
static uint8_t started = 0;
|
|||
|
|
uint8_t resp[16];
|
|||
|
|
uint16_t rlen = 0;
|
|||
|
|
int rc;
|
|||
|
|
|
|||
|
|
if (!started) {
|
|||
|
|
rc = modbus_read(0xFF, 0x2500, 1, resp, &rlen, "<EFBFBD>ܽ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
|
|||
|
|
if (rc == 0) {
|
|||
|
|
started = 1;
|
|||
|
|
log_info("> SENSOR: <20>ܽ<EFBFBD><DCBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
|
|||
|
|
}
|
|||
|
|
return 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
rc = modbus_read(0xFF, 0x2600, 4, resp, &rlen, "<EFBFBD>ܽ<EFBFBD><EFBFBD><EFBFBD>");
|
|||
|
|
if (rc != 0) {
|
|||
|
|
started = 0; /* <20>´<EFBFBD><C2B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
|
return 1;
|
|||
|
|
}
|
|||
|
|
float temp, sat;
|
|||
|
|
memcpy(&temp, &resp[3], 4); /* <20>¶<EFBFBD> float С<><D0A1> */
|
|||
|
|
memcpy(&sat, &resp[7], 4); /* <20><><EFBFBD>Ͷ<EFBFBD>% float С<><D0A1> */
|
|||
|
|
float press = ((g_sensor_online & (1u << 1)) && g_sensor.air_press > 0.0f)
|
|||
|
|
? g_sensor.air_press : 101.325f; /* <20><><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD>⣬<EFBFBD><E2A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><D7BC><EFBFBD><EFBFBD>ѹ */
|
|||
|
|
g_sensor.water_t = temp;
|
|||
|
|
g_sensor.do_sat = sat;
|
|||
|
|
g_sensor.do_mgl = do59_mg_l(sat, temp, press);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*==== <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ + <20><><EFBFBD>루0=<3D>ɹ<EFBFBD><C9B9><EFBFBD>====*/
|
|||
|
|
|
|||
|
|
static int read_phec(void) /* 0x01 ˮ<><CBAE>PH-EC */
|
|||
|
|
{
|
|||
|
|
uint8_t r[16]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x01, 0x0000, 2, r, &rl, "PH-EC") != 0) return 1;
|
|||
|
|
g_sensor.ph = (float)(r[3] * 256 + r[4]) / 10.0f;
|
|||
|
|
g_sensor.ec = (float)(r[5] * 256 + r[6]) / 10.0f;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int read_airpress(void) /* 0x02 <20><><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9>ʪ<EFBFBD><CAAA>(<28><><EFBFBD>ӿ<EFBFBD>)<29><><EFBFBD><EFBFBD> 12 <20><><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD> */
|
|||
|
|
{
|
|||
|
|
uint8_t r[40]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x02, 0x0000, 12, r, &rl, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѹ") != 0) return 1;
|
|||
|
|
g_sensor.air_rh = (float)(r[3] * 256 + r[4]) / 10.0f;
|
|||
|
|
g_sensor.air_t = parse_temp(r[5], r[6]);
|
|||
|
|
/* <20><>ѹ<EFBFBD><D1B9><EFBFBD>ֽ<EFBFBD> 24~26<32><36>3 <20>ֽڴ<D6BD><DAB4>ˣ<EFBFBD><CBA3><EFBFBD>λ 0.01mbar<EFBFBD><EFBFBD> */
|
|||
|
|
uint32_t ph = ((uint32_t)r[24] << 16) | ((uint32_t)r[25] << 8) | r[26];
|
|||
|
|
g_sensor.air_press = ph / 100.0f / 10.0f; /* -> kPa */
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int read_byx(void) /* 0x06 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҷ<EFBFBD>䣬<EFBFBD><E4A3AC> 9 <20><><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD> */
|
|||
|
|
{
|
|||
|
|
uint8_t r[32]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x06, 0x0000, 9, r, &rl, "<EFBFBD><EFBFBD>Ҷ<EFBFBD><EFBFBD>") != 0) return 1;
|
|||
|
|
g_sensor.by_rh = (float)(r[3] * 256 + r[4]) / 10.0f;
|
|||
|
|
g_sensor.by_t = parse_temp(r[5], r[6]);
|
|||
|
|
g_sensor.by_co2 = (uint16_t)(r[13] * 256 + r[14]);
|
|||
|
|
g_sensor.by_hv = (uint16_t)(r[19] * 256 + r[20]);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int read_leaf(void) /* 0x07 Ҷ<><D2B6><EFBFBD><EFBFBD>ʪ<EFBFBD><CAAA> */
|
|||
|
|
{
|
|||
|
|
uint8_t r[16]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x07, 0x0020, 2, r, &rl, "Ҷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʪ") != 0) return 1;
|
|||
|
|
g_sensor.leaf_rh = (float)(r[3] * 256 + r[4]) / 10.0f;
|
|||
|
|
g_sensor.leaf_t = parse_temp(r[5], r[6]);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int read_soilth(void) /* 0x08 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʪ<EFBFBD><CAAA> */
|
|||
|
|
{
|
|||
|
|
uint8_t r[16]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x08, 0x0012, 2, r, &rl, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʪ") != 0) return 1;
|
|||
|
|
g_sensor.soil_rh = (float)(r[3] * 256 + r[4]) / 10.0f;
|
|||
|
|
g_sensor.soil_t = parse_temp(r[5], r[6]);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int read_npk(void) /* 0x09 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
|
{
|
|||
|
|
uint8_t r[16]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x09, 0x001E, 3, r, &rl, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>") != 0) return 1;
|
|||
|
|
g_sensor.soil_n = (float)(r[3] * 256 + r[4]);
|
|||
|
|
g_sensor.soil_p = (float)(r[5] * 256 + r[6]);
|
|||
|
|
g_sensor.soil_k = (float)(r[7] * 256 + r[8]);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int read_soilph(void) /* 0x0A <20><><EFBFBD><EFBFBD>PH */
|
|||
|
|
{
|
|||
|
|
uint8_t r[16]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x0A, 0x0006, 1, r, &rl, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>PH") != 0) return 1;
|
|||
|
|
g_sensor.soil_ph = (float)(r[3] * 256 + r[4]) / 100.0f;
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int read_soilec(void) /* 0x0B <20><><EFBFBD><EFBFBD><EFBFBD>絼<EFBFBD><E7B5BC> */
|
|||
|
|
{
|
|||
|
|
uint8_t r[16]; uint16_t rl;
|
|||
|
|
if (modbus_read(0x0B, 0x0015, 1, r, &rl, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>EC") != 0) return 1;
|
|||
|
|
g_sensor.soil_ec = (float)(r[3] * 256 + r[4]);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*==== <20><>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD> ====*/
|
|||
|
|
|
|||
|
|
typedef struct {
|
|||
|
|
const char *name; /* <20><>־/OLED <20><><EFBFBD><EFBFBD> */
|
|||
|
|
int (*read)(void); /* <20><>ȡ+<2B><><EFBFBD>룬0=<3D>ɹ<EFBFBD> */
|
|||
|
|
void (*report)(void); /* <20><><EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD> */
|
|||
|
|
} sensor_entry_t;
|
|||
|
|
|
|||
|
|
static void report_phec(void) { char b[64]; snprintf(b, sizeof(b), "\"PH\":%.1f,\"EC\":%.1f", g_sensor.ph, g_sensor.ec); NET_publish_props(b); }
|
|||
|
|
static void report_air(void) { char b[96]; snprintf(b, sizeof(b), "\"BS_T\":%.2f,\"BS_RH\":%.2f,\"BS_DO\":%.2f", g_sensor.air_t, g_sensor.air_rh, g_sensor.air_press); NET_publish_props(b); }
|
|||
|
|
static void report_do(void) { char b[64]; snprintf(b, sizeof(b), "\"DS_T\":%.2f,\"DO\":%.2f", g_sensor.water_t, g_sensor.do_mgl); NET_publish_props(b); }
|
|||
|
|
static void report_byx(void) { char b[112]; snprintf(b, sizeof(b), "\"BY_TP\":%.1f,\"BY_RH\":%.1f,\"BY_HV\":%u,\"BY_CO2\":%u", g_sensor.by_t, g_sensor.by_rh, g_sensor.by_hv, g_sensor.by_co2); NET_publish_props(b); }
|
|||
|
|
static void report_leaf(void) { char b[80]; snprintf(b, sizeof(b), "\"YSMD_TP\":%.1f,\"YSMD_RH\":%.1f", g_sensor.leaf_t, g_sensor.leaf_rh); NET_publish_props(b); }
|
|||
|
|
static void report_sth(void) { char b[80]; snprintf(b, sizeof(b), "\"SOIL_TP\":%.1f,\"SOIL_RH\":%.1f", g_sensor.soil_t, g_sensor.soil_rh); NET_publish_props(b); }
|
|||
|
|
static void report_npk(void) { char b[112]; snprintf(b, sizeof(b), "\"SOIL_NV\":%.1f,\"SOIL_PV\":%.1f,\"SOIL_KV\":%.1f", g_sensor.soil_n, g_sensor.soil_p, g_sensor.soil_k); NET_publish_props(b); }
|
|||
|
|
static void report_sph(void) { char b[48]; snprintf(b, sizeof(b), "\"SOIL_PH\":%.2f", g_sensor.soil_ph); NET_publish_props(b); }
|
|||
|
|
static void report_sec(void) { char b[48]; snprintf(b, sizeof(b), "\"SOIL_RV\":%d", (int)g_sensor.soil_ec); NET_publish_props(b); }
|
|||
|
|
|
|||
|
|
static const sensor_entry_t s_table[] = {
|
|||
|
|
{ "PH-EC", read_phec, report_phec },
|
|||
|
|
{ "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѹ", read_airpress, report_air },
|
|||
|
|
{ "<EFBFBD>ܽ<EFBFBD><EFBFBD><EFBFBD>", do59_read, report_do },
|
|||
|
|
{ "<EFBFBD><EFBFBD>Ҷ<EFBFBD><EFBFBD>", read_byx, report_byx },
|
|||
|
|
{ "Ҷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʪ", read_leaf, report_leaf },
|
|||
|
|
{ "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʪ", read_soilth, report_sth },
|
|||
|
|
{ "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>", read_npk, report_npk },
|
|||
|
|
{ "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>PH", read_soilph, report_sph },
|
|||
|
|
{ "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>EC", read_soilec, report_sec },
|
|||
|
|
};
|
|||
|
|
#define SENSOR_COUNT (sizeof(s_table) / sizeof(s_table[0]))
|
|||
|
|
|
|||
|
|
static int online_count(void)
|
|||
|
|
{
|
|||
|
|
int n = 0;
|
|||
|
|
for (uint16_t i = 0; i < SENSOR_COUNT; i++) {
|
|||
|
|
if (g_sensor_online & (1u << i)) n++;
|
|||
|
|
}
|
|||
|
|
return n;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* <20><><EFBFBD>ض<EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD>ڣ<EFBFBD>10s<30><73>ֻ<EFBFBD><D6BB><EFBFBD>»<EFBFBD><C2BB><EFBFBD>/OLED<45><44><EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD><CFB1><EFBFBD> */
|
|||
|
|
#define SENSOR_READ_MS 10000u
|
|||
|
|
|
|||
|
|
/* <20>ϴ<EFBFBD><CFB4><EFBFBD><EFBFBD>ȣ<EFBFBD>ê<EFBFBD><C3AA>"OTA <20><>ȷ<EFBFBD><C8B7>"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>+5s/+30s/+60s ů<><C5AF><EFBFBD><EFBFBD><EFBFBD>Σ<EFBFBD>֮<EFBFBD><D6AE>ÿ SENSOR_CYCLE_MS */
|
|||
|
|
static const uint16_t s_pub_warmup_s[3] = {5, 30, 60};
|
|||
|
|
static uint32_t s_read_tick = 0;
|
|||
|
|
static uint32_t s_pub_anchor = 0; /* 0=δê<CEB4><C3AA><EFBFBD><EFBFBD>OTA δȷ<CEB4><C8B7>/δ<><CEB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
|
static uint8_t s_pub_stage = 0; /* 0~2=ů<><C5AF><EFBFBD>Σ<D7B6>3=<3D><><EFBFBD><EFBFBD> 5 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
|
static uint32_t s_pub_last = 0; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><D7B6>ϴ<EFBFBD><CFB4>ϴ<EFBFBD>ʱ<EFBFBD><CAB1> */
|
|||
|
|
|
|||
|
|
void sensor_init(void)
|
|||
|
|
{
|
|||
|
|
log_info("> SENSOR: <20><>ʼ<EFBFBD><CABC>, %d <20>ִ<EFBFBD><D6B4><EFBFBD><EFBFBD><EFBFBD>, 10s <20><><EFBFBD><EFBFBD>ˢ<EFBFBD><CBA2>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> +5s/+30s/+60s <20>ϴ<EFBFBD>, ֮<><D6AE>ÿ %u <20><><EFBFBD><EFBFBD>",
|
|||
|
|
(int)SENSOR_COUNT, (unsigned)(SENSOR_CYCLE_MS / 60000u));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* <20><> app_task <20><>"OTA <20><>ȷ<EFBFBD><C8B7>"ʱ<><CAB1><EFBFBD>ã<EFBFBD>ê<EFBFBD><C3AA><EFBFBD>ϴ<EFBFBD><CFB4>ƻ<EFBFBD> */
|
|||
|
|
void sensor_notify_ota_confirmed(void)
|
|||
|
|
{
|
|||
|
|
if (s_pub_anchor == 0) {
|
|||
|
|
s_pub_anchor = HAL_GetTick();
|
|||
|
|
s_pub_stage = 0;
|
|||
|
|
log_info("> SENSOR: <20>ϴ<EFBFBD><CFB4>ƻ<EFBFBD><C6BB><EFBFBD>ê<EFBFBD><C3AA>(+5s/+30s/+60s, ֮<><D6AE>ÿ %u <20><><EFBFBD><EFBFBD>)",
|
|||
|
|
(unsigned)(SENSOR_CYCLE_MS / 60000u));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ȫ<><C8AB><EFBFBD><EFBFBD>ѯһ<D1AF>鴫<EFBFBD><E9B4AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
|
static void scan_sensors(void)
|
|||
|
|
{
|
|||
|
|
for (uint16_t i = 0; i < SENSOR_COUNT; i++) {
|
|||
|
|
if (s_table[i].read() == 0) {
|
|||
|
|
g_sensor_online |= (1u << i);
|
|||
|
|
} else {
|
|||
|
|
g_sensor_online &= ~(1u << i);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* <20>ϴ<EFBFBD><CFB4><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߴ<EFBFBD><DFB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> + <20><><EFBFBD>ص<EFBFBD><D8B5><EFBFBD> */
|
|||
|
|
static void publish_all(void)
|
|||
|
|
{
|
|||
|
|
if (!g_net_mqtt_ready) return;
|
|||
|
|
for (uint16_t i = 0; i < SENSOR_COUNT; i++) {
|
|||
|
|
if (g_sensor_online & (1u << i)) {
|
|||
|
|
s_table[i].report();
|
|||
|
|
vTaskDelay(300); /* <20>ϱ<EFBFBD>֮<EFBFBD><D6AE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>϶<EFBFBD><CFB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD> AT ͨ<><CDA8><EFBFBD><EFBFBD>ѹ */
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/* <20><><EFBFBD>ص<EFBFBD><D8B5><EFBFBD><EFBFBD><EFBFBD>ÿ<EFBFBD><C3BF><EFBFBD>ϴ<EFBFBD>һ<EFBFBD><D2BB> */
|
|||
|
|
{
|
|||
|
|
int bat = battery_percent();
|
|||
|
|
char b[32];
|
|||
|
|
snprintf(b, sizeof(b), "\"BAT_VAL\":%d", bat);
|
|||
|
|
NET_publish_props(b);
|
|||
|
|
log_info("> SENSOR: <20><><EFBFBD>ص<EFBFBD><D8B5><EFBFBD> %d%%", bat);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void sensor_process(void)
|
|||
|
|
{
|
|||
|
|
uint32_t now = HAL_GetTick();
|
|||
|
|
|
|||
|
|
/* 10s <20><><EFBFBD>ض<EFBFBD>ȡˢ<C8A1>£<EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD>OLED ʵʱ<CAB5><CAB1>ʾ<EFBFBD>ã<EFBFBD> */
|
|||
|
|
if ((now - s_read_tick) >= SENSOR_READ_MS) {
|
|||
|
|
s_read_tick = now;
|
|||
|
|
scan_sensors();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* <20>ϴ<EFBFBD><CFB4><EFBFBD><EFBFBD><EFBFBD> */
|
|||
|
|
if (s_pub_anchor == 0) return;
|
|||
|
|
if (s_pub_stage < 3) {
|
|||
|
|
/* ů<><C5AF><EFBFBD>Σ<D7B6>+5s/+30s/+60s */
|
|||
|
|
if ((now - s_pub_anchor) >= (uint32_t)s_pub_warmup_s[s_pub_stage] * 1000u) {
|
|||
|
|
publish_all();
|
|||
|
|
s_pub_stage++;
|
|||
|
|
if (s_pub_stage == 3) s_pub_last = now;
|
|||
|
|
}
|
|||
|
|
} else if ((now - s_pub_last) >= SENSOR_CYCLE_MS) {
|
|||
|
|
s_pub_last = now;
|
|||
|
|
publish_all();
|
|||
|
|
log_info("> SENSOR: <20><><EFBFBD><EFBFBD><EFBFBD>ϴ<EFBFBD><CFB4><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD> %d/%d", online_count(), (int)SENSOR_COUNT);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*==== OLED ժҪ<D5AA><D2AA>ÿ 5s <20><>һҳ<D2BB><D2B3>һҳһ·<D2BB><C2B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>R2/R3 <20><><EFBFBD>У<EFBFBD>====
|
|||
|
|
* <EFBFBD>п<EFBFBD>Լ<EFBFBD><EFBFBD>: 128px<EFBFBD><EFBFBD>ȫ<EFBFBD><EFBFBD> 16px/<EFBFBD>֣<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 8px/<EFBFBD>֣<EFBFBD>ÿ<EFBFBD>в<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 128px */
|
|||
|
|
int sensor_oled_summary(char *l1, char *l2, int cap)
|
|||
|
|
{
|
|||
|
|
int total = online_count();
|
|||
|
|
if (total == 0) return 0;
|
|||
|
|
|
|||
|
|
int want = (int)((HAL_GetTick() / 5000u) % (uint32_t)total);
|
|||
|
|
int idx = -1;
|
|||
|
|
for (int i = 0, seen = 0; i < (int)SENSOR_COUNT; i++) {
|
|||
|
|
if (g_sensor_online & (1u << i)) {
|
|||
|
|
if (seen == want) { idx = i; break; }
|
|||
|
|
seen++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (idx < 0) return 0;
|
|||
|
|
|
|||
|
|
l1[0] = l2[0] = 0;
|
|||
|
|
switch (idx) {
|
|||
|
|
case 0: snprintf(l1, cap, "ˮ<EFBFBD><EFBFBD>PH:%.1f", g_sensor.ph);
|
|||
|
|
snprintf(l2, cap, "<EFBFBD>絼<EFBFBD><EFBFBD>:%.0fuS/cm", g_sensor.ec); break;
|
|||
|
|
case 1: snprintf(l1, cap, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¶<EFBFBD>:%.1fC", g_sensor.air_t);
|
|||
|
|
snprintf(l2, cap, "ʪ%.0f%% ѹ%.0fkPa", g_sensor.air_rh, g_sensor.air_press); break;
|
|||
|
|
case 2: snprintf(l1, cap, "<EFBFBD>ܽ<EFBFBD><EFBFBD><EFBFBD>:%.2fmg/L", g_sensor.do_mgl);
|
|||
|
|
snprintf(l2, cap, "ˮ<EFBFBD><EFBFBD>%.1fC <20><>%.0f%%", g_sensor.water_t, g_sensor.do_sat); break;
|
|||
|
|
case 3: snprintf(l1, cap, "<EFBFBD><EFBFBD>%.1fC ʪ%.0f%%", g_sensor.by_t, g_sensor.by_rh);
|
|||
|
|
snprintf(l2, cap, "CO2:%u <20><>:%u", g_sensor.by_co2, g_sensor.by_hv); break;
|
|||
|
|
case 4: snprintf(l1, cap, "Ҷ<EFBFBD><EFBFBD><EFBFBD>¶<EFBFBD>:%.1fC", g_sensor.leaf_t);
|
|||
|
|
snprintf(l2, cap, "Ҷ<EFBFBD><EFBFBD>ʪ<EFBFBD><EFBFBD>:%.1f%%", g_sensor.leaf_rh); break;
|
|||
|
|
case 5: snprintf(l1, cap, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¶<EFBFBD>:%.1fC", g_sensor.soil_t);
|
|||
|
|
snprintf(l2, cap, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʪ<EFBFBD><EFBFBD>:%.1f%%", g_sensor.soil_rh); break;
|
|||
|
|
case 6: snprintf(l1, cap, "<EFBFBD><EFBFBD>:%.0f <20><>:%.0f", g_sensor.soil_n, g_sensor.soil_p);
|
|||
|
|
snprintf(l2, cap, "<EFBFBD><EFBFBD>:%.0fmg/kg", g_sensor.soil_k); break;
|
|||
|
|
case 7: snprintf(l1, cap, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>PH:%.2f", g_sensor.soil_ph); break;
|
|||
|
|
case 8: snprintf(l1, cap, "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>絼<EFBFBD><EFBFBD>:%.0f", g_sensor.soil_ec); break;
|
|||
|
|
default: return 0;
|
|||
|
|
}
|
|||
|
|
return 1;
|
|||
|
|
}
|