1 of 224

EE 629 �Internet of Things �Lesson 4: Django and Flask

Kevin W. Lu

2023-07-02

2 of 224

Lesson 4: Django and Flask

2

* Contribution by Yuan Liu, 2019 Fall

3 of 224

Lab 4A — Django

  • Install Django software
  • Start Django "stevens" projects and apps
  • Edit settings, copy all files
  • Make and run migrations
  • Create Django Administration username (pi) and PASSWORD
  • Run server
  • Login localhost/admin to add date/time, temperature, and lat/lon
  • View app at localhost
  • Reminders
    • Change MySQL database PASSWORD in settings.py
    • Change Django Administration PASSWORD in views.py

3

4 of 224

Lab 4B — Django REST

  • Start Django "myraspi" project and app
  • Edit settings, copy all files, make and run migrations, create Django Administration username (pi) and PASSWORD
  • Run server; login localhost/admin to add location and lat/lon; post initial values at localhost/dt, tmp, and cpu
  • Install psutil (process and system utilities) by pip3
  • Run controller; and view app at localhost/home
  • Optional Django projects/apps
    • Weather — post initial values at localhost/dt, tmp, and hmd
    • Lighting — post initial values at localhost/state, mode
    • Parking — post initial values at localhost/state
    • Sensing — post initial values at localhost/room, door
  • Reminders
    • Change MySQL database PASSWORD in settings.py (except SQLite3 in Lighting)
    • Change Django Administration PASSWORD in views.py and controller.py

4

5 of 224

View App — Landscape

5

6 of 224

View App — Portrait

6

7 of 224

Lab 4C — Flask

  • On a Raspberry Pi
    • Run 'python3 ~/iot/lesson4/hello_world.py' on a Raspberry Pi
    • Install Flask-Ask and Ngrok
    • Run 'python3 ~/iot/lesson4/memory_game.py' and './ngrok http 5000' in two separate terminals
  • On a laptop
    • Login the Alexa Skills Kit Developer Console
      • Follow the slide on 'Create Alexa Skill'
  • Enable and test Alexa Skill
    • Type "memory game" or click and hold the microphone button to speak "memory game"

7

8 of 224

Lab 4D — LAMP

  • Install Apache, PHP, and WordPress
  • Build a Linux-Apache-MySQL-PHP (LAMP) web server with WordPress

8

9 of 224

Milestones of Internet Layers

9

10 of 224

CERN and WWW

  • Established in 1954, the European Organization for Nuclear Research CERN (in French Conseil Européen pour la Recherche Nucléaire) is based in a northwest suburb of Geneva on the France–Switzerland border and has 22 member states
  • CERN operates the largest particle physics laboratory in the world with six particle accelerators and a decelerator
  • Built by CERN between 1998 and 2008, the Large Hadron Collider (LHC) is the world's largest and most powerful particle collider and the largest machine in the world in a tunnel 27 kilometers in circumference and as deep as 175 meters
    • A hadron (such as the proton and the neutron) is a composite particle made of two or more quarks held together by the strong force in a similar way as molecules are held together by the electromagnetic force
  • Robert Cailliau proposed the first hypertext system for CERN in 1987
  • Tim Berners-Lee initiated a CERN project named ENQUIRE on 1989-03-12, and the World Wide Web with Robert Cailliau on 1990-11-12

10

11 of 224

Web 3.0

11

Year

World Wide Web Evolution

1.0

1989—1990

First web browser by Tim Berners-Lee

1999—2004

User-generated content (UGC), usability, and interoperability

3.0

2001—2006

Semantic Web with common data formats and exchange protocols on the web, most fundamentally the Resource Description Framework (RDF)

12 of 224

Clearnet and Surface Web

  • The clearnet refers to the publicly accessible internet
  • A darknet is an overlay network within the internet that can only be accessed with specific software, configurations, or authorization, often using a unique customized communication protocol
  • The dark web refers to the content that exists on darknets
  • The surface web refers to the portion of the World Wide Web that is readily available to the general public and searchable with standard web search engines
  • The deep web refers to the contents that are not indexed by standard web search-engines
  • The dark web is a portion of the deep web

12

13 of 224

Splinternet

  • The splinternet is a characterization of the internet as splintering and dividing due to various factors, such as technology, commerce, politics, nationalism, religion, and divergent national interests
  • There are ideas to set a global baseline of online expression, and a process for adjudicating disputes
  • A coalition called the Global Network Initiative has worked to set a code of conduct for tech and telecom companies to protect online speech and privacy globally
  • Groups including Article 19, which works on promoting freedom of expression, and the Facebook Oversight Board have also worked on resolution mechanisms for people around the world to challenge internet companies’ decisions

13

14 of 224

Framework

  • In computer programming, a software framework is a universal, reusable software environment that provides particular functionality as part of a larger computing platform to facilitate development of software applications, products and solutions
  • Software frameworks may include support programs, compilers, code libraries, tool sets, and application programming interfaces (APIs) that bring together all the different components to enable development of a project or system
  • Unlike in libraries or in standard user applications, the framework flow of control is not dictated by the caller, but by the framework
  • The framework code is non-modifiable although a user can extend the framework usually by selective overriding or programmers can add specialized user code to provide specific functionality
  • A web framework or web application framework is a software framework that provides a standard way to build and deploy web applications on the World Wide Web, and to automate the overhead associated with common activities performed in web development

14

15 of 224

Request-Response by REST(ful) API

15

Client

Server

Request (GET, PUT, POST, DELETE) with payload (JSON or XML)

Request (GET, PUT, POST, DELETE) with payload (JSON or XML)

Response (JSON or XML)

Response (JSON or XML)

REST: Representational State Transfer

API: Application Programming Interface

JSON: JavaScript Object Notation

XML: Extensible Markup Language

16 of 224

Django Web Framework

  • Adrian Holovaty, Simon Willison, Jacob Kaplan-Moss, and Wilson Miner created Django in the fall of 2003 at the Lawrence Journal-World newspaper in Lawrence, Kansas
  • Adrian Holovaty, a guitarist, named the framework after Django Reinhardt
  • It was released publicly under a BSD license in July 2005
  • Django Software Foundation (DSF) maintains Django as of June 2008
  • Django REST Framework is a toolkit for building Web APIs
  • Tom Christie from the U.K. is the primary maintainer of Django REST Framework

16

17 of 224

Django Architecture

17

Client

Side

Server

Side

Database

Django

Framework

Django Template

App

Logic

Model

View

Logic

18 of 224

Django Software Foundation (DSF)

18

19 of 224

SQLite

19

20 of 224

Django REST Framework

20

21 of 224

Django Reinhardt 1910—1953

  • Jean "Django" Reinhardt was a Belgian-born French jazz guitarist and composer with Romani ethnicity
  • His nickname Django \ˈjaŋ-ˌɡō\, French: \ˈdʒãŋ-ˌɡo\ (the D is silent) is Romani for "I awake"
  • He created a whole new fingering system built around his left index and middle fingers after he was burned in a fire on the night of November 2, 1928
  • Guitarist Carlos Alomar [Website, Wikipedia] is the Director and founder of the Stevens Sound Synthesis Research Center

21

22 of 224

Romani People and Flamenco

  • A genomic study reported that the Romani people often called by the exonym Gypsies emigrated from Rajasthan, Haryana, Punjab, and Sindh possibly as early as 500 A.D.
  • Flamenco has been influenced by and become associated with the Romani people in Spain
  • Flamenco includes cante (singing), toque (guitar playing), baile (dance), jaleo (vocalizations), palmas (hand clapping), and pitos (finger snapping)
  • The flamenco dance resembles the flame-colored flamingo (flamenco in Spanish), a bird common in the salt lakes and lagoons of Andalusia

22

23 of 224

Berkeley Software Distribution

  • The earliest distributions of Unix from Bell Labs in the 1970s included the source code to the operating system, allowing researchers at universities to modify and extend Unix
  • Berkeley Software Distribution (BSD) is a Unix operating system derivative developed and distributed by the Computer Systems Research Group (CSRG) of the University of California, Berkeley, from 1977 to 1995
  • The BSD mascot, Beastie, is named after software daemons that runs as a background process
  • Beastie carries a trident to symbolize a process fork or a project fork

23

24 of 224

Install Django and REST Framework

pi@piot4:~ $ pip3 -V

pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)

pi@piot4:~ $ sudo pip3 install -U setuptools

pi@piot4:~ $ sudo pip3 install -U django

pi@piot4:~ $ sudo pip3 install -U djangorestframework

pi@piot4:~ $ sudo pip3 install -U django-filter

pi@piot4:~ $ sudo pip3 install -U markdown

pi@piot4:~ $ sudo pip3 install -U requests

pi@piot4:~ $ python3

>>> import django

>>> django.VERSION

(4, 0, 2, 'final', 0)

>>> exit()

pi@piot4:~ $ cd iot

pi@piot4:~/iot $ git pull

pi@piot4:~/iot $ cd

pi@piot4:~ $

24

  • Text-to-HTML conversion
  • HTTP library for Python
  • Set up Python packages
  • The Django 1.11.x series is the last to support Python 2.7

25 of 224

Install MariaDB Server and Client

pi@piot4:~ $ sudo apt update

pi@piot4:~ $ sudo apt install mariadb-server mariadb-client

pi@piot4:~ $ sudo apt install python3-mysqldb

pi@piot4:~ $ sudo pip3 install -U mysqlclient

pi@piot4:~ $ sudo mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB

SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current

password for the root user. If you've just installed MariaDB, and

you haven't set the root password yet, the password will be blank,

so you should just press enter here.

Enter current password for root (enter for none):

OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MariaDB

root user without the proper authorisation.

25

MariaDB and MySQL were named after Michael Widenius' daughters

  • Python3 interface to MySQL

26 of 224

Set Root Password and

Remove Anonymous Users

Set root password? [Y/n]

New password:

Re-enter new password:

Password updated successfully!

Reloading privilege tables..

... Success!

By default, a MariaDB installation has an anonymous user, allowing anyone

to log into MariaDB without having to have a user account created for

them. This is intended only for testing, and to make the installation

go a bit smoother. You should remove them before moving into a

production environment.

Remove anonymous users? [Y/n]

... Success!

26

  • Enter for Y (uppercase means default)
  • Set root password
  • Enter for Y

27 of 224

Disallow Root Login Remotely and

Remove Test Database and Access

Normally, root should only be allowed to connect from 'localhost'. This

ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n]

... Success!

By default, MariaDB comes with a database named 'test' that anyone can

access. This is also intended only for testing, and should be removed

before moving into a production environment.

Remove test database and access to it? [Y/n]

- Dropping test database...

... Success!

- Removing privileges on test database...

... Success!

27

  • Enter for Y
  • Enter for Y

28 of 224

Reload Privilege Tables

Reloading the privilege tables will ensure that all changes made so far

will take effect immediately.

Reload privilege tables now? [Y/n]

... Success!

Cleaning up...

All done! If you've completed all of the above steps, your MariaDB

installation should now be secure.

Thanks for using MariaDB!

pi@piot4:~ $

28

  • Enter for Y

29 of 224

Connect to MariaDB Server

pi@piot4:~ $ sudo mysql -u root -p

Enter password:

Welcome to the MariaDB monitor. Commands end with ; or \g.

Your MariaDB connection id is 55

Server version: 10.3.17-MariaDB-0+deb10u1 Raspbian 10

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use mysql

Reading table information for completion of table and column names

You can turn off this feature to get a quicker startup with -A

