Route Planning for Garbage collection with IoT – Part 1

by May 16, 2020Projects

The garbage collection process is a crucial service for any community or society. It ensures the cleanliness of our surroundings. However, the process has for long been rudimentary. Hence we’ve decided to automate the process. Our idea is to incorporate IoT into the already existing systems to increase the efficiency and to reduce the work load of the manual labors.

But how do we plan on accomplishing this ?

First, we need to set up some hardware in all the dustbins to help us collect data on how much garbage is present in the bin. We use an ultrasonic distance sensor and a Raspberry Pi to accomplish this. This data will be sent to the firestore database. We use this data to come up with the most efficient route plan. Finally, this route is displayed on a map for the truck drivers to follow. The resident can also view how much of the dustbin is filled and hence plan their trips to the community dustbin.

Contents

Hardware Setup

Components Required

Circuit Diagram

Logic

The HC-SR04 Ultrasonic sensor has four pins: ground (GND), Echo Pulse Output (ECHO), Trigger Pulse Input (TRIG), and 5V Supply (Vcc).

  • First, power the module using Vcc, ground it using GND,
  • Send an input signal to TRIG, which triggers the sensor to send an ultrasonic pulse.
  • This pulse gets reflected back to the sensor from a nearby object.

Once a return pulse is detected ECHO is set “high” (5V) for the duration of that pulse.

NOTE: The sensor output signal (ECHO) on the HC-SR04 is rated at 5V. However, the input pin on the Raspberry Pi GPIO is rated at 3.3V.Sending a 5V signal into that unprotected 3.3V input port could damage your GPIO pins, which is something we want to avoid! We’ll need to use a small voltage divider circuit, consisting of two resistors(1k,2k), to lower the sensor output voltage to something our Raspberry Pi can handle.

Code Explanation

  • First import required modules
#import required modules
import RPi.GPIO as GPIO
import time
  • Next, Assign GPIO pins to trigger and echo pins
GPIO.setmode(GPIO.BCM)
GPIO_TRIGGER = 26 #Connect trigger pin to GPIO 26 of Pi
GPIO_ECHO = 19    #Connect Echo pin to GPIO 19 of Pi
  • Initialize  trigger as Output and Echo as input
GPIO.setwarnings(False)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT) #output :As Pi triggers US sensor
GPIO.setup(GPIO_ECHO, GPIO.IN)     #input :As we read Echo status
  • Initially set trigger to false and delay of 2 sec to let the sensor settle
GPIO.output(GPIO_TRIGGER, False) #set trigger to 0 or off state
print ("Waiting For Sensor To Settle")
time.sleep(2) #delay of 2 sec
  • We define a new class dustbin(), inside which we will write all the required functions
  • Within __init__(), set initial_depth to -1
class dustbin():
    def __init__ (self):
        self.initial_Depth =-1 #-1 represents not yet initialized
  • In current_Depth() method, we write the code to get the depth dustbin is not filled. This value is later used to calculate what percentage of the dustbin is filled.
    def current_Depth(self):
        #trigger the ultrasonic sensor for a very short period (10us).
        GPIO.output(GPIO_TRIGGER, True)
        time.sleep(0.00001)     # 10us or 0.00001sec 
        GPIO.output(GPIO_TRIGGER, False)
        while GPIO.input(GPIO_ECHO) == 0:
            Pass
        #start timer once echo becomes high i.e. '1' 
        StartTime = time.time() 
        while GPIO.input(GPIO_ECHO) == 1:
            Pass
        #stop timer once signal is completely received and echo becomes 0
        StopTime = time.time() 
        # This records the time duration for which echo pin was high 
        TimeElapsed = StopTime - StartTime 
        speed=34300 #speed of sound in air 343 m/s  or 34300cm/s
        #Time elapsed = time it takes for the pulse to go and come back
        twicedistance = (TimeElapsed * speed)
        distance=twicedistance/2  # to get actual distance simply divide it by 2
        return round(distance,2) # round off upto 2 decimal points

  • Update_Initial() function is called only at the initial setup/installation of the dustbin(). This reads the depth when dustbin is empty and assigns the same to initial_Depth() variable.
    def update_Initial(self):
        #set the current_Depth as the Depth of dustbin
        self.initial_Depth=self.current_Depth()
    def percentage_Filled(self):
        diff=self.initial_Depth -self.current_Depth()
        percentage=diff/self.initial_Depth
        percentage*=100
        if(percentage<0):
            percentage=0
        return round(percentage,2)

