边缘采集网关项目可实现家居物联网设备和工业物联网设备的数据采集和控制。整个项目分为三大模块:QT提供了用户界面,向用户展示设备数据,并提供用户控制操作;网关用虚拟机代替,基于Linux系统开发,起到数据中转作用,向上对接QT,向下对接设备;设备分为家居设备和工业设备两种,家居设备使用M0单片机开发板进行模拟,单片机开发板上有各种传感器和外设,工业设备使用modbus slave软件进行模拟。
实现modbus工业采集,主要是接收CGI发送到消息队列的命令,控制设备的开关,把采集到的数据上报到共享内存
代码示例如下:
#include <stdio.h>
#include <modbus.h>
#include <modbus-tcp.h>
#include <string.h>
#include <stdlib.h>
#include "shmem.h"
#include "cJSON.h"
#include "list.h"
#include <log_utils.h>
pthread_t t1, t2;
static struct shm_param para;
struct list_head *pos;
struct list_head head;
struct mb_node *tmp;
//共享内存结构体
union val_t {
int b_val; //bool类型存储空间
int i_val; //整形值存储空间
float f_val; //浮点值存储空间
};
struct std_node
{
int key; //唯一键值
int type; //数据点类型
int dev_type; //数据点属于哪个设备,根据网关支持的设备自行定义
union val_t old_val; //变化上报后需要更新旧值
union val_t new_val; //从共享内存取出最新数据,放到new_val中
int ret; //默认为-1,采集成功后设置为0,采集失败再置-1
};
//消息队列结构体
struct msg
{
long mtype;
char mdata[512];
};
//链表
struct mb_node
{
int key; //唯一键值
char name[128]; //数据点名称
int type; //数据点类型
int addr;
};
struct mb_node_list
{
struct mb_node node;
struct list_head list;
};
//数据采集
void *get_info(void *arg)
{
uint16_t data[4] = {};
uint16_t data1[2] = {};
uint8_t dest[2] = {};
int ret = -1;
ret = shm_init(¶, "main", 1024);
if (ret < 0)
{
return -1;
}
void *addr = shm_getaddr(¶);
if (addr == NULL)
{
return -1;
}
int *p = NULL;
p = addr;
printf("%d\n", p);
int *num=addr;
struct std_node *add = (addr + sizeof(int));
modbus_t *ctx = (modbus_t *)arg;
while (1)
{
//读保持寄存器的数据
//温湿度
modbus_read_input_registers(ctx, 0, 4, data);
//将数据打印
printf("温度:%u 湿度:%u\n", data[0], data[2]);
//空调温度
modbus_read_registers(ctx, 0, 2, data1);
printf("空调温度:%u\n", data1[0]);
//空调与灯开关
modbus_read_bits(ctx, 0, 4, dest);
printf("%d %d", dest[0], dest[1]);
//插入到共享内存
//温度
for (int i = 0; i < *num; i++)
{
LOGE("%d",add[i].key);
LOGE("%d",*num);
if (add[i].key == 101)
{
add[i].new_val.f_val = modbus_get_float_dcba(data);
printf("%f\n", add[i].new_val.f_val);
if (add[i].new_val.f_val != 0)
{
add[i].ret = 0;
}
else
{
printf("modbus get err\n");
}
}
else if (add[i].key == 102)
{
add[i].new_val.f_val = modbus_get_float_dcba(data + 2);
printf("%f\n", add[i].new_val.f_val);
if (add[i].new_val.f_val != 0)
{
add[i].ret = 0;
}
else
{
printf("modbus get err\n");
}
}
else if (add[i].key == 103)
{
add[i].new_val.b_val = dest[0];
printf("%d\n", add[i].new_val.b_val);
add[i].ret = 0;
}
else if (add[i].key == 104)
{
add[i].new_val.f_val = modbus_get_float_dcba(data1);
printf("%f\n", add[i].new_val.f_val);
if (add[i].new_val.f_val != 0)
{
add[i].ret = 0;
}
else
{
printf("modbus get err\n");
}
}
else if (add[i].key == 105)
{
add[i].new_val.b_val = dest[1];
printf("%d\n", add[i].new_val.b_val);
add[i].ret = 0;
}
sleep(1);
}
}
// shm_del(¶);
for (int i = 0; i < 3; i++)
{
printf("key = %d\n", add[i].key);
printf("type = %d\n", add[i].type);
printf("type = %f\n", add[i].new_val.f_val);
printf("key = %d\n", add[i].ret);
}
}
void *set_op(void *arg)
{
modbus_t *ctx = (modbus_t *)arg;
struct msg msb;
msb.mtype = 1;
//反序列化命令
while (1)
{
if (msg_queue_recv("modbus", &msb, sizeof(msb), 1, 0) > 0)
{
printf("recv from msga data = %s\n", msb.mdata);
cJSON *item = cJSON_Parse(msb.mdata);
if (!item)
{
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
}
else
{
printf("%s\n", cJSON_Print(item));
}
cJSON *MSB_DA = cJSON_GetObjectItem(item, "data"); //解析对象
int msb_key = cJSON_GetObjectItem(MSB_DA, "key")->valueint; //解析对象中的字符串
printf("%d\n", msb_key);
int msb_val = atoi(cJSON_GetObjectItem(MSB_DA, "val")->valuestring); //解析对象中的整型数字
printf("%d\n", msb_val);
//遍历链表
list_for_each(pos, &head)
{
tmp = list_entry(pos, struct mb_node_list, list);
if (msb_key == tmp->key)
{
if (msb_key == 103) //空调开关是线圈寄存器
{
modbus_write_bit(ctx, tmp->addr-1, msb_val);
}
else if (msb_key == 104) //空调温度控制,保持寄存器
{
u_int16_t op[4];
modbus_set_float_dcba((float)atof(cJSON_GetObjectItem(MSB_DA, "val")->valuestring),op);
modbus_write_registers(ctx, tmp->addr-1,2, op);
}
else if (msb_key == 105) //灯控,线圈寄存器
{
modbus_write_bit(ctx, tmp->addr-1, msb_val);
}
}
}
printf("\n");
}
else
{
perror("recv error:");
}
}
}
int main(int argc, char const *argv[])
{
INIT_LIST_HEAD(&head);
//打开文件
FILE *fd = fopen("/home/hq/mnt/config/node.json", "r+");
if (fd == NULL)
{
printf("fopen err\n");
}
//定位操作
fseek(fd, 0, SEEK_END);
//获取当前位置
int len = ftell(fd);
fseek(fd, 0, SEEK_SET);
char *p = (char *)malloc(len + 1);
fread(p, 1, len, fd);
cJSON *root = NULL;
root = cJSON_Parse(p);
if (!root)
{
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
}
// else
// {
// printf("%s\n", cJSON_Print(root));
// }
//解析json的数据
//解析version
char *ver = cJSON_GetObjectItem(root, "version")->valuestring;
printf("%s\n", ver);
//解析mb_dev
cJSON *DEV = cJSON_GetObjectItem(root, "mb_dev"); //解析对象
char *ADD = cJSON_GetObjectItem(DEV, "addr")->valuestring; //解析对象中的字符串
printf("%s\n", ADD);
int por = cJSON_GetObjectItem(DEV, "port")->valueint; //解析对象中的整型数字
printf("%d\n", por);
//解析modbus并插入链表
cJSON *MOD = cJSON_GetObjectItem(root, "modbus");
cJSON *DATA = cJSON_GetObjectItem(MOD, "data"); //解析数组
int DA_size = cJSON_GetArraySize(DATA);
cJSON *DA_item = DATA->child; //获取子对象
for (int i = 0; i < DA_size; i++)
{
struct mb_node_list *arr = (struct mb_node_list *)malloc(sizeof(struct mb_node_list));
printf("key:%d\n", cJSON_GetObjectItem(DA_item, "key")->valueint);
printf("name:%s\n", cJSON_GetObjectItem(DA_item, "name")->valuestring);
printf("type:%d\n", cJSON_GetObjectItem(DA_item, "type")->valueint);
printf("addr:%d\n", cJSON_GetObjectItem(DA_item, "addr")->valueint);
arr->node.key = cJSON_GetObjectItem(DA_item, "key")->valueint;
arr->node.addr = cJSON_GetObjectItem(DA_item, "addr")->valueint;
list_add(&arr->list, &head);
DA_item = DA_item->next;
}
//创建modbus实例
modbus_t *ctx = modbus_new_tcp(ADD, por);
if (ctx == NULL)
{
perror("modbus err");
return -1;
}
//设置从机id
modbus_set_slave(ctx, 1);
//建立链接,
if (modbus_connect(ctx) < 0)
{
perror("modbus connect err");
return -1;
}
//创建数据采集的线程
if (pthread_create(&t1, NULL, get_info, ctx) != NULL)
{
perror("get_info thread create err");
return -1;
if (pthread_create(&t1, NULL, get_info, ctx) != NULL)
{
perror("get_info thread create err");
return -1;
}
}
//创建指令处理的线程
if (pthread_create(&t2, NULL, set_op, ctx) != 0)
{
perror("set_op thread create err");
return -1;
}
//线程回收
pthread_join(t1, NULL);
pthread_join(t2, NULL);
//关闭套接字
pthread_cancel(ctx);
//释放空间
free(ctx);
return 0;
}