通过socket与树莓派通信

此文章来记录一下学习树莓派的过程
这是一道作业题目
原题目:

使用树莓派、智能手机等设备制作一台网络电灯(实验中可以用LED灯模拟),
通过手机端可以远程遥控树莓派,从而点亮、关闭电灯以及调节灯光亮度

目录

  • 前言
  • 一、socket是什么?
  • 二、服务端
    • 1.引入库
    • 2.服务端代码
      • 1全部代码
      • 2部分代码讲解
  • 客户端
    • 说明
    • 包含kivy部分的客户端代码
      • 全部代码
      • 部分通信关键代码
      • 关于kivy
  • 总结


前言

需要的设备:

树莓派:小编使用的是树莓派3B+(已配置好系统)
可以运行程序的设备:小编的笔记本电脑
电源:给树莓派供电
WiFi:可以是手机热点或者路由器,用来查询树莓派ip地址,以及作为一个局域网供树莓派和电脑通信

分析

曾经学过单片机的串口通信,在电脑上运行串口助手,配置好相关参数之后,和单片机进行通信。
树莓派的原理也是如此,电脑发,树莓派接受并判断是否有效,从而执行相应的功能命令
看别的使用的是socket进行通信,这里也是如此,树莓派作为服务端,在电脑上运行客户端
服务端时刻监听着,客户端随时准备发消息
说干就干,干

下文将介绍socket的一些基本知识和kivy的一些基本知识,可能不全,莫怪莫怪,原理直到就行了。

一、socket是什么?

socket是对TCP/IP协议的封装,这里涉及到了TCP/IP,就简单说一下百度百科说的很明白的
TCP/IP
TCP/IP 全称:Transmission Control Protocol/Internet Protocol-----传输控制协议/网际协议
是实现信息传输的协议族,是一个协议的集合,包含多种协议,并不值TCP/IP本身。包含应用层的协议FTP、SMTP等,功能:接受来自传输层的数据或者按不同应用要求和方式将数据传输到传输层;传输层的UDP、TCP,功能:实现数据传输和数据共享;网络层的ICMP、IP、ICMP,功能:负责网络中的数据包传输等;数据链路层(网络接口层)的ARP、RARP等,功能:提供链路管理错误检测、对不同通信媒体有关信息节问题进行有效处理等。
socket也是python里面的一个库函数,大佬们已经为我们封装好了一些功能,例如配置ip和端口,连接,监听等


二、服务端

1.引入库

树莓派作为服务端且控制led,首先要引入RPi.GPIO库,实现对GPIO口的控制,另外就是socket库了:

import socket
import sys #这个库用来处理当创建socket时发生的异常
import RPi.GPIO

2.服务端代码

1全部代码

# -*- codeing = utf-8 -*-
# @Time :2021/6/3 21:45
# @Author : 刘念卿
# @File : shumeipai_sever.py
# @Software : PyCharm
import socket
import sys
import RPi.GPIO

# 指定GPIO口的选定模式为GPIO引脚扁号模式
RPi.GPIO.setmode(RPi.GPIO.BCM)
# 指定GPIO18的模式为输出模式
RPi.GPIO.setup(18, RPi.GPIO.OUT)
pwm = RPi.GPIO.PWM(18, 70)

def socket_service_data():
    ##获取服务端的地址
    # host = socket.gethostname()
    host = '192.168.63.101'
    # 打印服务器主机名称
    print("当前服务器主机名称为:", host)
    port = 1234
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((host, port))  # 在同一台主机的ip下使用测试ip进行通信
        s.listen(10)
    except socket.error as msg:
        print(msg)
        sys.exit(1)

    print("Wait for Connection..................")
    num = 50
    while True:
        sock, addr = s.accept()
        print('connection success', addr[0])
        buf = sock.recv(1024)  # 接收数据
        buf = buf.decode()  # 解码
        print("客户端发来的消息" + str(buf))
        print('num:', num)
        if str(buf) == "on":
            RPi.GPIO.output(18, True)  # 让GPIO输出高电平
            # 启用 PWM,参数是占空比(可以理解为电流大小),范围:0.0 <= 占空比 >= 100.0
            pwm.start(50)
        elif str(buf) == "off":
            RPi.GPIO.output(18, False)  # 让GPIO输出高电平
            # 停用 PWM
            pwm.stop()
        elif str(buf) == '+':
            if num in range(101):
                try:
                    num += 10
                    pwm.ChangeDutyCycle(num)
                except ValueError:
                    num = 100
            else:
                sock.sendall('error'.encode('utf-8'))
        elif str(buf) == '-':
            if num in range(101):
                try:
                    num -= 10
                    pwm.ChangeDutyCycle(num)
                except ValueError:
                    num = 0
        else:
            print('error commond')
if __name__ == '__main__':
    socket_service_data()

2部分代码讲解

# 指定GPIO口的选定模式为GPIO引脚扁号模式
RPi.GPIO.setmode(RPi.GPIO.BCM)
# 指定GPIO18的模式为输出模式
RPi.GPIO.setup(18, RPi.GPIO.OUT)
pwm = RPi.GPIO.PWM(18, 70)

这一部分首先设置GPIO的模式,配置GPIO18位输出模式,通过GPIO18输出低电平或者高电平;最后一行时配置pwm,参数一位GPIO端口,这里是第18,第二个参数时频率,这里是70

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))  # 在同一台主机的ip下使用测试ip进行通信
s.listen(10)

第一行是创建一个socket

AF_INET是面向网络的,SOCK_STREAM是面向连接的套接字

第二行是的到socket选项

SOL_SOCKET,意思是正在使用的socket选项。它还可以通过设置一个特殊协议号码来设置协议选项,
SO_REUSEADDR,当socket关闭后,本地端用于该socket的端口号立刻就可以被重用。通常来说,只有经过
		系统定义一段时间后,才能被重用。
1,表示将SO_REUSEADDR标记为TRUE,操作系统会在服务器socket被关闭或服务器进程终止后马上释放该
		服务器的端口,否则操作系统会保留几分钟该端口

第三行是进行绑定操作

绑定指定的host,和端口号
这里的host可设置为一个ip或者通过socket.gethostname()获取本机地址,这里的host要和客户端的host一致,不然无法通信

第四行是监听

进行监听,10位最大允许10个连接
sock, addr = s.accept()
print('connection success', addr[0])
buf = sock.recv(1024)  # 接收数据
buf = buf.decode()  # 解码

第一行接收客户端的连接

socket是接受的套接字
addr是客户端的地址,包含ip和端口号

第三行接收数据

接受一个小于1024的数据

第四行解码

对接收到的数据进行解码
sock.sendall('error'.encode('utf-8'))

发送数据

send:发送TCP数据,返回发送的字节大小。这个字节长度可能少于实际要发送的数据的长度。
	换句话说,这个函数执行一次,并不一定能发送完给定的数据,可能需要重复多次才能发送完成。
sendall:发送完整的TCP数据,成功返回None,失败抛出异常

客户端

说明

下面只讲解和客户端相关的代码,关于kivy部分的代码,在不久的将来就会涉及到,敬请期待
本来是想着把客户端打包成app的,但由于某些原因还未尝试。

附:客户端和服务端通信流程图来源

通过socket与树莓派通信_第1张图片

包含kivy部分的客户端代码

全部代码

# -*- codeing = utf-8 -*-
# @Time :2021/6/3 7:59
# @Author : 刘念卿
# @File : shumeipia_client.py
# @Software : PyCharm
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
import socket

Builder.load_string("""
#:import gmtime time.gmtime
#:import strftime time.strftime

    canvas:
        Color:
            rgba: (0.1, 0.5, 0.9, 0.9) 
        Rectangle:
            size: self.size
            pos: self.pos
            #source: image_name
    FloatLayout:
        orientation: 'vertical'
        Label:
            id: label
            text: 'connect'
            pos_hint: {"right":0.65}

        Button:
            id: on
            color: 0,1,0,1
            background_color: 1.0, 0.0, 0.0, 1.0  #背景色设置
            font_size: 5
            size_hint: 0.15,0.05
            on_release: root.on()
            text: "Led_on"
            font_size: 20
            pos_hint: {"right":0.7, "bottom":0.2,"top":0.8,"left":0.3}
        Button:
            id: off
            color: 0,1,0,1
            background_color: 1.0, 0.0, 0.0, 1.0  #背景色设置
            font_size: 5
            size_hint: 0.15,0.05
            on_release: root.off()
            text: "Led_off"
            font_size: 20
            pos_hint: {"right":0.45, "bottom":0.2,"top":0.8,"left":0.55}
        Button:
            id: bright
            color: 0,1,0,1
            background_color: 0.5 ,0.6, 1, 1.0  #背景色设置
            font_size: 5
            size_hint: 0.15,0.05
            on_release: root.bright()
            text: "Brighten"
            font_size: 20
            pos_hint: {"right":0.7, "bottom":0.4,"top":0.6,"left":0.3}

        Button:
            id: darken
            color: 0,1,0,1
            background_color: 0.5, 0.6, 1, 1.0  #背景色设置
            font_size: 5
            size_hint: 0.15,0.05
            on_release: root.darken()
            text: "Darken"
            font_size: 20
            pos_hint: {"right":0.45, "bottom":0.4,"top":0.6,"left":0.55}
        Button:
            id: ex
            color: 0,1,0,1
            background_color: 6, 0, 0, 1  #背景色设置
            font_size: 5
            size_hint: 0.15,0.05
            on_release: root.exit()
            text: "exit"
            font_size: 20
            pos_hint: {"right":0.56, "bottom":0.6,"top":0.4,"left":0.44}
                    
""")


class RootWidget(Screen):
    """
    on:开灯
    off:关灯
    bright:调亮 每次亮度+10 最大值为100
    darken:调暗 每次亮度-10 最小值为0
    _socket:发送套接字
    """
    def __init__(self,**kwargs):
        self.host='192.168.63.101'
        self.port=1234
        self.commond='waiting'
        super(RootWidget,self).__init__(**kwargs)
        self.ids['label'].text='connect ip:'+self.host


    @staticmethod
    def lianjie():
        print("ok")

    def _socket(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 连接服务器
        s.connect((self.host, self.port))
        s.send(self.commond.encode('utf-8'))
        s.close()

    def on(self):
        self.commond='on'
        self._socket()
        print('on')

    def off(self):
        self.commond='off'
        self._socket()
        print('off')

    def bright(self):
        self.commond = '+'
        self._socket()
        print('bright')

    def darken(self):
        self.commond = '-'
        self._socket()
        print('darken')

    def exit(self):
        self.ids['ex'].text='again'
        self.ids['ex'].bind(on_press=exit)

class TestApp(App):
    def __init__(self,**kwargs):
        super(TestApp, self).__init__(**kwargs)
        self.title='树莓派控制' #修改窗口名称
    def build(self):

        return RootWidget()

if __name__ == '__main__':
    TestApp().run()

部分通信关键代码

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
s.connect((self.host, self.port))
s.send(self.commond.encode('utf-8'))
s.close()

第一行和服务端的代码是一样的
第二行是连接服务器

第一个参数是连接的ip,第二个参数是连接的端口号

第三行是以解码的方式发送数据

关于kivy

有兴趣的小伙伴可先行参考官网kivy文档,或者是别的博客,哔哩哔哩上面也有讲解,由于准备不充分,看kivy相关的部分暂时不解释,见谅。kivy学习

总结

树莓派作为服务端,电脑运行的程序作为客户端,需要注意的是,先运行服务端,在运行客户端,客户端和服务端的ip需要一致,不然会出现连接错误。
如果上述表达方式错误,请指出来,感激不尽!

附:github地址
下载地址

你可能感兴趣的:(python,kivy,socket,python,kivy)