DS18B20S树莓派获取温度上报服务器(详解客户端)


先看看需要实现的功能吧,服务器端比较简单,主要是客户端方面需要实现的功能比较多,就很恼火,但是花了两个星期,也是在摸爬滚打中给掌握出来了,自己的能力也有了一点提升,还是值得记录一下的。
Gitee: ds18b20获取温度

1-客户端满足要求

满足服务器端:

  • =======================
  • 树莓派每隔时间采样一次并上报温度到服务器,这个时间满足参数输入;
  • 服务器满足端口、IP、是否后台运行参数输入设计;
  • 服务器退出后,客户端采样的数据临时暂存到数据库中,并且客户端一直等待连接;
  • 一旦服务器上线后,客户端自动重连并上报数据,包括数据库中暂存的数据,发送一条删除一条;
  • 数据库使用sqlite3,实现增(断开时放入数据)、删(发送一条删除一条)、查(获取表格中的行数);
  • 安装信号实现完美退出;
  • 自己创建简单的syslog日志系统记录报错信息;
  • =======================

2-客户端流程图

请添加图片描述

3-整体代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <time.h>
#include <sqlite3.h>
#include <signal.h>
#include <libgen.h>
#include <sys/types.h>   
#include <libgen.h>
#include <linux/tcp.h>
#include <netinet/in.h>
#include <netinet/ip.h>

int g_stop = 0;
typedef struct 
{
	char sed_del_buf[512];//暂存获取的第1条数据
	int  count;//获取的数据库的行数,判断数据库中是否有内容
}package;

float get_temperature();//获取温度
void  get_date_time(char *buf);//获取时间
void  syslog(const char* ms, ... );//日志系统,形如printf输入就可以了,会自己创建文件写进去
int   socket_client_connect(char *IP, int port);//连接服务器返回sockfd
void  create_table(sqlite3 *db);//创建表格
char* get_first_data(sqlite3 *db);//获取第一条数据,目的是发送给服务器
int   get_line_count(sqlite3 *db);//获取数据库表格的行数
void  delete_first_date(sqlite3 *db);//删除发送的内容(第一条数据)
void  insert_unsent_date(sqlite3 *db, char *buf, float temper, int port);//插入未发送的内容
int   inspect_socket_connected(int sockfd);//判断sockfd是否算开连接

int main (int argc, char *argv[])
{
	int                   sockfd = -1;
	int                   rv = -1;
	int                   port;
	char                  *IP;
	int                   ch = -1;
	float                 temper;
	char                  buf[512];
	char                  packet_buf[512];
	int                   return_time = 10;
	int                   run_daemon = -1;
	sqlite3               *db = 0;
	int                   rc = -1;
	int                   start_time = 0;
	time_t                end_time;
	int                   line_count;
	char                  first_data_buf[512];
	int                   whether_get_time;//这个比较关键,进入循环之后,初始化为0,获取到时间就制为1。后面判断四个方面:1.断开连接,获取到了温度;2.断开连接,没有获取到温度;3.没有断开连接,获取到温度;4.没有断开连接,没有获取到温度。四个方面来展开的。

	syslog("%s :Program start running!\n", buf);

	static struct option long_options[] = {
		{"ipaddr", required_argument, 0, 'i'},
		{"port", required_argument, 0, 'p'},
		{"help", no_argument, 0, 'h'},
		{"daemon", no_argument, 0, 'd'},
		{"return_time", required_argument, 0, 't'},
		{0, 0, 0, 0}
	};

	void usage( char *program_name)
	{
		printf("%s usage: \n", program_name);
		printf("-i(--ipaddr): server IP address \n");
		printf("-p(--port):server Port \n");
		printf("-h(--help): For help \n");
		printf("-d(--daemon): run background \n");
		printf("-t(--return_time): return temper time \n");
		return ;
	}

	while((ch = getopt_long(argc, argv, "i:p:ht:d", long_options, NULL)) != -1)
	{
		switch(ch)
		{
			case 'i':
				{
					IP = optarg;
					break;
				}
			case 'p':
				{
					port = atoi(optarg);
					break;
				}
			case 'h':
				{
					usage(argv[0]);
					return 0;
				}

			case 't':
				{
					return_time = atoi(optarg);
					break;
				}
			case 'd':
				{
					run_daemon = 1;
					break;

				}
		}
	}
	if(run_daemon > 0)
	{
		daemon(0, 0);
	}


	void stop(int signum)
	{
		if(signum == SIGINT) 
		{
			syslog("Perfect exit\n");

		}
		g_stop = 1;
	}

	signal(SIGINT, stop);

	/*create database*/
	rc = sqlite3_open("temper.db", &db);
	if(rc)
	{
		syslog("open database failure:%s\n", strerror(errno));
		return 0;
	}
	else
	{
		syslog("open database success!\n");
	}

	/*create table*/
	create_table(db);
	sockfd = socket_client_connect( IP, port);
	if(sockfd < 0)
	{
		syslog("sockfd failure:%s\n", strerror(errno));
	}

	while(!g_stop)
	{
		time(&end_time);       
		whether_get_time = 0;
		if((end_time - start_time) >= return_time)
		{
			memset(buf, 0, sizeof(buf));
			memset(packet_buf, 0, sizeof(packet_buf));
			temper = get_temperature();
			get_date_time(buf);

			snprintf(packet_buf, sizeof(packet_buf), "%d/%s/%f C", port, buf, temper);
			whether_get_time = 1;
			start_time = end_time;
		}

		rc = inspect_socket_connected(sockfd);
		
		if(rc == 1)
		{
			if(whether_get_time == 1)
			{
				rv = write(sockfd, packet_buf, strlen(packet_buf) );
				//printf("success\n");
				if( rv < 0 )
				{
					syslog("server disconnected\n");				
				}

			}
		}

		if(rc == 0)
		{
			close(sockfd);
			if(whether_get_time == 1)
			{	
				insert_unsent_date(db, buf, temper, port);
			}
			if(whether_get_time == 0)
			{

				sockfd = socket_client_connect(IP, port);
				if(sockfd > 0)
				{
					syslog("reconnected success\n");
				}
			}
		}

		if(rc == 1)
		{
			line_count = get_line_count(db);
			if(line_count > 0)
			{

				rv = snprintf(first_data_buf, sizeof(first_data_buf),"%s", get_first_data(db));

				if(rv > 0)
				{
					rv = write(sockfd, first_data_buf, strlen(first_data_buf) );
					if(rv <= 0)
					{
						syslog("send data from database failure:\n", strerror(errno));
						close(sockfd);
						continue;
					}
					syslog("send data from database success!\n" );

					delete_first_date(db);
				}

			}

		}
	}

	sqlite3_close(db);
	return 0;
}

