**EECS Workshop: The Internet of Things** ![](./media/favicon.gif width="50px") MOSTEC 2018 Workshop Electronic devices that are connected to one another via local and global networks are becoming extremely pervasive in our lives. This is a fascinating area of research that touches a large swath of Electrical Engineering and Computer Science (EECS) and that's the focus of today's workshop! # Introduction In this workshop we'll be exploring the "Internet of Things," often abbreviated as IOT. We'll work on the "full-stack", which means we'll be developing in three different environments: * **Hardware:** Constructing a few simple circuits and integrating them in with our embedded computing system (a Raspberry Pi single-board computer) * **Client-Side Programming:** Creating Python to perform requests to centralized servers for both reporting data and retrieving commands from (this will also involve a pinch of HTML and CSS, but won't be our big focus) * **Server-Side Programming:** Placing executable Python code on a globally accessible server to act as our host and central command. The entire eco-system looks like the following: ![Figure [big_picture.png]: The internet as we'll investigate it is a server on which we'll run Python code on, an embedded system, on which we'll also run Python, standard web-browsers (like your phone or computer), and the core/base of circuits which allow our embedded system to interface to the world. All of these parts must work together seemlessly to ensure a functioning system!](media/big_picture.png width="800px" border="0") As we march through these topics, we'll try to keep track of these three things with a check list. Right now we've done the following: - [ ] Hardware - [ ] Client-Side Programming - [ ] Server-Side Programming Having nothing done is never a good state to be in. We should probably get started. First we need to talk about some general internet stuff. ## Clients and Hosts We use the internet every day, more than we even think about now. Almost everything you do on your phone uses the internet. Even when you do something non-internetty like take a photo, there's some internetting happening in there (every photo you take your phone reports diagnostics over the internet) The entire internet is built up of computational devices, or what we'll just call computers. We're using computer in the general sense here, so we don't mean *just* laptops, but instead, desktops, laptops, phones, microcontrollers, and everything else that does some electronic computation. More and more things have or count as computers (cars, medical equipment, appliances...basically every-*thing*...and this "thing" is where the whole Internet of Things terms comes from.) Anyways the internet has two classes of computation devices: __**Hosts**__ are the "always-on" entities in the network. They generally have static (fixed) globally accessible IP addresses (a uniquely identifier number like a social security number except for electronics), and often have a domain name associated with them (google.com, mit.edu, for example). Ideally you can always access them and they'll be in a known "location" (their domain name). Physically, hosts are just computers that sit in a closet or a server farm somewhere, connected to the internet. Ideally they are always running and listening for incoming requests. I have two different hosts sitting in my office right now: ([iesc-s1.mit.edu]("https://iesc-s1.mit.edu") and [iesc-s3.mit.edu](http://iesc-s3.mit.edu)). We'll be using the latter today for our experiments. __**Clients**__ are the "sometimes-on" entities in the network. Most of your everyday exposure to the internet is as and through a client. Your phone and how you use it is usually as a client. Same with your laptop...same with most stuff that you think of as connected. Clients are computers that make contact (either automatically or through human-initiated action) to Hosts. Clients must **always** initiate contact with a Host. This is because a Host has a static (fixed) address whereas clients usually have ever-changing addresses (As well as other reasons). ![Figure [clients_and_hosts.png]: The internet is comprised of lots of clients interacting with lots of hosts. Clients send requests (step 1) to Hosts and Hosts send responses back to clients (step 2). A client **always** initiates contact with a host. Never the other way around! Things like push notifications and reminder updates which appear as if the Host is initiating contact with you are almost always the result of background programs on your computer repeatedly sending check-in requests to a host and reporting the result!](media/clients_and_hosts.png width="300px" border="0") While there's lots of exceptions to this general framework (and the internet as a whole is shifting away from it in many sub-fields), this client-initiated request-response cycle forms much of the whole internet: * When you visit facebook.com, your computer (client) sends a request to the host called (facebook.com) requesting data that represents your profile page, and the host responds with that data, which your web-browser then interprets into pretty text, colors, images, videos, etc... * When you post an update to whatever flavor-of-the-month social media app you're using, you (the client) are sending a request (with information) to the social media app's host (snapchat.com or something). The host will respond with something like "post successful" or "post unsuccessful."" We'll see in a minute, that the word "post" actually has special meaning. ## GETs and POSTs To reiterate, since it bears repeating: Clients first send requests to Hosts. Hosts then send a response. It goes in that order always. Always. Never the other way around! Things like push notifications and reminder updates which *appear* as if the Host is initiating contact with you (the client) are almost always the result of background programs on your computer repeatedly sending check-in requests to a host and reporting the result! Now, moving on, there are two major types of requests that a Client can send: * __**GET request:**__ A request sent by a client to lookup information. This is used by your browser to GET the HTML (Hyper-Text-Markup-Language) that makes your web-page, by Facebook to GET status updates, to GET tasks that an application needs to perform, to GET software updates, to GET the current state of a multi-player game and so on. * __**POST request:**__ A request sent by a client to report information to the host. When you enter information into a web form, or post a status update, or upload an image to your Insta or when you save a file to your Dropbox/OneDrive/whatever...all of these actions involve the client sending up data with the expectation that it will be saved/remembered/have some long-term impact. ![Figure [get_post.png]: The internet is comprised of lots of clients interacting with lots of hosts. Clients retrieve information from hosts using GET requests (what is the temperature today?). Clients report information to hosts using POST requests. (Dear Host, the temperature at my location is 85 degrees F.)](media/get_post.png width="400px" border="0") ## Specifying Data How do we actually specify the data we want to request (with a GET) or submit (with a POST)? There's a ton of different ways, and I'm going to lie to you a bit for simplicity[^syntax]. In order to specify data, you append a query string to the end of your URL. A URL stands for "Universal Resource Location" which usually specifies the host of interest and the sub-resource we want to use on the host...these are what you type into the address bar in your web browser. For example, the following is a standard URL. If you type it into you web browser's address bar your browser performs a GET request with no data specified: ``` http://website.com ``` If you do the following: ``` http://website.com?favorite_pet=cats ``` You have just performed a GET request on the same host (server), but now provided the data pair "favorite_pet:cats" and the host might know what to do with that. In this case it doesn't, since website.com is being squatted on, so let's look at another example where it actually has an impact: You might have heard of Google and its search capabilities. It provides us with a URL to its search servers (go ahead type it into the browser): ``` https://www.google.com/search ``` This is the unique identification of its search resource. Without any button pushing we can manually specify our search with a query string "?q=mostec": ``` https://www.google.com/search?q=mostec ``` Putting that into your web-browser immediately brings up the search result (try it!). What we've done is manually build up the GET request that we generate when we type something into Google's search bar and press "submit". Behind the scenes, all that button is doing is formulating the GET request we just specified [^syn] ## Case Study: the Browser Let's keep investigating GETs and POSTs in the web browser, an environment you're familiar with.. Go to https://eesjs1.net/mostec2018workshop/site_1. You'll see an *absolutely* gorgeous MIT-MOSTEC page. When on the page, right click and go to "View Page Source". This will open up a window that looks very similar to the following: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ html <html> <body> <style> body { background-color: maroon; } </style> </head> <body>

