close-circle
Close

#software : switching between raspberry pi depending on day/night

A project log for Elephant AI

a system to prevent human-elephant conflict by detecting elephants using machine vision, and warning humans and/or repelling elephants

Neil K. Sheridan 10/09/2017 at 20:110 Comments

What's the problem?

We use two raspberry pi computers! One for daytime elephant detection, and one for night-time elephant detection. One having NoIR camera + IR illumination, and one having IR-filtered camera. In addition, only one has mobile connectivity. So they both need to communicate! The daytime detection raspberry pi must tell the night-time detection pi when it is night! And the night-time detection raspberry pi must tell the daytime when it is day - based on what their respective light sensors inform them!



So, we looked at some approaches to do this here: https://hackaday.io/project/20448-elephant-ai/log/67566-switching-between-raspberry-pi-depending-on-lighting-daynight

The problem is that we can't multiplex raspberry pi IR and NoIR cameras to the CSI port. Well, we can using http://www.ivmech.com/magaza/en/development-modules-c-4/ivport-v2-raspberry-pi-camera-module-v2-multiplexer-p-107 I guess, but it's kinda expensive at $77 + shipping + taxes. And we could perhaps use a Raspberry Pi camera on the CSI + a webcam etc. via USB (e.g.https://www.raspberrypi.org/documentation/usage/webcams/ ) .

You might think we could communicate between the two raspberry pi computers (we'll refer to them as dayPI and nightPI from now one) via serial. But we can't do that, because one of them is using serial to communicate with the cellular network (2G/3G/GPRS) modem!



Anyway, so first here I'll go through software ideas for connecting two raspberry pi's together using Ethernet cable. Then I'll show what I did.

1. Send and receive UDP with DGRAM as proof of concept (code concept) 

send.py

UDP_IP = "IP_OF_RECEIVER"
UDP_PORT = 5005
# we need an unassigned port
MESSAGE = "It's night-time!"
print "UDP target IP:", UDP_IP
print "UDP target port:", UDP_PORT
print "message:", MESSAGE
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))

 receive.py

import socket
UDP_IP = "IP_OF_RECEIVER"
UDP_PORT = 5005
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
        data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
        #buffer size to prevent overflow 
        print "received message:", data

This is UDP with socket.SOCK_DGRAM is a datagram socket. Order and reliability for message is not guaranteed for these type of sockets. Alternatively we use socket.SOCK_STREAM  for TCP which is sequenced (https://en.wikipedia.org/wiki/Stream_socket)

Here we go with an example code idea for the server with TCP and SOCK_STREAM:

import socket
host = 'host IP'
post = 5580
message = "this is a message"
def setupServer():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print("Socket made")
    try:
        s.bind(host, port)
    except socket.error as msg:
        print(msg)
    print("Binded to socket")
    return s
def setupConnection():
    s.listen(1) # one connection only at a time
    conn, address = s.accept()
    print("connection to: " + address[0] ":" + str(address[1]))
    return conn
def GET():
    reply = storedValue
    return reply
def dataTransfer(conn):
    while True:
    #receive data
        data = conn.recv(1024)
        data = data.decode('utf-8')
        #this is for split with first bit of string having a command word
        dataMessage = data.split(' ', 1)
        command = dataMessage[0]
        if command == 'GET':
            reply = GET()
        elif command == 'REPEAT':
            reply = REPEAT(dataMessage)
        elif command == 'EXIT':
            print("no client")
            break
        elif command == 'KILL':
            print("server shut down now")
            s.close()
            #close socket
            break
        else:
            reply = 'Unknown command given'
        # send reply to client
    conn.sendall(str.encode(reply))
    print("Data sent to client")
    conn.close()
def REPEAT(dataMessage):
    reply = dataMessage[1]
    # so the split from second bit of string.. [0] was the command
    # this is just proof of concept
    return reply
s = setupServer()
    
while True:
    try:
        conn = setupConnection()
        dataTransfer(conn)
    except:
        break

 Here we go for the client:

import socket
host = 'IP address'
port = 'port as same'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port)) 
while True:
    command = input("client enter your command:")
    if command == 'EXIT':
        # send the EXIT command to server
        s.send(str.encode(command))
        break
    elif command == 'KILL':
        # send the KILL command to server
        s.send(str.encode(command))
        break
    s.send(str.encode(command))
    #send other commands to server
    reply = s.recv(1024)
    #buffer size 
    print(reply.decode('utf-8'))
s.close()
#close socket

2.

Now, I didn't find much point is using that.. So I just used DGRAM. This is a demo of using DGRAM (datagram) over Ethernet for enabling communication between the dayPi and the nightPi:

Now before trying this, you need to find the IPs of your dayPi and nightPi! You can do this is the GUI by hovering with the mouse over the icons on top right of screen:

There is is! Under eth0.

Or you can run:

sudo ifconfig