int inspect_socket_connected(int sockfd)
{
	struct tcp_info info;

	if (sockfd <= 0)
	{
		return 0;
	}

	int len = sizeof(info);
	getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *) & len);
	if ((info.tcpi_state == 1)) 
	{
		return 1;
	}
	else 
	{
		return 0;
	}
}
void insert_unsent_date(sqlite3 *db, char *buf, float temper, int port)
{
	int rc = -1;
	char *sql_insert;
	sql_insert = sqlite3_mprintf("INSERT INTO temper VALUES(NULL,'%d','%s','%f')", port, buf, temper);
	rc = sqlite3_exec(db, sql_insert, NULL, NULL, NULL);
	if(rc != SQLITE_OK)
	{
		syslog("insert data failure:%s\n", strerror(errno));
	}
	else
		syslog("insert data success\n");
}

void delete_first_date(sqlite3 *db)
{   
	int rc = -1; 
	char *sql_delete;
	sql_delete = "delete from temper limit 1";
	rc = sqlite3_exec(db, sql_delete, NULL, NULL, NULL);
	if(rc != SQLITE_OK)
	{
		syslog("dalete data failure:%s\n", strerror(errno));
	}   
	else
		syslog("delete data success!\n");
}


int callback_count(void *data, int argc, char **argv, char **azColName)
{
	package *sq_count;
	sq_count = (package*)data;
	int i;
	for(i=0; i<argc; i++){
		sq_count->count = atoi(argv[i] ? argv[i] : "NULL");
	}
	//printf("count:%d\n", sq_count->count);
	return 0;
}


int get_line_count(sqlite3 *db)
{
	package tmp_count;
	int rc = -1;
	char *sql_count;
	sql_count = "select count(*) from temper";
	rc = sqlite3_exec(db, sql_count, callback_count, &tmp_count, NULL);
	return tmp_count.count;
}

int callback(void *data, int argc, char **argv, char **azColName)
{       
	int i;
	int ofset = 0;
	int rv = 0;
	package *sq_data;
	sq_data = (package*)data;
	memset(sq_data -> sed_del_buf, 0, sizeof(sq_data -> sed_del_buf));
	for(i=0; i<argc; i++)
	{       
		rv = snprintf( (sq_data -> sed_del_buf) + ofset, sizeof(sq_data -> sed_del_buf) - ofset, "%s ", argv[i] ? argv[i] : "NULL");
		ofset += rv;
	}
}

char* get_first_data(sqlite3 *db)
{
	package *tmp_data = (package*)malloc(sizeof (package));
	int rc = -1;
	char first_data_buf[512];
	char *sql_select;
	sql_select = "select *from temper limit 1";
	rc = sqlite3_exec(db, sql_select, callback, tmp_data, NULL);
	return tmp_data -> sed_del_buf;
}


