M0设备开发小项目指导(采集控制)——学习记录
基于NXPLPC11C4微控制器(ARMCortex-M0内核)。集成多种传感器、RFID、ZigBee、OLED显示模块等。丰富的硬件资源及物联网相关实验程序,适合于物联网教学及工程师做研发参考平台。如果是虚拟机,按照如下步骤进行连接。用USB线将模块和电脑的USB口连接。打开模块的开关,将设备连接到虚拟机中。使用dmesg命令可以看到内核的连接输出信息。/dev。...
目录
M0简介
用下图的在华清远见学习期间,华清专为物联网教学开发的 FS11C14 开发板(Cortex-M0 ARM架构),基于 NXP LPC11C4 微控制器(ARM Cortex-M0 内核)。集成多种传感器、RFID、ZigBee、OLED 显示模块等。丰富的硬件资 源及物联网相关实验程序,适合于物联网教学及工程师做研发参考平台。
如果是虚拟机,按照如下步骤进行连接。
- 用USB线将模块和电脑的USB口连接。
- 打开模块的开关,将设备连接到虚拟机中。
- 使用dmesg命令可以看到内核的连接输出信息。
- 连接成功后,在/dev目录下会产生串口的设备节点。注意,一般是ttyUSB0,偶尔也会变为ttyUSB1
$ ls /dev/ttyUSB0 /dev/ttyUSB0
- 打开M0设备的开关,等待连接成功后,即可通过这个设备节点进行通信。
-
如果出现
vm虚拟机上栏的“虚拟机-可移动设备”里没有usb设备
的问题,按如下网址解决:vm虚拟机上栏的“虚拟机-可移动设备”里没有usb设备_百度知道
如果上述方案仍解决不了,重装一下
VMware
软件尝试,如果仍解决不了,更换电脑尝试Linux串口编程
串口属性
这部分简单了解即可:Linux串口属性设置
Linux串口设置
串口名
如果是普通串口:一般名称为 /dev/ttySACX
如果为usb转串口:一般名称为 /dev/ttyUSBx
串口属性设置
波特率、数据位、停止位、校验位、流控
Linux中代表串口属性的结构体 struct termios,串口的设置代码是标准接口。
int fd = open("/dev/ttyUSB0", O_RDWR);
struct termios options;
cfsetispeed(&options, B115200); //输入波特率
cfsetospeed(&options, B115200); //输出波特率
options.c_cflag |= CS8; //数据位可取值:CS5、CS6、CS7、CS8
options.c_cflag &= ~CSTOPB; //停止位为2或者1,与为1,或为2
options.c_iflag |= IGNPAR; //校验位,一般取无校验
options.c_iflag &= ~(ICRNL | IXON); //关闭流控
tcsetattr(fd, TCSANOW, &options);
//调用read/write进行读写操作即可
read();
write();
M0数据采集上报
M0会将传感器的数据封装成下面的结构体发送给网关设备。
struct env_info
{
uint8_t head[3]; //标识位st: (保留字段,暂未使用)
uint8_t type; //数据类型 (保留字段,暂未使用)不
uint8_t snum; //房间编号(保留字段,暂未使用)
uint8_t temp[2]; //温度
uint8_t hum[2]; //湿度
uint8_t x; //三轴信息
uint8_t y;
uint8_t z;
uint32_t ill; //光照
uint32_t bet; //电池电量
uint32_t adc; //电位器信息
};
对数据的采集只需要打开串口M0会自动读取上面结构体中的成员的数据,所有读取其实很简单,要打印也只需要取对应结构体成员。所有要上报的话也会非常简单。
//打开串口
if ((fd = open(DEV_UART, O_RDWR)) < 0)
{
perror("open uart err\n");
return -1;
}
serial_init(fd); //初始化串口
while (1) //采集m0打印到终端
{
int nbytes = 0;
nbytes = read(fd, &envinfo, ENV_LEN);
sleep(1);
if (nbytes == ENV_LEN)
{
printf("raw data ill=%d\n, 三轴:x=%d, y=%d, z=%d\n",
envinfo.ill, envinfo.x, envinfo.y, envinfo.z); //光照和三轴
printf("bet=%d,adc=%d\n", envinfo.bet, envinfo.adc); //电池电量与电位器信息
}
else
{
printf("err data\n");
}
temperature = envinfo.temp[0] + data_atof(envinfo.temp[1]); //温度
humidity = envinfo.hum[0] + data_atof(envinfo.hum[1]); //湿度
printf("conver temperature=%0.2f, humidity=%0.2f\n",
temperature, humidity);
putchar(10);
M0对文档和湿度的整数和小数做了分离(上面代码中写的很详细),收到数据后需要程序组合,这部分和硬件传感器的规定有关,这里直接给出转换程序参考:
void serial_init(int fd) //初始化
{
struct termios options;
tcgetattr(fd, &options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CSIZE;
options.c_cflag &= ~CRTSCTS;
options.c_cflag |= CS8;
options.c_cflag &= ~CSTOPB;
options.c_iflag |= IGNPAR;
options.c_iflag &= ~(ICRNL | IXON);
options.c_oflag = 0;
options.c_lflag = 0;
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
tcsetattr(fd, TCSANOW, &options);
}
float data_atof(char unitl)
{
if (unitl > 100)
{
return unitl / 1000.0;
}
else if (unitl > 10)
{
return unitl / 100.0;
}
else
{
return unitl / 10.0;
}
}
int data_atoi(const char *cDecade)
{
int result = 0;
if (' ' != cDecade[0])
{
result = (cDecade[0] - 48) * 10;
}
result += cDecade[1] - 48;
return result;
}
float data_adc(unsigned int ratio)
{
return ((ratio * 3.3) / 1024);
}
将m0中的数据上报到共享内存中,这样前端或者边缘上报进程直接从共享内存取值即可。
int ret = shm_init(&livingroom, "shm_m0", 1024); //打开livingroom共享内存
if (ret < 0)
{
printf("m0 shm_init err\n");
return NULL;
}
msgaddr = shm_getaddr(&livingroom); //映射地址
if (msgaddr == NULL)
{
perror("shm_getaddr err\n");
return NULL;
}
//上报温度
msgaddr[0].key = 1;
msgaddr[0].type = 3;
msgaddr[0].old_val.f_val = msgaddr[0].new_val.f_val;
msgaddr[0].new_val.f_val = temperature;
//上报湿度
msgaddr[1].key = 2;
msgaddr[1].type = 3;
msgaddr[1].old_val.f_val = msgaddr[1].new_val.f_val;
msgaddr[1].new_val.f_val = humidity;
//上报光照
msgaddr[2].key = 3;
msgaddr[2].type = 2;
msgaddr[2].old_val.i_val = msgaddr[2].new_val.i_val;
msgaddr[2].new_val.i_val = envinfo.ill;
M0的控制
上报是上报到共享内存,下发和接收指令通过消息队列,因为我们库中封装好了则直接调用,前面共享内存也一样。
if (msg_queue_recv("msg_mbapp", &recv_buf, sizeof(recv_buf), 2, 0) < 0) //接受消息队列
{
perror("msg_queue_recv err\n");
return NULL;
}
控制首先要从上面接收下发的指令。因为我们规定用json的格式,所有开始需要做json的解析。
char *jsonCon = recv_buf.mdata;
cJSON *root = NULL;
cJSON *item = NULL;
int ret;
root = cJSON_Parse(jsonCon);
item = cJSON_GetObjectItem(root, "data");
int key = cJSON_GetObjectItem(item, "key")->valueint;
M0设备控制
网关设备只需要将控制指令发送给M0设备即可实现M0设备的控制。具体的控制指令定义如下:
根据以上协议,了解相应控制:
unsigned char cmd1 = 0, cmd2 = 0, cmd3 = 0, cmd4 = 0, cmd5 = 0, cmd6 = 0, cmd7 = 0, cmd8 = 0, cmd9 = 0, cmd10 = 0;
cmd1 = 0x80; //风扇关闭
cmd2 = 0x81; //一档
cmd3 = 0x82; //二档
cmd4 = 0x83; //三档
cmd5 = 0x90; //蜂鸣器关
cmd6 = 0x91; //蜂鸣器开
cmd7 = 0x92; //自动报警关
cmd8 = 0x93; //自动报警开
cmd9 = 0xA0; //led管
cmd10 = 0xA1; //led开
具体实现伪代码如下:
switch (cJSON_GetObjectItem(item, "key")->valueint)
{
case 4: //led灯
msgaddr[key - 1].old_val.b_val = msgaddr[key - 1].new_val.b_val;
msgaddr[key - 1].new_val.b_val = atoi(cJSON_GetObjectItem(item, "val")->valuestring);
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 0)
ret = write(fd, &cmd9, 1); //led灯关
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 1)
ret = write(fd, &cmd10, 1); //led灯开
break;
case 5:
msgaddr[key - 1].old_val.b_val = msgaddr[key - 1].new_val.b_val;
msgaddr[key - 1].new_val.b_val = atoi(cJSON_GetObjectItem(item, "val")->valuestring);
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 0)
ret = write(fd, &cmd5, 1); //蜂鸣器关
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 1)
ret = write(fd, &cmd6, 1); //蜂鸣器开
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 2)
ret = write(fd, &cmd7, 1); //自动报警关
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 3)
ret = write(fd, &cmd8, 1); //自动报警开
break;
case 6:
msgaddr[key - 1].old_val.b_val = msgaddr[key - 1].new_val.b_val;
msgaddr[key - 1].new_val.b_val = atoi(cJSON_GetObjectItem(item, "val")->valuestring);
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 0)
ret = write(fd, &cmd1, 1); //风扇关
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 1)
ret = write(fd, &cmd2, 1); //风扇一档
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 2)
ret = write(fd, &cmd3, 1); //风扇二档
if (atoi(cJSON_GetObjectItem(item, "val")->valuestring) == 3)
ret = write(fd, &cmd4, 1); //风扇三档
break;
default:
break;
}
最后需要将结果返回回去,我们依旧是按照json形式返回,添加一个json形式的返回结果并发送回消息队列:
//创建空对象
cJSON *jsonBack = cJSON_CreateObject();
//在对象上添加键值对
cJSON_AddStringToObject(jsonBack, "type", "1");
cJSON_AddStringToObject(jsonBack, "result", "0");
cJSON_AddStringToObject(jsonBack, "msg", "success");
struct msgbuf send_buf; //发送的消息队列
send_buf.mtype = 1; //设置消息类型
strcpy(send_buf.mdata, cJSON_Print(jsonBack));
if (msg_queue_send("msg_m0", &send_buf, sizeof(send_buf), 0) < 0) //发送到消息队列
{
printf("msg_queue_send error\n");
return NULL;
}
更多推荐
所有评论(0)