通过 Pi 与 python 之间的套接字通信 (TCP/IP) 发送传感器数据

Send sensor data over socket communication (TCP/IP) between Pi's with python

大家好,

我对某些套接字通信编码有疑问。我有两个 pi 通过 TCP/IP 与 python 脚本进行通信。这个想法是一个 pi(客户端)读取 temperature/humidity 传感器数据并将其发送到另一个 pi(服务器),它将存储在 SQLite 数据库中。将来我希望多个 pi(客户端)将传感器数据发送到一个带有数据库的(网络)服务器,以在本地网站上显示数据。

我已经为服务器端和客户端编写了一些 python 代码,并且运行良好。如果我启动服务器端,它将监听其他连接。当我启动客户端时,它将生成传感器数据并将其发送到服务器。服务器接收数据。但是当我想关闭连接以为(未来的其他 pi)腾出空间时,它会完全终止脚本并且不监听新的 "calls"。双方请参见下面的代码。代码写在Python2.7。

这个项目的目标:在一个动物园设置监控和记录多个展品和水族馆的温度和湿度,在每个展品的 LCD 上显示信息,使用 LED 作为警告,并有一个 store/log 数据的中央服务器,用中央计算机显示。

RaspiServer - 服务器端

import socket
from LED import callLED

host = ''
port = 5560

def setupServer():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print("Socket created.")
    try:
        s.bind((host, port))
    except socket.error as msg:
        print(msg)
    print("Socket bind complete.")
    return s

def setupConnection():
    s.listen(1)
    conn, address = s.accept()
    print("Connected to: " + address[0] + ":" + str(address[1]))
    return conn

def dataTransfer(conn):
    while True:
        data = conn.recv(1024)
        data = data.decode('utf-8')
        dataMessage = data.split(":", 2)
        command = dataMessage[0]
        humidity = dataMessage[1]
        temperature = dataMessage[2]
        if command == 'DATA':
            print("Received: " + humidity + " : " + temperature)
            callLED()
        elif command == 'EXIT':
            print("Disconnected with Client")
            break
        else: 
            reply = 'Unknow Command'    
        conn.sendall(str.encode(reply))
        Print("Reply has been send.")
    conn.close()

s = setupServer()

while True:
    try:
        conn = setupConnection()
        dataTransfer(conn)
    except:
        break

在客户端有三个 python 脚本,其中主要的 python 脚本与其他脚本进行通信。我决定将它拆分成多个脚本,以便将它们也用作独立的(闪烁的 LED、在 LCD 上显示数据等)。

RaspiMonitor - 客户端

Run this script on client-side

from time import sleep
from client_sensordata import GetTemp
from monitor_client import transmit

sleepTime = 20

def tempMonitorServer():
    humidity, temperature = GetTemp()
    temp = str(temperature)
    hum = str(humidity)
    message = "DATA:" + hum + ":" + temp
    print("Transmitting Data.")
    response = transmit(message)
    print(response)

while True:
    tempMonitorServer()
    sleep(sleepTime)

Use this script to send and receive data over TCP/IP

import socket

host = '192.168.2.3'
port = 5560

def setupSocket():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    return s

def sendReceive(s, message):
    s.send(str.encode(message))
    print("Message transmitted")
    reply = s.recv(1024)
    print("We have received a reply.")
    print("Send closing message.")
    s.send(str.encode("EXIT"))
    s.close()
    reply = reply.decode('utf-8')
    return reply

def transmit(message):
    s = setupSocket()
    response = sendReceive(s, message)
    return response

Use this script to retrieve sensor data

def GetReading():
    import sys
    import Adafruit_DHT
    humidity, temperature = Adafruit_DHT.read_retry(Adafruit_DHT.DHT22, 17)
    if humidity is not None and temperature is not None:
        print('Reading sensor data')
        return humidity, temperature
    else:
        print('no sensor data')

def GetTemp():
    humidity, temperature = GetReading()
    name = "DHT22Client1"
    print('Read sensor: {0} humidity: {1:0.2f}% temperature: {2:0.2f}C'.format(name, humidity, temperature))
    return humidity, temperature

终端输出

Terminal output, left server and right the client

