简体   繁体   中英

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

Hello everyone,

I have a problemen with some socket communication coding. I have two pi's communicating over TCP/IP with python scripts. The idea is that one pi (client)reads temperature/humidity sensor data and sends it over to the other pi (server), where it will be stored in a SQLite database. In the future I want multiple pi's (clients) sends over sensor data to one (web)server with database to display data on a local website.

I have written some python code for server and client side and it works pretty well. If I start the server side it will listen for other connections. When I start the client side it will generate sensor data and send it to the server. The server receives the data. But when I want to close to connection to make room for (a future other pi) it terminates completely the script and don't listens for new "calls". See code below for both sides. Code is written in Python2.7.

Goal of this project: Within a zoo setting monitor and log multiple exhibits and aquariums on temperature and humidity, display the information on LCD in every exhibit, use LED as warning, and have a central-server to store/log data, that is displayed with a central computer.

RaspiServer - server-side

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

On the client-side there are three python scripts, where the main python scripts communicate with other scripts. I have decides to split it in multiple script to use them also as stand-alone (blinking LED, display data on LCD etc.).

RaspiMonitor - client-side

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

Terminal output, left server and right the client

Could anyone give some tips or help, or tell me why how to fix this problemen? I searched already multiple threads and other posts, but can't find the solution.

Thanks in advance for every help you give.

You have may few error in your server socket setup. The way a server socket works is it should be always open listening for new connection. When you accept a connection from the client(s.accept), it create a connection to the client. If you close that connection , it should not impact the server listening to incomming socket, The number of client or conn to client is limited only by the number you specify in the server socket.listen(NUMBER). When that number is reached, incoming connection will be rejected.

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

Then remove s.listen from setup connection()

I also suggests that you handle data transfer() in a new thread to be able to process incoming connection concurrently insted of consecutive.

Here is a reference: https://docs.python.org/2/howto/sockets.html

Rather than closing and opening new sockets repeatedly, I would suggest maintaining multiple open socket connections server side and handling each socket using the select() method.

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

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

Can a server handle multiple sockets in a single thread?

The code that works for now (still under construction for adding more functionalities) is:

Server-side

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()

And the client-side looks like this:

Client-side

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)

It is looping 5 cyclus for internal measurement (for on a LCD display) and then send the data once to the server, that will store it in the database (SQlite3). Still want to add more functionalities (LED when reading, warning LED when under or above limits, alarm sound, LCD connected. When there is progress I'll update the code.

Offcourse any tips and sugestions are more then welcome!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM