目录
一、DS18B20温度传感器
二、逻辑分析
三、实战操作
1、服务端
2、客户端
3、运行结果
一、DS18B20温度传感器
DS18B20是比较常用到的温度传感器,采用单总线控制。是美国DALLAS半导体公司继DS1820之后最新推出的一种改进型智能温度传感器。关于该温度传感器的具体介绍可以看这里-DS18B20。
以下是连接DS18B20的基本步骤:
(1)DS18B20有三个引脚:VCC(电源)、GND(地)和DQ(数据引脚),DQ引脚连接到微控制器的输入引脚,用于数据通信。
(2)DS18B20通过1-wire协议与主控制器通信,树莓派需要相应的1-wire库或者驱动程序。
(3)读取DS18B20获取的温度值,并进行相应温度转换和计算
二、逻辑分析
我使用的是树莓派4B,DS18B20将踩到的的温度值存放在 /sys/bus/w1/devices/28-0317320a8aff/w1_slave 文件中(不同的传感器型号“28-”处会略有不同),在终端使用命令 cat /sys/bus/w1/devices/28-0317320a8aff/w1_slave即可看到文件内容,如下图所示:
前面那些数据我们不管他,可以看到“t=11250”,这个就是我这个设备上的实时温度(11.25℃)。了解到这些之后,我们来捋一捋代码里面要用什么样的方式来读取这个温度,并显示出来。
(1)socket编程,建立客户端与服务端的通信。对socket网络编程不熟悉的同学可以看《APUE学习之socket网络编程》。
(2)由于每个DS18B20的产品序列号不一样,那么温度保存文件的路径也就会不一样,我们没办法直接调用read来读取文件的内容。那该怎么办呢?用opendir()和readdir()的组合,打开每个设备都一样的默认路径(/sys/bus/w1/devices/),并读取路径下的内容。
(3)当打开上面的路径之后,因为型号的原因,大家的路径将有所不同,我们可以找出“28-”开头的的文件夹,并将其名称保存到一个缓存区中,然后用这个缓存区的内容来更新目标路径。
(4)用open()和read()读取目标路径的内容,找出其中“t=”的部分,并记录下来。随后上传至客户端。
三、实战操作
题目要求:
(1)利用网络socket编程,编写一个服务端和一个客户端,实现通信;
(2)使用树莓派DS18B20温度传感器每10秒采集一次温度,并上传至服务端。
代码如下:
1、服务端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <ctype.h>
#define BACKLOG 13 /*令listen最大监听数为13*/
#define PORT 8888 /*监听8888号端口*/
int main(int argc,char *argv[])
{
int fd = -1;
int client_fd = -1;
int rv = -1;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
char buf[1024];
if((fd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("Socket failure:%s\n",strerror(errno));
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if((bind(fd,(struct sockaddr *)&server_addr,sizeof(server_addr)))<0)
{
printf("bind failure:%s\n",strerror(errno));
return -2;
}
listen(fd,BACKLOG);
printf("\nWaitting for client...\n");
if((client_fd=accept(fd,(struct sockaddr *)&client_addr,&client_len))<0)
{
printf("Accept failure:%s\n",strerror(errno));
return -3;
}
printf("Connected with client [%d]\n",client_fd);
while(1)
{
memset(&buf,0,sizeof(buf));
if((rv=read(client_fd,buf,sizeof(buf))) <= 0)
{
printf("Read failure or get disconnect:%s\n",strerror(errno));
return -4;
}
printf("read %d Byte data from client [%d] :%s\n",rv,client_fd,buf);
if(write(client_fd,buf,rv)<0)
{
printf("Write failure:%s\n",strerror(errno));
return -5;
}
}
close(client_fd);
close(fd);
}
2、客户端
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define PORT 8888
#define IP "127.0.0.1"
int get_temperature(float *temp); /*该函数用于ds18b20采集温度*/
int main(int argc,char *argv[])
{
int fd = -1;
int rv = -1;
struct sockaddr_in servaddr;
socklen_t len = sizeof(servaddr);
int r = -1;
char t[16];
float temp;
char buf[128];
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
printf("create sockfd failure:%s\n",strerror(errno));
return -1;
}
printf("create socket_fd [%d] successfully!\n",fd);
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
inet_aton(IP,&servaddr.sin_addr);
if((rv = connect(fd,(struct sockaddr*)&servaddr,len)) < 0)
{
printf("client connect failure:%s\n",strerror(errno));
return -2;
}
printf("client connect successfully!\n");
while(1)
{
if((r = get_temperature(&temp)) < 0)
{
printf("get temperature failure:%s\n",strerror(errno));
return -7;
}
printf("get temperature successfully and temperature is %.2f!\n",temp);
memset(t,0,sizeof(t));
sprintf(t,"%.2f",temp);
if((write(fd,t,sizeof(t))) < 0)
{
printf("write failure:%s\n",strerror(errno));
return -8;
}
printf("write bytes:%s\n",t);
if((rv = read(fd,buf,sizeof(buf))) <= 0)
{
printf("read failure or get disconnect:%s\n",strerror(errno));
return -9;
}
printf("read [%d] bytes:%s\n",rv,buf);
sleep(10);
}
close(fd);
return 0;
}
int get_temperature(float *temp)
{
int fd = -1;
int found = 0;
char w1_path[64] = "/sys/bus/w1/devices/";
DIR *dirp = NULL;
struct dirent *direntp = NULL;
char chip_sn[32];
char buf[128];
char *ptr;
if( !(dirp = opendir(w1_path)) )
{
printf("open directory failure:%s\n",strerror(errno));
return -3;
}
while( direntp = readdir(dirp) )
{
if( strstr(direntp->d_name,"28-") )
{
strncpy(chip_sn,direntp->d_name,sizeof(chip_sn));
found = 1; /*found用于确保是否能找到温度存储的文件*/
}
}
if( !found )
{
printf("can not find ds18b20 directory!\n");
return -4;
}
strncat(w1_path,chip_sn,sizeof(w1_path)-strlen(w1_path)); /*更新文件路径*/
strncat(w1_path,"/w1_slave",sizeof(w1_path)-strlen(w1_path));
closedir(dirp);
fd = open(w1_path,O_RDONLY); /*打开文件*/
if(fd < 0)
{
printf("open file failure:%s\n",strerror(errno));
return -5;
}
lseek(fd,0,SEEK_SET); /*不设置文件偏移量可能造成读不到任何内容*/
memset(buf,0,sizeof(buf));
read(fd,buf,sizeof(buf));
ptr = strstr(buf,"t="); /*找到t=字符串*/
if(NULL == ptr)
{
printf("can not find t= string\n",strerror(errno));
return -6;
}
ptr += 2; /*将指针ptr移至t=后的温度值处*/
*temp = atof(ptr)/1000; /*进行温度转换和相应计算*/
close(fd);
return 0;
}
3、运行结果
服务端:
客户端:
到这里,这道问题就已经成功解决了!加油,相信你自己,你一定是最棒的!