树莓派定时发送图片到服务器

一、 概述

     采用树莓派4B + 官方摄像头+阿里云服务器实现树莓派定时拍摄照片上传到服务器的功能。主要使用python 和C语言编程。树莓派和服务器之间采用TCP/IP 协议通信,树莓派通过串口发送指令给单片机,单片机在使用PWM控制舵机式摄像头可以拍摄不同角度的照片。

二、相关环境及使用材料说明

硬件:STM32F103RCT6
           两个SG90舵机
           树莓派4B
           CSI摄像头500W像素

软件及环境:Keil5-MDK(5.30)
                     操作系统:Windows10 家庭版
                     树莓派镜像:2020-12-02-raspios-buster-armhf.img
                     服务器版本:ubuntu16.04 LTS(32位)

三、具体实现过程

1.服务器代码

import socket, time, struct, os, threading 
host = '0.0.0.0'
port = 8000 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 定义socket类型 
s.bind((host, port)) # 绑定需要监听的Ip和端口号,tuple格式 
s.listen(5) 

def conn_thread(connection, address):
    i = 0
    while True:
        try:
            connection.settimeout(600)
            fileinfo_size = struct.calcsize('128sl')
            buf = connection.recv(fileinfo_size)
            if buf:
                filename, filesize = struct.unpack('128sl', buf)
                print("照片大小:"+str(filesize))
                if filesize< 0 or filesize > 2432075:
                    continue
                filename = filename.decode().strip('\00')
                print('file new name is %s, filesize is %s' % (filename, filesize))
                # 获取当前时间年月日时分秒
                localtime =time.strftime("%Y%m%d%H%M%S",time.localtime(time.time()))
                # 构造文件路径
                filepath = '/root/PIC/'+ str(localtime) + '.jpg'
                file = open(filepath,'wb')
                print('stat receiving...filesize:' + str(filesize))
                recvd_size = 0 # 定义接收了的文件大小
                while recvd_size != filesize:
                    if filesize - recvd_size >= 1024:
                        rdata = connection.recv(1024)
                        recvd_size += len(rdata)
                    elif filesize - recvd_size <1024 and filesize - recvd_size > 0:
                        print(filesize - recvd_size)
                        rdata = connection.recv(filesize - recvd_size)
                        recvd_size += len(rdata)
                    file.write(rdata)
                file.close()
                print('receive done')
                # connection.close()
            i += 1
        except socket.timeout:
            connection.close()
            con.close() 
def main():
    while True:
        print("开始接收图片")
        connection, address = s.accept()
        print('Connected by ', address)
        thread = threading.Thread(target=conn_thread, args=(connection, address)) 
        thread.start()
    s.close() 

if __name__ == '__main__':
    main()

未解之谜:当时用64位的服务器时,照片死活都传不上去,报错如下:

	
	服务端代码运行在服务器上时发生错误(此时服务器版本:ubuntu12 Python版本:3.5)
			File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
			self.run()
			File "/usr/lib/python3.5/threading.py", line 862, in run
			self._target(*self._args, **self._kwargs)
			File "RecievePic.py", line 16, in conn_thread
			filename, filesize = struct.unpack('139sl', buf)
			struct.error: unpack requires a bytes object of length 152

后来更换成32位的服务器就好了,现在也不知道为啥。

2. 树莓派客户端代码

树莓派客户端采用了opencv对图像进行了处理,opencv具体的安装过程可以参考下面的几篇博客:
https://blog.csdn.net/cold_hl/article/details/106195325
https://www.cnblogs.com/gghy/p/11916830.html
我安装的是4.03版本的,安装过程中遇到的一些错误如下:

Archive:  opencv-3.4.8.zip
		End-of-central-directory signature not found.  Either this file is not
		a zipfile, or it constitutes one disk of a multi-part archive.  In the
		latter case the central directory and zipfile comment will be found on
		the last disk(s) of this archive.
		note:  opencv-3.4.8.zip may be a plain executable, not an archive
		unzip:  cannot find zipfile directory in one of opencv-3.4.8 or
				opencv-3.4.8.zip, and cannot find opencv-3.4.8.ZIP, period.
				
		①安装包不完全,选择重新下载
		② jar xvf opencv-3.4.8.zip 使用此命令解压
		
		
		编译Opencv报错:
			/home/pi/opencv-3.4.3/modules/stitching/include/opencv2/stitching/detail/matchers.hpp:52:10: fatal error: /home/pi/opencv_contrib-3.4.3/modules/xfeatures2d/cuda.hpp: No such file or directory
			#include "/home/pi/opencv_contrib-3.4.3/modules/xfeatures2d/cuda.hpp"
			在此路径中找不到头文件
			
			查询发现文件路径在这:/home/pi/opencv_contrib-3.4.3/modules/xfeatures2d/include/opencv2/xfeatures2d
			修改后便可
			
		最后无法找到 hdf.h 而失败告终
		
		
	更换树莓派系统,再次安装,成功
		系统版本:2020-12-02-raspios-buster-armhf.img
		opencv版本:opencv-3.4.3
		参考博客:https://blog.csdn.net/cold_hl/article/details/106195325
		
		注意事项:opencv.3.4.3.zip 可以在windows上下载然后拷贝下来
		          opencv_contrib-3.4.3.zip依赖还是使用wget方式,不然可能会缺失一些文件导致make失败
				  wget -O opencv_contrib-3.4.3.zip \
				  https://github.com/Itseez/opencv_contrib/archive/3.4.3.zip
				  
				  使用CMAKE 命令时,需要注意修改路径对应自己依赖包下面的modules
				  cmake -D CMAKE_BUILD_TYPE=RELEASE \
					-D CMAKE_INSTALL_PREFIX=/usr/local \
					-D INSTALL_C_EXAMPLES=ON \
					-D INSTALL_PYTHON_EXAMPLES=ON \
					-D OPENCV_EXTRA_MODULES_PATH=/home/pi/Opencv/opencv_contrib-3.4.3/modules \#这一行
					-D BUILD_EXAMPLES=ON \
					-D WITH_LIBV4L=ON \
					-D PYTHON3_EXECUTABLE=/usr/bin/python3.7 \
					-D PYTHON_INCLUDE_DIR=/usr/include/python3.7 \
					-D PYTHON_LIBRARY=/usr/lib/arm-linux-gnueabihf/libpython3.7m.so \
					-D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include \
					..

客户端代码如下

在这里插入代码片# -*- coding: UTF-8 -*-
import socket, os, struct
import time
import serial
import cv2
from picamera import  PiCamera


