树莓派获取温度并上报

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;
}
这里将write封装成了一个函数,原因在于,若只是用write函数发送数据,当要发送数据的字节数大于write函数能发送的字节数时,则数据的后半段会被丢弃,数据不能完整发送,使用该逻辑是确保所要发送的数据能够完整的发送。