代码拉取完成,页面将自动刷新
/*********************************************************************************
* Copyright: (C) 2024 Xu Jiangfeng
* All rights reserved.
*
* Filename: test_esp.c
* Description: This file test esp.
*
* Version: 1.0.0(2024年07月27日)
* Author: Xu Jiangfeng <1922788776@qq.com>
* ChangeLog: 1, Release initial version on "2024年07月27日 21时36分15秒"
*
********************************************************************************/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <sys/select.h>
#include <pthread.h>
#include <semaphore.h>
#include "modbus.h"
#define REV_OK 0
#define REV_WAIT 1
#define UART_BUF 128
#define BAUD_RATE 115200
#define MODBUS_DEVICE "/dev/ttymxc1"
#define UART_DEVICE "/dev/ttymxc3"
#define WiFi_ID "Xiaomi_77FA"
#define WiFi_Pass "913913913"
#define MQTT_USER "admin"
#define MQTT_PASSWORD "mja123456"
#define MQTT_SERVER "127.0.0.1"
#define CLIENT_ID "85cf8b7aeb184710921b1b1"
#define MQTT_TOPIC "datas"
#define JSON_FORMAT "{\\\"voltage\\\":%.3f\\,\\\"current\\\":%.3f\\,\\\"temperature\\\":%.1f\\}"
static unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
static unsigned char buf[UART_BUF] = {0};
static char modbus_data[256] = {0};
static sem_t data_ready_sem;
static pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
_Bool is_server_connected = 0;
static struct termios old_cfg;
static int fd;
#define MAX_SLAVES_COUNT 10 // 最多支持从机数
#define INITIAL_LIFETIME 10 // 每个从机的初始访问寿命
#define MAX_LIFETIME 10 // 最大寿命
#define SUCCESS_THRESHOLD 50 // 连续成功读取50次增加一次寿命
#define VOL_SHARP 1.00 // 电压剧烈变化
#define VOL_SLOW 0.50 // 电压缓慢变化
#define CUR_SHARP 1.00 // 电流剧烈变化
#define CUR_SLOW 0.50 // 电流缓慢变化
#define TEMP_SHARP 5.0 // 温度剧烈变化
#define TEMP_SLOW 1.0 // 温度缓慢变化
#define DETECT_INTE_MS 300050
#define BROADCAST_INTE_MS 600000 // 每10分钟广播一次时间戳(10 * 60 * 1000)
#define MAX_TIME_MS 864000000 // 主机最大计数10天
typedef struct{
int slave_addr; // 从机地址
int lifetime; // 读取寿命
int is_damaged; // 是否损坏的标志(1 损坏; 0 正常)
int success_count; // 连续成功读取次数
}ModbusSlave;
ModbusSlave slaves[MAX_SLAVES_COUNT];
int slave_count = 0; // 判断一开始有无从机接入系统,如果没有从机接入,系统将不断检测所有从机地址。
uint32_t last_detection_time = 0; // 上次检测从机的时间
uint32_t last_broadcast_time = 0; // 上次广播时间
typedef struct uart_hardware_cfg {
unsigned int baudrate;
unsigned char dbit;
char parity;
unsigned char sbit;
}uart_cfg_t;
static int uart_init(const char *device)
{
fd = open(device, O_RDWR | O_NOCTTY);
if (fd < 0)
{
fprintf(stderr, "open error:%s:%s\n", device, strerror(errno));
return -1;
}
if (0 > tcgetattr(fd, &old_cfg)) {
fprintf(stderr, "tcgetattr error: %s\n", strerror(errno));
close(fd);
return -1;
}
return 0;
}
static int uart_cfg(const uart_cfg_t *cfg)
{
struct termios new_cfg = {0};
speed_t speed;
cfmakeraw(&new_cfg);
new_cfg.c_cflag |= CREAD;
switch (cfg->baudrate) {
case 1200: speed = B1200;
break;
case 1800: speed = B1800;
break;
case 2400: speed = B2400;
break;
case 4800: speed = B4800;
break;
case 9600: speed = B9600;
break;
case 19200: speed = B19200;
break;
case 38400: speed = B38400;
break;
case 57600: speed = B57600;
break;
case 115200: speed = B115200;
break;
case 230400: speed = B230400;
break;
case 460800: speed = B460800;
break;
case 500000: speed = B500000;
break;
default:
speed = B115200;
printf("default baud rate: 115200\n");
break;
}
if (0 > cfsetspeed(&new_cfg, speed)) {
fprintf(stderr, "cfsetspeed error: %s\n", strerror(errno));
return -1;
}
new_cfg.c_cflag &= ~CSIZE;
switch (cfg->dbit) {
case 5:
new_cfg.c_cflag |= CS5;
break;
case 6:
new_cfg.c_cflag |= CS6;
break;
case 7:
new_cfg.c_cflag |= CS7;
break;
case 8:
new_cfg.c_cflag |= CS8;
break;
default:
new_cfg.c_cflag |= CS8;
break;
}
switch (cfg->parity) {
case 'N':
new_cfg.c_cflag &= ~PARENB;
new_cfg.c_iflag &= ~INPCK;
break;
case 'O':
new_cfg.c_cflag |= (PARODD | PARENB);
new_cfg.c_iflag |= INPCK;
break;
case 'E':
new_cfg.c_cflag |= PARENB;
new_cfg.c_cflag &= ~PARODD;
new_cfg.c_iflag |= INPCK;
break;
default:
new_cfg.c_cflag &= ~PARENB;
new_cfg.c_iflag &= ~INPCK;
break;
}
switch (cfg->sbit) {
case 1:
new_cfg.c_cflag &= ~CSTOPB;
break;
case 2:
new_cfg.c_cflag |= CSTOPB;
break;
default:
new_cfg.c_cflag &= ~CSTOPB;
break;
}
new_cfg.c_cc[VTIME] = 0;
new_cfg.c_cc[VMIN] = 0;
if (0 > tcflush(fd, TCIOFLUSH)) {
fprintf(stderr, "tcflush error: %s\n", strerror(errno));
return -1;
}
if (0 > tcsetattr(fd, TCSANOW, &new_cfg)) {
fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));
return -1;
}
return 0;
}
static void io_handler(int sig, siginfo_t *info, void *context)
{
if(SIGRTMIN != sig)
return;
if (POLL_IN == info->si_code) {
if (esp8266_cnt >= sizeof(buf))
esp8266_cnt = 0;
esp8266_cnt = read(fd, buf, sizeof(buf));
pthread_mutex_lock(&data_mutex);
strncpy(modbus_data, (const char*)buf, sizeof(modbus_data) - 1);
modbus_data[sizeof(modbus_data) - 1] = '\0';
pthread_mutex_unlock(&data_mutex);
sem_post(&data_ready_sem);
}
}
static void async_io_init(void)
{
struct sigaction sigatn;
int flag;
flag = fcntl(fd, F_GETFL);
flag |= O_ASYNC;
fcntl(fd, F_SETFL, flag);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETSIG, SIGRTMIN);
sigatn.sa_sigaction = io_handler;
sigatn.sa_flags = SA_SIGINFO;
sigemptyset(&sigatn.sa_mask);
sigaction(SIGRTMIN, &sigatn, NULL);
}
void uart4_init(unsigned int baud, char *device)
{
uart_cfg_t cfg = {0};
if (NULL == device) {
fprintf(stderr, "Error: the device no found!\n");
exit(EXIT_FAILURE);
}
if (uart_init(device))
exit(EXIT_FAILURE);
cfg.baudrate = baud;
if (uart_cfg(&cfg)) {
tcsetattr(fd, TCSANOW, &old_cfg);
close(fd);
exit(EXIT_FAILURE);
}
}
void esp8266_clear(void)
{
memset(buf, 0, sizeof(buf));
esp8266_cnt = 0;
}
_Bool esp8266_waitrecive(void)
{
if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
return REV_WAIT;
if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
{
esp8266_cnt = 0; //清0接收计数
return REV_OK; //返回接收完成标志
}
esp8266_cntPre = esp8266_cnt; //置为相同
return REV_WAIT; //返回接收未完成标志
}
_Bool esp8266_sendcmd(char *cmd, char *res, int timeout)
{
int ret = 0;
ret = write(fd, (unsigned char *)cmd, strlen((const char *)cmd));
if (ret == 0)
{
printf("write error!\r\n");
}
if(res == NULL || timeout == 0)
{
return REV_OK;
}
while(timeout > 0)
{
if(esp8266_waitrecive() == REV_OK)
{
if(strstr((const char *)buf, res) != NULL)
{
esp8266_clear();
return REV_OK;
}
}
timeout--;
usleep(1000);
}
return 1;
}
_Bool esp8266_connectserver(void)
{
char connect_cmd[512];
if(is_server_connected)
{
printf("Already connected to server.\r\n");
return 0;
}
printf("Connect to server...\r\n");
snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTUSERCFG=0,1,\"NULL\",\"%s\",\"%s\",0,0,\"\"\r\n", MQTT_USER, MQTT_PASSWORD);
while(esp8266_sendcmd(connect_cmd, "OK", 1000))
{
printf("AT+MQTTUSERCFG is failed!\r\n");
}
snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTCLIENTID=0,\"%s\"\r\n", CLIENT_ID);
while(esp8266_sendcmd(connect_cmd, "OK", 1000))
{
printf("AT+MQTTCLIENTID is failed!\r\n");
}
snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTCONN=0,\"192.168.31.103\",1883,1\r\n");
while(esp8266_sendcmd(connect_cmd, "OK", 1000))
{
printf("AT+MQTTCONN is failed!\r\n");
}
snprintf(connect_cmd, sizeof(connect_cmd), "AT+MQTTSUB=0,\"%s\",1\r\n", MQTT_TOPIC);
while(esp8266_sendcmd(connect_cmd, "OK", 1000))
{
printf("AT+MQTTSUB is failed!\r\n");
}
is_server_connected = 1;
printf("Connect to server successful!\r\n");
return 0;
}
_Bool esp8266_connectWifi(const char* ssid, const char* password)
{
char wifi_buf[256];
printf("Connect to WiFi...\r\n");
snprintf(wifi_buf, sizeof(wifi_buf), "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, password);
while(esp8266_sendcmd(wifi_buf, "OK", 2000))
{
printf("Connect to wifi is failed.\r\n");
}
printf("Connect to WiFi OK!\r\n");
return 0;
}
_Bool check_wifi()
{
printf("Check WiFi Connection status!\r\n");
while(esp8266_sendcmd("AT+RST\r\n", "OK", 1000));
if(esp8266_sendcmd("AT+CWJAP?\r\n", "OK", 1000) == 0)
{
printf("Already connected to WiFi.\r\n");
return 1;
}
printf("Not connected to WiFi, need to connect to WiFi.\r\n");
return 0;
}
void send_data_to_server(const char *topic, const unsigned char *data)
{
char mqtt_buf[256];
esp8266_clear();
snprintf(mqtt_buf, sizeof(mqtt_buf), "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n", topic, data);
esp8266_sendcmd(mqtt_buf, "OK", 1000);
return;
}
_Bool esp8266_init()
{
char connect_cmd[256];
esp8266_clear();
while(esp8266_sendcmd("AT+RESTORE\r\n", "OK", 1000))
{
printf("RESTORE is failed!\r\n");
}
while(esp8266_sendcmd("AT+RST\r\n", "OK", 500))
{
printf("RST is failed!\r\n");
}
while(esp8266_sendcmd("ATE0\r\n", "OK", 500))
{
printf("ATE0 is failed!\r\n");
}
while(esp8266_sendcmd("AT\r\n", "OK", 500))
{
printf("AT test is failed!\r\n");
}
while(esp8266_sendcmd("AT+CWMODE=1\r\n", "OK", 1000))
{
printf("CWMODE=1 is failed!\r\n");
}
return 0;
}
// read modbus slave data.
int read_modbus_data(modbus_t *ctx, int slave_addr, uint16_t *tab_reg, int reg_count)
{
int rc = -1;
modbus_set_slave(ctx, slave_addr);
rc = modbus_read_input_registers(ctx, 0, reg_count, tab_reg);
if (rc == -1)
{
fprintf(stderr, "Read failed for slave %d: %s\n", slave_addr, modbus_strerror(errno));
return -1;
}
printf("Data from slave %d:\n", slave_addr);
for (int i = 0; i < reg_count; i++)
{
printf("Reg[%d]=%d\n", i, tab_reg[i]);
}
return 0;
}
// 将整数位和小数位组合成浮点型数据
float combine_float(uint16_t int_part, uint16_t frac_part)
{
char buffer[20];
snprintf(buffer, sizeof(buffer), "%d.%d", int_part, frac_part);
return strtof(buffer, NULL);
}
// 计算数据变化的绝对值
float calculate_change(float old_data, float new_data)
{
return fabs(new_data - old_data);
}
// 检测某个从机是否存在
_Bool is_slave_present(modbus_t *ctx, int slave_addr)
{
uint16_t tab_reg[2];
int rc = read_modbus_data(ctx, slave_addr, tab_reg, 1);
return (rc != -1);
}
// 开机时轮询所有从机地址,初始化从机列表
void initialize_slaves(modbus_t *ctx)
{
int i;
int slave_addr;
for(slave_addr = 1; slave_addr <= MAX_SLAVES_COUNT; slave_addr++)
{
if(is_slave_present(ctx, slave_addr))
{
slaves[slave_addr - 1].slave_addr = slave_addr;
slaves[slave_addr - 1].lifetime = INITIAL_LIFETIME;
slaves[slave_addr - 1].is_damaged = 0;
slaves[slave_addr - 1].success_count = 0;
printf("从机 %d 初始化成功\n", slave_addr);
slave_count++;
}
}
}
// 定期检测是否有新的从机接入
void check_new_slaves(modbus_t *ctx)
{
int i;
for(int slave_addr = 1; slave_addr <= MAX_SLAVES_COUNT; slave_addr++)
{
// 只检查地址为0的位置是否有新从机加入
_Bool found = 0;
for(i = 0; i < MAX_SLAVES_COUNT; i++)
{
if(slaves[i].slave_addr == slave_addr)
{
found = 1;
break;
}
}
// 如果没有找到该从机,说明是新的,从机加入
if(!found && is_slave_present(ctx, slave_addr))
{
for(i = 0; i < MAX_SLAVES_COUNT; i++)
{
if(slaves[i].slave_addr == 0)
{
slaves[i].slave_addr = slave_addr;
slaves[i].lifetime = INITIAL_LIFETIME;
slaves[i].is_damaged = 0;
slaves[i].success_count = 0;
printf("新从机 %d 接入系统\n", slave_addr);
slave_count++;
break;
}
}
}
}
}
uint32_t get_current_time_ms()
{
struct timeval tv;
uint32_t time_ms;
gettimeofday(&tv, NULL); // 获取当前时间
time_ms = (uint32_t)(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
return time_ms % MAX_TIME_MS; // 返回时间戳并确保它不会超过10天
}
void broadcast_timestamp(modbus_t *ctx)
{
int rc;
uint16_t timestamp_regs[2];
uint32_t timestamp;
timestamp = get_current_time_ms();
timestamp_regs[0] = timestamp & 0xFFFF; // 时间戳的低16位
timestamp_regs[1] = (timestamp >> 16) & 0xFFFF; // 时间戳的高16位
// 通过广播方式发送时间戳给所有从机
modbus_set_slave(ctx, 0);
rc = modbus_write_registers(ctx, 0x00, 2, timestamp_regs);
printf("广播的时间为:%u\n", timestamp);
}
int main(int argc, char *argv[])
{
int baud_rate = BAUD_RATE;
char *uart_device = UART_DEVICE;
modbus_t *ctx;
// 初始化上次采集的电压、电流和温度
float last_vol_data = 0.00;
float last_cur_data = 0.00;
float last_temp_data = 0.00;
int sleep_time_ms = 10000; // 默认上报时间间隔10秒
uart4_init(baud_rate, uart_device);
async_io_init();
ctx = modbus_new_rtu(MODBUS_DEVICE, baud_rate, 'N', 8, 1);
if (ctx == NULL)
{
fprintf(stderr, "Unable to create the libmodbus context\n");
return -1;
}
if (modbus_connect(ctx) == -1)
{
fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return -2;
}
// 开机时广播主机时间给从机校准
uint32_t after_time;
broadcast_timestamp(ctx);
after_time = get_current_time_ms();
printf("广播后的时间为:%u\n", after_time);
// 开机时初始化所有从机
initialize_slaves(ctx);
if(check_wifi() == 0)
{
if(esp8266_init() == 0)
{
esp8266_connectWifi(WiFi_ID, WiFi_Pass);
esp8266_connectserver();
}
}
else
{
esp8266_connectserver();
}
while(1)
{
int i;
int rc = -1;
uint16_t tab_reg[32];
char modbus_data[128];
uint32_t current_time;
uint32_t before_readtime;
uint32_t last_readtime;
current_time = get_current_time_ms();
// 当没有从机接入或者满5分钟,检测是否有新的从机接入
if( !slave_count || (current_time - last_detection_time >= DETECT_INTE_MS) )
{
check_new_slaves(ctx);
last_detection_time = current_time;
}
if(current_time - last_broadcast_time >= BROADCAST_INTE_MS)
{
broadcast_timestamp(ctx);
last_broadcast_time = current_time;
}
for(i = 0; i < MAX_SLAVES_COUNT; i++)
{
ModbusSlave *slave = &slaves[i];
// 如果地址为0或损坏,则跳过读取该从机数据
if(slave->slave_addr == 0 || slave->is_damaged)
{
continue;
}
before_readtime = get_current_time_ms();
rc = read_modbus_data(ctx, slave->slave_addr, tab_reg, 7);
last_readtime = ((uint32_t)tab_reg[5] << 16) | (uint32_t)tab_reg[6];
printf("主机接收从机 %d 数据的时间差为:%d\n", slave->slave_addr, last_readtime - before_readtime);
if(rc < 0)
{
// 如果读取失败,寿命减1,并重置连续成功计数
slave->lifetime--;
slave->success_count = 0;
printf("从机 %d 读取失败,访问寿命减为:%d\n", slave->slave_addr, slave->lifetime);
if(slave->lifetime <= 0)
{
slave->is_damaged = 1;
printf("从机 %d 已损坏,将其移除采集系统!\n", slave->slave_addr);
slave->slave_addr = 0;
slave_count--;
}
}
else
{
// 读取成功,提取数据
float vol_data = tab_reg[1] / 1000.0;
float cur_data = tab_reg[2] / 1000.0;
float temp_data = combine_float(tab_reg[3], tab_reg[4]);
printf("从机 %d 的电压、电流和温度为: %.3f, %.3f, %.1f\n", tab_reg[0], vol_data, cur_data, temp_data);
// 计算数据变化
float vol_change = calculate_change(last_vol_data, vol_data);
float cur_change = calculate_change(last_cur_data, cur_data);
float temp_change = calculate_change(last_temp_data, temp_data);
// 判断电压变化
if(vol_change > VOL_SHARP || cur_change > CUR_SHARP || temp_change > TEMP_SHARP)
{
sleep_time_ms = 100;
}
else if(vol_change > VOL_SLOW || cur_change > CUR_SLOW || temp_change > TEMP_SLOW)
{
sleep_time_ms = 1000;
}
else
{
sleep_time_ms = 10000;
}
snprintf(modbus_data, sizeof(modbus_data), JSON_FORMAT, vol_data, cur_data, temp_data);
send_data_to_server(MQTT_TOPIC, (unsigned char*)modbus_data);
// 更新上次数据
last_vol_data = vol_data;
last_cur_data = cur_data;
last_temp_data = temp_data;
// 如果读取成功,增加成功计数
slave->success_count++;
// 如果连续读取成功次数达到50次,并且寿命小于最大寿命,则增加寿命。
if(slave->success_count >= SUCCESS_THRESHOLD && slave->lifetime < MAX_LIFETIME)
{
slave->lifetime++;
slave->success_count = 0; // 增加寿命后重置成功计数
printf("从机 %d 连续成功读取 %d 次,增加寿命,当前寿命:%d\n", slave->slave_addr, SUCCESS_THRESHOLD, slave->lifetime);
}
}
usleep(sleep_time_ms * 1000);
}
}
modbus_close(ctx);
modbus_free(ctx);
tcsetattr(fd, TCSANOW, &old_cfg);
close(fd);
return 0;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。