void create_table(sqlite3 *db)
{
	int rc =-1;
	const char * creat = "create table temper("
		"ID  integer primary key autoincrement,"
		"DEVICE varchar(10), "
		"Data varchar(500),"
		"Temperature valchar(30));";
	rc = sqlite3_exec(db, creat, 0, 0, NULL );
	if(rc != SQLITE_OK)
	{
		syslog("table already exit\n");
	}
	else
	{
		syslog("Create table failure:%s\n",strerror(errno));
	}
}

float get_temperature()
{
	char path[50] = "/sys/bus/w1/devices/";
	int     fd = -1;
	int     rv = -1;
	DIR     *dirp;
	struct  dirent *direntp;
	char    buf[128];
	char    *ptr;
	char    chip[128];
	float   temp;

	if( (dirp = opendir(path)) == NULL)
	{
		syslog("Opendir failure: %s\n", strerror(errno));
		return -1;
	}

	while((direntp = readdir(dirp)) != NULL)
	{
		if(strstr(direntp -> d_name, "28-"))
		{
			strcpy(chip, direntp -> d_name);
		}
	}

	closedir(dirp);

	strncat( path, chip, sizeof(path)-1 );
	strncat( path, "/w1_slave", sizeof(path)-1 );

	if((fd = open(path, O_RDONLY)) < 0)
	{
		syslog("Open %s failure:%s\n",path, strerror(errno));
		return -2;
	}
	memset(buf, 0, sizeof(buf));
	if((rv = read(fd, buf ,sizeof(buf))) < 0)
	{
		syslog("Read %s failure:%s\n",path, strerror(errno));
		return -3;
	}
	ptr = strstr(buf, "t=") + 2;
	temp = atof(ptr)/1000;
	close(fd);
	return temp;
}


void get_date_time(char *buf)
{
	struct tm *tm_ptr;
	time_t the_time;
	(void)time(&the_time);
	tm_ptr = gmtime(&the_time);
	sprintf(buf, "%02d-%02d-%02d  %02d:%02d:%02d",tm_ptr->tm_year+1900, tm_ptr->tm_mon+1, tm_ptr->tm_mday,(tm_ptr->tm_hour)+8, tm_ptr->tm_min, tm_ptr->tm_sec);
}


int socket_client_connect(char *IP, int port)
{
	int      sockfd = -1;
	int      rv = 1;
	int      on = 1;
	struct   sockaddr_in server_addr;
	int      server_fd = -1;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		//syslog("Creat socket failure: %s\n", strerror(errno));
		rv = -1;
		return rv;
	}

	//printf("Creat sockfd[%d] successfully!\n", sockfd);

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	inet_aton(IP, &server_addr.sin_addr);

	server_fd = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if(server_fd < 0)
	{
		//syslog("Connect socket failure: %s\n", strerror(errno));
		rv = -2;
		goto Cleanup;
	}
	//printf("Connect server[%s:%d] successfully!\n", IP, sockfd);
Cleanup:
	if(rv < 0)
	{
		close(sockfd);
	}
	else
	{
		rv = sockfd;
	}

	return rv;
}


/*log record*/
void syslog(const char* ms, ... )
{
	char wzLog[1024];
	char buffer[2049];
	memset(wzLog, 0, sizeof(wzLog));
	memset(buffer, 0, sizeof(buffer));
	char time_buf[1024];

	va_list args;
	va_start(args, ms);
	vsprintf( wzLog ,ms,args);
	va_end(args);

	get_date_time(time_buf);

	snprintf(buffer, sizeof(buffer), "%s %s\n", time_buf, wzLog);
	FILE* file = fopen("client_syslog.log","a+");
	fwrite(buffer, 1, strlen(buffer), file);
	fclose(file);
	return ;
}

需要掌握的知识点或者需要注意的地方:
1.TCP/IP socket编程
2.sqlite3数据库的使用,建立数据库,建立表格,插入数据,获取数据,删除数据等
3.动态库静态库的知识,这个主要是树莓派上没有sudo权限,只能源码安装sqlite3,然后gcc的时候需要指定头文件和动态库的位置,这个想了解的看这篇:
源码安装安装sqlite3
4.makefile的使用,这个我还没实现,到时候写一篇博客记录一下
5.gitee上传文件获取文件
linux上使用git命令上传下载文件
6.函数命名简单明了,一看就懂。自定义函数已完成任务为主,尽量可以直接返回需要获取的内容
7.不懂就去网上查找,像自定义日志或者监测sockfd是否断开一般网上都有代码的,弄懂然后拿过来用,也省的自己写了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值