Dustbin Configuration

The purpose of this GUI is to help low skill technicians to get the dustbins up and running. The GUI does have certain functions that need not necessarily be used in a real life scenario. For instance, the current data button gets the current depth of the dustbin form which we can calculate the percentage of garbage present in it. However, in real life we will be running this function as a scheduled task with a time interval of 6 hours.

We can also automate the enter coordinates button by integrating the system with a GPS module.

Data flow

  1. Getting  Data from Ultrasonic sensor
  2. Display the result to the user
  3. Get some inputs from the user
  4. Send the data to Cloud

User Interface

Functionality of the App

  1. Initial Data: It gets the initial depth of the dustbin. This data would ideally be collected only once during setup. This data is required to calculate the percentage of dustbin filled.
  2. Current Data: It gets the depth of the dustbin at the moment. This data tells us exactly how much of the dustbin is full. Ideally this function would be automated with a scheduler, it has been made manual here to make it easier to demonstrate.
  3. Enter Area: User enters the location in which the dustbin is placed.
  4. Enter co-ordinate: User enters  the x, y co-ordinates (Longitude and Latitude) of the dustbin.
  5. Return Data: Sends the dustbin’s setup data to the cloud.  

Code Explanation

  • Importing all required modules.
from tkinter import * 
import tkinter.font as tkFont
from DepthSensor.ultrasonic import dustbin#From ultrasonic.py file we import the    dustbin class
from cloud_integration import transfer_data as transfer_data
from google.cloud import firestore
  • We first initialize the window size (width and height) of the window using geometry. In data we store the values that we are going to send to the cloud. Finally, we create the object b of the type dustbin .
class app():
    
    def __init__(self, obj):
        self.obj = obj
        self.obj.title("User Interface")
        self.height = self.obj.winfo_screenheight() 
        self.width = self.obj.winfo_screenwidth()
        self.app_width = int(0.5*self.width)
        self.app_length = int(0.5*self.height)
        self.obj.geometry(str(self.app_width)+"x"+str(self.app_length))#Geometry is defined
        self.data = { }# This dictinary stores the data with key values which are to be sent to the cloud
        self.b=dustbin() #to invoke the dustbin object from the dustbin class of ultrasonic.py file
  • The senddata method sends data to the database as a dictionary, where the keys represent the field names and the values represent the data.
    def senddata(self):
        print(self.data)#prints the data with key values 
        transfer_data.send_data(self.data)
  • co_ordinate method takes the comma separated values from the user which is latitude and longitude of the dustbin’s location. We then define an Entrybox which takes the coordinates as an input from the user. This data is then stored into a list. The data from this list is then converted to the GeoPoint data type.
    def co_ordinate(self):
        
        def printdata(entrybox):#Entrybox which takes the input cordinate from the user
            text = entrybox.get()
            print(str(text))
            l=[] # list to store x and y cordinated at the index 0 and one respectively
            for cordinate in text.split(','):
                l.append(float(cordinate))
                
            self.data['location']= firestore.GeoPoint(float(l[0]), float(l[1]))
            print(self.data)
        
        cordinate = Tk() # this object is used to create the new window
        
        cordinate.geometry(str(int(0.5*self.height))+"x"+str(int(0.3*self.width)))
        cordinate.title("enter the co_ordinte values")
        
        label1 = Label(cordinate, text= "Enter the co ordinates seperated by comma" )
        label1.pack()
        
        textin = StringVar() #String variable
        e=Entry(cordinate, width=30)
        button = Button(cordinate,text='okay',command=lambda:printdata(e))
        button.pack(side='bottom')
        
        e.pack()            
        cordinate.mainloop()
  • The getarea method takes in the area’s name as an input from the user.
    # getarea function takes the area where dustbin is located   
    def getarea(self):
        
        def printdata(entrybox):#Entrybox which takes the input area from the user
            text = entrybox.get()
            self.data['area']=str(text)
            print(self.data)
            
            
        area=Tk() # this object is used to create the new window
        area.geometry(str(int(0.5*self.height))+"x"+str(int(0.3*self.width)))
        
        label1 = Label(area, text= "Enter the Area" )
        label1.pack()
        
        e=Entry(area, width=30)
        button = Button(area,text='okay',command= lambda: printdata(e))
        button.pack(side='bottom')
        
        e.pack()            
        area.mainloop()
  • get_depth method used to get the data from the sensor. We pass either True or false. Passing True runs the initial setup whereas passing False gets the current depth.
    def get_depth(self,setup):
        
        # if setup is True then initial depth is obtained else current depth is obtained.
        if setup:
            current_data = Tk()
            current_data.geometry(str(int(0.5*self.height))+"x"+str(int(0.3*self.width)))
            current_data.title("Info Page") 
           
            current = self.b.update_Initial()
            
            self.data['initialDepth']=float(current)
            
            label1 = Label(current_data, text= "current depth in cm = "+str(current))
            label2 = Label(current_data, text= "percentage filled now= "+str(0))
            
            label1.pack()
            label2.pack()
            
            print(self.data)

        else:
            current_data = Tk()
            current_data.geometry(str(int(0.5*self.height))+"x"+str(int(0.3*self.width)))
            current_data.title("Info Page") 
           
            current=self.b.current_Depth() 
            current_percentage=self.b.percentage_Filled(self.b.initial_Depth,current)
            
            self.data['currentDepth']=float(current)
            
            label1 = Label(current_data, text= "current depth in cm = "+str(current))
            label2 = Label(current_data, text= "percentage filled now= "+str(current_percentage))
            
            label1.pack()
            label2.pack()
            
            print(self.data)
  • The start_app method starts the app and acts as a backbone for all other functionalities.
    def start_app(self,object1):
        
        #To start the APP
        
        welcome = tkFont.Font(family='arial', size=55)
        welcome = Label(self.obj, text='Garbage collection \n App \n\n\n',font=welcome,background="green")
        welcome.pack(fill='both', expand=True, anchor=CENTER)
        
        initial_display_button = Button(object1,text="Initial Data",background="white",command=lambda: self.get_depth(True) )
        initial_display_button.place(relx=0.5, rely=0.5, anchor=CENTER)
        
        current_display_button = Button(object1,text="current Data",background="white",command=lambda: self.get_depth(False) )
        current_display_button.place(relx=0.5, rely=0.6, anchor=CENTER)
        
        return_button = Button(object1,text="Return Data",background="white",command=self.senddata) 
        return_button.place(relx=0.5, rely=0.9, anchor=CENTER)
        
        co_ordinate_button = Button(object1,text="Enter Co-Ordinates",background="white",command=self.co_ordinate )  
        co_ordinate_button.place(relx=0.5, rely=0.8, anchor=CENTER)
        
        area_button = Button(object1,text="Enter Area",background="white",command=self.getarea )  
        area_button.place(relx=0.5, rely=0.7, anchor=CENTER)
  • Finally we run the app. Here object1 is our main window object which houses our UI. Object2 creates an instance of the object app. object2.start_app(object1) passes the window into the object that we created.
if __name__ == "__main__": 
    object1 = Tk()
    object2 = app(object1)
    object2.start_app(object1)
    object1.mainloop()

Cloud integration

The various data produced in the GUI has to be stored in the cloud to be used later by the android application. For this function we have decided to use Google Firestore. Firestore works on the principle of documents and fields. For this project we’ve created a collection called “dustbin” which contains as many documents as there are dustbins.

Each document has exactly 5 fields namely area, currentDepth, initialDepth, location, perc.

  • area: This field contains a simple string which is the locality’s name.
  • currentDepth: This field contains details on the depth of the dustbin at present.
  • initialDepth: This field has data that stores the depth of the empty dustbin.
  • location: It stores the latitude and longitude of the dustbin. The data type used to store this data is GeoPoint. It can help us determine the route to be followed by the truck driver.
  • perc: perc stores the percentage of the dustbin that has been filled (as explained here).

Code Explanation

  • First of all, lets import the required libraries. For this section we need to import only 3 modules – Firestore, credentials and firebase_admin (all of them belong to the firebase_admin package)
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
cred = credentials.Certificate('firebase-test-4b2e17880be1.json')
firebase_admin.initialize_app(cred)
  • Next we initialize a database object called db
db = firestore.client()
  • Now let’s create a function that sends data to our collection named dustbin
def send_data(data):
	db.collection(u'dustbin').add(data)

The add method used above essentially adds data into the specified collection without requiring us to set a name for each of the documents. This feature is required in this case because by using this we eliminate the need to know the number of dustbins that are in service.

Here the data that we pass as parameter is a dictionary such that the keys of the dictionary correspond to the field names in each document.

  • Next, we will write a function to get the data back from our Firestore database. This function returns a list of dictionaries which hold data from each document.
def get_data():
	users_ref = db.collection(u'dustbin')
	docs = users_ref.stream()
	doc_list = []
	for doc in docs:
		data = doc.to_dict()
		print(data)
		doc_list.append(data)

	return doc_list

Plotting the Data on Google Maps

Routing And Optimization

To optimize the routes between the dustbins we will use google maps. The dustbin nearest to the truck becomes the source, farthest becomes the destination and all others act as way-points.

Why google Maps? Today, google maps contains over 20 petabytes of data and is used by millions of people everyday for routing. It not only uses one routing algorithm like A* or Dijkstra but several combined together as shown in the above diagram.

Directions API

For routing the shortest path, we will use the directions API provided by google maps in python to get an efficient route and plot it. We will provide it with a source, way-points and destination to get a JSON output. It gives directions between various points, also through various driving modes. Start by enabling the directions API and generating an API key from google cloud.

Code Explanation

We can either use the gmaps library to perform the task or simply access it through its URL. For the latter we need only two simple dependencies, install them and import

import urllib.request, json
import webbrowser

Create a function map with all the points as input which returns a URL containing the final plotted map.

def opt_route(origin, waypoints, destination):

    #Google MapsDdirections API endpoint
    endpoint = 'https://maps.googleapis.com/maps/api/directions/json?'

    # please set your api_key below
    api_key = "API_key"

    #adding mode of routing
    mode='driving'
    #Building the URL for the request
    nav_request = 'origin={}&destination={}&waypoints=optimize:true|{}|{}|{}|{}&mode={}&key={}'.format(origin,destination,waypoints[0],waypoints[1],waypoints[2],waypoints[3],mode,api_key)
    request = endpoint + nav_request

    #Sends the request and reads the response.
    response = urllib.request.urlopen(request).read()

    #Loads response as JSON
    directions = json.loads(response)

    routes=directions["routes"]

    # get the order for route planning
    waypoint_order=routes[0]["waypoint_order"]

    # url part without optimisation
    part1='https://www.google.co.in/maps/dir/'

    # url part with optimisation (using index values stored in waypoint_order)
    part2='{}/{}/{}/{}/{}/{}/'.format(origin,waypoints[waypoint_order[0]],waypoints[waypoint_order[1]],waypoints[waypoint_order[2]],waypoints[waypoint_order[3]],destination)
    url=part1+part2

    return url
  • The API key created must be copied here.
  • The directions API returns a JSON file from which data needs to be extracted.
  • 1 source, 1 destination and several way-points can be provided. The mode of routing (i.e. driving, walking) should also be given.

Output

You can find the entire source code for this project on GitHub here : https://github.com/sashreek1/garbage_collection_system

Hope this article was enlightening and helped you to understand the usage of IoT in our day-to-day life. The process of garbage collection has been around for quite a while but it still continues to be the inefficient process that it had always been. We hope our idea will change the communities that we all live in. Thank you for reading.

Happy Learning 😄

Creating a multiplication Skill in Alexa using python

Written By Sashreek Shankar

Hey reader! I am Sashreek, a 16 year old programmer who loves Robotics, IoT and Open Source. I am extremely enthusiastic about the Raspberry Pi and Arduino hardware as well. I also believe in sharing any knowledge I have so feel free to ask me anything 🙂

RELATED POSTS

How to Simulate IoT projects using Cisco Packet Tracer

How to Simulate IoT projects using Cisco Packet Tracer

In this tutorial, let's learn how to simulate the IoT project using the Cisco packet tracer. As an example, we shall build a simple Home Automation project to control and monitor devices. Introduction Firstly, let's quickly look at the overview of the software. Packet...

How to design a Wireless Blind Stick using  nRF24L01 Module?

How to design a Wireless Blind Stick using nRF24L01 Module?

Introduction Let's learn to design a low-cost wireless blind stick using the nRF24L01 transceiver module. So the complete project is divided into the transmitter part and receiver part. Thus, the Transmitter part consists of an Arduino Nano microcontroller, ultrasonic...

How to implement Machine Learning on IoT based Data?

How to implement Machine Learning on IoT based Data?

Introduction The industrial scope for the convergence of the Internet of Things(IoT) and Machine learning(ML) is wide and informative. IoT renders an enormous amount of data from various sensors. On the other hand, ML opens up insight hidden in the acquired data....

Smart Display Board based on IoT and Google Firebase

Smart Display Board based on IoT and Google Firebase

Introduction In this tutorial, we are going to build a Smart Display Board based on IoT and Google Firebase by using NodeMCU8266 (or you can even use NodeMCU32) and LCD. Generally, in shops, hotels, offices, railway stations, notice/ display boards are used. They are...

Smart Gardening System – GO GREEN Project

Smart Gardening System – GO GREEN Project

Automation of farm activities can transform agricultural domain from being manual into a dynamic field to yield higher production with less human intervention. The project Green is developed to manage farms using modern information and communication technologies....

How to build a Safety Monitoring System for COVID-19

How to build a Safety Monitoring System for COVID-19

It is expected that the world will need to battle the COVID-19 pandemic with precautious measures until an effective vaccine is developed. This project proposes a real-time safety monitoring system for COVID-19. The proposed system would employ an Internet of Things...

VIDEOS – FOLLOW US ON YOUTUBE

EXPLORE OUR IOT PROJECTS

IoT Smart Gardening System – ESP8266, MQTT, Adafruit IO

Gardening is always a very calming pastime. However, our gardens' plants may not always receive the care they require due to our active lifestyles. What if we could remotely keep an eye on their health and provide them with the attention they require? In this article,...

How to Simulate IoT projects using Cisco Packet Tracer

In this tutorial, let's learn how to simulate the IoT project using the Cisco packet tracer. As an example, we shall build a simple Home Automation project to control and monitor devices. Introduction Firstly, let's quickly look at the overview of the software. Packet...

All you need to know about integrating NodeMCU with Ubidots over MQTT

In this tutorial, let's discuss Integrating NodeMCU and Ubidots IoT platform. As an illustration, we shall interface the DHT11 sensor to monitor temperature and Humidity. Additionally, an led bulb is controlled using the dashboard. Besides, the implementation will be...

All you need to know about integrating NodeMCU with Ubidots over Https

In this tutorial, let's discuss Integrating NodeMCU and Ubidots IoT platform. As an illustration, we shall interface the DHT11 sensor to monitor temperature and Humidity. Additionally, an led bulb is controlled using the dashboard. Besides, the implementation will be...

How to design a Wireless Blind Stick using nRF24L01 Module?

Introduction Let's learn to design a low-cost wireless blind stick using the nRF24L01 transceiver module. So the complete project is divided into the transmitter part and receiver part. Thus, the Transmitter part consists of an Arduino Nano microcontroller, ultrasonic...

Sending Temperature data to ThingSpeak Cloud and Visualize

In this article, we are going to learn “How to send temperature data to ThingSpeak Cloud?”. We can then visualize the temperature data uploaded to ThingSpeak Cloud anywhere in the world. But "What is ThingSpeak?” ThingSpeak is an open-source IoT platform that allows...

Amaze your friend with latest tricks of Raspberry Pi and Firebase

Introduction to our Raspberry Pi and Firebase trick Let me introduce you to the latest trick of Raspberry Pi and Firebase we'll be using to fool them. It begins with a small circuit to connect a temperature sensor and an Infrared sensor with Raspberry Pi. The circuit...

How to implement Machine Learning on IoT based Data?

Introduction The industrial scope for the convergence of the Internet of Things(IoT) and Machine learning(ML) is wide and informative. IoT renders an enormous amount of data from various sensors. On the other hand, ML opens up insight hidden in the acquired data....

Smart Display Board based on IoT and Google Firebase

Introduction In this tutorial, we are going to build a Smart Display Board based on IoT and Google Firebase by using NodeMCU8266 (or you can even use NodeMCU32) and LCD. Generally, in shops, hotels, offices, railway stations, notice/ display boards are used. They are...

Smart Gardening System – GO GREEN Project

Automation of farm activities can transform agricultural domain from being manual into a dynamic field to yield higher production with less human intervention. The project Green is developed to manage farms using modern information and communication technologies....