树莓派获取温度并上报
1.项目介绍
1.1
树莓派上通过一线协议连接DS18B20,采用网络socket编程同时实现客户端与服务器端程序,客户端实现定时上报温度给服务器端的功能,服务器端则将接收到的数据永久的存储在数据库中。
1.2客户端功能介绍
·连接服务器
·定时采样
·网络异常断开后能在网络恢复正常后自动重连
·网络异常时正常采样,并将采样数据存储在数据库中
·重连后将数据库中的数据发送给服务器端,并将发送的数据从数据库中删除
1.3服务器端功能介绍
·监听指定端口
·采用多路复用实现客户端并发上报
·将上报的数据解析成功后永久存储进数据库中
·压力测试
2.代码实现
2.1客户端代码
#include "main.h"
static void print_usage(char *progname);
int main(int argc,char **argv)
{
sock_t sock;
struct sockaddr_in servaddr;
char *servip=NULL;
int port=0;
int rv=-1;
int ch;
char *progname=NULL;
char *logfile="sock_client.log";
int loglevel=LOG_LEVEL_INFO;
int logsize=10;
int daemon_run=1;
int pack_bytes=0;
pack_data_t pack_data;/* Declare a struct variable */
pack_t pack=packet_data;/* using string packet */
int len=20;
int size=64;
int interval;
time_t last_time=0;
char buf[1024];
char data_buf[1024];
int sample_flag=0;
struct option opts[]=
{
{"ipaddr",required_argument,NULL,'i'},
{"port",required_argument,NULL,'p'},
{"interval",required_argument,NULL,'t'},
{"help",no_argument,NULL,'h'},
{"debug",no_argument,NULL,'d'},
{NULL,0,NULL,0}
};
progname = (char *)basename(argv[0]);
while((ch=getopt_long(argc,argv,"i:p:t:hd",opts,NULL))!=-1)
{
switch(ch)
{
case 'i':
servip=optarg;
break;
case 'p':
port=atoi(optarg);
break;
case 't':
interval=atoi(optarg);
break;
case 'd':
daemon_run=0;
logfile="console";
loglevel=LOG_LEVEL_DEBUG;
break;
case 'h':
print_usage(argv[0]);
return 0;
default:
break;
}
}
if(!servip||!port)
{
print_usage(argv[0]);
return 0;
}
if( log_open( logfile, loglevel, logsize, THREAD_LOCK_NONE) < 0 )
{
printf("Initial log system failed\n");
return 1;
}
install_default_signal();
sock_init(&sock,servip,port);
if(database_table_init(DB_NAME,TABLE_NAME)<0)
{
log_error("Create table failure:%s\n",strerror(errno));
}
while( !g_signal.stop )
{
sample_flag=0;/* set a flag */
if(check_interval_time(&last_time,interval)>0)
{
if(get_temperature(&pack_data.tempera)<0)
{
log_error("Get tempareture failure:%s\n",strerror(errno));
}
if(get_dev(pack_data.Id,len,SN)<0)
{
log_error("Get ID failure:%s\n",strerror(errno));
}
if(get_tm(pack_data.local_t,size)<0)
{
log_error("Get time failure:%s\n",strerror(errno));
}
pack_bytes = pack(&pack_data,buf,sizeof(buf));
if( pack_bytes>0)
{
sample_flag = 1;
}
}
/* if the server is disconnected then reconnect it */
if( sock.connfd < 0 )
{
if((socket_connect(&sock))<0)
{
log_error("Reconnect server failure:%s\n",strerror(errno));
sock_close(&sock);
}
else
log_info("Reconnect to server successfully\n");
}
/* Check whether the server is disconnected */
if(socket_alive(sock.connfd)<0)
{
sock_close(&sock);
if( sample_flag )
{
if(database_data_insert(TABLE_NAME,&pack_data)<0)
{
log_error("Insert data error\n");
}
else
{
log_info("Insert data successfully\n");
}
}
continue;
}
/* The socket is connected */
if(sample_flag)
{
if(sock_send_data(&sock,buf,pack_bytes)<0)
{
log_error("Write 1data to server failure:%s\n",strerror(errno));
if(database_data_insert(TABLE_NAME,&pack_data)<0)
{
log_info("Insert data error\n");
}
else
{
log_info("Insert data successfully\n");
}
sock_close(&sock);
continue;
}
}
/* Check if there is data in the database */
if(!database_data_select(TABLE_NAME))
{
memset(data_buf,0,sizeof(data_buf));
if(!database_data_take(TABLE_NAME,data_buf))
{
if(sock_send_data(&sock,data_buf,pack_bytes)<0)
{
log_error("Write data to server failure:%s\n",strerror(errno));
sock_close(&sock);
}
else
{
database_data_delete(TABLE_NAME);
}
}
}
// usleep(50000);
}//end of while
CleanUp:
sock_close(&sock);
database_close();
log_close();
return 0;
}
void print_usage(char *progname)
{
printf("%s usage:\n",progname);
printf("-i(--ipaddr):specify server IP address.\n");
printf("-p(--port):specify server port.\n");
printf("-t(--time):sampling interval.\n");
printf("-d(--debug):running in debug mode.\n");
printf("-h(--help):print this help information.\n");
return ;
}
/* set interval */
int check_interval_time(time_t *last_time,int interval)
{
int need=0;
time_t now;
time(&now);
if(now>=*last_time+interval)
{
need=1;
*last_time=now;
}
return need;
}
2.2客户端头文件
2.2.1database.h
#ifndef _DATABASE_H
#define _DATABASE_H
#include "data_pack.h"
/* Description:close the database */
extern void database_close(void);
/* Description:Create database and table */
extern int database_table_init(char *db_name,char *table_name);
/* Description:Delete the table */
extern int table_delete(char *table_name);
/* Description:Insert data to database */
extern int database_data_insert(char *table_name,pack_data_t *pack_data);
/* Description:Delete the first row of data from the database */
extern int database_data_delete(char *table_name);
/* Description:Take the first row of data from database */
extern int database_data_take(char *table_name,char *data_buf);
/* Description:Query whether there is data in the database */
extern int database_data_select(char *table_name);
#endif
2.2.2data_pack.h
#ifndef _DATA_PACK_H
#define _DATA_PACK_H
#define DEV_LEN 16
#define TIME_LEN 32
/* Description:Define a new struct named pack_data_t */
typedef struct pack_data_s
{
char Id[DEV_LEN];
char local_t[TIME_LEN];
float tempera;
}pack_data_t;
/* Description:Define a new struct pointer */
typedef int (*pack_t)(pack_data_t *pack_data,char *buf,int size);
/* Description:Get id of the device */
extern int get_dev(char* ID,int len,int sn);
/* Description:Get current time */
extern int get_tm(char* localt, int size);
extern int packet_data(pack_data_t *pack_info,char *buf,int size);
#endif
2.2.3ds18b20_get_temp.h
#ifndef _DS18B20_GET_TEMP_
#define _DS18B20_GET_TEMP_
#if 0
#define CONFIG_DEBUG
#ifdef CONFIG_DEBUG
#define dbg_print(format,args...) printf(format,##args)
#else
#define dbg_print(format,args...) do{} while(0)
#endif
#endif
extern int get_temperature(float *temp);
#endif
2.2.4proc.h
#ifndef _PROC_H_
#define _PROC_H_
#include <signal.h>
typedef struct proc_signal_s
{
int signal;
unsigned stop;
}proc_signal_t;
extern proc_signal_t g_signal;
extern void proc_default_sighandler(int sig);
/* Description:install default signal process functions */
extern void install_default_signal(void);
#endif
2.2.5sock.h
#ifndef _SOCK_H
#define _SOCK_H
#define HOST_LEN 128
/* Description:Define a new struct named sock_t */
typedef struct sock_s
{
char host[HOST_LEN];
int port;
int connfd;
}sock_t;
/* Description:Initialize the struct */
extern int sock_init(sock_t *sock,char *host,int port);
/* Description:Determine whether the server is disconnected */
extern int socket_alive(int fd);
/* Description:Connect to server */
extern int sock_connect(sock_t *sock);
/* Description:Send data to the server */
extern int sock_send_data(sock_t *sock,char *buf,int bytes);
/* Description:Close socket */
extern int sock_close(sock_t *sock);
/* Description:Connect to server */
extern int socket_connect(sock_t *sock);
#endif
2.3服务器端代码
#include "main.h"
int main(int argc,char **argv)
{
int listenfd,connfd;
int serv_port=0;
int daemon_run=0;
char *progname=NULL;
int opt;
int rv;
int i,j;
int found;
int epollfd;
int events;
struct epoll_event event;
struct epoll_event event_array[MAX_EVENTS];
char buf[1024];
char id[16];
float temp;
char local_t[128];
sqlite3 *db;
struct option long_options[]=
{
{"deamon",no_argument,NULL,'b'},
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,0}
};
progname=basename(argv[0]);
while((opt=getopt_long(argc,argv,"bp:h",long_options,NULL))!=-1)
{
switch(opt)
{
case 'b':
daemon_run=1;
break;
case 'p':
serv_port=atoi(optarg);
break;
case 'h':
print_usage(progname);
return EXIT_SUCCESS;
default:
break;
}
}
if(!serv_port)
{
print_usage(progname);
return -1;
}
set_socket_limit();
if((listenfd=socket_server_init(NULL,serv_port))<0)
{
dbg_print("%s server listen on %d port failure:%s\n",argv[0],serv_port,strerror(errno));
return -2;
}
dbg_print("%server start listen on %d port\n",argv[0],serv_port);
if(daemon_run)
{
daemon(0,0);
}
if((epollfd=epoll_create(MAX_EVENTS))<0)
{
dbg_print("epoll and listen socket failure:%s\n",strerror(errno));
return -3;
}
event.events=EPOLLIN;
event.data.fd=listenfd;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event)<0)
{
dbg_print("epoll add listen socket failure:%s\n",strerror(errno));
return -4;
}
for(;;)
{
events=epoll_wait(epollfd,event_array,MAX_EVENTS,-1);
if(events<0)
{
dbg_print("epoll failure:%s\n",strerror(errno));
break;
}
else if(events==0)
{
dbg_print("epoll get timeout\n");
continue;
}
for(i=0;i<events;i++)
{
if((event_array[i].events&EPOLLERR)||(event_array[i].events&EPOLLHUP))
{
dbg_print("epoll_wait get error on fd[%d]:%s\n",event_array[i].data.fd,strerror(errno));
epoll_ctl(epollfd,EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
close(event_array[i].data.fd);
}
if(event_array[i].data.fd==listenfd)
{
if((connfd=accept(listenfd,(struct sockaddr *)NULL,NULL))<0)
{
dbg_print("accept new client failure:%s\n",strerror(errno));
continue;
}
event.data.fd=connfd;
event.events=EPOLLIN;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&event)<0)
{
dbg_print("epoll add client socket failure:%s\n",strerror(errno));
close(event_array[i].data.fd);
continue;
}
dbg_print("epoll add new client socket[%d] successfully\n",connfd);
}
else
{
if((rv=read(event_array[i].data.fd,buf,sizeof(buf)))<=0)
{
dbg_print("Socket[%d] read failure or get disconnnected\n",event_array[i].data.fd);
epoll_ctl(epollfd,EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
close(event_array[i].data.fd);
continue;
}
else
{
dbg_print("Socket[%d] read %d bytes data\n",event_array[i].data.fd,rv);
printf("%s\n",buf);
if(write(event_array[i].data.fd,buf,rv)<0)
{
dbg_print("Socket[%d] write failure:%s\n",event_array[i].data.fd,strerror(errno));
epoll_ctl(epollfd,EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
close(event_array[i].data.fd);
}
if((str_sscanf(buf,id,&temp,local_t))<0)/* 分割字符串 */
{
dbg_print("Failed to split character:%s\n",strerror(errno));
}
db=sqlite3_open_database(DB_NAME);/* 打开数据库 */
if(sqlite3_create_table(db,TABLE_NAME)==0)/* 创建表 */
{
dbg_print("Create table %s successfully\n",TABLE_NAME);
if(sqlite3_insert(db,TABLE_NAME,id,&temp,local_t)==0)/*插入数据*/
{
dbg_print("Insert data successfully\n");
}
}
sqlite3_close_database(db);/* 关闭数据库 */
}
}
}
}
CleanUp:
close(connfd);
return 0;
}
2.4服务器端头文件
2.4.1database.h
#ifndef _DATABASE_H
#define _DATABASE_H
#define CONFIG_DEBUG
#ifdef CONFIG_DEBUG
#define dbg_print(format,args...) printf(format,##args)
#else
#define dbg_print(format,args...) do{} while(0)
#endif
sqlite3* sqlite3_open_database(char* db_name);
void sqlite3_close_database(sqlite3* db);
int sqlite3_create_table(sqlite3* db,char* table_name);
int sqlite3_insert(sqlite3* db,char* table_name,char* id,float* temp,char* localtime);
#endif
2.4.2sock_fun.h
#ifndef _SOCK_FUNC_H
#define _SOCK_FUNC_H
#define CONFIG_DEBUG
#ifdef CONFIG_DEBUG
#define dbg_print(format,args...) printf(format,##args)
#else
#define dbg_print(format,args...) do{} while(0)
#endif
void set_socket_limit(void);
void print_usage(char* programe);
int socket_server_init(char* listenip,int listenport);
#endif
3.注意问题
3.1 长连接与短连接
长连接:
指在一个TCP连接上可以连续发送多个数据包,在TCP链接保持期间,如果没有数据包发送,需要双方发检测包以维持连接。
短连接:
指通信双方由数据交互时,就建立一个TCP连接,数据发送完成后,则断开此次TCP连接。
3.2 饿死现象
注意在发送数据库中数据的同时要保证正常间隔时间的采样并上报服务器端。
3.3 发送数据函数封装
/* Description:Send data to the server */
int sock_send_data(sock_t *sock,char *buf,int bytes)
{
int rv=0;
int i=0;
int left_bytes=bytes;
if(!sock||!buf||bytes<0)
{
return -1;
}
while(left_bytes > 0)
{
rv=write(sock->connfd,&buf[i],left_bytes);
if(rv<0)
{
log_error("socket[%d] write data failure\n",sock->connfd);
sock_close(sock);
return -1;
}
else if(rv==left_bytes)
{
log_info("socket[%d] write %d bytes data over\n",bytes);
return 0;
}
else
{
i+=rv;
left_bytes-=rv;
continue;
}
}
return 0;
}