Database changed

MariaDB [mysql]>

29

  • Enter root password

30 of 224

Create or Drop User 0/1

MariaDB [mysql]> select user, host from mysql.user;

+------+-----------+

| User | Host |

+------+-----------+

| root | localhost |

+------+-----------+

1 row in set (0.001 sec)

MariaDB [mysql]> create user pi@localhost identified by 'raspberry';

Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> select user, host from mysql.user;

+------+-----------+

| user | host |

+------+-----------+

| pi | localhost |

| root | localhost |

+------+-----------+

2 rows in set (0.001 sec)

MariaDB [mysql]>

30

  • Change password

31 of 224

Create or Drop User 1/1

MariaDB [mysql]> select user, host from mysql.user;

+------+-----------+

| user | host |

+------+-----------+

| pi | localhost |

| root | localhost |

+------+-----------+

2 rows in set (0.001 sec)

MariaDB [mysql]> drop user pi@localhost;

Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> select user, host from mysql.user;

+------+-----------+

| User | Host |

+------+-----------+

| root | localhost |

+------+-----------+

1 row in set (0.001 sec)

MariaDB [mysql]>

31

  • If pi@localhost is dropped, create it again for Exercise 4

32 of 224

Create or Drop Database

MariaDB [mysql]> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| mysql |

| performance_schema |

+--------------------+

3 rows in set (0.001 sec)

MariaDB [mysql]> create database test;

Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| mysql |

| performance_schema |

| test |

+--------------------+

4 rows in set (0.001 sec)

MariaDB [mysql]> drop database test;

Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| mysql |

| performance_schema |

+--------------------+

3 rows in set (0.001 sec)

MariaDB [mysql]>

32

33 of 224

Create All Databases in Exercise 4

MariaDB [mysql]> create database stevens;

Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> create database weather;

Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> create database wordpress;

Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> create database parking;

Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> create database sensing;

Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> create database myraspi;

Query OK, 1 row affected (0.001 sec)

33

34 of 224

Grant Privileges to Users

MariaDB [mysql]> grant all privileges on stevens.* to pi@localhost;

Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> grant all privileges on weather.* to pi@localhost;

Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> grant all privileges on wordpress.* to pi@localhost;

Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> grant all privileges on parking.* to pi@localhost;

Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> grant all privileges on sensing.* to pi@localhost;

Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> grant all privileges on myraspi.* to pi@localhost;

Query OK, 0 rows affected (0.001 sec)

34

35 of 224

Four Ways to Quit MariaDB

MariaDB [mysql]> quit

Bye

pi@piot4:~ $

MariaDB [mysql]> exit

Bye

pi@piot4:~ $

MariaDB [mysql]> Bye

pi@piot4:~ $

MariaDB [(none)]> Ctrl-C -- exit!

Aborted

pi@piot4:~ $

35

  • control-d
  • control-c

36 of 224

Django Project Files and Databases

  • Django project files
    • manage.py runs startproject, startapp, runserver, etc.
    • controller.py interacts with databases, sensors, and actuators
    • __init__.py tells Python that it’s a Python package
    • settings.py specifies apps, databases, static files, etc.
    • urls.py registers viewsets with router classes, etc.
    • wsgi.py for production with Apache HTTP server and mod-wsgi
    • admin.py admin interface at http://127.0.0.1:8000/admin
    • models.py Python classes for database tables
    • views.py specifies viewsets
    • serializers.py converts data types (Python, JSON, XML, etc.)
    • tests.py runs tests
    • templates typically index.html
    • static files CSS (Cascading Style Sheets), JavaScript, etc.
  • Databases: lighting (SQLite3), stevens/myraspi/parking/sensing/weather (MySQL)

36

SQL: Structured Query Language, URL: Uniform Resource Locator, WSGI: Web Server Gateway Interface

37 of 224

HTTP Response Status Code

In response to a client's request, the server issues the HTTP response status code in five categories defined by the standard

  • 1xx: Informational — Request received, continuing process
  • 2xx: SuccessThe action was successfully received, understood, and accepted
  • 3xx: RedirectionFurther action must be taken in order to complete the request
  • 4xx: Client ErrorThe request contains bad syntax or cannot be fulfilled
  • 5xx: Server ErrorThe server failed to fulfill an apparently valid request

37

38 of 224

Stevens Project

Django Framework

39 of 224

Start Django Project and App

First create MySQL database stevens and grant all privileges to pi@localhost

pi@piot4:~ $ django-admin startproject stevens

pi@piot4:~ $ cd stevens

pi@piot4:~/stevens $ ls

manage.py stevens

pi@piot4:~/stevens $ python3 manage.py startapp myapp

pi@piot4:~/stevens $ ls

manage.py myapp stevens

pi@piot4:~/stevens $ cd stevens

pi@piot4:~/stevens/stevens $ ls

__init__.py __pycache__ settings.py urls.py wsgi.py

pi@piot4:~/stevens/stevens $ nano settings.py

pi@piot4:~/stevens/stevens $ cp ~/iot/lesson4/stevens/urls.py .

pi@piot4:~/stevens/stevens $ cd ../myapp

pi@piot4:~/stevens/myapp $ ls

admin.py apps.py __init__.py migrations models.py tests.py views.py

pi@piot4:~/stevens/myapp $ cp ~/iot/lesson4/stevens/admin.py .

pi@piot4:~/stevens/myapp $ cp ~/iot/lesson4/stevens/models.py .

pi@piot4:~/stevens/myapp $ cp ~/iot/lesson4/stevens/views.py .

39

  • ~ (tilde) is home directory
  • . (single period) is current directory
  • .. (double period) is parent directory
  • Django creates the project directory "stevens" and subdirectory "stevens"

40 of 224

Copy Templates and Static Files

pi@piot4:~/stevens/myapp $ mkdir static templates

pi@piot4:~/stevens/myapp $ cd templates

pi@piot4:~/stevens/myapp/templates $ mkdir myapp

pi@piot4:~/stevens/myapp/templates $ cd myapp