Ok, so off we go with the demo code.. 

So here's the code you need to run on the dayPi:

import socket
import time
UDP_IP = "THIS IS THE IP OF DETECTION DEVICE TO BE SENT THE MESSAGE"
UDP_PORT = 5005
MESSAGE= "NIGHT"
print("Debug: UDP_IP is ", UDP_IP)
print("Debug: UDP_PORT is ", UDP_PORT)
time.sleep(2)
print("Debug: I am the daytime elephant detection device and I am monitoring light levels")
time.sleep(2)
print("Debug: It is night time time now! I will send a message telling the night detection device")
time.sleep(2)
print("Debug: Sending my message")
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
print("Debug: Sent messsage. Now I will begin waiting for a message back when it is morning")
time.sleep(2)
sock.close()
print("Debug: I will create a socket and wait listening to it")
print("Debug: I am going to start waiting now")
UDP_IP = "THIS IS THE IP OF THIS DETECTION DEVICE TO LISTEN ON"
# bind our own IP to listen
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
while True:
    data = sock.recvfrom(1024)
    print("Debug: I just got a message from night-time detection device: ", data)
    if data[0] == "DAY":
        break
print("Debug: I got a message saying it is DAY. Time to begin my detection again")
sock.close()

GitHub: https://github.com/nksheridan/elephantAI/blob/master/demo_Ethernet_comms_DGRAM_dayPi

-- sending a message to nightPi--

Note that initially the dayPi  will send a message to the nightPi when its sensor detects that night time condition has occurred. We don't have all that code in at this point - so we just simulate it with the MESSAGE variable being given "NIGHT". So we bind to the socket on the nightPi with 'sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)' and send it MESSAGE with 'sock.sendto(MESSAGE, (UDP_IP, UDP_PORT)). 

Here UDP_IP is the IP of the nightPi and UDP_PORT is anything that isn't allocated (e.g. 5005)

-- receiving a message back from nightPi --

Now the dayPi will bind to its own socket (same port 5005) so here the UDP_IP is the IP of the dayPi, and wait for a message back from nightPi. We have a while loop that we break out of when the message is equal to "DAY". The dayPi will perform its detection duty at this point, and be prepared to do the whole process again when night time condition occurs again!

And here's the code you need to run on the nightPi:

import socket
import time
UDP_IP = "THIS IS OUR OWN IP TO LISTEN ON"
UDP_PORT = 5005
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
print("Debug: I am the night-time elephant detection pi")
time.sleep(2)
print("Debug: I am waiting for my message from the daytime elephant detection pi")
time.sleep(2)
print("Debug: I will stop listening when I receive a data message that contains NIGHT")
time.sleep(2)
print("Debug: I am waiting now!")
while True:
    data = sock.recvfrom(1024)
    print("Debug: got the following message from dayPi: ", data[0])
    if data[0] == "NIGHT":
        break
print("Debug: I got the NIGHT message so I quite waiting for a message!")
time.sleep(2)
print("Debug: Now I will perform elephant detection and monitor light condition")
time.sleep(2)
print("Debug: I will prepare to send a message to the dayPi when morning occurs!")
time.sleep(2)
#variable for light condition. Updated by light sensor
light = 0
#light is 0 for dark, and 1 for light
print("Debug: Currently light is ", light)
time.sleep(2)
light = 1 
#simulate light being 1
print("Debug: Currently light is ", light)
time.sleep(2)
print("Debug: Now it is time to message the dayPi since light is 1")
UDP_IP = "IP OF THE dayPi"
# IP of the dayPi
MESSAGE = "DAY"
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
print("Debug: I sent a message to the dayPi: ", MESSAGE)
time.sleep(2)
print("Debug: Now I wait for a message back when it is night again!")
sock.close()

It's the same method for sending messages back and forth, as we explained for the dayPi above, but with alterations for UDP_IP and the messages sent! 

GitHub: https://github.com/nksheridan/elephantAI/blob/master/demo_Ethernet_comms_DGRAM_nightPi

Here's what we get back when we run those two bits of code (if you didn't watch the video!). First off, this is what the dayPi gets:

And, here's what the nightPi gets:

3.

How do we connect the dayPi and nightPi? We just connect them with an Ethernet cable from their Ethernet ports!

4.

So that was fairly simple! And we just used DGRAM. 

CONCLUSION

Another issue we can solve with this comms method is the fact that only one of the detection devices will have mobile connectivity! So if the other detection device, that doesn't have mobile connectivity (via either HAT or USB dongle) , detects an elephant, what can it do? Well it can send a message to the detection device that does have mobile connectivity using this method! Then that device can send the message out! 

Another approach would be to multiplex the inputs to the mobile connectivity device so both detection devices can output to it. This should be ok for the HATs, but rather difficult involving cable splitting for the dongles

*there's a worry about impact of RF comms

Discussions