ESP8266基于MicroPython的TCP socket回调函数实现案例

参考链接

其它参考

python socket和简单tcp通信实现 - Katherina.K - 博客园

GitHub - robert-hh/FTP-Server-for-ESP8266-ESP32-and-PYBD: Small FTP server for ESP8266/ESP32/PYBD on the MicroPython platform

引用:对于不让 socket 套接字阻塞 micropython 主进程的方法。通常的做法是启动一个socket套接字专属的线程来 accept 和 recv 因为有些开发版(例如esp8266)是不支持多线程的,网上有说可以用_thread实现多线程的,但是查了一些这个指令好像是用于cpython的,并不能跑在ESP8266上,而且一直挂着个线程对于某些需要低功耗的场景来说显然是不明智的。上述文章作者在webrepl 的代码中找到了通过回调函数实现的方式,核心代码如下,其中这个 20 的值是 micropython开发组自己定义的, 主要的功能就是给 socket 套接字绑定一个回调函数

listen_s.setsockopt(socket.SOL_SOCKET, 20, callback)

参考上述思路,自己编写了一个demo程序,client_demo3.py,实现效果为,ESP8266作为TCP client端,具有服务器掉线重连功能,每隔2秒往服务器发送一个255以内的随机数,同时接受服务器控制指令,如果为“on”则打开ESP8266上的灯,为“off”则关灯,其它指令不动作

基本库引用和设置

import socket
import time
import urandom

from machine import Pin
p2 = Pin(2, Pin.OUT)

SERVER_IP = "192.168.1.101"
SERVER_PORT = 8080
BUFFER_SIZE = 1024

通过回调函数实现服务器数据下控功能,下面是定义socket介绍数据时的回调函数

#定义接收服务器指令的回调函数,下面是打开ESP8266上led灯的指令
def receiveHandler(sck: socket.socket()):
    cmdInfo = sck.recv(BUFFER_SIZE).decode("utf8")
    if cmdInfo == "on":
        p2.off()
        print("Led Opened!")
    if cmdInfo == "off":
        p2.on()
        print("Led Closed!")

socket的基本设置,回调函数绑定

#套接字基本设置及连接操作
def doConnect(serverIp, serverPort):
    #创建套接字,socket.AF_INET表示服务器之间网络通信;socket.SOCK_STREAM表示流式socket,for TCP
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    address = (serverIp, serverPort)
    #下面是socket对象绑定了一个回调函数,这样不会因为套接字阻塞(如,未收到服务器端信息)而影响micropython主进程
    #当接收到指令后,则打断现行的操作,运行回调函数,之后自动回到之前的代码,下面的20不要更改
    s.setsockopt(socket.SOL_SOCKET, 20, receiveHandler)
    try:
        #说明,如果下面的连接失败,正常会有报错,这里用pass指令掉过报错
        s.connect(address)
        print("Server connected!")
    except:
        pass
    return s

主函数定义,包含每隔2秒发送随机数,服务器断线重现功能

def main(serverIp = SERVER_IP, serverPort = SERVER_PORT):

    sConnect = doConnect(serverIp, serverPort)
    while True:
        try:
            #下述指令表示生产一个8位数的整数,这个8位指的是2进制的8个位,最大为255
            #下面代码是每隔2秒往服务器端发送一个255以内的随机数
            randomNum = str(urandom.getrandbits(8))
            sConnect.send(randomNum+"\n")
            print("随机数: ", randomNum)
            time.sleep_ms(2000)

        #下面代码用于处理发送失败事件,正常产生失败原因有服务器掉线、发送数据错误等原因
        #但是下面仅用于显示报错原因和进行服务器断线重连
        except Exception as err:
            print("Excepetion Info: ", err)
            print("Re connect...")
            time.sleep_ms(3000)
            sConnect = doConnect(serverIp, serverPort)

在main.py程序调用上述客户端

import client_demo3
client_demo3.main("192.168.1.101", 8080)

代码运行后即可实现与规划功能,效果如下图所示

client_demo3.py完整代码如下

import socket
import time
import urandom

from machine import Pin
p2 = Pin(2, Pin.OUT)

SERVER_IP = "192.168.1.101"
SERVER_PORT = 8080
BUFFER_SIZE = 1024

#定义接收服务器指令的回调函数,下面是打开ESP8266上led灯的指令
def receiveHandler(sck: socket.socket()):
    cmdInfo = sck.recv(BUFFER_SIZE).decode("utf8")
    if cmdInfo == "on":
        p2.off()
        print("Led Opened!")
    if cmdInfo == "off":
        p2.on()
        print("Led Closed!")

#套接字基本设置及连接操作
def doConnect(serverIp, serverPort):
    #创建套接字,socket.AF_INET表示服务器之间网络通信;socket.SOCK_STREAM表示流式socket,for TCP
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    address = (serverIp, serverPort)
    #下面是socket对象绑定了一个回调函数,这样不会因为套接字阻塞(如,未收到服务器端信息)而影响micropython主进程
    #当接收到指令后,则打断现行的操作,运行回调函数,之后自动回到之前的代码,下面的20不要更改
    s.setsockopt(socket.SOL_SOCKET, 20, receiveHandler)
    try:
        #说明,如果下面的连接失败,正常会有报错,这里用pass指令掉过报错
        s.connect(address)
        print("Server connected!")
    except:
        pass
    return s

def main(serverIp = SERVER_IP, serverPort = SERVER_PORT):

    sConnect = doConnect(serverIp, serverPort)
    while True:
        try:
            #下述指令表示生产一个8位数的整数,这个8位指的是2进制的8个位,最大为255
            #下面代码是每隔2秒往服务器端发送一个255以内的随机数
            randomNum = str(urandom.getrandbits(8))
            sConnect.send(randomNum+"\n")
            print("随机数: ", randomNum)
            time.sleep_ms(2000)

        #下面代码用于处理发送失败事件,正常产生失败原因有服务器掉线、发送数据错误等原因
        #但是下面仅用于显示报错原因和进行服务器断线重连
        except Exception as err:
            print("Excepetion Info: ", err)
            print("Re connect...")
            time.sleep_ms(3000)
            sConnect = doConnect(serverIp, serverPort)

版权声明:本文为zhigedali原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。