pi@piot4:~/stevens/myapp/templates/myapp $ cp ~/iot/*4/stevens/index.html .

pi@piot4:~/stevens/myapp/templates/myapp $ ls

index.html

pi@piot4:~/stevens/myapp/templates/myapp $ cd ~/stevens/myapp/static

pi@piot4:~/stevens/myapp/static $ cp ~/iot/*4/static/favicon.ico .

pi@piot4:~/stevens/myapp/static $ mkdir myapp

pi@piot4:~/stevens/myapp/static $ cd myapp

pi@piot4:~/stevens/myapp/static/myapp $ cp ~/iot/*4/static/*css .

pi@piot4:~/stevens/myapp/static/myapp $ cp ~/iot/*4/static/*js .

pi@piot4:~/stevens/myapp/static/myapp $ ls

bootstrap.min.css bootstrap.min.js jquery.min.js script.js

pi@piot4:~/stevens/myapp/static/myapp $ cd ~/stevens

40

  • Home directory "~"
  • Current directory "."
  • 16x16 favicon

41 of 224

Django Project Directory

41

/stevens

manage.py

/stevens

__init__.py settings.py urls.py wsgi.py

/myapp

admin.py apps.py __init__.py models.py tests.py views.py

/migrations

__init__.py

/static

favicon.ico

/myapp

*.css *.js

/templates

/myapp

index.html

  • Edit settings.py and copy the files in red from ~/iot/lesson4/stevens

42 of 224

Print Directory Tree

pi@piot4:~/stevens $ tree -d

.

├── myapp

│ ├── migrations

│ ├── static

│ │ └── myapp

│ └── templates

│ └── myapp

└── stevens

└── __pycache__

10 directories

pi@piot4:~/stevens $ cd stevens

pi@piot4:~/stevens/stevens $ tree

.

├── asgi.py

├── __init__.py

├── __pycache__

│ ├── __init__.cpython-37.pyc

│ ├── settings.cpython-37.pyc

├── settings.py

├── urls.py

└── wsgi.py

1 directory, 7 files

pi@piot4:~/stevens/stevens $ cd ..

pi@piot4:~/stevens $ tree -I *pyc

.

├── manage.py

├── myapp

│ ├── admin.py

│ ├── apps.py

│ ├── __init__.py

│ ├── migrations

│ │ ├── __init__.py

│ ├── models.py

│ ├── __pycache__

│ ├── static

│ │ ├── favicon.ico

│ │ └── myapp

│ │ ├── bootstrap.min.css

│ │ ├── bootstrap.min.js

│ │ ├── jquery.min.js

│ │ └── script.js

│ ├── templates

│ │ └── myapp

│ │ └── index.html

│ ├── tests.py

│ └── views.py

└── stevens

├── asgi.py

├── __init__.py

├── __pycache__

├── settings.py

├── urls.py

└── wsgi.py

8 directories, 19 files

42

Contribution by Dler Hasan, 2017 Spring

43 of 224

settings.py 0/1

Since settings.py generated from the command "django-admin startproject" contains SECRET_KEY and other important settings, only copy the code highlighted in bold from ~/iot/lesson4/stevens/settings.txt and keep all other in settings.py intact:

...

ALLOWED_HOSTS = ['*']

...

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'myapp',

]

...

43

  • Add 'myapp',
  • Blank means 'localhost' or '127.0.0.1' by default; enter '*' for all Raspberry Pi IP addresses

Contributions by Allison Butler, Isaac Hirschfeld, and Brian Voyer, 2017 Spring;

Nicholas Antonov, 2018 Spring; Matthew Bergwall, 2018 Summer

44 of 224

settings.py 1/1

...

DATABASES = {

'default': {

# 'ENGINE': 'django.db.backends.sqlite3',

# 'NAME': BASE_DIR / 'db.sqlite3',

'ENGINE': 'django.db.backends.mysql',

'NAME': 'stevens',

'USER': 'pi',

'PASSWORD': 'raspberry',

'HOST': '',

'PORT': '',

'OPTIONS': {

'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",

},

}

}

...

TIME_ZONE = 'America/New_York'

...

44

  • Change time from UTC to America/New_York
  • Change database from sqlite3 to mysql
  • Change database password
  • Copy and paste from ~/iot/lesson4/stevens/settings.txt

45 of 224

urls.py

...

from django.contrib import admin

from django.urls import path, include

from django.contrib.staticfiles.storage import staticfiles_storage

from django.views.generic.base import RedirectView

from myapp import views

admin.autodiscover()

urlpatterns = [

path('favicon.ico', RedirectView.as_view(

url=staticfiles_storage.url('favicon.ico'),

permanent=False), name="favicon"),

path('', views.home, name='home'),

path('admin/doc/', include('django.contrib.admindocs.urls')),

path('admin/', admin.site.urls),

]

45

46 of 224

admin.py

from django.contrib import admin

from myapp.models import TemperatureData

# Register your models here.

admin.site.register(TemperatureData)

46

47 of 224

models.py

from django.db import models

# Create your models here.

class TemperatureData(models.Model):

date_time = models.CharField(max_length=30)

temperature = models.CharField(max_length=5)

latitude = models.CharField(max_length=20)

longitude = models.CharField(max_length=20)

def __str__(self):

return self.date_time

47

  • __unicode__ in Python 2

48 of 224

views.py

from django.shortcuts import render

from myapp.models import TemperatureData

from django.template import RequestContext

# Create your views here.

def home(request):

tempData = TemperatureData.objects.order_by('-id')[0]

date_time = tempData.date_time

temperature = tempData.temperature

lat = tempData.latitude

lon = tempData.longitude

return render(request, 'myapp/index.html', {'date_time': date_time,

'temperature': temperature, 'lat': lat, 'lon': lon})

48

49 of 224

index.html 0/2

<!DOCTYPE html>

<html lang="en">

<head>

<title>Weather Station</title>

<meta charset="utf-8">

<meta name="viewport" content="width=device width, initial-scale=1">

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

<link rel="icon" href="/favicon.ico" type="image/x-icon">

<link rel="stylesheet" href="/static/myapp/bootstrap.min.css">

<script src="/static/myapp/jquery.min.js"></script>

<script src="/static/myapp/bootstrap.min.js"></script>

<style>

#map-canvas {

width: 400px;

height: 300px;

}

</style>

49

50 of 224

index.html 1/2

<script src="https://maps.googleapis.com/maps/api/js"></script>

<script>

function initialize() {

var mapCanvas = document.getElementById('map-canvas');

var mapOptions = {

center: new google.maps.LatLng({{lat}}, {{lon}}),

zoom: 16,

mapTypeId: google.maps.MapTypeId.ROADMAP

}

var map = new google.maps.Map(mapCanvas, mapOptions)

}

google.maps.event.addDomListener(window, 'load', initialize);

</script>

</head>

50

Contribution by Piyush Rao, 2017 Fall

myCenter = new google.maps.LatLng({{lat}}, {{lon}})

var marker = new google.maps.Marker({position: myCenter,

animation: google.maps.Animation.BOUNCE});

marker.setMap(map)

var infowindow = new google.maps.InfoWindow({content: "Location"});

infowindow.open(map,marker);

?key=YOUR_API_KEY

51 of 224

index.html 2/2

<body>

<div class="container-fluid">

<center><h1>Weather Station</h1></center>

<center><h3>Stevens Institute of Technology</h3></center>

<center><h4>{{date_time}}</h4></center>

<div class="row">

<div class="col-sm-3"><h4>Temperature</h4></div>

<div class="col-sm-3"><h4>{{temperature}}&deg;F</h4></div>

<div class="col-sm-6" style="height:300px">

<div id="map-canvas"></div></div>

</div>

</div>

</body>

</html>

51

52 of 224

Static Files

  • Responsive web design is about creating web sites that automatically adjust themselves to look good on all devices, from small phones to large desktops
  • Bootstrap is a front-end framework that includes HTML and CSS based design templates for typography, forms, buttons, tables, navigation, modals, image carousels and many other, as well as optional JavaScript plugins
  • MaxCDN provides CDN (Content Delivery Network) support for Bootstrap CSS and JavaScript
  • bootstrap.min.css

https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css

  • bootstrap.min.js

https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js

  • jquery.min.js

https://code.jquery.com/jquery-3.7.0.min.js

  • script.js

https://github.com/ded/script.js/blob/master/src/script.js

52

53 of 224

Make and Run Migrations

Make and run migrations once and only after any changes to the Django files

pi@piot4:~/stevens $ python3 manage.py makemigrations myapp

Migrations for 'myapp':

myapp/migrations/0001_initial.py

- Create model TemperatureData

pi@piot4:~/stevens $ python3 manage.py migrate

Operations to perform:

Apply all migrations: admin, auth, contenttypes, myapp, sessions

Running migrations:

Applying contenttypes.0001_initial... OK

Applying auth.0001_initial... OK

Applying admin.0001_initial... OK

Applying admin.0002_logentry_remove_auto_add... OK

Applying admin.0003_logentry_add_action_flag_choices... OK

Applying contenttypes.0002_remove_content_type_name... OK

Applying auth.0002_alter_permission_name_max_length... OK

Applying auth.0003_alter_user_email_max_length... OK

Applying auth.0004_alter_user_username_opts... OK

Applying auth.0005_alter_user_last_login_null... OK

Applying auth.0006_require_contenttypes_0002... OK

Applying auth.0007_alter_validators_add_error_messages... OK

Applying auth.0008_alter_user_username_max_length... OK

Applying auth.0009_alter_user_last_name_max_length... OK

Applying auth.0010_alter_group_name_max_length... OK

Applying auth.0011_update_proxy_permissions... OK

Applying auth.0012_alter_user_first_name_max_length... OK

Applying myapp.0001_initial... OK

Applying sessions.0001_initial... OK

53

54 of 224

Set Django Server Administration

Set Django administration username and password

pi@piot4:~/stevens $ python3 manage.py createsuperuser

Username (leave blank to use 'pi'):

Email address: EMAIL_ADDRESS

Password: ADMIN_PASSWORD

Password (again): ADMIN_PASSWORD

Superuser created successfully.

pi@piot4:~/stevens $

54

  • The default username is pi
  • Enter a valid email address
  • ADMIN_PASSWORD shall be at least eight characters

55 of 224

Run Django Server and View App

  • Run Django server at localhost, i.e., 127.0.0.1:8000

pi@piot4:~/stevens $ python3 manage.py runserver

Watching for file changes with StatReloader

Performing system checks...

System check identified no issues (0 silenced).

September 29, 2019 - 10:35:06

Django version 2.2.5, using settings 'stevens.settings'

Starting development server at http://127.0.0.1:8000/

Quit the server with CONTROL-C.

  • Open the Chromium browser on Raspberry Pi or via VNC Viewer
  • Go to http://127.0.0.1:8000/admin (or http://localhost:8000/admin)
  • Login with Django administration username (pi) and ADMIN_PASSWORD
  • Click "temperature data" to add date and time in YYYY-MM-DD HH:MM:SS, temperature in Fahrenheit, latitude 40.7451, and longitude -74.0255
  • Click SAVE then LOG OUT
  • Go to http://127.0.0.1:8000 (or http://localhost:8000) to view app

55

56 of 224

Run Django Server and View App

  • Alternatively, run Django server at all Raspberry Pi IP addresses 0.0.0.0:8000

pi@piot4:~/stevens $ python3 manage.py runserver 0.0.0.0:8000

Watching for file changes with StatReloader

Performing system checks...

System check identified no issues (0 silenced).

September 29, 2019 - 10:35:06

Django version 2.2.5, using settings 'stevens.settings'

Starting development server at http://0.0.0.0:8000/

Quit the server with CONTROL-C.

  • Open a browser on a laptop or phone
  • Go to the Raspberry Pi IP address http://155.246.x.x:8000/admin
  • Login with Django administration username (pi) and PASSWORD
  • Click "temperature data" to add date and time in YYYY-MM-DD HH:MM:SS, temperature in Fahrenheit, latitude 40.7451, and longitude -74.0255
  • Click SAVE then LOG OUT
  • Go to http://155.246.x.x:8000 to view app

56

Contributions by Allison Butler, Isaac Hirschfeld, and Brian Voyer, 2017 Spring;

Nicholas Antonov, 2018 Spring; Matthew Bergwall, 2018 Summer

57 of 224

Django Administration Log In

57

58 of 224

Click Temperature Data

58

59 of 224

Add Temperature Data

59

60 of 224

Save Data

60

61 of 224

Django Administration Log Out

61

62 of 224

Django Administration Log Out

62

63 of 224

View App — Landscape

63

64 of 224

View App — Portrait

64

65 of 224

Darkened Watermarked Maps

65

66 of 224

Maps API Key

  • Google Maps Platform requires an API key
  • Without a valid API key, the map is darkened and watermarked with “this page didn’t load google maps correctly”
  • Instructions to get an API key
  • Substitute YOUR_API_KEY in index.html with the API key:

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"

></script>

66

67 of 224

Weather Project

Django REST Framework

68 of 224

Start Django REST Project and App

First create MySQL database weather and grant all privileges to pi@localhost

pi@piot4:~ $ django-admin startproject weather

pi@piot4:~ $ cd weather

pi@piot4:~/weather $ python3 manage.py startapp myapp

pi@piot4:~/weather $ cd weather

pi@piot4:~/weather/weather $ nano settings.py

pi@piot4:~/weather/weather $ cp ~/iot/lesson4/weather/urls.py .

pi@piot4:~/weather/weather $ cd ../myapp

pi@piot4:~/weather/myapp $ cp ~/iot/lesson4/weather/admin.py .

pi@piot4:~/weather/myapp $ cp ~/iot/lesson4/weather/models.py .

pi@piot4:~/weather/myapp $ cp ~/iot/lesson4/weather/views.py .

pi@piot4:~/weather/myapp $ nano views.py

68

  • Change the default ADMIN_PASSWORD 'raspberry' in views.py

69 of 224

Copy Templates and Static Files

pi@piot4:~/weather/myapp $ cp ~/iot/*4/weather/serializers.py .

pi@piot4:~/weather/myapp $ mkdir static templates

pi@piot4:~/weather/myapp $ cd static

pi@piot4:~/weather/myapp/static $ cp ~/iot/*4/static/favicon.ico .

pi@piot4:~/weather/myapp/static $ mkdir myapp

pi@piot4:~/weather/myapp/static $ cd myapp

pi@piot4:~/weather/myapp/static/myapp $ cp ~/iot/*4/static/*css .

pi@piot4:~/weather/myapp/static/myapp $ cp ~/iot/*4/static/*js .

pi@piot4:~/weather/myapp/static/myapp $ cd ~/weather/myapp/templates

pi@piot4:~/weather/myapp/templates $ mkdir myapp

pi@piot4:~/weather/myapp/templates $ cd myapp

pi@piot4:~/weather/myapp/templates/myapp $ cp ~/iot/*4/weather/index.html .

pi@piot4:~/weather/myapp/templates/myapp $ cd ~/weather

pi@piot4:~/weather $ cp ~/iot/*4/weather/controller.py .

pi@piot4:~/weather $ nano controller.py

pi@piot4:~/weather $

69

  • Change the default ADMIN_PASSWORD 'raspberry' in controller.py

70 of 224

Django REST Project Directory

70

/weather

controller.py manage.py

/weather

__init__.py settings.py urls.py wsgi.py

/myapp

admin.py __init__.py models.py serializers.py tests.py views.py

/migrations

__init__.py

/static

favicon.ico

/myapp

*.css *.js

/templates

/myapp

index.html

  • Edit settings.py and copy the files in red from ~/iot/lesson4/weather

71 of 224

settings.py 0/1

Since settings.py generated from the command "django-admin startproject" contains SECRET_KEY and other important settings, only copy the code highlighted in bold from ~/iot/lesson4/weather/settings.txt and keep all other in settings.py intact:

...

ALLOWED_HOSTS = ['*']

...

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'myapp',

'rest_framework',

]

...

71

  • Add 'myapp',
  • Add 'rest_framework',
  • Blank means 'localhost' or '127.0.0.1' by default; enter '*' for all Raspberry Pi IP addresses

Contributions by Allison Butler, Isaac Hirschfeld, and Brian Voyer, 2017 Spring;

Nicholas Antonov, 2018 Spring; Matthew Bergwall, 2018 Summer

72 of 224

settings.py 1/1

...

DATABASES = {

'default': {

# 'ENGINE': 'django.db.backends.sqlite3',

# 'NAME': BASE_DIR / 'db.sqlite3',

'ENGINE': 'django.db.backends.mysql',

'NAME': 'weather',

'USER': 'pi',

'PASSWORD': 'raspberry',

'HOST': '',

'PORT': '',

'OPTIONS': {

'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",

},

}

}

...

TIME_ZONE = 'America/New_York'

...

72

  • Change time from UTC to America/New_York
  • Change database from sqlite3 to mysql
  • Change password
  • Copy and paste from ~/iot/lesson4/weather/settings.txt

73 of 224

urls.py

...

from django.contrib import admin

from django.urls import path, include

from django.contrib.staticfiles.storage import staticfiles_storage

from django.views.generic.base import RedirectView

from rest_framework import routers

from myapp import views

admin.autodiscover()

router = routers.DefaultRouter()

router.register('dt', views.DtViewSet)

router.register('tmp', views.TmpViewSet)

router.register('hmd', views.HmdViewSet)

urlpatterns = [

path('favicon.ico', RedirectView.as_view(

url=staticfiles_storage.url('favicon.ico'),

permanent=False), name="favicon"),

path('', include(router.urls)),

path('api-auth/', include('rest_framework.urls',

namespace='rest_framework')),

path('admin/', admin.site.urls),

path('home/', views.home),

]

73

74 of 224

admin.py

from django.contrib import admin

from myapp.models import LocationData

# Register your models here.

admin.site.register(LocationData)

74

75 of 224

models.py

from django.db import models

# Create your models here.

class LocationData(models.Model):

location = models.CharField(max_length=30)

latitude = models.CharField(max_length=20)

longitude = models.CharField(max_length=20)

def __str__(self):

return self.location

class Dt(models.Model):

name = models.CharField(max_length=30)

class Tmp(models.Model):

name = models.CharField(max_length=50)

class Hmd(models.Model):

name = models.CharField(max_length=50)

75

  • __unicode__ in Python 2

76 of 224

views.py 0/1

from django.shortcuts import render

from myapp.models import LocationData, Dt, Tmp, Hmd

from rest_framework import viewsets

from django.template import RequestContext

from myapp.serializers import DtSerializer, TmpSerializer, HmdSerializer

import requests

import json

# Create your views here.

class DtViewSet(viewsets.ModelViewSet):

queryset = Dt.objects.all()

serializer_class = DtSerializer

class TmpViewSet(viewsets.ModelViewSet):

queryset = Tmp.objects.all()

serializer_class = TmpSerializer

class HmdViewSet(viewsets.ModelViewSet):

queryset = Hmd.objects.all()

serializer_class = HmdSerializer

76

77 of 224

views.py 1/1

def home(request):

locData = LocationData.objects.order_by('-id')[0]

lat = locData.latitude

lon = locData.longitude

dtstate = '2017-02-11T17:45:00-05:00'

r = requests.get('http://127.0.0.1:8000/dt/1/', auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

dtstate = output['name']

tmpstate = '20'

r = requests.get('http://127.0.0.1:8000/tmp/1/', auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

tmpstate = output['name']

hmdstate = '50'

r = requests.get('http://127.0.0.1:8000/hmd/1/', auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

hmdstate = output['name']

return render(request, 'myapp/index.html',

{'lat': lat, 'lon': lon, 'dtstate':dtstate, 'tmpstate':tmpstate, 'hmdstate':hmdstate})

77

Change Django administration password in three instances

78 of 224

serializers.py

from myapp.models import Dt, Tmp, Hmd

from rest_framework import serializers

class DtSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = Dt

fields = ('url', 'name')

class TmpSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = Tmp

fields = ('url', 'name')

class HmdSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = Hmd

fields = ('url', 'name')

78

79 of 224

index.html 0/2

<!DOCTYPE html>

<html lang="en">

<head>

<title>Weather Station</title>

<meta charset="utf-8">

<meta http-equiv="refresh" content="10">

<meta name="viewport" content="width=device width, initial-scale=1">

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

<link rel="icon" href="/favicon.ico" type="image/x-icon">

<link rel="stylesheet" href="/static/myapp/bootstrap.min.css">

<script src="/static/myapp/jquery.min.js"></script>

<script src="/static/myapp/bootstrap.min.js"></script>

<style>

#map-canvas {

width: 400px;

height: 300px;

}

</style>

79

80 of 224

index.html 1/2

<script src="https://maps.googleapis.com/maps/api/js"></script>

<script>

function initialize() {

var mapCanvas = document.getElementById('map-canvas');

var mapOptions = {

center: new google.maps.LatLng({{lat}}, {{lon}}),

zoom: 16,

mapTypeId: google.maps.MapTypeId.ROADMAP

}

var map = new google.maps.Map(mapCanvas, mapOptions)

}

google.maps.event.addDomListener(window, 'load', initialize);

</script>

</head>

80

Contribution by Piyush Rao, 2017 Fall

myCenter = new google.maps.LatLng({{lat}}, {{lon}})

var marker = new google.maps.Marker({position: myCenter,

animation: google.maps.Animation.BOUNCE});

marker.setMap(map)

var infowindow = new google.maps.InfoWindow({content: "Location"});

infowindow.open(map,marker);

?key=YOUR_API_KEY

81 of 224

index.html 2/2

<body>

<div class="container-fluid">

<center><h1>Weather Station</h1></center>

<center><h3>Stevens Institute of Technology</h3></center>

<center><h4>{{dtstate}}</h4></center>

<div class="row">

<div class="col-sm-3"><h4>Temperature</h4>

<h4>{{tmpstate|floatformat:1}}&deg;C</h4></div>

<div class="col-sm-3"><h4>Humidity</h4>

<h4>{{hmdstate|floatformat:1}}%</h4></div>

<div class="col-sm-6" style="height:300px">

<div id="map-canvas"></div></div>

</div>

</div>

</body>

</html>

81

82 of 224

Ladyada and Adafruit

Raspberry Pi

Projects

Robotics

Sensors

Wearables

Arduino

Beaglebone

82

83 of 224

CircuitPython-DHT Library

  • Install adafruit-blinka and run blinkatest.py

$ pip3 install adafruit-blinka

$ cd ~/iot/lesson4

$ python3 blinkatest.py

Hello blinka!

Digital IO ok!

I2C ok!

SPI ok!

done!

  • Install CircuitPython-DHT library and run dht_simpletest.py with pin 24

$ pip3 install adafruit-circuitpython-dht

$ sudo apt install libgpiod2

$ python3 dht_simpletest.py

Temp: 75.7 F / 24.3 C Humidity: 50.2%

Temp: 75.9 F / 24.4 C Humidity: 50.2%

Temp: 75.9 F / 24.4 C Humidity: 50.2%

Temp: 75.9 F / 24.4 C Humidity: 50.3%

Temp: 75.9 F / 24.4 C Humidity: 50.4%

Temp: 75.9 F / 24.4 C Humidity: 50.4%

Temp: 75.9 F / 24.4 C Humidity: 50.4%

83

84 of 224

controller.py 0/1

import time

import datetime

import board

import adafruit_dht

import sys

import requests

dhtDevice = adafruit_dht.DHT22(board.D24)

def runController():

now = datetime.datetime.now()

dt = now.replace(microsecond=0)

print(dt)

print('Temperature: {0:0.1f} C'.format(tmp))

print('Humidity: {0:0.1f} %'.format(hmd))

setDtState(dt)

setTmpState(tmp)

setHmdState(hmd)

84

85 of 224

controller.py 1/1

def setDtState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/dt/1/', data=values, auth=('pi', 'raspberry'))

def setTmpState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/tmp/1/', data=values, auth=('pi', 'raspberry'))

def setHmdState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/hmd/1/', data=values, auth=('pi', 'raspberry'))

while True:

try:

hmd = dhtDevice.humidity

tmp = dhtDevice.temperature

if hmd is None or tmp is None:

time.sleep(2)

continue

runController()

time.sleep(10)

except KeyboardInterrupt:

exit()

85

Change Django administration password in three instances

86 of 224

Make and Run Migrations

pi@piot4:~/weather $ python3 manage.py makemigrations myapp

Migrations for 'myapp':

myapp/migrations/0001_initial.py

- Create model Dt

- Create model Hmd

- Create model LocationData

- Create model Tmp

pi@piot4:~/weather $ python3 manage.py migrate

Operations to perform:

Apply all migrations: admin, auth, contenttypes, myapp, sessions

Running migrations:

Applying contenttypes.0001_initial... OK

Applying auth.0001_initial... OK

Applying admin.0001_initial... OK

Applying admin.0002_logentry_remove_auto_add... OK

Applying admin.0003_logentry_add_action_flag_choices... OK

Applying contenttypes.0002_remove_content_type_name... OK

Applying auth.0002_alter_permission_name_max_length... OK

Applying auth.0003_alter_user_email_max_length... OK

Applying auth.0004_alter_user_username_opts... OK

Applying auth.0005_alter_user_last_login_null... OK

Applying auth.0006_require_contenttypes_0002... OK

Applying auth.0007_alter_validators_add_error_messages... OK

Applying auth.0008_alter_user_username_max_length... OK

Applying auth.0009_alter_user_last_name_max_length... OK

Applying auth.0010_alter_group_name_max_length... OK

Applying auth.0011_update_proxy_permissions... OK

Applying auth.0012_alter_user_first_name_max_length... OK

Applying myapp.0001_initial... OK

Applying sessions.0001_initial... OK

86

87 of 224

Set Django Server Administration

Set Django administration username and password

pi@piot4:~/weather $ python3 manage.py createsuperuser

Username (leave blank to use 'pi'):

Email address: EMAIL_ADDRESS

Password: PASSWORD

Password (again): PASSWORD

Superuser created successfully.

pi@piot4:~/weather $

87

  • The default username is pi
  • Enter a valid email address
  • PASSWORD shall be at least eight characters

88 of 224

Run Server and Controller

Run Django server

pi@piot4:~/weather $ python3 manage.py runserver

At the first time, open a browser and go to http://127.0.0.1:8000/admin to add location data with Location Stevens, Latitude 40.7451, and Longitude -74.0255, click SAVE

Post the following in HTML form:

2020 to the Dt List at http://127.0.0.1:8000/dt

25 to the Tmp List at http://127.0.0.1:8000/tmp

50 to the Hmd List at http://127.0.0.1:8000/hmd

Run native controller service on a separate terminal window

pi@piot4:~/weather $ python3 controller.py

View app with a browser at http://127.0.0.1:8000/home

88

89 of 224

Django Administration Log In

89

90 of 224

Click Location Data

90

91 of 224

Add Location Data

91

92 of 224

Save Location Data

92

93 of 224

Location Data Saved

93

94 of 224

Post Date and Time

94

95 of 224

Date and Time Posted

95

96 of 224

Post Temperature Data

96

97 of 224

Temperature Data Posted

97

98 of 224

Post Humidity Data

98

99 of 224

Humidity Data Posted—Log Out

99

100 of 224

View App — Landscape

100

101 of 224

View App — Portrait

101

102 of 224

Run Controller.py

102

103 of 224

Lighting Project

Django REST Framework

104 of 224

Lighting Project and App 0/1

This Django project for lighting uses the default SQLite3 database

pi@piot4:~ $ django-admin startproject lighting

pi@piot4:~ $ cd lighting

pi@piot4:~/lighting $ ls

lighting manage.py

pi@piot4:~/lighting $ python3 manage.py startapp myapp

pi@piot4:~/lighting $ ls

lighting manage.py myapp

pi@piot4:~/lighting $ cd lighting

pi@piot4:~/lighting/lighting $ ls

__init__.py __pycache__ settings.py urls.py wsgi.py

pi@piot4:~/lighting/lighting $ nano settings.py

pi@piot4:~/lighting/lighting $ cp ~/iot/lesson4/lighting/urls.py .

pi@piot4:~/lighting/lighting $ cd ../myapp

pi@piot4:~/lighting/myapp $ ls

admin.py apps.py __init__.py migrations models.py tests.py views.py

pi@piot4:~/lighting/myapp $ cp ~/iot/lesson4/lighting/models.py .

pi@piot4:~/lighting/myapp $ cp ~/iot/lesson4/lighting/views.py .

pi@piot4:~/lighting/myapp $ nano views.py

104

  • Current directory "."
  • Parent directory ".."
  • Change the default password 'raspberry' in views.py

105 of 224

Lighting Project and App 1/1

pi@piot4:~/lighting/myapp $ cp ~/iot/*4/lighting/serializers.py .

pi@piot4:~/lighting/myapp $ mkdir static templates

pi@piot4:~/lighting/myapp $ cd static

pi@piot4:~/lighting/myapp/static $ cp ~/iot/*4/static/favicon.ico .

pi@piot4:~/lighting/myapp/static $ cd ../templates

pi@piot4:~/lighting/myapp/templates $ mkdir myapp

pi@piot4:~/lighting/myapp/templates $ cd myapp

pi@piot4:~/lighting/myapp/templates/myapp $ cp ~/iot/*4/light*/index.html .

