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)
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))
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 ":" + str(address)) 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 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 # so the split from second bit of string..  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
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:
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 == "DAY": break print("Debug: I got a message saying it is DAY. Time to begin my detection again") sock.close()
-- 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) if data == "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!
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:
How do we connect the dayPi and nightPi? We just connect them with an Ethernet cable from their Ethernet ports!
So that was fairly simple! And we just used DGRAM.
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