MOSTEC

MOSTEC EECS test page 1. Stuff!

</body> </html> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now I don't know what you're background is or not, but the text you see above is the HTML (and a little styling mixed in) which represents that page. Each line above means something and it is one of the jobs of a web-browser (like Chrome or Firefox) to **interpret** and **render** this HTML into a pretty page. When we type in that URL above, the computer does a **GET** request to that eesjs1.net server and it literally send the text you see above back to your computer in the response and the browser renders/interprets it. Most Browser experiences we encounter are GET requests, but there are also POST requests. You can't just type in a POST request in the address bar of a web browser like you can with a GET request, but there are special pages with "Forms" that can generate POST requests. Go here: https://eesjs1.net/mostec2018workshop/site_2. Right click on the page and you'll see the following: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ html <html> <body> <form method="post"> Red
Green
Blue
</form> </body> </html> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We won't obsess over what is going on here in detail, but in short this is an **HTML form** which when submitted sends a POST request to the host (eesjs1.net) with the results of your checkbox choices. This is how we often times POST when using the internet via a web browser...the tools to do it are often first requested with a GET request and then using the form it provides we can then do POSTs. This might confusing, so to review (since it is important): Going to https://eesjs1.net/mostec2018workshop/site_2 in your web browser performs a GET request to the eesjs1.net host, and in particular the URL: eesjs1.net/mostec2018workshop/site_2 and it returns the HTML you see as its response. That HTML renders a form that you can use to generate a POST request to that same host. !!! WARNING Confused? This stuff is confusing. If you're not sure what exactly happened above, ask for help!* OK we just went over GETs and POSTs in a web-browser. Web-browsers are meant for humans, but our Raspberry Pi really doesn't care about pretty-rendered pages. Instead it needs cold-hard programming to do its GET and POST requets. This is where we need to start programming. # Client-Side Python The key to building automation into the internet is programming. Programming is what makes clients and hosts do what they should. We're going to use Python for both our client-side and server-side automation code. Let's first work on the client. Open up the Python editor on the Raspberry Pi Computer as shown below: ![Figure [go_to_idle.png]: Open up Python on our Raspberry Pi by running IDLE3.](media/go_to_idle.png width="400px" border="0") This is the Python shell. We'll be writing and rewriting whole Python files (which we'll save as `.py` files, such as `joe.py`) !!! note If you're new to programming or Python, please ask groupmates or staff for help! ## The `requests` Module Python has a really nice library called `requests`. We can use it to perform web-based actions in a nice and clean way. The following three-line piece of code does this ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python #Anything starting with "#" is a comment in Python (note for person) import requests #imports the library (has lots of web unctionality in it) #send GET request to server: a = requests.get("https://eesjs1.net/mostec2018workshop/site_1") #print response out: print(a.text) #print response ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ What you should see is text fly-by and you might vaguely notice that the text is the same as when we went to https://eesjs1.net/mostec2018workshop/site_2 in our web browser earlier. We can do this for any site! Google.com, for example. Run the following code: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python import requests #imports a = requests.get("https://google.com") print(a.text) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You'll notice a huge amount of crap flies by. This is weird right?...since we're just visiting google.com and we know from experience that it is a very clean and simple site. Why is the file so long? The reason is there is a lot more to a browser than just rendering pretty font or colors...you can also run javascript and Google does a lot of that to "help" (e.g. track) you. If we wanted to specify data in our GET request we could do the following (perform a Google search for "MOSTEC") ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python import requests #imports a = requests.get("https://google.com/search?q=mostec") print(a.text) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ and if we wanted to POST data we could do the following: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python import requests #imports a = requests.post("http://iesc-s3.mit.edu/esp32test/mostecworkshop/test_12345.py?red=true") print(a.text) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ So great! We can perform both GETs and POSTs with Python. ## Inputs and Outputs In addition sending out web requests, we also want to be able to control local phenomenon with our computation device such as LEDs and buzzers and/or read the inputs of switches. We'll also use Python, but some slightly different bits of code and libraries. To get started on this, we first need to figure out how to electrically interface with the Pi. This is done by wiring up parts via the breakout board (the blue thing) and the breadboard (The long white thing with holes) to parts we want. For example, let's say we'd like to attach an LED to pin 4 on the Raspberry Pi as shown in the schematic below: ![Figure [one_led_hookup.png]: Attaching one LED.](media/one_led_hookup.png width="280px" border="0") To implement this schematic, we first need to be able to interpret the symbols and the schematic. The Raspberry Pi is the Grey box and the pins we want to use are labeled. We access its pins through its **breakout board** ![Figure [breakout.png]: The breakout of the Raspberry Pi. How we get electrical access to it. The breakout provides labeled access to pins on the Pi.](media/breakout.png width="400px" border="0") !!! note Many connections such as GND or 3.3V have multiple pins on the Raspberry Pi. An LED (light-emitting diode) is depicted by the symbol shown below and that corresponds to its real-life version like shown: ![Figure [led_schematic.png]: Mapping a real-life LED to its schematic symbol.](media/led_schematic.png width="400px" border="0") The solid lines are wires/electrical connections. So all together, this means an LED needs to somehow get jammed together with the Pi. This is where the breadboard and/or wires come into play. The breadboard consists of specific rows of connected conductors with springy-grabby things inside. This means we can easily electrically attach two or more parts together by shoving their "leads"/"connectors" into the holes where appropriate. Areas of common connections are shown below: ![Figure [bb_rc.png]: Regions of common connection in a breadboard.](media/bb_rc.png width="580px" border="0") Pi pins that can be used for GPIO outputs are indicated with the "#" symbols on the board. For example in the photo below, which is an implementation of the LED schematic above, the red LED is connected to GPIO Pin 4! So for example to build our LED circuit from above we could do: ![Figure [one_led_hooked_up.png]: An LED attached to Pin 4!.](media/one_led_hooked_up.png width="280px" border="0") If you don't want the LED *right* near the breakout you can move it like so and use wires to link it electrically: ![Figure [one_led_hooked_up_b.png]: Another way to attach an LED to Pin 4!.](media/one_led_hooked_up_b.png width="280px" border="0") ### LED If we were to then run the following code in Python (In IDLE3 go to File>New and then copy/paste it into a Python file, save it, and run it) you should see the LED flash! The code is commented with hashtags (spots that are meant to explain stuff to us humans.) Try to map the behavior of the LED to the code. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python #Comments start with hashtags in Python #Simple LED Flasher from workshop_setup import * #sets up 4,17, and 27 as outputs, 25 as an input, and 26 as a buzzer output time_pause = 0.25 #how long a standard pause is in this program led_pin = 4 GPIO.output(led_pin,1) #turn on time.sleep(time_pause) #wait time_pause seconds GPIO.output(led_pin,0) #turn off time.sleep(2*time_pause) #wait time_pause *2 seconds GPIO.output(led_pin,1) #turn on time.sleep(0.5*time_pause) #wait 0.5*time_pause seconds GPIO.output(led_pin,0) #turn off time.sleep(time_pause) GPIO.cleanup() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ![Figure [led_flashing.png]: A red LED hooked up so that when GPIO 4 is ON, the LED glows, and when it is OFF, the LED does not glow.](media/led_flashing.png width="400px" border="0") !!! WARNING If you don't see the LED flashing, double check your wiring! !* We could try a more complicated piece of code like the following. This uses a loop and some if/else control logic. Run the code and study it and if you have questions, let us know!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python #Simple More Interesting Structure from workshop_setup import * #sets up 4,17, and 27 as outputs, 25 as an input, and 26 as a buzzer output time_pause = 0.25 #how long a standard pause is in this program try: times_through = 0 #remembers how many times through while True: if times_through%2==0: GPIO.output(led_pin,1) else: GPIO.output(led_pin,0) print(times_through) #print message time.sleep(time_pause) #sleep time_pause aount times_through +=1 #increment it except KeyboardInterrupt: GPIO.cleanup() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some things to note: the `while True` structure is a piece of code that will run forever...within reason. If we want to stop it we just need to press Control-C! ### Buzzer There's two other input/output devices we can interface with in this workshop. The first is a Piezo buzzer. It looks like the following: ![Figure [buzzer.jpg]: Our Piezo Buzzer.](media/buzzer.jpg width="280px" border="0") ![Figure [buzzer_hookup.png]: Hooking up the Buzzer for this lab.](media/buzzer_hookup.png width="280px" border="0") A buzzer is a device that makes noise. On the Pi in Python because of reasons we won't get into, it sounds like something gross/grinding, but it is still noise nonetheless. We can control the buzzer using the following chunk of example Python. Again mess with this code a bit to make sure you have your buzzer hooked up and running fine. If you need to exit (and you might since the sound is annoying), press Control-C. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python #Simple More Interesting Structure from workshop_setup import * #sets up 4,17, and 27 as outputs, 25 as an input, and 26 as a buzzer output time_pause = 0.25 #how long a standard pause is in this program try: toneOn() #turn on buzzer (starts at 800Hz-ish) time.sleep(2) #wait two seconds toneOff() while True: changeFrequency(200) #change frequency to 200 Hz toneOn() time.sleep(2) toneOff() time.sleep(2) changeFrequency(300) toneOn() #turn back on time.sleep(2) except KeyboardInterrupt: GPIO.cleanup() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ### Switch The two parts above are outputs, controlled by the Pi. What we'd now like to add is an input...something that a human controls and the Pi responds to. For this we'll use a simple switch. ![Figure [real_switch.jpg]: Our Switch.](media/real_switch.jpg width="280px" border="0") A switch is merely a device which converts mechanical position into electrical contact/no contact. When we hook up our switch like shown below, it'll allow the Pi to read a High voltage when it is pushed and a low voltage when it is not pushed. ![Figure [switch_hookup.png]: Hooking up the Switch for this lab.](media/switch_hookup.png width="280px" border="0") The code below will do the following: * Measure the switch * If the switch is pushed (value == 1), print "Pushed" * If the siwtch is **not** pushed (value==0) print "UnPushed" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python #Simple More Interesting Structure from workshop_setup import * #sets up 4,17, and 27 as outputs, 25 as an input, and 26 as a buzzer output time_pause = 0.25 #how long a standard pause is in this program try: while True: button_val = GPIO.input(25) if button_val: print("Pushed") else: print("UnPushed") except KeyboardInterrupt: GPIO.cleanup() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Make sure all three code examples above worked. You'll need bits from them later on! # Server-Side Python OK so we have a basic ability to control an embeddable computer and interface with a few basic sensors. We're halfway there (more than halfway actually, more like $\frac{2}{3}\cdot 100 \%$ there.) - [x] Hardware - [x] Client-Side Programming - [ ] Server-Side Programming We next need to write code to manage our server/host. You're going to do that too! And it is in Python as well! Sweet. On the host side, often called "server-side" code will exist in a general form of a continuous high-level loop which responds to incoming requests and runs the appropriate sub-chunk of code as needed: ![Figure [host_checking_loop.png]: The general idea behind how code exists on the server. The high-level program is always running. If a request comes in directed at your particular URL, it is passed off to it and your `request_handler` function is run.](media/host_checking_loop.png width="400px" border="0") ## Handling Requests !!! WARNING Remember, we're on the side of the host now...so you're responding to GETs and POSTs...not sending them!* Our server-infrastructure works off of the `request_handler` function and you'll be writing versions of it as needed. `request_handler` takes in two arguments: * `type`: which is either `'GET'` or `'POST'` depending on the request * `data`: a Python dictionary containing the data sent up. For example if you did a GET request with a query string of `?pet_name_1=chessie&pet_name_2=carol`, this would result in a Python dictionary of `{'pet_name_1':'chessie', 'pet_name_2':'carol'}`. A simple, generic `request_handler` looks like the following code snippet. Create a file, and call it something unique (maybe like your name or something). ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python def request_handler(type,data): return "SOME STRING YOU MAKE UP" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We need to place this code on our server then. To do this open up a Terminal (Control-Alt-T). While in the "home" directory of the computer and type the following bit and when prompted, enter the password of `oeop`, replacing `filename.py` with whatever you named your file (**name it something unique to your group!!!!**).: !!! ERROR: Server Account As of August 8, 2018, this account has been modified so that the above credentials do not work. Contact me if you'd like access to that machine! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ scp filename.py mostec2018@iesc-s3.mit.edu:~/ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If this is successful, your Python code for handling a web request will then be accessible at the following: http://iesc-s3.mit.edu/esp32test/mostecworkshop/filename.py ...from anywhere in the world (where `filename.py` is the name of your file)!! !!! Note Make sure you try this out on the computer browser and your phone! Try a unique response message so you can be sure it is working! To simply return back the data sent up modify your file to be the following (then re-upload to the server): ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python def request_handler(type,data): return data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Visit your site add in some query arguments by modifying the URL (append "?chicken=bird&cat=pet" to the end of the URL. !!! Note Make sure to add some query arguments and verify they show up in the response! We can act upon what is in the data, like for example: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python def request_handler(type,data): if 'animal' in data.keys(): #check if included return "One type of animal is a " + data['animal'] else: return "no animal provided :(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Assuming `URL` is the full URL name, `URL?animal=cat` would return "One type of animal is a cat", `URL?animal=moose` would return "One type of animal is a moose", and `URL?candy=reeses` would return "no animal provided :(", So far we've done the same thing in response to a GET or a POST. But we'll actually want to respond differently in real-life. We can also act upon whether the request being sent up is a GET or a POST! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python def request_handler(type,data): if type == "GET": return "Thanks for the GET" else: return "Thanks for the POST" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is how you can start to create "dynamic web pages"...for example maybe you have a `user` argument in your code: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python def request_handler(type,data): if type == "GET": if 'user' in data.keys(): return "Welcome, "+ str(data['user']) + "!" else: return "You must provide a User!" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! Note Show the above User Welcoming Code with and without a user specified! ## Persistence The code that exists on the server will analyze incoming requests for information. However based on what we've gone over so far there's no way for a client to "change-the-world" so to speak. Basically the server code will never remember what it's previously seen since every time you access the site, the `request_handler` function is run anew, and only bases what it does on inputs. What we need to do is allow the server code to be able to remember stuff so that the next time it runs, we can augment our output based on possible older data. This is done using what's called **persistent storage** and is usually done using what's known as a **database**. Now people spend their lives becoming experts in databases, so it is a very deep topic, but at the end of the day, a database is just a way to store information. A simple type of database is a file we write to and read from. We can do that in Python as in the following which records the last temperature that was POSTed to it and reports that most recent temperature whenever a GET is performed. Upload this code and see what it does when you visit it in the browser! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python database_name = "temp_storage" #come up with unique name!! def request_handler(type,content): if type=="POST": f=open(database_name,'w') #change this filename to something unique! temp = content['temperature'] f.write(temp) f.close() return "data posted" elif type=="GET": f = open(database_name,'r') data = f.read() temp = float(data) return "Most recent temperature is " +str(temp) + " degrees!" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You might notice that the code above doesn't work right away. This is because when you do a GET you're looking up data that hasn't been written yet. We need data, and the only way to to do that is with a POST in this code. How can we POST? We'll write a little chunk of Python to run like the following, (which you'll need to modify to match your code's URL) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python import requests c = requests.post("URL_OF_YOUR_FILE?QUERY ARGUMENT") print(c) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After running the code, and getting a successful ("data posted") response, you should *then* be able to run your GET! ![Figure [temp_example.png]: Our example temperature sensor infrastructure. One client periodically POSTs temperature values. The server code stores this value in a file called `"temp_storage"`. When a second client (a human), visits this URL in the Browser (GET request), the most recently written temperature is looked up and returned!.](media/temp_example.png width="400px" border="0") # Your Assignment(s) OK: - [x] Hardware - [x] Client-Side Programming - [x] Server-Side Programming We're done. You understand the internet. Now we're going to build two simple IOT systems. First up: Smart-Bulb: ## Smart-Bulb The first is a smart-lamp where you go to a URL on your smartphone, and specify the color you want of an LED bulb. The basic behavior should look like the following video: We'll be using a tri-color LED. Originally LEDs were single colors (red for many years with other colors such as blue and white only becoming economically feasible in recent times...blue LEDs were not ubiquitous in my childhood like they are now). A lot of modern LEDs (including OLED televisions), are three-color packages including the one we'll be using. Our tri-color LED is RGB (Red-Green-Blue), meaning it has a Red, a Green, and a Blue LED inside it of it, all independently controllable. By modulating which ones are on/off and their brightnesses we can ideally generate any color. ![Figure [tri_color_led.jpg]: The type of RGB-LED we'll be using.](media/tri_color_led.jpg width="400px" border="0") ### Hardware Wire up the LED as shown: ![Figure [rgb_hookup.png]: Hook up the RGB LED like shown](media/rgb_hookup.png width="340px" border="0") ### Server-Side Code For the server, start with the following code for the server. It mostly works, with the exception that you should give it a good database name. Right now it: * In response to a GET with no query arguments, returns a HTML form * In response to a POST, it writes the POST data to a database * In response to a GET with the query argument "command=true", it will read the database and return what is in it. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python default_body = r""" <html> <body> <form method="post"> Red
Green
Blue
</form> </body> </html> """ database = "your_file" def request_handler(type,data): if type == "GET": if 'command' in data.keys(): f = open(database_name,'r') data = f.read() f.close() return data else: return default_body else: #POST!! f=open(database,'w') #change this filename to something unique! f.write(str(data)) f.close() return "data posted. Shanks!" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ) ### Client-Side Code Most of your work needs to come on the client-side. We've included some **client-side** code to get you started. You will put this on your Raspberry Pi (and expand on it) and run it *locally* on the Pi. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python #Final Deliverable 1 from workshop_setup import * #same as before try: while True: b = requests.get("YOUR_URL")# perform GET request on your server code print(b.text) #based on response, turn on or off the correct LEDs! time.sleep(1) #run loop about once a second except KeyboardInterrupt: GPIO.cleanup() #live your best life ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the following flow-diagram as overall guidance. ![Figure [smart_bulb_diagram.png]: Smart Bulb Flow Diagram](media/smart_bulb_diagram.png width="540px" border="0") * There is more than one way/format to store the color preference for your LED in your database. * Remember to use unique names for your server code and your database. * This is tough! Ask questions as needed!!! !!! Help When done, show the system working!!!. ## Remote Activation: The next system is a bit different. You should pair up with another team in class, and build a system where pressing the button on one user's Raspberry Pi can turn on the buzzer and green LED on of another user. This is demonstrated in the video below: !!! Note Need another team? Ask the staff to help find you one!. ![Figure [remote_control.png]: Remote Control Flow Diagram](media/remote_control.png width="540px" border="0") I'll leave this one more open...it is up to you now...create the two different client codes (one for the button-Raspberry Pi and one for the LED/buzzer Raspberry Pi) as well as the server code that interfaces between them. You will definitely need a database, but your server code will probably be a bit shorter/simpler than the previous deliverable. # Moving On So what we just went through is just one of **many** ways in which to use the internet. Some would in fact argue this is not how the future will be, but that's sort of beside the point. Interested in messing with this stuff more? Always feel free to email me (jodalyst@mit.edu) if you need help setting up a system. All of the code and resources we used here are freely available/open source. Some reading/work/stuff which you might find interesting: * [IOT Background](https://www.forbes.com/sites/jacobmorgan/2014/05/13/simple-explanation-internet-things-that-anyone-can-understand/#1d8130211d09) * [Wikipedia's always good](https://en.wikipedia.org/wiki/Internet_of_things) * [Got a Pi/Want one?](https://www.raspberrypi.org/help/) * [Good Python Resource](https://eesjs1.net/2018/__STATIC__/mostec/lectures/thinkpython2.pdf) * [Flask: The Python code we used behind scenes on server](http://flask.pocoo.org/) * [Requests Library Docs](http://docs.python-requests.org/en/master/) # Background File For the sake of record-keeping, the file `workshop_setup.py` is included below (it defines some basic functions) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python import RPi.GPIO as GPIO #allows us to control LEDs on the Pi import time #imports a timing library import requests GPIO.setmode(GPIO.BCM) #setup pin-numbering. GPIO.setwarnings(False) GPIO.setup(4,GPIO.OUT) #make that pin an output. GPIO.output(4,0) #Set that pin low. GPIO.setup(17,GPIO.OUT) #make that pin an output. GPIO.output(17,0) #Set that pin low. GPIO.setup(27,GPIO.OUT) #make that pin an output. GPIO.output(27,0) #Set that pin low. GPIO.setup(26,GPIO.OUT) #make that pin an output. GPIO.output(26,0) #Set that pin low. GPIO.setup(25,GPIO.IN, pull_up_down=GPIO.PUD_DOWN) #make that pin an input. pwm=GPIO.PWM(26,800) pwm.start(0) def toneOn(): pwm.ChangeDutyCycle(50) def toneOff(): pwm.ChangeDutyCycle(0) def changeFrequency(freq): try: freq=float(freq) pwm.ChangeFrequency(freq) except: print("ERROR: Frequency Needs to be valid number") ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [^syntax]: Not because you wouldn't understand it, but just because there's so much to cover and I don't want to pile too much on right now. [^syn]: Lie...there's a lot more to it than that, but this is the general idea.