目录
一、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 ",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 ",strerror(errno)); return -2; } listen(fd,BACKLOG); printf(" Waitting for client... "); if((client_fd=accept(fd,(struct sockaddr *)&client_addr,&client_len))<0) { printf("Accept failure:%s ",strerror(errno)); return -3; } printf("Connected with client [%d] ",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 ",strerror(errno)); return -4; } printf("read %d Byte data from client [%d] :%s ",rv,client_fd,buf); if(write(client_fd,buf,rv)<0) { printf("Write failure:%s ",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 ",strerror(errno)); return -1; } printf("create socket_fd [%d] successfully! ",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 ",strerror(errno)); return -2; } printf("client connect successfully! "); while(1) { if((r = get_temperature(&temp)) < 0) { printf("get temperature failure:%s ",strerror(errno)); return -7; } printf("get temperature successfully and temperature is %.2f! ",temp); memset(t,0,sizeof(t)); sprintf(t,"%.2f",temp); if((write(fd,t,sizeof(t))) < 0) { printf("write failure:%s ",strerror(errno)); return -8; } printf("write bytes:%s ",t); if((rv = read(fd,buf,sizeof(buf))) <= 0) { printf("read failure or get disconnect:%s ",strerror(errno)); return -9; } printf("read [%d] bytes:%s ",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 ",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! "); 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 ",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 ",strerror(errno)); return -6; } ptr += 2; /*将指针ptr移至t=后的温度值处*/ *temp = atof(ptr)/1000; /*进行温度转换和相应计算*/ close(fd); return 0; }
3、运行结果
服务端:
客户端:
到这里,这道问题就已经成功解决了!加油,相信你自己,你一定是最棒的!