|
|
@@ -184,35 +184,89 @@ static int tcp_connect(const char *host, uint16_t port, int timeout_sec)
|
|
|
return sockfd;
|
|
|
}
|
|
|
|
|
|
-/* ---- Send Modbus RTU frame and receive response ---- */
|
|
|
+/* ---- Drain any leftover data from socket buffer ---- */
|
|
|
+static void drain_socket(int sockfd)
|
|
|
+{
|
|
|
+ uint8_t tmp[256];
|
|
|
+ struct timeval tv;
|
|
|
+ fd_set rfds;
|
|
|
+
|
|
|
+ tv.tv_sec = 0;
|
|
|
+ tv.tv_usec = 100000; /* 100ms timeout */
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ FD_ZERO(&rfds);
|
|
|
+ FD_SET(sockfd, &rfds);
|
|
|
+ int ret = select(sockfd + 1, &rfds, NULL, NULL, &tv);
|
|
|
+ if (ret <= 0) break;
|
|
|
+ ssize_t n = recv(sockfd, tmp, sizeof(tmp), MSG_DONTWAIT);
|
|
|
+ if (n <= 0) break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* ---- Send Modbus RTU frame and receive complete response ---- */
|
|
|
static int modbus_send_recv(int sockfd, const uint8_t *tx_frame, int tx_len,
|
|
|
uint8_t *rx_buf, int rx_buf_size, int timeout_sec)
|
|
|
{
|
|
|
+ /* Drain any leftover data before sending */
|
|
|
+ drain_socket(sockfd);
|
|
|
+
|
|
|
/* Send */
|
|
|
ssize_t sent = send(sockfd, tx_frame, tx_len, 0);
|
|
|
if (sent != tx_len) {
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- /* Receive with timeout */
|
|
|
- fd_set rfds;
|
|
|
- struct timeval tv;
|
|
|
- FD_ZERO(&rfds);
|
|
|
- FD_SET(sockfd, &rfds);
|
|
|
- tv.tv_sec = timeout_sec;
|
|
|
- tv.tv_usec = 0;
|
|
|
+ /* Receive with timeout - read complete response */
|
|
|
+ int total_received = 0;
|
|
|
+ int empty_reads = 0;
|
|
|
+
|
|
|
+ while (total_received < rx_buf_size) {
|
|
|
+ fd_set rfds;
|
|
|
+ struct timeval tv;
|
|
|
+ FD_ZERO(&rfds);
|
|
|
+ FD_SET(sockfd, &rfds);
|
|
|
+ tv.tv_sec = (total_received == 0) ? timeout_sec : 1;
|
|
|
+ tv.tv_usec = 0;
|
|
|
|
|
|
- int ret = select(sockfd + 1, &rfds, NULL, NULL, &tv);
|
|
|
- if (ret <= 0) {
|
|
|
- return -1; /* Timeout or error */
|
|
|
- }
|
|
|
+ int ret = select(sockfd + 1, &rfds, NULL, NULL, &tv);
|
|
|
+ if (ret < 0) return -1; /* Error */
|
|
|
+ if (ret == 0) {
|
|
|
+ /* Timeout */
|
|
|
+ if (total_received > 0) break; /* Got some data, use it */
|
|
|
+ return -1; /* No data at all */
|
|
|
+ }
|
|
|
|
|
|
- ssize_t received = recv(sockfd, rx_buf, rx_buf_size, 0);
|
|
|
- if (received <= 0) {
|
|
|
- return -1;
|
|
|
+ ssize_t received = recv(sockfd, rx_buf + total_received,
|
|
|
+ rx_buf_size - total_received, MSG_DONTWAIT);
|
|
|
+ if (received < 0) {
|
|
|
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
+ empty_reads++;
|
|
|
+ if (empty_reads > 5) break;
|
|
|
+ usleep(10000); /* 10ms */
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (received == 0) break; /* Connection closed */
|
|
|
+
|
|
|
+ total_received += (int)received;
|
|
|
+ empty_reads = 0;
|
|
|
+
|
|
|
+ /* Check if we have a complete Modbus RTU frame */
|
|
|
+ if (total_received >= 5) {
|
|
|
+ /* For normal response: addr(1) + func(1) + byte_count(1) + data(N) + crc(2) */
|
|
|
+ if (!(rx_buf[1] & 0x80)) {
|
|
|
+ int expected = 3 + rx_buf[2] + 2; /* header + data + crc */
|
|
|
+ if (total_received >= expected) break; /* Complete frame */
|
|
|
+ } else {
|
|
|
+ /* Exception response: addr(1) + func|0x80(1) + exception(1) + crc(2) = 5 */
|
|
|
+ if (total_received >= 5) break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return (int)received;
|
|
|
+ return total_received;
|
|
|
}
|
|
|
|
|
|
/* ---- Build and send read input registers command (0x04) ---- */
|
|
|
@@ -369,8 +423,8 @@ static int poll_device(modbus_client_t *client, mppt_device_data_t *dev)
|
|
|
dev->software_version_raw = regs[10]; /* 30011 */
|
|
|
|
|
|
/* Read Output Registers (0x03): 9 registers starting at 0x0000 */
|
|
|
- /* Wait 1 second between commands per protocol spec */
|
|
|
- usleep(200000); /* 200ms - reduced for faster polling */
|
|
|
+ /* Protocol spec: two commands must have 1 second interval */
|
|
|
+ usleep(1000000); /* 1 second delay per protocol specification */
|
|
|
|
|
|
ret = read_output_registers(client->sockfd, dev->device_addr,
|
|
|
0x0000, MODBUS_OUTPUT_REG_COUNT,
|
|
|
@@ -429,6 +483,10 @@ static void *poll_thread_func(void *arg)
|
|
|
client->connected = true;
|
|
|
printf("[MODBUS] Connected to %s:%d\n",
|
|
|
client->config.host, client->config.port);
|
|
|
+
|
|
|
+ /* Drain any stale data after connecting */
|
|
|
+ usleep(200000); /* 200ms for connection to stabilize */
|
|
|
+ drain_socket(client->sockfd);
|
|
|
}
|
|
|
|
|
|
/* Poll each device */
|