目录

M0简介

Linux串口编程

串口属性

Linux串口设置

串口名

串口属性设置

M0数据采集上报

M0的控制

M0设备控制


M0简介

        用下图的在华清远见学习期间,华清专为物联网教学开发的 FS11C14 开发板(Cortex-M0 ARM架构),基于 NXP LPC11C4 微控制器(ARM Cortex-M0 内核)。集成多种传感器、RFID、ZigBee、OLED 显示模块等。丰富的硬件资 源及物联网相关实验程序,适合于物联网教学及工程师做研发参考平台。

 

如果是虚拟机,按照如下步骤进行连接。

  1. 用USB线将模块和电脑的USB口连接。
  2. 打开模块的开关,将设备连接到虚拟机中。

  3. 使用dmesg命令可以看到内核的连接输出信息。

  4. 连接成功后,在/dev目录下会产生串口的设备节点。注意,一般是ttyUSB0,偶尔也会变为ttyUSB1
    $ ls /dev/ttyUSB0
    /dev/ttyUSB0

  5. 打开M0设备的开关,等待连接成功后,即可通过这个设备节点进行通信。
  6. 如果出现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;
        }

 

 

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