Previous Blog
Project R.A.G. - Blog #10 - Mechanical Build P3 (Final)

The Robot Assisted Garden Project

 

1. Introduction

     Hi! This will be my eleventh and final blog for the 1 Meter of Pi - Design Challenge. It was a great competition to be part of with a lot of amazing projects. Because of some other work and projects, I unfortunately had to start pretty late with my first blog being posted on November 29th. With a little over a month from then, I managed to write 10 additional blogs including this one. I've tried putting it as much as work and effort as I can both into the project and into the blogs. I'm really happy and proud of how far the projects has come since the first blog. But this won't be just the project summary blog, I will also finally cover the GUI I designed and programmed for the Raspberry and then I'll do some final tests and do a final overview of my whole journey for this project, hope you enjoy!

 

2. Software

     Now it's time for the par that I've been putting aside, avoiding for the longest period possible, the software. This is something I should have started at the very beginning so I could grow the software as the project goes, rather than finishing up everything else and doing a one shot for the software, but I managed to make it work in the end. First thing we need to check out, which is also the most important thing is the I2C communication.

 

I2C Communication

     I2C is a communication protocol that I'm going to use to get and send data to my modules, Arduinos inside, using a Raspberry Pi 4. There are many ways to do this, but I chose to go with I2C because it turned to be pretty easy to use and is also extremely flexible for adding new devices. This protocol has a hierarchy of a master devices with smaller slave devices surrounding it. There are ways to have multiple masters, but I'm only going to use one master for this project. The master will be the Raspberry of course with the Arduinos being the slaves. I'll now go over what's needed to set up this communication on both of the boards I'm using for this project.

 

Master

     Let's begin with master, the first thing that needs to be done when using I2C with a Raspberry is enabling it. It can be easily enabled through preferences-communications where we can also configure other things such as enabling the camera or SPI communication. The library that I'm going to use to utilize the I2C communication is smbus2.

Just a quick heads up for anyone who runs into these issues, if you look online, smbus2 should be installed with a command pip install smbus2, which will work great until you try running it using Python3. I had a lot of problems with this, but the solution turned out to be simple and that is to install it again like pip3 install smbus2. In other words, there's a difference if it's for Python2 or Python3

     After installing the library, I followed a small example I saw online, the link to that example and the original author is here: Raspberry Pi - Arduino  - I2C Communication. Following that example I just took the bits out of it that I needed and then put all of that together into my code. One notable thing is this function for converting String to Bytes.

 

def ConvertStringToBytes(src):
    converted = []
    for b in src:
        converted.append(ord(b))


    return converted

 

     Besides that, if we want to now send data to the Arduino via the I2C bus, we need to define the bus as well as the address on which are Arduino will be, of course, that address is something we are setting up in the first place, so we need to make sure that it's the same on both sides.

 

SLModule_address = 0x04
WRModule_address = 0x05


I2Cbus = smbus2.SMBus(1)


BytesToSend = ConvertStringToBytes(message)
I2Cbus.write_i2c_block_data(SLModule_address, 0x00, BytesToSend)

 

     And that would be pretty much it for the master. Reading the data from the slaves is simple to using this library. All you need to do is send a request for data to the address that you want, and that device will respond with the bytes that you need. This is something I couldn't implement because I didn't have enough time, while getting the data across is easy, because I needed a lot of different things and measurements, I had a problem with translating the raw data into useful data. Something that just needs a bit of tinkering around, but I didn't want to leave it in the code half finished.

 

Slave

     On the slave side we will have Arduino Nanos, for them, I will be using the Wire Library. It has a couple of great built in functions which make it really easy to use with I2C. We need an address of course, this is the address I already mentioned that needs to match the address on the Raspberry and we need a function to call as an event when we receive some data over I2C.

 

// Setting up I2C
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(ReceiveData);

 

     Besides that, we need a function ReceiveData that will grab the bytes coming over I2C and store them in our variables which we can use further in our program. For example, for the SL module I just sliced the data into 2 parts where each of them showed how bright the LED strip should be, while for the WR Module, since it was controlled by buttons, if any of the buttons was pressed, I would send a single integer which I can then read and decode. Here is an example of a Receive Data function which I used in the WR module.

 

void ReceiveData(int byteCoun){
    while ( Wire.available()) {
        char c = (char)Wire.read();
        //Serial.println(c);
        RaspberryData += c;
    }
    command = RaspberryData.substring(1).toInt();
    Serial.println(command);
    
    Serial.println(RaspberryData);
    RaspberryData = "";


    Execute(command);
    
  }

 

GUI

     This is a part that took a while. I'm not experienced with Python, so I just went and did what I see worked and repeated that until I got the result I wanted. This GUI is in my opinion 90% finished, the only thing I didn't manage to sort out was a central PiCamera live feed in the GUI. It can be done through a label and OpenCV, but I didn't have time to experiment and make it work. My idea for the GUI was essentially this.

     For controlling the lights I wanted to have sliders, which also goes for all of the joints on the robot, while for the rail controls and the water pump, I wanted just plain and ordinary buttons. When it comes to the rail system, I wanted 3 sets of buttons, ones for small movements, 10 steps, big movements, 100 steps, and to go from one end to the other. After a couple of hours of coding I ended up with this, and I'm more than happy with the looks.

Finished code

     These here are the codes that I've updated to support the communication that I talked about as well as work with the GUI that I designed. The Arduino codes are 95% the same as the codes I used in the blogs for each module, while the code for the Raspberry I wrote from scratch. For designing the GUI I used Python and a library called PyQt5. The code is pretty long, but I don't think that there's anything to complex in it. If you have any questions about it, feel free to reach out!

 

Raspberry code

from PyQt5.QtWidgets import QApplication, QPushButton, QLineEdit, QLabel, QWidget, QSlider
from PyQt5.QtGui import QPainter, QColor, QPen, QFont
from PyQt5.QtCore import Qt
import sys
import threading
import time
import smbus2


#Variables


SL_tempData = 25;
SL_humData = 47;
SL_illuminationData = 315;
SL_COData = 0
SL_alcoholData = 0
SL_CO2Data = 0
SL_toluenoData = 0
SL_NH4Data = 0
SL_acetoneData = 0
ENVIRO_tempData = 0
ENVIRO_humData = 0
ENVIRO_illuminationData = 0
ENVIRO_pressureData = 0


LED1 = 0
LED2 = 0


def ConvertStringToBytes(src):
    converted = []
    for b in src:
        converted.append(ord(b))


    return converted


SLModule_address = 0x04
WRModule_address = 0x05


I2Cbus = smbus2.SMBus(1)




def SendLEDData():
    global LED1
    global LED2
    led1 = str(LED1)
    led2 = str(LED2)
    if LED1 < 100:
        led1 = "0" + led1
    if LED1 < 10:
        led1 = "0" + led1


    if LED2 < 100:
        led2 = "0" + led2
    if LED2 < 10:
        led2 = "0" + led2


    message = led1 + "&" + led2
    BytesToSend = ConvertStringToBytes(message)
    I2Cbus.write_i2c_block_data(SLModule_address, 0x00, BytesToSend)




class App(QWidget):
    def __init__(self):
        super(App, self).__init__()
        self.title = 'Project R.A.G.'
        self.left = 200
        self.top = 200
        self.width = 1920
        self.height = 1080
        self.initUI()
        
    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        
        self.setAutoFillBackground(True)
        p = self.palette()
        p.setColor(self.backgroundRole(), QColor(35, 35, 40))
        self.setPalette(p)
       
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("LED Lights Control")
        self.labelLED.setGeometry(65, 50, 300, 30)
        self.labelLED.setFont(QFont("Arial", 24))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("_______________________________________________")
        self.labelLine.setGeometry(60, 60, 290, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        self.Slider1 = QSlider(Qt.Horizontal, self)
        self.Slider1.setGeometry(70, 110, 200, 30)
        self.Slider1.setRange(0,255)
        self.Slider1.valueChanged[int].connect(self.changeValue1)
        
        self.Slider2 = QSlider(Qt.Horizontal, self)
        self.Slider2.setGeometry(70, 210, 200, 30)
        self.Slider2.setRange(0,255)
        self.Slider2.valueChanged[int].connect(self.changeValue2)
        
        self.labelSlider1 = QLabel(self)
        self.labelSlider1.setText("000")
        self.labelSlider1.setGeometry(280, 110, 50, 24)
        self.labelSlider1.setFont(QFont("Arial", 15))
        self.labelSlider1.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSlider2 = QLabel(self)
        self.labelSlider2.setText("000")
        self.labelSlider2.setGeometry(280, 210, 50, 24)
        self.labelSlider2.setFont(QFont("Arial", 15))
        self.labelSlider2.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("_______________________________________________")
        self.labelLine.setGeometry(60, 240, 290, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
    
        self.labelSLData = QLabel(self)
        self.labelSLData.setText("SL Module Data")
        self.labelSLData.setGeometry(60, 320, 300, 30)
        self.labelSLData.setFont(QFont("Arial", 30))
        self.labelSLData.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("_______________________________________________")
        self.labelLine.setGeometry(60, 350, 290, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        self.labelSLTemp = QLabel(self)
        self.labelSLTemp.setText("Temperature:")
        self.labelSLTemp.setGeometry(60, 390, 300, 30)
        self.labelSLTemp.setFont(QFont("Arial", 18))
        self.labelSLTemp.setStyleSheet("QLabel {color : white}")
        
        self.labelSLTempValue = QLabel(self)
        self.labelSLTempValue.setText(str(SL_tempData))
        self.labelSLTempValue.setGeometry(280, 390, 50, 24)
        self.labelSLTempValue.setFont(QFont("Arial", 15))
        self.labelSLTempValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSLHumidity = QLabel(self)
        self.labelSLHumidity.setText("Humidity:")
        self.labelSLHumidity.setGeometry(60, 440, 300, 30)
        self.labelSLHumidity.setFont(QFont("Arial", 18))
        self.labelSLHumidity.setStyleSheet("QLabel {color : white}")
        
        self.labelSLHumidityValue = QLabel(self)
        self.labelSLHumidityValue.setText(str(SL_humData))
        self.labelSLHumidityValue.setGeometry(280, 440, 50, 24)
        self.labelSLHumidityValue.setFont(QFont("Arial", 15))
        self.labelSLHumidityValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSLIllumination = QLabel(self)
        self.labelSLIllumination.setText("Illumination:")
        self.labelSLIllumination.setGeometry(60, 490, 300, 30)
        self.labelSLIllumination.setFont(QFont("Arial", 18))
        self.labelSLIllumination.setStyleSheet("QLabel {color : white}")
        
        self.labelSLIlluminationValue = QLabel(self)
        self.labelSLIlluminationValue.setText(str(SL_illuminationData))
        self.labelSLIlluminationValue.setGeometry(280, 490, 50, 24)
        self.labelSLIlluminationValue.setFont(QFont("Arial", 15))
        self.labelSLIlluminationValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("-----------------------------------------------")
        self.labelLine.setGeometry(60, 520, 290, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        self.labelSLCO = QLabel(self)
        self.labelSLCO.setText("CO:")
        self.labelSLCO.setGeometry(60, 560, 300, 30)
        self.labelSLCO.setFont(QFont("Arial", 18))
        self.labelSLCO.setStyleSheet("QLabel {color : white}")
        
        self.labelSLCOValue = QLabel(self)
        self.labelSLCOValue.setText(str(SL_COData))
        self.labelSLCOValue.setGeometry(280, 560, 50, 24)
        self.labelSLCOValue.setFont(QFont("Arial", 15))
        self.labelSLCOValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSLAlcohol = QLabel(self)
        self.labelSLAlcohol.setText("Alcohol:")
        self.labelSLAlcohol.setGeometry(60, 610, 300, 30)
        self.labelSLAlcohol.setFont(QFont("Arial", 18))
        self.labelSLAlcohol.setStyleSheet("QLabel {color : white}")
        
        self.labelSLAlcoholValue = QLabel(self)
        self.labelSLAlcoholValue.setText(str(SL_alcoholData))
        self.labelSLAlcoholValue.setGeometry(280, 610, 50, 24)
        self.labelSLAlcoholValue.setFont(QFont("Arial", 15))
        self.labelSLAlcoholValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSLCO2 = QLabel(self)
        self.labelSLCO2.setText("CO2:")
        self.labelSLCO2.setGeometry(60, 660, 300, 30)
        self.labelSLCO2.setFont(QFont("Arial", 18))
        self.labelSLCO2.setStyleSheet("QLabel {color : white}")
        
        self.labelSLCO2Value = QLabel(self)
        self.labelSLCO2Value.setText(str(SL_CO2Data))
        self.labelSLCO2Value.setGeometry(280, 660, 50, 24)
        self.labelSLCO2Value.setFont(QFont("Arial", 15))
        self.labelSLCO2Value.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSLTolueno = QLabel(self)
        self.labelSLTolueno.setText("Tolueno:")
        self.labelSLTolueno.setGeometry(60, 710, 300, 30)
        self.labelSLTolueno.setFont(QFont("Arial", 18))
        self.labelSLTolueno.setStyleSheet("QLabel {color : white}")
        
        self.labelSLToluenoValue = QLabel(self)
        self.labelSLToluenoValue.setText(str(SL_toluenoData))
        self.labelSLToluenoValue.setGeometry(280, 710, 50, 24)
        self.labelSLToluenoValue.setFont(QFont("Arial", 15))
        self.labelSLToluenoValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSLNH4 = QLabel(self)
        self.labelSLNH4.setText("NH4:")
        self.labelSLNH4.setGeometry(60, 760, 300, 30)
        self.labelSLNH4.setFont(QFont("Arial", 18))
        self.labelSLNH4.setStyleSheet("QLabel {color : white}")
        
        self.labelSLNH4Value = QLabel(self)
        self.labelSLNH4Value.setText(str(SL_NH4Data))
        self.labelSLNH4Value.setGeometry(280, 760, 50, 24)
        self.labelSLNH4Value.setFont(QFont("Arial", 15))
        self.labelSLNH4Value.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelSLAcetone = QLabel(self)
        self.labelSLAcetone.setText("Acetone:")
        self.labelSLAcetone.setGeometry(60, 810, 300, 30)
        self.labelSLAcetone.setFont(QFont("Arial", 18))
        self.labelSLAcetone.setStyleSheet("QLabel {color : white}")
        
        self.labelSLAcetoneValue = QLabel(self)
        self.labelSLAcetoneValue.setText(str(SL_acetoneData))
        self.labelSLAcetoneValue.setGeometry(280, 810, 50, 24)
        self.labelSLAcetoneValue.setFont(QFont("Arial", 15))
        self.labelSLAcetoneValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("_______________________________________________")
        self.labelLine.setGeometry(60, 830, 290, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
           
        self.labelLED = QLabel(self)
        self.labelLED.setText("ENVIRO HAT MINI DATA")
        self.labelLED.setGeometry(650, 40, 700, 40)
        self.labelLED.setFont(QFont("Arial", 36))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("____________________________________________________________________________________________________________________________________________________")
        self.labelLine.setGeometry(450, 60, 920, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        
        self.labelENVTemp = QLabel(self)
        self.labelENVTemp.setText("Temperature:")
        self.labelENVTemp.setGeometry(500, 120, 300, 30)
        self.labelENVTemp.setFont(QFont("Arial", 18))
        self.labelENVTemp.setStyleSheet("QLabel {color : white}")
        
        self.labelENVTempValue = QLabel(self)
        self.labelENVTempValue.setText(str(ENVIRO_tempData))
        self.labelENVTempValue.setGeometry(720, 120, 50, 24)
        self.labelENVTempValue.setFont(QFont("Arial", 15))
        self.labelENVTempValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelENVHum = QLabel(self)
        self.labelENVHum.setText("Humidity:")
        self.labelENVHum.setGeometry(500, 200, 300, 30)
        self.labelENVHum.setFont(QFont("Arial", 18))
        self.labelENVHum.setStyleSheet("QLabel {color : white}")
        
        self.labelENVHumValue = QLabel(self)
        self.labelENVHumValue.setText(str(ENVIRO_humData))
        self.labelENVHumValue.setGeometry(720, 200, 50, 24)
        self.labelENVHumValue.setFont(QFont("Arial", 15))
        self.labelENVHumValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelENVIllum = QLabel(self)
        self.labelENVIllum.setText("Illumination:")
        self.labelENVIllum.setGeometry(1025, 120, 300, 30)
        self.labelENVIllum.setFont(QFont("Arial", 18))
        self.labelENVIllum.setStyleSheet("QLabel {color : white}")
        
        self.labelENVIllumValue = QLabel(self)
        self.labelENVIllumValue.setText(str(ENVIRO_illuminationData))
        self.labelENVIllumValue.setGeometry(1245, 120, 50, 24)
        self.labelENVIllumValue.setFont(QFont("Arial", 15))
        self.labelENVIllumValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelENVPressure = QLabel(self)
        self.labelENVPressure.setText("Pressure:")
        self.labelENVPressure.setGeometry(1025, 200, 300, 30)
        self.labelENVPressure.setFont(QFont("Arial", 18))
        self.labelENVPressure.setStyleSheet("QLabel {color : white}")
        
        self.labelENVPressureValue = QLabel(self)
        self.labelENVPressureValue.setText(str(ENVIRO_pressureData))
        self.labelENVPressureValue.setGeometry(1245, 200, 50, 24)
        self.labelENVPressureValue.setFont(QFont("Arial", 15))
        self.labelENVPressureValue.setStyleSheet("QLabel {background-color : white; color : black}")
        
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("____________________________________________________________________________________________________________________________________________________")
        self.labelLine.setGeometry(450, 240, 920, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
         
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("ROBOT")
        self.labelLED.setGeometry(1605, 40, 300, 40)
        self.labelLED.setFont(QFont("Arial", 36))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("__________________________________________________________________")
        self.labelLine.setGeometry(1500, 60, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("Rail Controls")
        self.labelLED.setGeometry(1620, 100, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("--------------------------------------------------------")
        self.labelLine.setGeometry(1500, 130, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("Small move")
        self.labelLED.setGeometry(1650, 175, 300, 30)
        self.labelLED.setFont(QFont("Arial", 15))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("Big move")
        self.labelLED.setGeometry(1660, 225, 300, 30)
        self.labelLED.setFont(QFont("Arial", 15))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("To end")
        self.labelLED.setGeometry(1670, 275, 300, 30)
        self.labelLED.setFont(QFont("Arial", 15))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.buttonSlowLeft = QPushButton('<-', self)
        self.buttonSlowLeft.resize(100, 40)
        self.buttonSlowLeft.move(1520, 170)
        self.buttonSlowLeft.clicked.connect(self.Button1Fcn)
        
        self.buttonFastLeft = QPushButton('<--', self)
        self.buttonFastLeft.resize(100, 40)
        self.buttonFastLeft.move(1520, 220)
        self.buttonFastLeft.clicked.connect(self.Button2Fcn)
        
        self.buttonToEndLeft = QPushButton('<---', self)
        self.buttonToEndLeft.resize(100, 40)
        self.buttonToEndLeft.move(1520, 270)
        self.buttonToEndLeft.clicked.connect(self.Button3Fcn)
        
        
        self.buttonSlowRight = QPushButton('->', self)
        self.buttonSlowRight.resize(100, 40)
        self.buttonSlowRight.move(1770, 170)
        self.buttonSlowRight.clicked.connect(self.Button4Fcn)
        
        self.buttonFastRight = QPushButton('-->', self)
        self.buttonFastRight.resize(100, 40)
        self.buttonFastRight.move(1770, 220)
        self.buttonFastRight.clicked.connect(self.Button5Fcn)
        
        self.buttonToEndRight = QPushButton('--->', self)
        self.buttonToEndRight.resize(100, 40)
        self.buttonToEndRight.move(1770, 270)
        self.buttonToEndRight.clicked.connect(self.Button6Fcn)
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("--------------------------------------------------------")
        self.labelLine.setGeometry(1500, 310, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}") 
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("Joint Controls")
        self.labelLED.setGeometry(1610, 350, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("--------------------------------------------------------")
        self.labelLine.setGeometry(1500, 370, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        #1
        self.labelLED = QLabel(self)
        self.labelLED.setText("J1")
        self.labelLED.setGeometry(1520, 410, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.SliderR1 = QSlider(Qt.Horizontal, self)
        self.SliderR1.setGeometry(1590, 410, 200, 30)
        self.SliderR1.setRange(0,180)
        self.SliderR1.valueChanged[int].connect(self.changeValueR1)
        
        self.labelSliderR1 = QLabel(self)
        self.labelSliderR1.setText("000")
        self.labelSliderR1.setGeometry(1810, 410, 50, 24)
        self.labelSliderR1.setFont(QFont("Arial", 15))
        self.labelSliderR1.setStyleSheet("QLabel {background-color : white; color : black}")
        
        
        #2
        self.labelLED = QLabel(self)
        self.labelLED.setText("J2")
        self.labelLED.setGeometry(1520, 460, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.SliderR2 = QSlider(Qt.Horizontal, self)
        self.SliderR2.setGeometry(1590, 460, 200, 30)
        self.SliderR2.setRange(0,180)
        self.SliderR2.valueChanged[int].connect(self.changeValueR2)
        
        self.labelSliderR2 = QLabel(self)
        self.labelSliderR2.setText("000")
        self.labelSliderR2.setGeometry(1810, 460, 50, 24)
        self.labelSliderR2.setFont(QFont("Arial", 15))
        self.labelSliderR2.setStyleSheet("QLabel {background-color : white; color : black}")
        
        
        #3
        self.labelLED = QLabel(self)
        self.labelLED.setText("J3")
        self.labelLED.setGeometry(1520, 510, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.SliderR3 = QSlider(Qt.Horizontal, self)
        self.SliderR3.setGeometry(1590, 510, 200, 30)
        self.SliderR3.setRange(0,180)
        self.SliderR3.valueChanged[int].connect(self.changeValueR3)
        
        self.labelSliderR3 = QLabel(self)
        self.labelSliderR3.setText("000")
        self.labelSliderR3.setGeometry(1810, 510, 50, 24)
        self.labelSliderR3.setFont(QFont("Arial", 15))
        self.labelSliderR3.setStyleSheet("QLabel {background-color : white; color : black}")
        
        
        #4
        self.labelLED = QLabel(self)
        self.labelLED.setText("J4")
        self.labelLED.setGeometry(1520, 560, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.SliderR4 = QSlider(Qt.Horizontal, self)
        self.SliderR4.setGeometry(1590, 560, 200, 30)
        self.SliderR4.setRange(0,180)
        self.SliderR4.valueChanged[int].connect(self.changeValueR4)
        
        self.labelSliderR4 = QLabel(self)
        self.labelSliderR4.setText("000")
        self.labelSliderR4.setGeometry(1810, 560, 50, 24)
        self.labelSliderR4.setFont(QFont("Arial", 15))
        self.labelSliderR4.setStyleSheet("QLabel {background-color : white; color : black}")
        
        
        #5
        self.labelLED = QLabel(self)
        self.labelLED.setText("J5")
        self.labelLED.setGeometry(1520, 610, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.SliderR5 = QSlider(Qt.Horizontal, self)
        self.SliderR5.setGeometry(1590, 610, 200, 30)
        self.SliderR5.setRange(0,180)
        self.SliderR5.valueChanged[int].connect(self.changeValueR5)
        
        self.labelSliderR5 = QLabel(self)
        self.labelSliderR5.setText("000")
        self.labelSliderR5.setGeometry(1810, 610, 50, 24)
        self.labelSliderR5.setFont(QFont("Arial", 15))
        self.labelSliderR5.setStyleSheet("QLabel {background-color : white; color : black}")
        
        
        #6
        self.labelLED = QLabel(self)
        self.labelLED.setText("J6")
        self.labelLED.setGeometry(1520, 660, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.SliderR6 = QSlider(Qt.Horizontal, self)
        self.SliderR6.setGeometry(1590, 660, 200, 30)
        self.SliderR6.setRange(0,180)
        self.SliderR6.valueChanged[int].connect(self.changeValueR6)
        
        self.labelSliderR6 = QLabel(self)
        self.labelSliderR6.setText("000")
        self.labelSliderR6.setGeometry(1810, 660, 50, 24)
        self.labelSliderR6.setFont(QFont("Arial", 15))
        self.labelSliderR6.setStyleSheet("QLabel {background-color : white; color : black}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("__________________________________________________________________")
        self.labelLine.setGeometry(1500, 680, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
              
        self.labelLED = QLabel(self)
        self.labelLED.setText("WATER")
        self.labelLED.setGeometry(1600, 750, 300, 40)
        self.labelLED.setFont(QFont("Arial", 36))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("__________________________________________________________________")
        self.labelLine.setGeometry(1500, 770, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("Pump: ")
        self.labelLED.setGeometry(1520, 820, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.buttonPumpOn = QPushButton('ON', self)
        self.buttonPumpOn.resize(100, 40)
        self.buttonPumpOn.move(1630, 820)
        self.buttonPumpOn.clicked.connect(self.Button7Fcn)
        
        self.buttonPumpOff = QPushButton('OFF', self)
        self.buttonPumpOff.resize(100, 40)
        self.buttonPumpOff.move(1750, 820)
        self.buttonPumpOff.clicked.connect(self.Button8Fcn)
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("--------------------------------------------------------")
        self.labelLine.setGeometry(1500, 860, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {color : white}")
        
        self.labelLED = QLabel(self)
        self.labelLED.setText("Water Lvl: ")
        self.labelLED.setGeometry(1520, 900, 300, 30)
        self.labelLED.setFont(QFont("Arial", 20))
        self.labelLED.setStyleSheet("QLabel {color : white}")
        
        self.labelWaterLevel = QLabel(self)
        self.labelWaterLevel.setText("000")
        self.labelWaterLevel.setGeometry(1700, 900, 300, 30)
        self.labelWaterLevel.setFont(QFont("Arial", 20))
        self.labelWaterLevel.setStyleSheet("QLabel {color : white}")
        
        self.labelLine = QLabel(self)
        self.labelLine.setText("__________________________________________________________________")
        self.labelLine.setGeometry(1500, 930, 390, 30)
        self.labelLine.setFont(QFont("Arial", 18))
        self.labelLine.setStyleSheet("QLabel {background-color : white; color : black}")
        
        
        self.show()
    
    def changeValue1(self, value):
        self.labelSlider1.setText(str(value))
        global LED1
        LED1 = value
        #SendLEDData()
        print(value)
        
    def changeValue2(self, value):
        self.labelSlider2.setText(str(value))
        global LED2
        LED2 = value
        #SendLEDData()
        print(value)
        
    def changeValueR1(self, value):
        self.labelSliderR1.setText(str(value))
        print(value)
        
    def changeValueR2(self, value):
        self.labelSliderR2.setText(str(value))
        print(value)
        
    def changeValueR3(self, value):
        self.labelSliderR3.setText(str(value))
        print(value)
        
    def changeValueR4(self, value):
        self.labelSliderR4.setText(str(value))
        print(value)
        
    def changeValueR5(self, value):
        self.labelSliderR5.setText(str(value))
        print(value)
        
    def changeValueR6(self, value):
        self.labelSliderR6.setText(str(value))
        print(value)


    def Button1Fcn(self):
        BytesToSend = ConvertStringToBytes("1")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("1")


    def Button2Fcn(self):
        BytesToSend = ConvertStringToBytes("2")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("2")


    def Button3Fcn(self):
        BytesToSend = ConvertStringToBytes("3")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("3")


    def Button4Fcn(self):
        BytesToSend = ConvertStringToBytes("4")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("4")


    def Button5Fcn(self):
        BytesToSend = ConvertStringToBytes("5")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("5")


    def Button6Fcn(self):
        BytesToSend = ConvertStringToBytes("6")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("6")


    def Button7Fcn(self):
        BytesToSend = ConvertStringToBytes("7")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("7")


    def Button8Fcn(self):
        BytesToSend = ConvertStringToBytes("8")
        I2Cbus.write_i2c_block_data(WRModule_address, 0x00, BytesToSend)
        print("8")
       
def fun1():
    app = QApplication(sys.argv)
    ex = App()
    app.exec_()


   


def fun2():
    while(True):
        
        SendLEDData()
        time.sleep(1)
        
        
t1 = threading.Thread(target = fun1)
t2 = threading.Thread(target = fun2)


t1.start()
t2.start()
        

 

SL Module code

/*
 *                                                  Water & Rail Module
 * 
 * ___________________________________________________________________________________________________________________
 * This code is for the Water & Rail module featured in my project for 1 Meter of Pi design challenge on element14.com
 * You are free to do with this code whatever you want!
 * ___________________________________________________________________________________________________________________
 * 
 */


//  Libraries
  #include <NewPing.h>
  #include <Servo.h>
  #include "Wire.h"


//  Pins
  #define Pin_MSwitch1 2
  #define Pin_MSwitch2 3
  #define Pin_Pump 4
  #define Pin_ToolChange 5
  #define Pin_ServoTool 6
  #define Pin_MuxA 7
  #define Pin_MuxB 8
  #define Pin_Step 9
  #define Pin_Direction 10
  #define Pin_HCSR04 11
  #define Pin_ToolInput 12
  #define Pin_ToolSensor A0
  #define Max_Distance 200
  #define SLAVE_ADDRESS 0x05


//  Setting up the HC-SR04
  NewPing WaterLevelSensor(Pin_HCSR04, Pin_HCSR04, Max_Distance);


// Setting up the servo motors
  Servo toolChangeServo;
  Servo toolServo;


//  Constants
  int containerDepth = 30; // This constant defines how deep our water storage container is, the measuremnt is in cm
  double containerSurface = 25; // The surface area of the container in cm^2
  int stepperDelay = 1000;  // This delay determines the speed of our stepper motor


//  Variables
  volatile bool limitSwitch1 = false; // This variable is for the first of the 2 limit switches on the rail
  volatile bool limitSwitch2 = false; // This variable is for the second of the 2 limit switches on the rail
  int muxA = 0;
  int muxB = 0;
  int selectedTool = 0;
  int command = 0;
  String RaspberryData;


/*
 * 
 *  FUNCTIONS
 * 
 */


//  Function for calculating how much water is left in the sytem
double LiquidLevel(){
  
    int sensorReading = 0;
    double liquidLevel = 0;
    double sensorData = 0;
    int i = 0;
    
    while(i < 3){
        sensorReading = WaterLevelSensor.ping_cm();
        if(sensorReading > 0 && sensorReading <= containerDepth){
            sensorData += sensorReading;
            i++;
          }
        delay(50);
      }
      
    sensorData = sensorData/3;
    liquidLevel = (containerDepth - sensorData)*containerSurface;
    liquidLevel = liquidLevel/1000; //  Converting the value to L
    
    Serial.print("Current volume of water in the container is: ");
    Serial.print(liquidLevel);
    Serial.println("L");
    
    return liquidLevel;
  }




// Function for turning on and off the water pump
void TurnPump(String s){
    if(s == "on" || s == "ON" || s == "1" || s == "On"){
        digitalWrite(Pin_Pump, LOW);
      }
      else if(s == "off" || s == "OFF" || s == "0" || s == "Off"){
          digitalWrite(Pin_Pump, HIGH);
        }
  }




//  Interrupt Pin Function 1
void LimitSwitch1On(){
    limitSwitch1 = true;
    Serial.println(1);
  }


//  Interrupt Pin Function 2
void LimitSwitch2On(){
    limitSwitch2 = true;
    Serial.println(2);
  }


//  Interrupt Pin Function 3
void LimitSwitch1Off(){
    limitSwitch1 = false;
    Serial.println(3);
  }


//  Interrupt Pin Function 4
void LimitSwitch2Off(){
    limitSwitch2 = false;
    Serial.println(4);
  }


// Function for moving the robot on rails
void MoveRobot(int dir, int numberOfSteps){


    if(dir == 0 || dir == 1){
      digitalWrite(Pin_Direction, dir);
      }
      else{
          return;
        }


    for(int i = 0; i < numberOfSteps; i++){


      if((dir == 0 && digitalRead(Pin_MSwitch1) == 0)||(dir == 1 && digitalRead(Pin_MSwitch1) == 0))
        digitalWrite(Pin_Step, HIGH);
        delayMicroseconds(stepperDelay);
        digitalWrite(Pin_Step, LOW);
        delayMicroseconds(stepperDelay);
      }
    
  }


//  Function for moving the robot until he reaches one end
void MoveRobotToStart(){
    digitalWrite(Pin_Direction, 0);
    bool continueLoop = true;
    while(continueLoop == true){
        if(digitalRead(Pin_MSwitch1) == 0){
            digitalWrite(Pin_Step, HIGH);
            delayMicroseconds(stepperDelay);
            digitalWrite(Pin_Step, LOW);
            delayMicroseconds(stepperDelay);
          }
          else{
              continueLoop = false;
              Serial.println("Limit switch 1 activated");
            }         
      } 
  }


//  Function for moving the robot until he reaches the other end
void MoveRobotToEnd(){
    digitalWrite(Pin_Direction, 1);
    bool continueLoop = true;
    while(continueLoop == true){
        if(digitalRead(Pin_MSwitch2) == 0){
            digitalWrite(Pin_Step, HIGH);
            delayMicroseconds(stepperDelay);
            digitalWrite(Pin_Step, LOW);
            delayMicroseconds(stepperDelay);
          }
          else{
              continueLoop = false;
              Serial.println("Limit switch 2 activated");
            }         
      }
  }


//  These functions are for using the ToolChange servo on the robot
void TakeTool(){
    toolChangeServo.write(0);
  }
  
void ReleaseTool(){
    toolChangeServo.write(180);
  }


// Function playing with the tool on the robot
void ChangeToTool(int toolNumber){   
    switch(toolNumber){
        case 0:
          muxA = 0;
          muxB = 0;
          Serial.println("You have chosen the Servo tool.");
          break;
        case 1:
          muxA = 1;
          muxB = 0;
          Serial.println("You have chosen the Sensor tool.");
          break;
        case 2:
          muxA = 0;
          muxB = 1;
          Serial.println("You have chosen the Input tool.");
          break;
        case 3:
          muxA = 1;
          muxB = 1;  
          Serial.println("You have chosen an undefined tool.");
        default:
          Serial.println("Error: Tool index out of range!");               
      }
    if(toolNumber >= 0 && toolNumber <=3){
        selectedTool = toolNumber;
        digitalWrite(Pin_MuxA, muxA);
        digitalWrite(Pin_MuxB, muxB);
      }    
  }


void ServoTool(int angle){
    if(selectedTool == 0){
      if(angle >= 0 && angle <= 180){
        toolServo.write(angle);
        }
        else{
            return;
          }
      }
      else{
          Serial.print("You have not chosen this tool, the tool which is selected currently is number ");
          Serial.print(selectedTool);
          Serial.println(". In order to use this tool, you must select tool 0 first");
        }
    }


int SensorTool(){   
    if(selectedTool == 1){
        return analogRead(Pin_ToolSensor);
      }
      else{
          Serial.print("You have not chosen this tool, the tool which is selected currently is number ");
          Serial.print(selectedTool);
          Serial.println(". In order to use this tool, you must select tool 1 first");
        }
  }


int InputTool(){    
    if(selectedTool == 2){
        return digitalRead(Pin_ToolInput);
      }
      else{
          Serial.print("You have not chosen this tool, the tool which is selected currently is number ");
          Serial.print(selectedTool);
          Serial.println(". In order to use this tool, you must select tool 2 first");
        }
  }




/*
 *  START OF OUR PROGRAM
 */




void setup() {


  // Setting up I2C
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(ReceiveData);
  
  //  Starting up Serial at 9600 baud rate
    Serial.begin(9600);


  //  Setting up all of the pins
    pinMode(Pin_MSwitch1, INPUT);
    pinMode(Pin_MSwitch2, INPUT);
    pinMode(Pin_Pump, OUTPUT);
    pinMode(Pin_MuxA, OUTPUT);
    pinMode(Pin_MuxB, OUTPUT);
    pinMode(Pin_Step, OUTPUT);
    pinMode(Pin_Direction, OUTPUT);
    pinMode(Pin_ToolSensor, INPUT);


  //  Setting up interrupts for the MicroSwitch pins
    attachInterrupt(digitalPinToInterrupt(Pin_MSwitch1), LimitSwitch1On, RISING);
    attachInterrupt(digitalPinToInterrupt(Pin_MSwitch2), LimitSwitch2On, RISING);
    attachInterrupt(digitalPinToInterrupt(Pin_MSwitch1), LimitSwitch1Off, FALLING);
    attachInterrupt(digitalPinToInterrupt(Pin_MSwitch2), LimitSwitch2Off, FALLING);  


  // Making sure the pump is turned off when starting the module
    TurnPump("Off");


  // Attaching servos to their deisgnated pins
    toolChangeServo.attach(Pin_ToolChange);
    toolServo.attach(Pin_ServoTool);
    delay(10000);
}


// Test function
void Bounce(){
    for(int i = 0; i < 2; i++){
        MoveRobotToStart();
        delay(200);
        MoveRobotToEnd();
        delay(200);
      }
  }


void ReceiveData(int byteCoun){
    while ( Wire.available()) {
        char c = (char)Wire.read();
        //Serial.println(c);
        RaspberryData += c;
    }
    command = RaspberryData.substring(1).toInt();
    Serial.println(command);
    
    Serial.println(RaspberryData);
    RaspberryData = "";


    Execute(command);
    
  }


void Execute(int com){


    switch(com){
      
        case 1:
          MoveRobot(0,10);
          command = 0;
          break;
        case 2:
          MoveRobot(0,100);
          command = 0;
          break;
        case 3:
          MoveRobotToStart();
          command = 0;
          break;
        case 4:
          MoveRobot(1,10);
          command = 0;
          break;
        case 5:
          MoveRobot(1,100);
          command = 0;
          break;
        case 6:
          MoveRobotToEnd();
          command = 0;
          break;
        case 7:
          TurnPump("On");
          command = 0;
          break;
        case 8:
          TurnPump("Off");
          command = 0;
          break;    
        default:
          command = 0;  
      }
  
  }


void loop() {
  /*
  Bounce();
  TurnPump("On");
  delay(15000);
  TurnPump("Off");
  delay(3000);
  TurnPump("On");
  delay(15000);
  TurnPump("Off");
  Bounce();  
  TakeTool();
  delay(500);
  ReleaseTool();
  delay(500);
  TakeTool();
  delay(500);
  ReleaseTool();
  delay(500);
  TakeTool();
  delay(500);
  ServoTool(180);
  delay(500);
  ServoTool(0);
  delay(500);
  ServoTool(180);
  delay(500);
  ServoTool(0);
  delay(500);
  ServoTool(180);
  delay(500);
  ServoTool(0);
  delay(50000);
  */
}

 

WR Module code

/*
 * 
 *                                                    SENSOR & LIGHT MODULE
 *  
 *  _____________________________________________________________________________________________________________________
 *  This code is for the sensor & light module featured in my project for 1 Meter of Pi design challenge on element14.com
 *  You are free to do with this code whatever you want!
 *  _____________________________________________________________________________________________________________________
 *  
 */


// Libraries
  #include <Arduino.h>
  #include <TM1637Display.h>  
  #include "DHT.h"
  #include <MQUnifiedsensor.h>
  #include "Wire.h"




// Pins we are going to use
  #define PinDisp1Data 2
  #define PinDisp2Data 3
  #define PinDispClock 4
  #define PinRB1 5
  #define PinG1 6
  #define PinGreenLED 7
  #define PinRedLED 8
  #define PinRB2 9
  #define PinG2 10
  #define PinDHT22Data 11
  /*
  * Besides these, we have 2 sensors connected to the analog pins in this way:
  * MQ135 ---- A0
  * DHT22 ---- A3
  */


// Slave address on the I2C bus
  #define SLAVE_ADDRESS 0x04


// Setting up the library objects


// DHT22
  #define DHTTYPE DHT22
  DHT dht(PinDHT22Data, DHTTYPE);


// TM1637 Displays
  TM1637Display display1(PinDispClock, PinDisp1Data);
  TM1637Display display2(PinDispClock, PinDisp2Data);


// MQ135 Sensor
  #define placa "Arduino Nano"
  #define Voltage_Resolution 5
  #define pin A0 //Analog input 0 of your arduino
  #define type "MQ-135" //MQ135
  #define ADC_Bit_Resolution 10 // For arduino UNO/MEGA/NANO
  #define RatioMQ135CleanAir 3.6//RS / R0 = 3.6 ppm 
  
  MQUnifiedsensor MQ135(placa, Voltage_Resolution, ADC_Bit_Resolution, pin, type);
  /*
    Exponential regression:
    GAS      | a      | b
    CO       | 605.18 | -3.937  
    Alcohol  | 77.255 | -3.18 
    CO2      | 110.47 | -2.862
    Tolueno  | 44.947 | -3.445
    NH4      | 102.2  | -2.473
    Acetona  | 34.668 | -3.369
  */




// Variables
  float temperature;
  float humidity;
  int illumination;
  float CO, Alcohol, CO2, Tolueno, NH4, Acetone;
  int i = 0;
  String L1, L2;
  String RaspberryData;


  
void setup() {


  // Setting up I2C
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(ReceiveData);


  // Setting up Serial
    Serial.begin(9600);
    Serial.println("Module has started");


  //Setting up DHT22
    dht.begin();  


  // Defining the pins for out indicator LEDs
    pinMode(PinRedLED, OUTPUT);
    pinMode(PinGreenLED, OUTPUT);


  // This is our power on indicator, meaning that we have power, and that the Arduino has gone through setup()
    digitalWrite(PinGreenLED, HIGH);


  // Testing our Red LED  
    digitalWrite(PinRedLED, HIGH);
    delay(500);
    digitalWrite(PinRedLED, LOW);
    delay(500);
    digitalWrite(PinRedLED, HIGH);
    delay(500);
    digitalWrite(PinRedLED, LOW);
    delay(500);
    digitalWrite(PinRedLED, HIGH);
    delay(500);
    digitalWrite(PinRedLED, LOW);
    delay(500);


  //Display Setup
    display1.setBrightness(7);
    display2.setBrightness(7);


  // MQ135 Setup
    MQ135.setRegressionMethod(1);
    MQ135.init();
    // There is a calibration procedure needed for the R0, which you can find in the example of the MQ library
    MQ135.setR0(13.02);


  Serial.println("This is a test code that will display all of the read data from the sensors.");
  Serial.println("");


  // This will read the voltage that is dependant on the illumination
    illumination = analogRead(A3);
    
  // The next 2 lines are for reading the values from the DHT22 sensor
    temperature = dht.readTemperature();
    humidity = dht.readHumidity();


  // Now we read the data from the MQ135 sensor
    ReadMQ135Data();


  // Printing the data to the serial monitor  
    //PrintDataToSerial();


  // Updating the data on the 4 digit 7 segment displays  
    UpdateDisplayData();
  
  delay(1000);


}






void ReceiveData(int byteCoun){
    while ( Wire.available()) {
        char c = (char)Wire.read();
        //Serial.println(c);
        RaspberryData += c;
        if(i > 0 && i < 4) L1 += c;
        if(i > 4) L2 += c;
        i++;
    }
    i = 0;
    Serial.println(RaspberryData);
    Serial.println(L1);
    Serial.println(L2);
    int LED1 = L1.toInt();
    int LED2 = L2.toInt();
    Serial.print("Turn the LED1 to: ");
    Serial.println(LED1);
    Serial.print("Turn the LED2 to: ");
    Serial.println(LED2);
    
    analogWrite(PinRB1, LED1);
    analogWrite(PinG1, LED1);
    analogWrite(PinRB2, LED2);
    analogWrite(PinG2, LED2);
   
    
    L1 = "";
    L2 = "";
    RaspberryData = "";


  }




// This function is used for updating the data on the 4 digit 7 segment displays
void UpdateDisplayData(){
    display1.showNumberDec((int)temperature, false);
    display2.showNumberDec((int)humidity, false);
  }


/* This function is used for reading all of the data we can acquire from the MQ135 sensor
 * This part of the code was taken from the example for the MQ library, if you plan on using this library, I highly suggest you check out
 * the original code, because there is a calibration method you should do once for every sensor.
 */
void ReadMQ135Data(){
    MQ135.update(); // Update data, the arduino will be read the voltage on the analog pin


    MQ135.setA(605.18); MQ135.setB(-3.937); // Configurate the ecuation values to get CO concentration
    CO = MQ135.readSensor(); // Sensor will read PPM concentration using the model and a and b values setted before or in the setup


    MQ135.setA(77.255); MQ135.setB(-3.18); // Configurate the ecuation values to get Alcohol concentration
    Alcohol = MQ135.readSensor(); // Sensor will read PPM concentration using the model and a and b values setted before or in the setup


    MQ135.setA(110.47); MQ135.setB(-2.862); // Configurate the ecuation values to get CO2 concentration
    CO2 = MQ135.readSensor(); // Sensor will read PPM concentration using the model and a and b values setted before or in the setup


    MQ135.setA(44.947); MQ135.setB(-3.445); // Configurate the ecuation values to get Tolueno concentration
    Tolueno = MQ135.readSensor(); // Sensor will read PPM concentration using the model and a and b values setted before or in the setup


    MQ135.setA(102.2 ); MQ135.setB(-2.473); // Configurate the ecuation values to get NH4 concentration
    NH4 = MQ135.readSensor(); // Sensor will read PPM concentration using the model and a and b values setted before or in the setup


    MQ135.setA(34.668); MQ135.setB(-3.369); // Configurate the ecuation values to get Acetona concentration
    Acetone = MQ135.readSensor(); // Sensor will read PPM concentration using the model and a and b values setted before or in the setup
  }


// This function is used for displaying the acquired data to the serial monitor of the Arduino
void PrintDataToSerial(){
    Serial.println("--------------------------------------------------");
    Serial.println("Current Sensor readings:");
    
    // This part will show our readings of temperature and humidity from the DHT22


      // Temperature readings
      // I am reading the data in Celsius, you can easily also read in Fahrenheit, it is shown how to do that easily in the library
        Serial.print("Temperature: ");
        Serial.print(temperature);
        Serial.println("°C");


      // Humidity readings  
        Serial.print("Humidity: ");
        Serial.print(humidity);
        Serial.println("%");


    // This part will show our readings from the phototransistor
      Serial.print("Illumination: ");
      Serial.println(illumination);


    // This part will show our readings for various gasses from the MQ-135 sensor  
      Serial.println("Air Quality: ");


      // CO readings
        Serial.print("\t CO: ");
        Serial.print(CO);
        Serial.println("ppm");


      // Alcohol readings
        Serial.print("\t Alcohol: ");
        Serial.print(Alcohol);
        Serial.println("ppm");


      // CO2 readings  
        Serial.print("\t CO2: ");
        Serial.print(CO2);
        Serial.println("ppm");


      // Tolueno readings  
        Serial.print("\t Tolueno: ");
        Serial.print(Tolueno);
        Serial.println("ppm");


      // NH4 readings  
        Serial.print("\t NH4: ");
        Serial.print(NH4);
        Serial.println("ppm");


      // Acetone readings  
        Serial.print("\t Acetone: ");
        Serial.print(Acetone);
        Serial.println("ppm");
        
      Serial.println("--------------------------------------------------");
      Serial.println("");
  }


void TestLEDStrips(){


    // Cycle through the brightness of white lights for both the strips
      for(int i = 0; i <= 255; i++){
          analogWrite(PinRB1, i);
          analogWrite(PinG1, i);
          analogWrite(PinRB2, i);
          analogWrite(PinG2, i);
          delay(50);
        }
      delay(1000);
      analogWrite(PinRB1, 0);
      analogWrite(PinG1, 0);
      analogWrite(PinRB2, 0);
      analogWrite(PinG2, 0);


    // Cycle first the RB than switch and cycle green
      for(int i = 0; i <= 255; i++){
          analogWrite(PinRB1, i);
          analogWrite(PinRB2, i);
          delay(50);
        }
      for(int i = 0; i <= 255; i++){
          analogWrite(PinG1, i);
          analogWrite(PinG2, i);
          delay(50);
        }
      for(int i = 255; i >= 0; i--){
          analogWrite(PinRB1, i);
          analogWrite(PinRB2, i);
          delay(50);
        }
      for(int i = 255; i >= 0; i--){
          analogWrite(PinG1, i);
          analogWrite(PinG2, i);
          delay(50);
        }            
   
  }


void loop() {
  // This will read the voltage that is dependant on the illumination
    //illumination = analogRead(A3);
    
  // The next 2 lines are for reading the values from the DHT22 sensor
    //temperature = dht.readTemperature();
    //humidity = dht.readHumidity();


  // Now we read the data from the MQ135 sensor
    //ReadMQ135Data();


  // Printing the data to the serial monitor  
    //PrintDataToSerial();


  // Updating the data on the 4 digit 7 segment displays  
    //UpdateDisplayData();


  // Testing the LED functionallity
   // TestLEDStrips();
          
  // A small time delay before we do our readings again  
    //delay(20);
}

3. Final Tests

     With the spine of the GUI completed, it was time to test it out a bit. While I did manage to get both way communication going fairly nicely over I2C, I didn't have time to implement data logging and translating all of the raw data from the modules into useful data which I could show on the GUI. What I did manage to was to go ahead and control some of the functions on the project using the GUI on the Raspberry. Here are those videos.

 

Controlling the lights through the GUI

 

Controlling the robot over the GUI

 

Controlling the pump over the GUI

 

     Everything is working great and as expected! Sorry if the videos aren't the best, I had trouble setting up a single shot which would catch both the monitor and the whole rails system. You can hear in the video that I'm clicking in the background with a mouse, also you can hear the pump going in the third video.

 

4. Through the blogs

     Before I get into the blogs themselves, here are the links to all 10 of the previous blogs. I'll link all of them at the end as well.

  1. Project R.A.G. - Blog #1 - Mission Briefing
  2. Project R.A.G. - Blog #2 - Strapped In For Launch - Connectors
  3. Project R.A.G. - Blog #3 - Lift Off
  4. Project R.A.G. - Blog #4 - Quick Tool Change Mechanism For The Robot Arm
  5. Project R.A.G. - Blog #5 - Sensor & Light Module
  6. Project R.A.G. - Blog #6 - Mechanical Build P1 - Robot On Rails
  7. Project R.A.G. - Blog #7 - The Mainframe - P1
  8. Project R.A.G. - Blog #8 - Mechanical Build P2 - Robot On Rails
  9. Project R.A.G. - Blog #9 - Water & Rail Module
  10. Project R.A.G. - Blog #10 - Mechanical Build P3 (Final)

     With the final tests complete, I'll now do a few sentences for each of the blogs, to summarize them up a bit, what were they written for, what went good in them, what went bad and so on. Also, where applicable, show the pictures or videos that summarize that blog the best. Of course, let's start from the beginning over a month ago back in November with the first blog and move onwards blog by blog.

 

Blog #1 --- Mission Briefing

     As I've already mentioned, this blog was filmed way back in November. This is the blog where I shortly went over my original idea and concept for this project, not knowing how it would all go. The concept of this project is something I will touch on more after this section. A picture that would describe this blog the best would be this:

1

 

Blog #2 --- Strapped In For Launch - Connectors

     This is the blog where I first introduced the 3D printed locking connectors that I've designed. These are the connectors that were used throughout the whole build later on. The connectors are easily printable without any supports and serve a function to simply enhance some standard off the shelf connectors by making them more robust and into locking connectors. If you're interested in the models for the connectors or any other thing that I've printed through this project, you can find the link to my GitHub where I've uploaded all of the STP and STL files for free for download. You are free to do whatever you want with them, I hope they are helpful to someone!

2

 

Blog #3 --- Lift Off

     This was a shorter blog compared to the others, in that blog, I went over the contents of the Starter Kit and what I planned on utilizing from that Starter Kit. Besides that, I also went a bit into detail for the idea for some of the modules as well as a block diagram showing my original concept.

3

 

Blog #4 --- Quick Tool Change Mechanism For The Robot Arm

     In this blog, I went over the first more serious mechanical design and I could also say, most complex when it comes to fitting all of it together. In this blog I designed and printed out a tool for my robot which allows it to switch between different tools. It's a small mechanical mechanism driven by a small servo embedded on top. The mechanism proved to be extremely tough and reliable. Here are some pictures of that mechanism as well as a video where I'm testing it with the full weight of the robot.

31234354

 

Blog #5 --- Sensor & Light Module

     Here we come to the first of the modules that I've designed for this project, the Sensor & Light Module. While my plan was to do measurements with the Enviro Hat Mini which was included in the Starter Kit, I also wanted to have another set of sensors for redundancy and some additional sensors which the Enviro didn't cover. This module is capable of measuring air temperature, humidity, illumination, and levels of multiples gases which are: CO, Alcohol, CO2, Toluene, NH4, Acetone. Besides being able to do that, the module is also capable of controlling 2 independent RGB LED strips using the MOSFET stage which I covered in that blog.

2376

board1

 

Blog #6 --- Mechanical Build P1 - Robot On Rails

     Now we come to the blog that proves once again that not everything can always go smoothly. This is the blog where I've designed and assembled the main construction for this project, the rail system on which the robot rides on between the plants. There was a lot of design work that went into this, a lot of various brackets, belt tensioner, wheels for the robot, legs for the whole construction and so on. I'm really proud with how this turned out, until I had a couple of hick ups. There was a lot of strange noise and the stepper motor driver decided to burn out.

burnt

     But even considering that, I consider this blog a success, because it was the foundation which let me finish on time. As for the issues and problems that've occurred in this blog, I've addressed all of them in one of the later blogs. But rather then just ending with the picture of a toasty stepper controller, here is what I actually accomplished in this blog.

gif1gif2

blog6

 

Blog #7 --- The Mainframe - P1

     While not strictly a module, it certainly has a shape of a one. This was supposed to be the enclosure for our main computer of the project, the Raspberry Pi 4. But I didn't have enough connectors on hand nor had the time to wait for the new ones to arrive because of the fast approaching deadline. I'm still happy with the design and the layout of it nevertheless. I also documented my attempt at making a power supply switching circuit which worked for lighter loads as well as the build of the 12V power supply.

inside

     On the pictures above you can see what I wanted to use for the main enclosure for this project with all of the connectors attached and the custom panels with signs that I've designed. And underneath, you'll find pictures of the small switcher circuit I made as well as the 12V power supply.

12vpsuswitcher

 

Blog #8 --- Mechanical Build P2 - Robot On Rails

     I'll call this the blog that saved the project. In this blog I covered how I went and solved to major problems I had, which would have stopped the project if I hadn't solved them. One of those problems I already mentioned and it was in the blog #6, as for the other problem, one of the adapter plates on the robot lost all of its teeth and I ran out of replacements, so I had to do some modifications to the servo and design my own adapter plates. As for the stepper motor and driver issue, I did a lot of research to see what can cause this and came to the conclusion that it was because I didn't properly protect the driver or the motor and the driver just ended up dying. It was a cheap solution utilizing only a few capacitors and a Zenner diode.

nema mountservo

 

 

Blog #9 --- Water & Rail Module

     The other module I made was the Water & Rail module. This was a real multipurpose module, again with the Arduino Nano inside, I've used everything that can be used on that little board. This module had a lot of work to do honestly. It was responsible for moving the robot up & down the rails, for watering the plants, for changing the tools on the robot, for interfacing with the tools. Here's a diagram to paint a bit of a better picture.

diagram1

schematic

board2board3

 

 

 

Blog #10 --- Mechanical Build P3 (Final)

     We're finally at the end, at the blog before this one. This is the blog where I've finished up the mechanical build. I designed and constructed a chassis if you will, for all of the lights and electronics to mount to out of some copper pipes and aluminum rails. This is the first time I've actually seen what was on paper in the first blog come to real life after a month of work on various parts of it. Here are some pictures of how it all looks assembled.

12349846

39794615

     The only blog not mentioned here of course is this one that I'm writing now. But considering you're reading this, you already know all of contents of this blog! While I touched up on that through some of the blogs and dedicated the whole first blog to it, there is something I haven't completely explained and that's the overall idea and concept of this project. As with every project, a lot of things change during the realization of it, with things not working out, time constraints, new & better ideas and so on. So I will now go and explain what was my idea for Project R.A.G.

 

5. Concept

     What is the concept of this project, the purpose? My idea with this project was to make a research and experimentation station which can be used by other people to grow plants, collect samples and track them remotely. As you can tell from the pictures and videos I focused on the build rather than planting plants. Since this isn't something that I wanted for home use, but rather envisioned this to be a concept of a module for plant grow experiments, the actual planting and taking care of plants should be done by someone studying that subject, I saw my job here is making the apparatus which would enable those people to conduct their experiments and research. Since the theme of the competition was to give it an interesting twist like microgravity setup, my idea was making a prototype/concept module whose purpose would be to fly to Mars and do experiments with Martian soil to see if it can be used for growing food. This is something that has already been tackled as I've discussed before, scientist analyzed the data collected by Curiosity Rover on Mars when it expected Martial sol samples and found that the closest thing on Earth to that is the volcanic dirt from Hawaii. To their surprise, the plants actually grew pretty good. That's the main reason I focused on modularity and flexibility when it comes to both modules and tools on the robot, because it would give different researechers an easier time designing their own experiments, by adding adiitonal modules or just adding another tool to the collection that's already there, which the robot can swap easily.

 

6. Ideas that didn't make it in time

     As with every time constrained project, priorities change through the build and some stuff just get left behind, sadly, this is what happened with one of the modules that I wanted to make so bad, but couldn't justify spending time on it when all of the other stuff wasn't being close to finished. The module in question is something I called the LAB.

 

The LAB

     Now that you're familiar with my idea and the full concept for this project, simply put, it is designed to be a research station. I wanted to make a small module called a LAB which would a whole subsystem within itself. Since the main purpose for growing plants with this system would be research, remote research, there should be a way for us to do some basic analysis of the samples we take. That's where the LAB comes in. My idea was for the LAB to be a box that would attach to this project and would have a small opening for placing different samples on it. The opening would be on a small conveyor belt which would take the sample further in the lab. Now if we're talking about plant samples, a camera would take a picture with a calibration sheet in the background and the sample would be left for pickup. But we could also have soil samples where we could analyze some aspects of the soil based and then can compare if it's optimal for the plant that is planted there. I even got the main sensor I wanted to use for this part of the project, a pH sensor for which I planned a whole contraption to measure the pH values and then clean the sensor to keep it functioning for a long time.

 

7. Sketches

     Something I've noticed a lot of people commented on is that they love the hand sketches and rough drawings which I put into some of my blogs. I love doing sketches and drawings for my projects, makes it much easier for me wrap my head around it and often brings out better ideas and easier ways to do stuff. Here are some of the sketches I did which helped me a lot with this project while other unfortunately didn't make it.

 

Robot Grippers

     One of the things I desperately wanted to design and assemble for this project was my own design of a gripper for a robot. I have a lot of ideas on this topic and have made one recently for a university class. It's a really fun thing to draw specially when you get into various ways of moving the gripper fingers and their relationship between them as they move. Here are a few sketches.

     As you can see from the drawings, all 3 have a different style of mechanism, the first one is a standard one which relies on gears, while I designed the other 2 to use a screw mechanism for more torque. While they both use the screw mechanism, there are major changes in the linkage design where one would move like a claw on a crab while the second one would keep the tip of the fingers parallel through the movement.

 

Tool holder & tool changer

     This is something I worked on a lot and something I made, but just didn't have enough time to implement. A lot of different designs went through my head for this design, but this one stuck until the end because it could be printed in just a couple of parts, and it was a mechanism that is actually printable and strong enough while not having to be based upon things like electromagnets and such things.

 

Watering System

     Now we come to the system that I've semi implemented for which I had so many more ideas. Even got some tests done for some of the components below, but again, didn't have the time to be able to implement them properly. I thought about making my own lost cost 3D printed valves for watering the plants, with the main goal being making it low cost, since the SG90 servo is 3 times cheaper than the cheapest electric valve I could find. I also wanted to print out and make a lot bits for delivering the water to the plants as well as a lot of adapter for attaching the hoses together.

     Above, you can see some of my ideas for delivering the water and nutrients to the plant, from the standard just putting a hose no top of the pot to making a ring around the plant which would equally distribute the water to a spike whose purpose was to deliver the water at all levels. On the second picture you can see the idea for the valve. Since this was never planned to be a high pressure system, I though just about making a small unsymmetrical motor mount which would spin around and gradually press and pinch the hose against the wall and stopping the water in that way.

     Inspired by the 3D printed connectors I used in this project, I wanted to design and make similar connectors but this time for small hoses. While I wanted to try the basic connector style like I did with the connectors, I also wanted to try locking latch mechanism where it would click into place, but this is something that would have required a lot of trial and error to get the tolerances dialed in so it can work flawlessly.

 

8. Summary

     This was a really fun project, but not a small one for sure. I could safely say that this is the biggest project I've tackled on which I worked alone. It had a lot mechanical and electrical design though I wish I had a bit more time for the software. It was a fun month of working on this project constantly, but it also really took a lot of work. If you've followed along and read some of my blogs, thanks, I hope they we're interesting to read and that they've maybe been of some help to someone. If you're interested in any questions feel free to reach and if you want any of the models for the things I've printed, you can find the link to my GitHub down below. Thanks for reading this blog, hope you like it, it was a really fun competition to be part of with many amazing projects!

 

Milos

 

Relevant links for the competition:

Link to my GitHub where you can find all of the files used for this project (code, 3D models,...):

Link to my Project Collection:

 

 

Previous Blog
Project R.A.G. - Blog #10 - Mechanical Build P3 (Final)