pi@piot4:~/lighting/myapp/templates/myapp $ cd ~/lighting

pi@piot4:~/lighting $ cp ~/iot/*4/lighting/controller.py .

pi@piot4:~/lighting $

105

  • Copy the red files from ~/iot/lesson4/lighting

106 of 224

Django REST Project Directory

106

/lighting

controller.py db.sqlite3 manage.py

/lighting

__init__.py settings.py urls.py wsgi.py

/myapp

admin.py apps.py __init__.py models.py serializers.py tests.py views.py

/migrations

__init__.py

/static

favicon.ico

/templates

/myapp

index.html

  • Edit settings.py and copy the red files from ~/iot/lesson4/lighting

107 of 224

settings.py 0/1

Since settings.py generated from the command "django-admin startproject" contains SECRET_KEY and other important settings, only insert the code highlighted in bold and keep all other in settings.py intact:

...

ALLOWED_HOSTS = ['*']

...

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'myapp',

'rest_framework',

]

...

107

  • Add myapp and rest_framework
  • Blank means 'localhost' or '127.0.0.1' by default; enter '*' for all Raspberry Pi IP addresses

Contributions by Allison Butler, Isaac Hirschfeld, and Brian Voyer, 2017 Spring;

Nicholas Antonov, 2018 Spring; Matthew Bergwall, 2018 Summer

108 of 224

settings.py 1/1

...

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.sqlite3',

'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

}

}

...

TIME_ZONE = 'America/New_York'

...

108

  • Change time from UTC to America/New_York

109 of 224

urls.py

...

from django.contrib import admin

from django.urls import path, include

from django.contrib.staticfiles.storage import staticfiles_storage

from django.views.generic.base import RedirectView

from rest_framework import routers

from myapp import views

admin.autodiscover()

router = routers.DefaultRouter()

router.register('mode', views.ModeViewSet)

router.register('state', views.StateViewSet)

urlpatterns = [

path('favicon.ico', RedirectView.as_view(

url=staticfiles_storage.url('favicon.ico'),

permanent=False), name="favicon"),

path('', include(router.urls)),

path('api-auth/', include('rest_framework.urls',

namespace='rest_framework')),

path('admin/', admin.site.urls),

path('home/', views.home),

]

109

110 of 224

models.py

from django.db import models

# Create your models here.

class Mode(models.Model):

name = models.CharField(max_length=50)

class State(models.Model):

name = models.CharField(max_length=50)

110

111 of 224

views.py 0/3

from django.shortcuts import render

from myapp.models import Mode, State

from rest_framework import viewsets

from django.template import RequestContext

from myapp.serializers import ModeSerializer, StateSerializer

import requests

import json

# Create your views here.

class ModeViewSet(viewsets.ModelViewSet):

queryset = Mode.objects.all()

serializer_class = ModeSerializer

class StateViewSet(viewsets.ModelViewSet):

queryset = State.objects.all()

serializer_class = StateSerializer

111

112 of 224

views.py 1/3

def home(request):

out = ''

currentmode = 'auto'

currentstate = 'off'

if 'on' in request.POST:

values = {"name": "on"}

r = requests.put('http://127.0.0.1:8000/state/1/',

data=values, auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

out = output['name']

if 'off' in request.POST:

values = {"name": "off"}

r = requests.put('http://127.0.0.1:8000/state/1/',

data=values, auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

out = output['name']

112

Change Django administration password in six instances

113 of 224

views.py 2/3

if 'auto' in request.POST:

values = {"name": "auto"}

r = requests.put('http://127.0.0.1:8000/mode/1/',

data=values, auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

out = output['name']

if 'manual' in request.POST:

values = {"name": "manual"}

r = requests.put('http://127.0.0.1:8000/mode/1/',

data=values, auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

out = output['name']

113

114 of 224

views.py 3/3

r = requests.get('http://127.0.0.1:8000/mode/1/',

auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

currentmode = output['name']

r = requests.get('http://127.0.0.1:8000/state/1/',

auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

currentstate = output['name']

return render(request, 'myapp/index.html', {'name':out,

'currentmode':currentmode, 'currentstate':currentstate})

114

115 of 224

serializers.py

from myapp.models import Mode, State

from rest_framework import serializers

class ModeSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = Mode

fields = ('url', 'name')

class StateSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = State

fields = ('url', 'name')

115

116 of 224

index.html

<!DOCTYPE html>

<html>

<head>

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

<link rel="icon" href="/favicon.ico" type="image/x-icon">

</head>

<body>

<p>{{r}}</p>

<h3>State</h3>

<form action="" method="post">{% csrf_token %}

<input type="submit" name="on" value="on" />

<input type="submit" name="off" value="off" />

</form>

<br>

<h3>Mode</h3>

<form action="" method="post">{% csrf_token %}

<input type="submit" name="auto" value="auto" />

<input type="submit" name="manual" value="manual" />

</form>

</body>

</html>

116

csrf_token is to prevent cross-site request forgeries (CSRF)

117 of 224

controller.py 0/7

import time

import datetime

import sqlite3

import spidev

import RPi.GPIO as GPIO

# Initialize SQLite

con = sqlite3.connect('db.sqlite3')

cur = con.cursor()

# LDR channel on MCP3008

LIGHT_CHANNEL = 0

# GPIO Setup

GPIO.setmode(GPIO.BCM)

LIGHT_PIN = 25 # 1k-ohm resistor between + and GPIO-25 (P6)

117

118 of 224

controller.py 1/7

# Open SPI bus

spi = spidev.SpiDev()

spi.open(0, 0)

spi.max_speed_hz = 976000

# Light Level Threshold

threshold = 100

# Function to read LDR connected to MCP3008

def readLDR():

light_level = ReadChannel(LIGHT_CHANNEL)

if light_level == 0:

lux = 0

else:

lux = ConvertLux(light_level, 2)

return lux

# Function to convert LDR reading to Lux

def ConvertLux(data, places):

R=10 #10k-ohm resistor between LDR and 3V3

volts = (data * 3.3) / 1023

volts = round(volts, places)

lux = 500 * (3.3 - volts) / (R * volts)

return lux

118

RLDR

R=10KΩ

VIN=3.3V

GND

VLDR

VLDR=VINRLDR/(R+RLDR)

119 of 224

controller.py 2/7

# Function to read SPI data from MCP3008 chip

def ReadChannel(channel):

# Send three bytes 00000001 10000000 00000000 over SPI MOSI to MCP3008

adc = spi.xfer2([1, (8 + channel) << 4, 0])

# Extract the rightmost 10 bits from the three bytes returned

data = ((adc[1]&3) << 8) + adc[2]

return data

119

120 of 224

controller.py 3/7

# Get current mode from DB

def getCurrentMode():

cur.execute('SELECT * FROM myapp_mode')

data = cur.fetchone() # (1, u'auto')

return data[1]

# Get current state from DB

def getCurrentState():

cur.execute('SELECT * FROM myapp_state')

data = cur.fetchone() # (1, u'on')

return data[1]

# Store current state in DB

def setCurrentState(val):

query = 'UPDATE myapp_state set name = "'+val+'"'

cur.execute(query)

120

  • Uncalled function

121 of 224

controller.py 4/7

def switchOnLight(PIN):

GPIO.setup(PIN, GPIO.OUT)

GPIO.output(PIN, True)

def switchOffLight(PIN):

GPIO.setup(PIN, GPIO.OUT)

GPIO.output(PIN, False)

121

122 of 224

controller.py 5/7

def runManualMode():

# Get current state from DB

currentState = getCurrentState()

if currentState == 'on':

print('Manual - On')

switchOnLight(LIGHT_PIN)

elif currentState == 'off':

print('Manual - Off')

switchOffLight(LIGHT_PIN)

122

123 of 224

controller.py 6/7

def runAutoMode():

# Read LDR

lightlevel = readLDR()

if lightlevel < threshold:

print('Auto - On (lux=%d)' % lightlevel)

switchOnLight(LIGHT_PIN)

else:

print('Auto - Off (lux=%d)' % lightlevel)

switchOffLight(LIGHT_PIN)

123

124 of 224

controller.py 7/7

# Controller main function

def runController():

currentMode = getCurrentMode()

if currentMode == 'auto':

runAutoMode()

elif currentMode == 'manual':

runManualMode()

return True

while True:

try:

runController()

time.sleep(5)

except KeyboardInterrupt:

GPIO.cleanup()

exit()

124

125 of 224

LDR, ADC, and LED Setup

125

126 of 224

Run Web and Native Services

After the first time, skip these three steps if no changes

pi@piot4:~/lighting $ python3 manage.py makemigrations myapp

pi@piot4:~/lighting $ python3 manage.py migrate

pi@piot4:~/lighting $ python3 manage.py createsuperuser

pi@piot4:~/lighting $ ls

controller.py db.sqlite3 lighting manage.py myapp

Run Django server

pi@piot4:~/lighting $ python3 manage.py runserver

At the first time, post the following in HTML form:

auto to the mode list at http://127.0.0.1:8000/mode

off to the state list at http://127.0.0.1:8000/state

Run native controller service on a separate terminal window

pi@piot4:~/lighting $ python3 controller.py

View app with a browser at http://127.0.0.1:8000/home

126

127 of 224

Post Mode

127

128 of 224

Post Mode

128

129 of 224

Post State

129

130 of 224

Post State

130

131 of 224

App Views

131

132 of 224

Parking Project

Django REST Framework

133 of 224

Parking Project and App 0/1

First create MySQL database parking and grant all privileges to pi@localhost

pi@piot4:~ $ django-admin startproject parking

pi@piot4:~ $ cd parking

pi@piot4:~/parking $ ls

manage.py parking

pi@piot4:~/parking $ python3 manage.py startapp myapp

pi@piot4:~/parking $ ls

manage.py myapp parking

pi@piot4:~/parking $ cd parking

pi@piot4:~/parking/parking $ ls

__init__.py __pycache__ settings.py urls.py wsgi.py

pi@piot4:~/parking/parking $ nano settings.py

pi@piot4:~/parking/parking $ cp ~/iot/lesson4/parking/urls.py .

pi@piot4:~/parking/parking $ cd ../myapp

pi@piot4:~/parking/myapp $ ls

admin.py apps.py __init__.py migrations models.py tests.py views.py

pi@piot4:~/parking/myapp $ cp ~/iot/lesson4/parking/models.py .

pi@piot4:~/parking/myapp $ cp ~/iot/lesson4/parking/views.py .

pi@piot4:~/parking/myapp $ nano views.py

133

  • Change the default password 'raspberry' in views.py

134 of 224

Parking Project and App 1/1

pi@piot4:~/parking/myapp $ cp ~/iot/*4/parking/serializers.py .

pi@piot4:~/parking/myapp $ mkdir static templates

pi@piot4:~/parking/myapp $ cd static

pi@piot4:~/parking/myapp/static $ cp ~/iot/*4/static/favicon.ico .

pi@piot4:~/parking/myapp/static $ mkdir myapp

pi@piot4:~/parking/myapp/static $ cd myapp

pi@piot4:~/parking/myapp/static/myapp $ cp ~/iot/*4/static/*css .

pi@piot4:~/parking/myapp/static/myapp $ cp ~/iot/*4/static/*js .

pi@piot4:~/parking/myapp/static/myapp $ cp ~/iot/*4/static/*png .

pi@piot4:~/parking/myapp/static/myapp $ cd ~/parking/myapp/templates

pi@piot4:~/parking/myapp/templates $ mkdir myapp

pi@piot4:~/parking/myapp/templates $ cd myapp

pi@piot4:~/parking/myapp/templates/myapp $ cp ~/iot/*4/parking/index.html .

pi@piot4:~/parking/myapp/templates/myapp $ cd ~/parking

pi@piot4:~/parking $ cp ~/iot/*4/parking/controller.py .

pi@piot4:~/parking $ nano controller.py

pi@piot4:~/parking $

134

  • Change the default password 'raspberry' in controller.py

135 of 224

Django REST Project Directory

135

/parking

controller.py manage.py

/parking

__init__.py settings.py urls.py wsgi.py

/myapp

admin.py apps.py __init__.py models.py serializers.py tests.py views.py

/migrations

__init__.py

/static

favicon.ico

/myapp

*.css *.js *.png

/templates

/myapp

index.html

  • Edit settings.py and copy the red files from ~/iot/lesson4/parking

136 of 224

settings.py 0/1

Since settings.py generated from the command "django-admin startproject" contains SECRET_KEY and other important settings, only insert the code highlighted in bold and keep all other in settings.py intact:

...

ALLOWED_HOSTS = ['*']

...

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'myapp',

'rest_framework',

]

...

136

  • Add myapp and rest_framework
  • Blank means 'localhost' or '127.0.0.1' by default; enter '*' for all Raspberry Pi IP addresses

Contributions by Allison Butler, Isaac Hirschfeld, and Brian Voyer, 2017 Spring;

Nicholas Antonov, 2018 Spring; Matthew Bergwall, 2018 Summer

137 of 224

settings.py 1/1

...

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.mysql',

'NAME': 'parking',

'USER': 'pi',

'PASSWORD': 'raspberry',

'HOST': '',

'PORT': '',

'OPTIONS': {

'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",

},

}

}

...

TIME_ZONE = 'America/New_York'

...

137

  • Change time from UTC to America/New_York
  • Change database from sqlite3 to mysql
  • Change password
  • Copy and paste from ~/iot/lesson4/stevens/settings.txt

138 of 224

urls.py

...

from django.contrib import admin

from django.urls import path, include

from django.contrib.staticfiles.storage import staticfiles_storage

from django.views.generic.base import RedirectView

from rest_framework import routers

from myapp import views

admin.autodiscover()

router = routers.DefaultRouter()

router.register('state', views.StateViewSet)

urlpatterns = [

path('favicon.ico', RedirectView.as_view(

url=staticfiles_storage.url('favicon.ico'),

permanent=False), name="favicon"),

path('', include(router.urls)),

path('api-auth/', include('rest_framework.urls',

namespace='rest_framework')),

path('admin/', admin.site.urls),

path('home/', views.home),

]

138

139 of 224

models.py

from django.db import models

# Create your models here.

class State(models.Model):

name = models.CharField(max_length=50)

139

140 of 224

views.py 0/1

from django.shortcuts import render

from myapp.models import State

from rest_framework import viewsets

from django.template import RequestContext

from myapp.serializers import StateSerializer

import requests

import json

# Create your views here.

class StateViewSet(viewsets.ModelViewSet):

queryset = State.objects.all()

serializer_class = StateSerializer

140

141 of 224

views.py 1/1

def home(request):

currentstate = 'empty'

r = requests.get('http://127.0.0.1:8000/state/1/',

auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

currentstate = output['name']

if currentstate == 'empty':

occupiedCount = 0

emptyCount = 1

else:

occupiedCount = 1

emptyCount = 0

return render(request, 'myapp/index.html',

{'currentstate': currentstate,

'occupiedCount': occupiedCount,

'emptyCount': emptyCount})

141

Change Django administration password

142 of 224

serializers.py

from myapp.models import State

from rest_framework import serializers

class StateSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = State

fields = ('url', 'name')

142

143 of 224

index.html 0/2

<html>

<head>

<meta charset="utf-8">

<meta http-equiv="refresh" content="3">

<title>Smart Parking App</title>

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

<link rel="icon" href="/favicon.ico" type="image/x-icon">

<link rel="stylesheet" href="/static/myapp/bootstrap.min.css">

<script src="/static/myapp/jquery.min.js"></script>

<script src="/static/myapp/script.js"></script>

</head>

<body>

<div class="app-container">

<header class="app-header clearfix">

<h1 class="app-logo js-app-title icon-home">Smart Parking Dashboard</h1>

<div class="app-state"><span class="app-loading-loader">

</span></div>

<h4>Parking Lot #: 123<br>

Empty: {{emptyCount}}<br>

Occupied: {{occupiedCount}}</h4>

</header>

143

  • Refresh every 3 seconds

144 of 224

index.html 1/2

<div role="main" class="app-content clearfix">

<div class="app-loading"><span class="app-loading-loader">

</span></div>

<div class="app-content-inner">

<form class="dashboard-control js-form clearfix">

<fieldset>

<div class="field clearfix">

<table width = "90%" border="1">

<tr>

<td width="100%"> 1

<center>

{% if currentstate == 'empty' %}

<img src="/static/myapp/g.png">

{% else %}

<img src="/static/myapp/r.png">

{% endif %}

</center>

<br>

</td>

</tr>

</table>

144

145 of 224

index.html 2/2

</div>

</fieldset>

</form>

</div></div></div>

</body>

</html>

145

146 of 224

Static Files

146

147 of 224

controller.py 0/2

import RPi.GPIO as GPIO

import time

import sys

import requests

GPIO.setmode(GPIO.BCM)

SENSOR_PIN = 17 # 1k-ohm resistor between Echo and GPIO-17 (P0)

TRIGGER_PIN = 18 # connect Trigger to GPIO-18 (P1)

threshold = 10 # cm

147

148 of 224

controller.py 1/2

def readUltrasonicSensor():

GPIO.setup(TRIGGER_PIN, GPIO.OUT)

GPIO.setup(SENSOR_PIN, GPIO.IN)

GPIO.output(TRIGGER_PIN, GPIO.LOW)

time.sleep(0.3)

GPIO.output(TRIGGER_PIN, True)

time.sleep(0.00001)

GPIO.output(TRIGGER_PIN, False)

while GPIO.input(SENSOR_PIN) == 0:

signaloff = time.time()

while GPIO.input(SENSOR_PIN) == 1:

signalon = time.time()

timepassed = signalon - signaloff

distance = timepassed * 17000 # speed of sound = 34000 cm/s

if distance < threshold:

return 1

else:

return 0

148

149 of 224

controller.py 2/2

def runController():

pinState = readUltrasonicSensor()

if pinState == 1:

print('Occupied')

setCurrentState('occupied')

else:

print('Empty')

setCurrentState('empty')

def setCurrentState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/state/1/', data=values,

auth=('pi', 'raspberry'))

while True:

try:

runController()

time.sleep(1)

except KeyboardInterrupt:

GPIO.cleanup()

exit()

149

Change password (or 127.0.0.1 with host IP address)

150 of 224

Ultrasonic Sensor Setup

150

5V

GND

Echo

Trigger

151 of 224

Run Web and Native Services

After the first time, skip these three steps if no changes

pi@piot4:~/parking $ python3 manage.py makemigrations myapp

pi@piot4:~/parking $ python3 manage.py migrate

pi@piot4:~/parking $ python3 manage.py createsuperuser

Run Django server

pi@piot4:~/parking $ python3 manage.py runserver

At the first time, post the following in HTML form:

empty to the state list at http://127.0.0.1:8000/state

Run native controller service on a separate terminal window

pi@piot4:~/parking $ python3 controller.py

View app with a browser at http://127.0.0.1:8000/home

151

152 of 224

Post State

152

153 of 224

Post State

153

154 of 224

App Views − Empty

154

155 of 224

App Views − Occupied

155

156 of 224

Sensing Project

Django REST Framework

157 of 224

Sensing Project and App 0/1

First create MySQL database sensing and grant all privileges to pi@localhost

pi@piot4:~ $ django-admin startproject sensing

pi@piot4:~ $ cd sensing

pi@piot4:~/sensing $ ls

manage.py sensing

pi@piot4:~/sensing $ python3 manage.py startapp myapp

pi@piot4:~/sensing $ ls

manage.py myapp sensing

pi@piot4:~/sensing $ cd sensing

pi@piot4:~/sensing/sensing $ ls

__init__.py __pycache__ settings.py urls.py wsgi.py

pi@piot4:~/sensing/sensing $ nano settings.py

pi@piot4:~/sensing/sensing $ cp ~/iot/lesson4/sensing/urls.py .

pi@piot4:~/sensing/sensing $ cd ../myapp

pi@piot4:~/sensing/myapp $ ls

admin.py apps.py __init__.py migrations models.py tests.py views.py

pi@piot4:~/sensing/myapp $ cp ~/iot/lesson4/sensing/models.py .

pi@piot4:~/sensing/myapp $ cp ~/iot/lesson4/sensing/views.py .

pi@piot4:~/sensing/myapp $ nano views.py

157

  • Change the default password 'raspberry' in views.py

158 of 224

Sensing Project and App 1/1

pi@piot4:~/sensing/myapp $ cp ~/iot/*4/sensing/serializers.py .

pi@piot4:~/sensing/myapp $ mkdir static templates

pi@piot4:~/sensing/myapp $ cd static

pi@piot4:~/sensing/myapp/static $ cp ~/iot/*4/static/favicon.ico .

pi@piot4:~/sensing/myapp/static $ mkdir myapp

pi@piot4:~/sensing/myapp/static $ cd myapp

pi@piot4:~/sensing/myapp/static/myapp $ cp ~/iot/*4/static/*css .

pi@piot4:~/sensing/myapp/static/myapp $ cp ~/iot/*4/static/*js .

pi@piot4:~/sensing/myapp/static/myapp $ cp ~/iot/*4/static/*png .

pi@piot4:~/sensing/myapp/static/myapp $ cd ~/sensing/myapp/templates

pi@piot4:~/sensing/myapp/templates $ mkdir myapp

pi@piot4:~/sensing/myapp/templates $ cd myapp

pi@piot4:~/sensing/myapp/templates/myapp $ cp ~/iot/*4/sensing/index.html .

pi@piot4:~/sensing/myapp/templates/myapp $ cd ~/sensing

pi@piot4:~/sensing $ cp ~/iot/*4/sensing/controller.py .

pi@piot4:~/sensing $ nano controller.py

pi@piot4:~/sensing $

158

  • Change the default password 'raspberry' in controller.py

159 of 224

Django REST Project Directory

159

/sensing

controller.py manage.py

/sensing

__init__.py settings.py urls.py wsgi.py

/myapp

admin.py __init__.py models.py serializers.py tests.py views.py

/migrations

__init__.py

/static

favicon.ico

/myapp

*.css *.js *.png

/templates

/myapp

index.html

  • Edit settings.py and copy the red files from ~/iot/lesson4/sensing

160 of 224

settings.py 0/1

Since settings.py generated from the command "django-admin startproject" contains SECRET_KEY and other important settings, only insert the code highlighted in bold and keep all other in settings.py intact:

...

ALLOWED_HOSTS = ['*']

...

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'myapp',

'rest_framework',

]

...

160

  • Add myapp and rest_framework
  • Blank means 'localhost' or '127.0.0.1' by default; enter '*' for all Raspberry Pi IP addresses

Contributions by Allison Butler, Isaac Hirschfeld, and Brian Voyer, 2017 Spring;

Nicholas Antonov, 2018 Spring; Matthew Bergwall, 2018 Summer

161 of 224

settings.py 1/1

...

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.mysql',

'NAME': 'sensing',

'USER': 'pi',

'PASSWORD': 'raspberry',

'HOST': '',

'PORT': '',

'OPTIONS': {

'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",

},

}

}

...

TIME_ZONE = 'America/New_York'

...

161

  • Change time from UTC to America/New_York
  • Change database from sqlite3 to mysql
  • Change password
  • Copy and paste from ~/iot/lesson4/stevens/settings.txt

162 of 224

urls.py

...

from django.contrib import admin

from django.urls import path, include

from django.contrib.staticfiles.storage import staticfiles_storage

from django.views.generic.base import RedirectView

from rest_framework import routers

from myapp import views

admin.autodiscover()

router = routers.DefaultRouter()

router.register('room', views.RoomViewSet)

router.register('door', views.DoorViewSet)

urlpatterns = [

path('favicon.ico', RedirectView.as_view(

url=staticfiles_storage.url('favicon.ico'),

permanent=False), name="favicon"),

path('', include(router.urls)),

path('api-auth/', include('rest_framework.urls',

namespace='rest_framework')),

path('admin/', admin.site.urls),

path('home/', views.home),

]

162

163 of 224

models.py

from django.db import models

# Create your models here.

class Room(models.Model):

name = models.CharField(max_length=50)

class Door(models.Model):

name = models.CharField(max_length=50)

163

164 of 224

views.py 0/1

from django.shortcuts import render

from myapp.models import Room, Door

from rest_framework import viewsets

from django.template import RequestContext

from myapp.serializers import RoomSerializer, DoorSerializer

import requests

import json

# Create your views here.

class RoomViewSet(viewsets.ModelViewSet):

queryset = Room.objects.all()

serializer_class = RoomSerializer

class DoorViewSet(viewsets.ModelViewSet):

queryset = Door.objects.all()

serializer_class = DoorSerializer

164

165 of 224

views.py 1/1

def home(request):

roomstate = 'no'

r = requests.get('http://127.0.0.1:8000/room/1/', auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

roomstate = output['name']

doorstate = 'closed'

r = requests.get('http://127.0.0.1:8000/door/1/', auth=('pi', 'raspberry'))

result = r.text

output = json.loads(result)

doorstate = output['name']

return render(request, 'myapp/index.html',

{'roomstate':roomstate, 'doorstate':doorstate})

165

Change Django administration password in two instances

166 of 224

serializers.py

from myapp.models import Room, Door

from rest_framework import serializers

class RoomSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = Room

fields = ('url', 'name')

class DoorSerializer(serializers.HyperlinkedModelSerializer):

class Meta:

model = Door

fields = ('url', 'name')

166

167 of 224

index.html 0/1

<!DOCTYPE html>

<html lang=en>

<head>

<title>Smart Sensing App</title>

<meta charset="utf-8">

<meta http-equiv="refresh" content="3">

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

<link rel="icon" href="/favicon.ico" type="image/x-icon">

<link rel="stylesheet" href="/static/myapp/bootstrap.min.css">

<script src="/static/myapp/jquery.min.js"></script>

<script src="/static/myapp/bootstrap.min.js"></script>

</head>

<body>

<div class="container-fluid">

<h1>Room</h1>

<div class="row">

<center>

{% if roomstate == 'no' %}

<img src="/static/myapp/g.png">

{% else %}

<img src="/static/myapp/r.png">

167

  • Refresh every 3 seconds

168 of 224

index.html 1/1

{% endif %}

</center>

</div>

<br>

<h1>Door</h1>

<div class="row">

<center>

{% if doorstate == 'closed' %}

<img src="/static/myapp/g.png">

{% else %}

<img src="/static/myapp/r.png">

{% endif %}

</center>

</div>

</div>

</body>

</html>

168

169 of 224

controller.py 0/2

import RPi.GPIO as GPIO

import time

import sys

import requests

GPIO.setmode(GPIO.BCM)

ROOM_SENSOR_PIN = 27

DOOR_SENSOR_PIN = 23

GPIO.setup(ROOM_SENSOR_PIN, GPIO.IN)

GPIO.setup(DOOR_SENSOR_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def readingRoomSensor():

if GPIO.input(ROOM_SENSOR_PIN):

print('motion detected')

return 1

else:

return 0

169

170 of 224

controller.py 1/2

def readingDoorSensor():

if GPIO.input(DOOR_SENSOR_PIN):

print('door opened')

return 1

else:

return 0

def runController():

roomState = readingRoomSensor()

if roomState == 1:

setRoomState('yes')

else:

setRoomState('no')

doorState = readingDoorSensor()

if doorState == 1:

setDoorState('open')

else:

setDoorState('closed')

170

171 of 224

controller.py 2/2

def setRoomState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/room/1/', data=values, auth=('pi', 'PASSWORD'))

def setDoorState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/door/1/', data=values, auth=('pi', 'PASSWORD'))

while True:

try:

runController()

time.sleep(3)

except KeyboardInterrupt:

GPIO.cleanup()

exit()

171

Change password (or 127.0.0.1 with host IP address) in two instances

172 of 224

Motion and Door Sensors Setup

172

Door Sensor

Motion Sensor

173 of 224

Run Web and Native Services

After the first time, skip these three steps if no changes

pi@piot4:~/sensing $ python3 manage.py makemigrations myapp

pi@piot4:~/sensing $ python3 manage.py migrate

pi@piot4:~/sensing $ python3 manage.py createsuperuser

Run Django server

pi@piot4:~/sensing $ python3 manage.py runserver

At the first time, post the following in HTML form:

no to the room list at http://127.0.0.1:8000/room

closed to the door list at http://127.0.0.1:8000/door

Run native controller service on a separate terminal window

pi@piot4:~/sensing $ python3 controller.py

View app with a browser at http://127.0.0.1:8000/home

173

174 of 224

Post Room

174

175 of 224

Post Room

175

176 of 224

Post Door

176

177 of 224

Post Door

177

178 of 224

App View

178

179 of 224

Motion Detected

179

180 of 224

Door Opened

180

181 of 224

Motion Detected and Door Opened

181

182 of 224

Flask

Alexa Skill

183 of 224

Flask with Jinja2 and Werkzeug

The Pocoo Team led by Georg Brandl (Germany) and Armin Ronacher (Austria) have worked on various Python libraries and applications since 2004

  • The name “Pocoo” bears no meaning and is now also the name of the logo owl

Armin Ronacher (nickname mitsuhiko) has led the following projects under the three-clause BSD license

  • Jinja template engine since 2006
  • Werkzeug WSGI (Web Server Gateway Interface) utility library since 2007
  • Flask microframework since 2010

183

184 of 224

Torii

  • A torii (bird abode) is a traditional Japanese gate most commonly found at the entrance of or within a Shinto shrine, where it symbolically marks the transition from the mundane to the sacred
  • The torii in Mont Saint-Michel commemorated 10 years of friendship between Mont Saint Michel and Itsukushima shrine in Miyajima, Hiroshima, Japan

184

185 of 224

Flask Example 0/1

Raspberry Pi Python has Flask (tutorial, adding a favicon) web development framework based on Jinja2 template engine and Werkzeug WSGI toolkit

pi@piot4:~/iot/lesson4 $ cat hello_world.py

from flask import Flask

from flask import send_from_directory

import os

app = Flask(__name__)

@app.route("/")

def hello():

return "Hello World!"

@app.route('/favicon.ico')

def favicon():

return send_from_directory(os.path.join(app.root_path, 'static'),

'favicon.ico',

mimetype='image/vnd.microsoft.icon')

if __name__ == "__main__":

app.run()

185

Contribution by Meng Cao, 2015 Spring

186 of 224

Flask Example 1/1

pi@piot4:~/iot/lesson4 $ python3 hello_world.py

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)�127.0.0.1 - - [22/Mar/2018 11:50:29] "GET / HTTP/1.1" 200 -�127.0.0.1 - - [22/Mar/2018 11:50:30] "GET /favicon.ico HTTP/1.1" 200 -

^C

pi@piot4:~/iot/lesson4 $

186

Contribution by Meng Cao, 2015 Spring

187 of 224

Flask-Ask

Flask-Ask is a Flask extension of rapid Alexa skills kit (ASK) development for Amazon Echo devices; quickstart to run memory_game.py with templates.yaml and ngrok tunneling service, then add, configure, and test a new Alexa skill of Memory Game that asks to repeat three numbers backwards

pi@piot4:~ $ sudo pip3 install -U flask-ask

pi@piot4:~ $ sudo pip3 install 'cryptography<2.2'

pi@piot4:~ $ cd iot/lesson4

pi@piot4:~/iot/lesson4 $ cat templates.yaml

welcome: Welcome to memory game. I'm going to say three numbers for you to repeat backwards. Ready?

round: Can you repeat the numbers {{ numbers|join(", ") }} backwards?

win: Good job!

lose: Sorry, that's the wrong answer.

pi@piot4:~/iot/lesson4 $ python3 memory_game.py

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

* Restarting with stat

* Debugger is active!

* Debugger pin code: 409-630-116

187

Contributions by Khalid Alnuaim and Mutni Alshammari, 2018 Spring

188 of 224

Run Ngrok on Another Terminal

Ngrok is secure tunneling service that makes a device available from anywhere online

Grok was coined by Robert A. Heinlein 1907—1988 in Stranger in a Strange Land (1961) and has been included in the Jargon File for computer programmers

pi@piot4:~ $ sudo wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip

pi@piot4:~ $ sudo unzip ngrok-stable-linux-arm.zip

pi@piot4:~ $ ./ngrok http 5000

ngrok by @inconshreveable (Ctrl+C to quit)� �Session Status online �Session Expires 7 hours, 59 minutes �Version 2.3.35 �Region United States (us) �Web Interface http://127.0.0.1:4040 �Forwarding http://3058ee9157f9.ngrok.io -> localhost:5000 �Forwarding https://3058ee9157f9.ngrok.io -> localhost:5000 � �Connections ttl opn rt1 rt5 p50 p90 � 0 0 0.00 0.00 0.00 0.00

188

189 of 224

Create Alexa Skill

  • On a laptop, open a browser and sign in Alexa Developer Console
  • Click "Create Skill" > Enter Skill name in title case: Memory Game > 1. Choose a model: Custom (SELECTED) > 1. Choose a host: Provision your own (SELECTED) > Click "Create Skill" > Choose a template: Hello World Skill > Click "Choose"
  • Invocation > Enter Skill Invocation Name in lowercase: memory game
  • Interaction Model >
    • JSON Editor > Replace all intent schema and sample utterances with iot/lesson4/memory_game.json

AMAZON.StopIntent, one of the Standard Built-in Intents, is required*

    • Save Model > Build Model
  • Endpoint >
    • Service Endpoint Type: HTTPS
    • Default Region: https://xxxxxxxxxxxx.ngrok.io
    • Select "My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority"
    • Save Endpoints

189

  • Copy from Ngrok

190 of 224

Test Alexa Skill

Click "Test" tab > Enable skill testing from "Off" to "Development"

  • Alternatively, login Echosim.io to use Alexa Skill Testing Tool

Type or click and hold the microphone button to speak

  • "memory game"

Alexa: "Welcome to memory game. I'm going to say three numbers for you to repeat backwards. Ready?"

  • "yes"

Alexa: "Can you repeat the numbers 0, 9, 6 backwards?"

  • "six nine zero"

Alexa: "Good job!"

190

191 of 224

Alexa Simulator

191

192 of 224

Apache

WordPress

193 of 224

Apache Software Foundation (ASF)

193

194 of 224

Apache

  • Apache are culturally related Native American tribes, and have traditionally lived in Eastern Arizona, Northern Mexico (Sonora and Chihuahua), New Mexico, West Texas, and Southern Colorado
  • Other tribes near the Four Corners of Colorado, Utah, Arizona, and New Mexico include Navajo, Hopi, Ute, and Zuni
  • Apache is the exonym (or xenonym) in a Spanish transliteration of Zuñi ápachu "enemy" for the Navajo
  • Aaron Carapella's 2013 Tribal Nations Map has Ndeh as the endonym (or autonym)
  • Apache are well-known for their superior skills in warfare strategy and their inexhaustible endurance

194

195 of 224

Four Color Theorem

  • Every planar graph is four-colorable
  • The nodes of every planar graph (with no lines cross each other) can be colored with at most four colors so that no two adjacent vertices receive the same color
  • Francis Guthrie 1831—1899 posed the conjecture in 1852 while trying to color the map of counties in England
  • Kenneth Appel 1932—2013 and Wolfgang Haken proved the theorem using computer in 1976
  • Georges Gonthier proved the theorem with proof assistant software tool Coq in 2005

195

196 of 224

Apache Web Server 0/2

pi@piot4:~ $ sudo apt update

pi@piot4:~ $ sudo apt install apache2

pi@piot4:~ $ sudo service apache2 restart

196

197 of 224

Apache Web Server 1/2

pi@piot4:~ $ sudo apt install php7.3

pi@piot4:~ $ cd /var/www/html

pi@piot4:/var/www/html $ ls

index.html

pi@piot4:/var/www/html $ sudo mv index.html index.html.bak

pi@piot4:/var/www/html $ sudo cp ~/iot/lesson4/index.php .

pi@piot4:/var/www/html $ cat index.php

<?php

echo "Hello world!";

echo "<br>";

echo date('Y-m-d H:i:s');

echo "<br>";

phpinfo();

?>

pi@piot4:/var/www/html $ cd

pi@piot4:~ $

197

  • /var directory contains variable data files
  • PHP (Hypertext Preprocessor) is a server-side scripting language designed for web development but also used as a general-purpose programming language
  • Originally created by Rasmus Lerdorf in 1994, the PHP reference implementation is now produced by The PHP Group

198 of 224

Apache Web Server 2/2

pi@piot4 ~ $ sudo service apache2 restart

pi@piot4 ~ $

198

199 of 224

WordPress — Download

Build a Linux-Apache-MySQL-PHP (LAMP) web server with WordPress

pi@piot4:~ $ sudo apt install php7.3-mysql

pi@piot4:~ $ cd /var/www/html

pi@piot4:/var/www/html $ sudo chown pi: .

pi@piot4:/var/www/html $ mv index.php index.php.bak

pi@piot4:/var/www/html $ wget https://wordpress.org/latest.tar.gz

pi@piot4:/var/www/html $ tar xzf latest.tar.gz

pi@piot4:/var/www/html $ mv wordpress/* .

pi@piot4:/var/www/html $ rm -rf wordpress latest.tar.gz

pi@piot4:/var/www/html $ cd

pi@piot4:~ $ sudo service apache2 restart

pi@piot4:~ $

199

  • chown: change ownership
  • tar: tape archive(r)

-x: extract

-z: gzipped archive

-f: get from a file, not a tape

Contribution by Xingyuan Guo, 2018 Fall

200 of 224

WordPress — Get Started

200

201 of 224

WordPress — Database

201

pi

202 of 224

WordPress — Copy Configuration

202

203 of 224

WordPress — Paste Configuration

pi@piot4:~ $ cd /var/www/html

pi@piot4:/var/www/html $ ls

index.html.bak wp-admin wp-includes wp-signup.php

index.php wp-blog-header.php wp-links-opml.php wp-trackback.php

index.php.bak wp-comments-post.php wp-load.php xmlrpc.php

license.txt wp-config-sample.php wp-login.php

readme.html wp-content wp-mail.php

wp-activate.php wp-cron.php wp-settings.php

Create the wp-config.php file

pi@piot4:/var/www/html $ nano wp-config.php

Paste the text and save the wp-config.php file

pi@piot4:/var/www/html $ ls

index.html.bak wp-admin wp-cron.php wp-settings.php

index.php wp-blog-header.php wp-includes wp-signup.php

index.php.bak wp-comments-post.php wp-links-opml.php wp-trackback.php

license.txt wp-config.php wp-load.php xmlrpc.php

readme.html wp-config-sample.php wp-login.php

wp-activate.php wp-content wp-mail.php

pi@piot4:/var/www/html $

203

204 of 224

WordPress — Installation

204

205 of 224

WordPress — Success

205

206 of 224

WordPress — Log In

206

207 of 224

WordPress — Dashboard

207

208 of 224

WordPress — Posts

208

209 of 224

WordPress — Themes

209

210 of 224

WordPress — 2020 Default Theme

210

211 of 224

WordPress — Widgets

211

212 of 224

WordPress — Menus

212

213 of 224

WordPress — Visit Site

213

214 of 224

WordPress — Log Out

214

215 of 224

Drupal

215

216 of 224

Wix

216

217 of 224

Mobirise

217

218 of 224

Lesson 4 Summary

  • Django is a model-template-view framework
    • Installed at laptop or remote server instances on Amazon Elastic Compute Cloud (EC2), Google Compute Engine, Microsoft Azure, etc.
  • Django uses SQLite3 by default
    • Supports relational database engines such as MySQL
    • Install non-relational database engines such as mongodb-engine for MongoDB
  • Django uses port 8000 by default, e.g.,
    • View http://127.0.0.1:8000/home
    • Admin at http://127.0.0.1:8000/admin
  • Flask is another web development framework for Python based on Jinja2 template engine and Werkzeug WSGI toolkit
  • Alternatives include Apache and WordPress for Linux-Apache-MySQL-PHP (LAMP) web servers

218

219 of 224

controller.py 0/1

import time

import datetime

import Adafruit_DHT

import sys

import requests

DHT_TYPE = Adafruit_DHT.DHT22

DHT_PIN = 24

def runController():

now = datetime.datetime.now()

dt = now.replace(microsecond=0)

print(dt)

print('Temperature: {0:0.1f} C'.format(tmp))

print('Humidity: {0:0.1f} %'.format(hmd))

setDtState(dt)

setTmpState(tmp)

setHmdState(hmd)

219

220 of 224

controller.py 1/1

def setDtState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/dt/1/', data=values, auth=('pi', 'raspberry'))

def setTmpState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/tmp/1/', data=values, auth=('pi', 'raspberry'))

def setHmdState(val):

values = {'name': val}

r = requests.put('http://127.0.0.1:8000/hmd/1/', data=values, auth=('pi', 'raspberry'))

while True:

try:

hmd, tmp = Adafruit_DHT.read(DHT_TYPE, DHT_PIN)

if hmd is None or tmp is None:

time.sleep(2)

continue

runController()

time.sleep(10)

except KeyboardInterrupt:

exit()

220

Change Django administration password in three instances

221 of 224

Adafruit Python Libraries

Install Adafruit_Python_DHT library on Raspberry Pi

pi@piot4:~ $ git clone https://github.com/adafruit/Adafruit_Python_DHT.git

pi@piot4:~ $ cd *DHT

pi@piot4:~/Adafruit_Python_DHT $ sudo python3 setup.py install

pi@piot4:~/Adafruit_Python_DHT $ cd

Install Adafruit_Python_BMP library on Raspberry Pi

pi@piot4:~ $ git clone https://github.com/adafruit/Adafruit_Python_BMP.git

pi@piot4:~ $ cd *BMP

pi@piot4:~/Adafruit_Python_BMP $ sudo python3 setup.py install

pi@piot4:~/Adafruit_Python_BMP $ cd

Install Adafruit_Python_ADXL345 library on Raspberry Pi

pi@piot4:~ $ git clone https://github.com/adafruit/Adafruit_Python_ADXL345.git

pi@piot4:~ $ cd *ADXL*

pi@piot4:~/Adafruit_Python_ADXK345 $ sudo python3 setup.py install

pi@piot4:~/Adafruit_Python_ADXL345 $ cd

pi@piot4:~ $

221

222 of 224

DHT Test

Run DHT simple test

pi@piot4:~ $ cd *DHT/ex*

pi@piot4:~/Adafruit_Python_DHT/examples $ ls

AdafruitDHT.py google_spreadsheet.py simpletest.py

pi@piot4:~/Adafruit_Python_DHT/examples $ cd

pi@piot4:~ $ cd demo

pi@piot4:~/demo $ cp ~/*DHT/ex*/simple* .