任何人都可以提供一些提示或帮助,或者告诉我为什么如何解决这个问题吗?我已经搜索了多个线程和其他帖子,但找不到解决方案。

在此先感谢您提供的所有帮助。

您的服务器套接字设置可能有一些错误。服务器套接字的工作方式是它应该始终打开侦听新连接。当您接受来自客户端 (s.accept) 的连接时,它会创建到客户端的连接。如果关闭该连接,它应该不会影响服务器监听传入的套接字,客户端或连接到客户端的数量仅受您在服务器中指定的数量限制 socket.listen(NUMBER)。当达到该数量时,传入连接将被拒绝。

def setupServer():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print("Socket created.")
    try:
        s.bind((host, port))
        s.listen(5)
    except socket.error as msg:
        print(msg)
    print("Socket bind complete.")
    return s

然后从设置连接中删除 s.listen

我还建议您在新线程中处理数据传输(),以便能够同时处理传入连接而不是连续处理。

这里有一个参考:https://docs.python.org/2/howto/sockets.html

与其反复关闭和​​打开新的套接字,不如在服务器端维护多个打开的套接字连接,并使用 select() 方法处理每个套接字。

https://docs.python.org/2/library/select.html

https://pymotw.com/2/select/

Can a server handle multiple sockets in a single thread?

目前可用的代码(仍在建设中以添加更多功能)是:

服务器端

import socket
import sys
from LED import callLED
from monitor_log3 import ExtractStoreData

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Bind the socket to the port
server_address = ('', 5560)
print >>sys.stderr, 'Starting up on %s port %s' % server_address
sock.bind(server_address)

# Listen for incoming connections
sock.listen(1)

while True:
    # Wait for a connection
    print >>sys.stderr, 'Waiting for a connection'
    connection, client_address = sock.accept()
    try:
        print >>sys.stderr, 'Connection from', client_address

        # Receive the data in small chunks and retransmit it
        while True:
            data = connection.recv(1024)
            data = data.decode('utf-8')
            message = data
            if data:
                print >>sys.stderr, 'Send data receive confirmation'
                connection.sendall(data)
                callLED()
                ExtractStoreData(message)
            else:
                print >>sys.stderr, 'No more data from', client_address
                break

    finally:
        # Clean up the connection
        print >>sys.stderr, 'Closing connection'
        print >>sys.stderr, '------------------'
        connection.close()

客户端看起来像这样:

客户端

import socket
import sys
import datetime
from time import sleep
from client_sensordata import GetTemp 

timeSleep = 10

def ClientSocket():
    # Create a TCP/IP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Connect the socket to the port where the server is listening
    server_address = ('192.168.2.3', 5560)
    print >>sys.stderr, 'Connecting to: %s port: %s' % server_address
    sock.connect(server_address)

    try:
        # Send data
        name, humidity, temperature = GetTemp()
        Sensorname = str(name)
        temp = str(temperature)
        hum = str(humidity)
        print(Sensorname + ":" + temp + ":" + hum)
        message = (Sensorname + ":" + temp + ":" + hum)
        print("Transmitting data...")
        print >>sys.stderr, 'Sending data...'
        sock.send(str.encode(message))

        # Look for the response
        amount_received = 0
        amount_expected = len(message)

        while amount_received < amount_expected:
            data = sock.recv(1024)
            amount_received += len(data)
            print >>sys.stderr, 'Send data'

    finally:
        print >>sys.stderr, 'Closing connection'
        print >>sys.stderr, '------------------'
        sock.close()


while True:
    print("Start external server measurement at " + datetime.datetime.now().strftime("%H:%M:%S"))
    ClientSocket()
    sleep(timeSleep)
    for t2 in range(5):
        print("Start internal display measurement at " + datetime.datetime.now().strftime("%H:%M:%S"))
        GetTemp()
        print("------------------")
        sleep(timeSleep)

它正在循环5个循环进行内部测量(在LCD显示器上),然后将数据发送一次到服务器,服务器将把它存储在数据库(SQlite3)中。仍然想添加更多功能(读取时 LED,低于或超过限制时警告 LED,警报声,连接 LCD。当有进展时我会更新代码。

当然欢迎任何提示和建议!