"""设置 ip 地址"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect(('xxx.xxx.xxx.xxx', 8000))#自己服务器的IP

"""设置 摄像头"""
camera = PiCamera()
camera.resolution = (1920,1080)
#camera.resolution = (160,120)
camera.framerate = 60

"""设置 串口 """
ser = serial.Serial("/dev/ttyAMA0", 9600)

PicNumber=0   #发送照片数量

#ser.write('S'.encode())
#ser.write('1'.encode())
#ser.write('E'.encode())

while True:
    PicNumber = PicNumber+1
    camera.capture('/home/pi/class/0.jpg')
    filepath = '/home/pi/class/0.jpg'
    if os.path.isfile(filepath):
        fileinfo_size = struct.calcsize('128sl')  # 定义打包规则
        # 定义文件头信息,包含文件名和文件大小
        fhead = struct.pack('128sl',bytes(os.path.basename(filepath),encoding='utf-8'), os.stat(filepath).st_size)
        s.send(fhead)
        print('Send the',PicNumber,'th pic')
        print('client filepath: ', os.path.basename(filepath), os.stat(filepath).st_size)

        # with open(filepath,'rb') as fo: 这样发送文件有问题,发送完成后还会发一些东西过去
        fo = open(filepath, 'rb')
        while True:
            filedata = fo.read(1024)
            if not filedata:
                break
            s.send(filedata)
        #time.sleep(0.5)
        fo.close()
       # print('Send the',PicNumber,'th pic')
    time.sleep(2)
    ser.write('S'.encode())
    ser.write('1'.encode())
    ser.write('E'.encode())
    if PicNumber >= 10:  #发送10张
       print('All send',PicNumber,'sheet')
       s.close()

3.STM32部分代码

STM32主要通过串口3接收树莓派发送过来的数据,然后完成驱动舵机转动的功能。
串口接收部分代码如下:

void USART3_IRQHandler()
{
    if(USART_GetITStatus(USART3,USART_IT_RXNE) != RESET) //中断产生
    {
        Usart3.pRxBuff[Usart3.RxReadPtr] = USART_ReceiveData(USART3) ; //接收一字节
        Usart3.RxOneFlag = 1 ;
        if(++Usart3.RxReadPtr > Usart3.RxBuffSize)
        {
            Usart3.RxReadPtr = 0 ;
            printf("Data Overflow\r\n") ; //提醒数据溢出
        }// ;
        
        USART_ClearITPendingBit(USART3, USART_IT_RXNE) ;
    }
    
    else if(USART_GetITStatus(USART3,USART_IT_IDLE) != RESET)  //空闲接收
    {
        USART3->SR;//先读SR    清除中断
		USART3->DR;//再读DR
        Usart3.RxFrameFlag = 1 ;
        Usart3.RxReadPtr = 0 ;
        
        if((Usart3.pRxBuff[0] == 'S') && (Usart3.pRxBuff[2] == 'E'))
        {
            //
             if(Usart3.pRxBuff[1] == '1')
             {
                  printf("AQA\r\n");    //just for test
                 
                  RevCnt++ ;
                  
                 if(RevCnt <= 5)   //正传
                 {
                     Servo1_Angle(RevCnt*36);
                 }
                 else if(RevCnt > 5 && RevCnt <= 10) //反转
                 {
                     Servo1_Angle((10-RevCnt)*36);
                     if(RevCnt == 10) RevCnt = 0 ;
                 }


             }
         memset(Usart3.pRxBuff, 0, USART3_RX_BUFF_SIZE);
        }
    } 
           
}

舵机驱动代码:

void Servo_Init(void)
{
    Servo_PWM_Init(SERVO_TIM_ARR, SERVO_TIM_PSC);
	TIM_SetCompare2(TIM2,50);    //		CH2捕获比较寄存器赋值         
	TIM_SetCompare1(TIM2, 50) ;  //     CH1捕获比较寄存器赋值
}


void Servo2_Angle(uint16_t Angle)
{
	uint16_t pulse ;
	
	if(Angle <= 0)
	{
	    Angle = 0;
	}
	
	if(Angle >= 300)
	{
	    Angle = 300 ;
	}
	
	 pulse = (uint16_t)(50 + Angle * 100/180.0); 
	 TIM_SetCompare2(TIM2, pulse);
}


void Servo1_Angle(uint16_t Angle)
{
	uint16_t pulse ;
	
	if(Angle <= 0)
	{
	    Angle = 0;
	}
	
	if(Angle >= 300)
	{
	    Angle = 300 ;
	}
	
	 pulse = (uint16_t)(50 + Angle * 100/180.0); 
	 TIM_SetCompare1(TIM2, pulse);
}

传输图片这部分参考了如下两篇博客,感谢博主!

https://www.cnblogs.com/lw77/p/11963174.html
https://blog.csdn.net/xqf1528399071/article/details/52850157



四、定时任务的实现

定时任务主要使用的是Linux系统下有一个定时运行的程序命令叫“crontab”,它是是任务调度的crond常驻命令,是Linux系统下的定时任务触发器 。采用这个需要用到实时的时间,若是时间不对的可以参考如下过程。

树莓派调整时间
		安装软件:sudo apt-get install ntpdate
				  sudo apt-get install ntp NTP(Network Time Protocol)服务
		修改时区:tzselect
		依次选择国家、城市、等,按提示进行操作
		
		sudo vim /root/.profile 在末尾添加:TZ="Asia/Shanghai";export TZ
		添加 ntp 服务器:sudo ntpdate cn.pool.ntp.org
		sudo ntpd -s –d 校准
		
		若是需要开机自启动对时,则执行以下命令:
		sudo nano /etc/rc.local  在最后添加一行:sudo ntpdate cn.pool.ntp.org
		
	采用 crontab命令定时执行任务详解:
		 sudo crontab -e 编辑事件	
		 	
		分钟 小时 日期 月份 周 指令
        0-59 0-23 1-31 1-12 0-7 指令 #07都代表星期天
		
		每五分钟执行 */5 * * * *
		每小时执行 0 * * * *
		每天执行 0 0 * * *
		每周执行 0 0 * * 0
		每月执行 0 0 1 * *
		每年执行 0 0 1 1 *
		
		每五分钟执行 */5 * * * * :(具体实现)
			*/5 * * * * /usr/bin/python3.7 /home/pi/DCXM2020/GetPic.py
		
		*:表示所有的值,如果m字段是星号,这说明每分钟都运行。
        ,:表示一个列表,如果m字段是 “151558”,这说明在第1分钟,第5分钟,第15分钟和第58分钟会运行
        -:表示一个范围,如果m字段是 1-5,这说明第1分钟、第2分钟、第3分钟、第4分钟和第5分钟都会运行
        /:表示时间间隔,如果m字段是 */10,这说明每隔10分钟运行
		
		
		编辑完成后记得重新启动:sudo /etc/init.d/cron restart
		主要若是采用上述语句无法看到效果后,可以采用如下命令:
			service cron restart  重新启动服务。

五、演示效果图

树莓派客户端
服务器端
接收的图片
树莓派拍摄的图片

  • 7
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值