pi@piot4:~/demo $ nano simpletest.py

Comment out Beaglebone Black pin = 'P8_11'

Uncomment Raspberry Pi pin, and change it to pin = 24

control-x y enter

pi@piot4:~/demo $ python3 simpletest.py

Temp=19.4*C Humidity=41.4%

pi@piot4:~/demo $

222

223 of 224

BMP Test

Run BMP simple test

pi@piot4:~ $ cd *BMP/ex*

pi@piot4:~/Adafruit_Python_BMP/examples $ ls

google_spreadsheet.py simpletest.py

pi@piot4:~/Adafruit_Python_BMP/examples $ python3 simpletest.py

Temp = 19.40 *C

Pressure = 100193.00 Pa

Altitude = 95.18 m

Sealevel Pressure = 100189.00 Pa

pi@piot4:~/Adafruit_Python_BMP/examples $

223

224 of 224

ADXL345 Test

Run ADXL345 simple test

pi@piot4:~ $ cd *ADXL345/ex*

pi@piot4:~/Adafruit_Python_ADXL345/examples $ ls

simpletest.py

pi@piot4:~/Adafruit_Python_ADXL345/examples $ python3 simpletest.py

Printing X, Y, Z axis values, press Ctrl-C to quit...

X=0, Y=0, Z=0

X=-28, Y=127, Z=208

X=-27, Y=126, Z=207

X=-28, Y=127, Z=207

X=-27, Y=127, Z=207

X=-27, Y=126, Z=208

X=-27, Y=127, Z=206

^C

pi@piot4:~/Adafruit_Python_ADXL345/examples $

224