Skip to content

01 Beginner tutorial

Barnabas Homola edited this page Feb 1, 2021 · 6 revisions

In this tutorial, a simple Unity screen will be implemented which is communicating with a LEGO EV3 motor which is powered by a Raspberry Pi + BrickPi combo through UDP. The communication is bidirectional, meaning Unity is sending messages to the motor and the motor can also send packages to Unity.

Step 1 - Hardware setup and prerequisites

You need to have Unity downloaded on your computer. This tutorial uses Unity version 2019.2.8f1.

For this tutorial, we will use the BrickPi + Raspberry Pi (RPI) control unit, an EV3 motor connected to Port B and a power bank to power up the system. (Make sure that the power supply can provide 9V, so the motor is able to operate. The XT-16000QC3 PowerBank is recommended)

See the picture below of the hardware setup:

Furthermore you need a router which provides a closed wireless network. You have to be able to give static IP addresses to the Raspberry Pi and the computer which runs the Unity environment. How to assign static IP addresses differs between different types of routers, so check your model. In this tutorial, the Raspberry Pi has the IP address of 192.168.0.200 and the computer which runs Unity has 192.168.0.101 .

You also need to be able to control the Raspberry Pi from your computer to make things easier. The easiest way to do that is by using SSH and to access and edit the files of the Raspberry Pi to set up a Samba share.

Step 2 - Raspberry Pi setup

Make sure that the Raspberry Pi is connecting to the wireless network before loading anything up. To do so follow the steps:

  1. SSH into the RPI
  2. Run sudo raspi-config
  3. Select 3 Boot Options
  4. Select B2 Wait for Network at Boot and select Yes
  5. Save and restart the RPI

Step 3 - Raspberry Pi programming

This system has two parts:

  • A program is running on the Raspberry Pi, taking commands from the Unity program and able to send messages to Unity
  • The Unity program which is able to send data to the Raspberry Pi, therefore also actuating the motors and listening to incoming data from the Raspberry Pi

In this step we will implement the first part, the side of the Raspberry Pi. On how to develop with BrickPi by using python please visit their official documentation/examples! This tutorial will also take codes from examples which can be found there.

  1. SSH into the RPI
  2. Create a python script called unity_motor.py
  3. Add the following lines:
#!/usr/bin/env python
from __future__ import print_function # use python 3 syntax but make it compatible with python 2
from __future__ import division

import brickpi3 # import the BrickPi3 drivers
import socket # import the socket library to communicate using UDP protocol
import threading # using threading to listen to UDP messages
import time

The lines except the last three are needed for the BrickPi3 system. The socket library lets us use the UDP protocol and bind to a socket. The threading library will be used to start a thread to listen to incoming messages. The time library will allow us to slow the running of the main loop down a little bit, so the Raspberry PI CPU is not overloaded.

  1. Add the following lines below (explained in the comments):
BP = brickpi3.BrickPi3() # Create an instance of the BrickPi3 class. BP will be the BrickPi3 object.
BP.set_led(0) # For indicating succesful setup, we turn off the LED here at the beginning and turn it on if the system is succesfully set up
  1. Add the following lines below (explained in the comments):
print("Setting up UDP connection...")

# Unity program
SERVER_IP = '192.168.0.101'
SERVER_PORT = 5013

# Raspberry Pi
CLIENT_IP = '192.168.0.200'
CLIENT_PORT = 5011

# Create the socket
sock = socket.socket(socket.AF_INET, # Internet - given, don't change it
                     socket.SOCK_DGRAM) # UDP - given, don't change it

# Bind to the socket
sock.bind((CLIENT_IP, CLIENT_PORT))

print("UDP connection set up!")

The ports can be changed (See later) but it is not necessary. Keep them like this for the tutorial.

  1. Add the function which will be executed once the right UDP message arrives:
def moveMotor():
    target = BP.get_motor_encoder(BP.PORT_B) + 90 # Read the position of the motor and add 90 degrees, so in the next line it will be moved by 90 degrees
    BP.set_motor_position(BP.PORT_B, target)
  1. Add the message callback function and the receive message functions which will run on its own thread later:
def messageCallback(data):
    message = data.decode("utf-8")
    print(message)

    if message == 'move':
        moveMotor()
    
def receiveMsg():
    while True:
        data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
        if (addr[0] == SERVER_IP):
            messageCallback(data)
  1. Add the main function and its main loop to the program:
def main():
    try:
        try:
            BP.offset_motor_encoder(BP.PORT_B, BP.get_motor_encoder(BP.PORT_B))
        except IOError as error:
            print(error)

        # Starting a thread which is listening to the UDP messages until the program is closed
        receiveMsgThread = threading.Thread(target=receiveMsg, args=())
        receiveMsgThread.daemon = True # Making the Thread daemon, so it stops when the main program has quit
        receiveMsgThread.start()

        BP.set_led(100) # Light up the LED to show the setup is successful
        print("Main loop running...")
    
        while True:       
            value_motor = BP.get_motor_encoder(BP.PORT_B) # get the position of the motor
            value_motor = str(value_motor) # turn it into a string, so it can be sent through UDP

            udp_message = str.encode(f"{value_motor}")
            sock.sendto(udp_message, (SERVER_IP, SERVER_PORT))
            time.sleep(0.01) # Without sleep the system logs and sends data to the server ~820 times per second (820 Hz)

    except KeyboardInterrupt: # except the program gets interrupted by Ctrl+C on the keyboard.
        sock.close() # close the UDP socket
        print('\nsocket closed')
        BP.reset_all()        # Unconfigure the sensors, disable the motors, and restore the LED to the control of the BrickPi3 firmware.
        print('program stopped')

if __name__ == "__main__":
    main()

The BrickPi system works in a similar fashion to the Arduino. It runs the main never-ending loop (while True:) In this main loop the position of the motor is constantly read and sent to the Unity program, which moves the cube accordingly. When the program has quit (by pressing Ctrl+C the socket is closed and the sensors are unconfigured. Much of this code is from the previously mentioned examples from the BrickPi3 examples.

That's it for the Raspberry Pi script. What this easy script does is:

  • Listens to UDP messages
  • If the message says 'move' it moves the motor by 90 degrees
  • It constantly sends the motor rotation data to Unity (we have to implement the Unity part, so it receives the message and moves the cube)

Step 4 - Unity programming

So the Raspbery Pi script is ready, now let's create a Unity program which moves a Cube based on the status of the motor and moves the motor if a key is pressed on the keyboard. All this will be done by sending UDP packages between Unity and the Raspberry Pi.

Step ? - Running the system

Optional: Run the RPI script on startup