diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2023-06-27 14:50:20 +0200 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2023-06-27 14:50:20 +0200 |
| commit | 8697555125c57ae64a0c9b78514b4aac4fd523de (patch) | |
| tree | a699df53a7c35a4425f30bca86982c4341f6de40 /content/posts/2017-08-11-simple-iot-application.md | |
| parent | 33b2615a5038bc85036081e8b5e0da8584d88097 (diff) | |
| download | mitjafelicijan.com-8697555125c57ae64a0c9b78514b4aac4fd523de.tar.gz | |
Massive formatting and added figcaption
Diffstat (limited to 'content/posts/2017-08-11-simple-iot-application.md')
| -rw-r--r-- | content/posts/2017-08-11-simple-iot-application.md | 308 |
1 files changed, 157 insertions, 151 deletions
diff --git a/content/posts/2017-08-11-simple-iot-application.md b/content/posts/2017-08-11-simple-iot-application.md index a0ac4be..e7e086b 100644 --- a/content/posts/2017-08-11-simple-iot-application.md +++ b/content/posts/2017-08-11-simple-iot-application.md | |||
| @@ -7,34 +7,35 @@ draft: false | |||
| 7 | 7 | ||
| 8 | ## Initial thoughts | 8 | ## Initial thoughts |
| 9 | 9 | ||
| 10 | I have been developing these kind of application for the better part of my last | 10 | I have been developing these kind of application for the better part of my last |
| 11 | 5 years and people keep asking me how to approach developing such application | 11 | 5 years and people keep asking me how to approach developing such application |
| 12 | and I will give a try explaining it here. | 12 | and I will give a try explaining it here. |
| 13 | 13 | ||
| 14 | IOT applications are really no different than any other kind of applications. | 14 | IOT applications are really no different than any other kind of applications. |
| 15 | We have data that needs to be collected and visualized in some form of tables | 15 | We have data that needs to be collected and visualized in some form of tables or |
| 16 | or charts. The main difference here is that most of the times these data is | 16 | charts. The main difference here is that most of the times these data is |
| 17 | collected by some kind of device foreign to developer that mainly operates in | 17 | collected by some kind of device foreign to developer that mainly operates in |
| 18 | web domain. But fear not, it's not that different than writing some JavaScript. | 18 | web domain. But fear not, it's not that different than writing some JavaScript. |
| 19 | 19 | ||
| 20 | There are many devices able to transmit data via wireless or wired network by | 20 | There are many devices able to transmit data via wireless or wired network by |
| 21 | default but for the sake of example we will be using commonly known Arduino | 21 | default but for the sake of example we will be using commonly known Arduino with |
| 22 | with wireless module already on the board → [Arduino MKR1000](https://store.arduino.cc/arduino-mkr1000). | 22 | wireless module already on the board → [Arduino |
| 23 | 23 | MKR1000](https://store.arduino.cc/arduino-mkr1000). | |
| 24 | In order to make this little project as accessible to others as possible I will | 24 | |
| 25 | try to make it as inexpensive as possible. And by this I mean that I will avoid | 25 | In order to make this little project as accessible to others as possible I will |
| 26 | using hosted virtual servers and will be using my own laptop as a server. But | 26 | try to make it as inexpensive as possible. And by this I mean that I will avoid |
| 27 | you must buy Arduino MKR1000 to follow steps below. But if you would want to | 27 | using hosted virtual servers and will be using my own laptop as a server. But |
| 28 | deploy this software I would suggest using | 28 | you must buy Arduino MKR1000 to follow steps below. But if you would want to |
| 29 | [DigitalOcean](https://www.digitalocean.com) → smallest VPS is only per month | 29 | deploy this software I would suggest using |
| 30 | making this one of the most affordable option out there. Please notice that | 30 | [DigitalOcean](https://www.digitalocean.com) → smallest VPS is only per month |
| 31 | this software will not run on stock web hosting that only supports LAMP | 31 | making this one of the most affordable option out there. Please notice that this |
| 32 | (Linux, Apache, MySQL, and PHP). | 32 | software will not run on stock web hosting that only supports LAMP (Linux, |
| 33 | 33 | Apache, MySQL, and PHP). | |
| 34 | But before we begin please take notice that this is strictly experimental code | 34 | |
| 35 | and not well optimized and there are much better ways in handling some aspects | 35 | But before we begin please take notice that this is strictly experimental code |
| 36 | of the application but that requires much deeper knowledge of technology that | 36 | and not well optimized and there are much better ways in handling some aspects |
| 37 | is not needed for an example like this. | 37 | of the application but that requires much deeper knowledge of technology that is |
| 38 | not needed for an example like this. | ||
| 38 | 39 | ||
| 39 | **Development steps** | 40 | **Development steps** |
| 40 | 41 | ||
| @@ -42,32 +43,34 @@ is not needed for an example like this. | |||
| 42 | 2. Prototype C++ code that will read "sensor data" and transmit it to API. | 43 | 2. Prototype C++ code that will read "sensor data" and transmit it to API. |
| 43 | 3. Data visualization with charts → extends Python web application. | 44 | 3. Data visualization with charts → extends Python web application. |
| 44 | 45 | ||
| 45 | Step 1. and 3. will share the same web application. One route will be dedicated | 46 | Step 1. and 3. will share the same web application. One route will be dedicated |
| 46 | to API and another to serving HTML with chart. | 47 | to API and another to serving HTML with chart. |
| 47 | 48 | ||
| 48 | Schema below represents what we will try to achieve and how different parts | 49 | Schema below represents what we will try to achieve and how different parts |
| 49 | correlates to each other. | 50 | correlates to each other. |
| 50 | 51 | ||
| 51 |  | 52 |  |
| 52 | 53 | ||
| 53 | ## Simple Python API | 54 | ## Simple Python API |
| 54 | 55 | ||
| 55 | I have always been a fan of simplicity so we will be using [Bottle: Python Web Framework](https://bottlepy.org/docs/dev/). | 56 | I have always been a fan of simplicity so we will be using [Bottle: Python Web |
| 56 | It is a single file web framework that seriously simplifies working with routes, | 57 | Framework](https://bottlepy.org/docs/dev/). It is a single file web framework |
| 57 | templating and has built-in web server that satisfies our need in this case. | 58 | that seriously simplifies working with routes, templating and has built-in web |
| 59 | server that satisfies our need in this case. | ||
| 58 | 60 | ||
| 59 | First we need to install bottle package. This can be done by downloading | 61 | First we need to install bottle package. This can be done by downloading |
| 60 | ```bottle.py``` and placing it in the root of your application or by using | 62 | ```bottle.py``` and placing it in the root of your application or by using pip |
| 61 | pip software ```pip install bottle --user```. | 63 | software ```pip install bottle --user```. |
| 62 | 64 | ||
| 63 | If you are using Linux or MacOS then Python is already installed. If you will | 65 | If you are using Linux or MacOS then Python is already installed. If you will |
| 64 | try to test this on Windows please install [Python for Windows](https://www.python.org/downloads/windows/). | 66 | try to test this on Windows please install [Python for |
| 65 | There may be some problems with path when you will try to launch | 67 | Windows](https://www.python.org/downloads/windows/). There may be some problems |
| 66 | ```python webapp.py``` so please take care of this before you continue. | 68 | with path when you will try to launch ```python webapp.py``` so please take care |
| 69 | of this before you continue. | ||
| 67 | 70 | ||
| 68 | ### Basic web application | 71 | ### Basic web application |
| 69 | 72 | ||
| 70 | Most basic bottle application is quite simple. Paste code below in | 73 | Most basic bottle application is quite simple. Paste code below in |
| 71 | ```webapp.py``` file and save. | 74 | ```webapp.py``` file and save. |
| 72 | 75 | ||
| 73 | ```python | 76 | ```python |
| @@ -96,59 +99,62 @@ if __name__ == "__main__": | |||
| 96 | ) | 99 | ) |
| 97 | ``` | 100 | ``` |
| 98 | 101 | ||
| 99 | To run this simple application you should open command prompt or terminal on | 102 | To run this simple application you should open command prompt or terminal on |
| 100 | your machine and go to the folder containing your file and type | 103 | your machine and go to the folder containing your file and type ```python |
| 101 | ```python webapp.py```. If everything goes ok then open your web browser and | 104 | webapp.py```. If everything goes ok then open your web browser and point it to |
| 102 | point it to ```http://0.0.0.0:5000```. | 105 | ```http://0.0.0.0:5000```. |
| 103 | 106 | ||
| 104 | If you would like change the port of your application (like port 80) and not | 107 | If you would like change the port of your application (like port 80) and not use |
| 105 | use root to run your app this will present a problem. The TCP/IP port numbers | 108 | root to run your app this will present a problem. The TCP/IP port numbers below |
| 106 | below 1024 are privileged ports → this is a security feature. So in order of | 109 | 1024 are privileged ports → this is a security feature. So in order of |
| 107 | simplicity and security use a port number above 1024 like I have used port | 110 | simplicity and security use a port number above 1024 like I have used port 5000. |
| 108 | 5000. | ||
| 109 | 111 | ||
| 110 | If this fails at any time please fix it before you continue, because nothing | 112 | If this fails at any time please fix it before you continue, because nothing |
| 111 | below will work otherwise. | 113 | below will work otherwise. |
| 112 | 114 | ||
| 113 | We use 0.0.0.0 as default host so that this app is available over your local | 115 | We use 0.0.0.0 as default host so that this app is available over your local |
| 114 | network. If you find your local ip ```ifconfig``` and try accessing this site | 116 | network. If you find your local ip ```ifconfig``` and try accessing this site |
| 115 | with your phone (if on same network/router as your machine) this should work | 117 | with your phone (if on same network/router as your machine) this should work as |
| 116 | as well (example of such ip ```http://192.168.1.15:5000```). This is a must | 118 | well (example of such ip ```http://192.168.1.15:5000```). This is a must have |
| 117 | have because Arduino will be accessing this application to send it's data. | 119 | because Arduino will be accessing this application to send it's data. |
| 118 | 120 | ||
| 119 | ### Web application security | 121 | ### Web application security |
| 120 | 122 | ||
| 121 | There is a lot to be said about security and is a topic of many books. Of course | 123 | There is a lot to be said about security and is a topic of many books. Of course |
| 122 | all this can not be written here but to just establish some basic | 124 | all this can not be written here but to just establish some basic security → you |
| 123 | security → you should always use SSL with your application. Some fantastic free | 125 | should always use SSL with your application. Some fantastic free certificates |
| 124 | certificates are available by [Let's Encrypt - Free SSL/TLS Certificates](https://letsencrypt.org). | 126 | are available by [Let's Encrypt - Free SSL/TLS |
| 125 | With SSL certificate installed you should then make use of HTTP headers and | 127 | Certificates](https://letsencrypt.org). With SSL certificate installed you |
| 126 | send your "API key" via a header. If your key is send via header then this | 128 | should then make use of HTTP headers and send your "API key" via a header. If |
| 127 | key is encrypted by SSL and send encrypted over the network. Never send your | 129 | your key is send via header then this key is encrypted by SSL and send encrypted |
| 128 | api keys by GET parameter like ```http://example.com/?api_key=somekeyvalue```. | 130 | over the network. Never send your api keys by GET parameter like |
| 129 | The problem that this kind of sending presents is that this key is visible in | 131 | ```http://example.com/?api_key=somekeyvalue```. The problem that this kind of |
| 130 | logs and by network sniffers. | 132 | sending presents is that this key is visible in logs and by network sniffers. |
| 131 | 133 | ||
| 132 | There is a fantastic article describing some aspects about security: [11 Web Application Security Best Practices](https://www.keycdn.com/blog/web-application-security-best-practices/). Please check it out. | 134 | There is a fantastic article describing some aspects about security: [11 Web |
| 135 | Application Security Best | ||
| 136 | Practices](https://www.keycdn.com/blog/web-application-security-best-practices/). Please | ||
| 137 | check it out. | ||
| 133 | 138 | ||
| 134 | ### Simple API for writing data-points | 139 | ### Simple API for writing data-points |
| 135 | 140 | ||
| 136 | We will now be using boilerplate code from example above and extend it to be | 141 | We will now be using boilerplate code from example above and extend it to be |
| 137 | able to write data received by API to local storage. For example use I will | 142 | SQLite3 because it plays well with Python and can store quite large amount of |
| 138 | use SQLite3 because it plays well with Python and can store quite large amount | 143 | able to write data received by API to local storage. For example use I will use |
| 139 | of data. I have been using it to collect gigabytes of data in a single database | 144 | data. I have been using it to collect gigabytes of data in a single database |
| 140 | without any corruption or problems → your experience may vary. | 145 | without any corruption or problems → your experience may vary. |
| 141 | 146 | ||
| 142 | To avoid learning SQLite I will be using [Dataset: databases for lazy people](https://dataset.readthedocs.io/en/latest/index.html). | 147 | To avoid learning SQLite I will be using [Dataset: databases for lazy |
| 143 | This package abstracts SQL and simplifies writing and reading data from | 148 | people](https://dataset.readthedocs.io/en/latest/index.html). This package |
| 144 | database. You should install this package with pip software | 149 | abstracts SQL and simplifies writing and reading data from database. You should |
| 145 | ```pip install dataset --user```. | 150 | install this package with pip software ```pip install dataset --user```. |
| 146 | 151 | ||
| 147 | Because API will use POST method I will be testing if code works correctly by | 152 | Because API will use POST method I will be testing if code works correctly by |
| 148 | using [Restlet Client for Google Chrome](https://chrome.google.com/webstore/detail/restlet-client-rest-api-t/aejoelaoggembcahagimdiliamlcdmfm). | 153 | using [Restlet Client for Google |
| 154 | Chrome](https://chrome.google.com/webstore/detail/restlet-client-rest-api-t/aejoelaoggembcahagimdiliamlcdmfm). | ||
| 149 | This software also allows you to set headers → for basic security with API_KEY. | 155 | This software also allows you to set headers → for basic security with API_KEY. |
| 150 | 156 | ||
| 151 | To quickly generate passwords or API keys I usually use this nifty website | 157 | To quickly generate passwords or API keys I usually use this nifty website |
| 152 | [RandomKeygen](https://randomkeygen.com/). | 158 | [RandomKeygen](https://randomkeygen.com/). |
| 153 | 159 | ||
| 154 | Copy and paste code below over your previous code in file ```webapp.py```. | 160 | Copy and paste code below over your previous code in file ```webapp.py```. |
| @@ -204,12 +210,12 @@ if __name__ == "__main__": | |||
| 204 | ) | 210 | ) |
| 205 | ``` | 211 | ``` |
| 206 | 212 | ||
| 207 | To run this simply go to folder containing python file and run | 213 | To run this simply go to folder containing python file and run ```python |
| 208 | ```python webapp.py``` from terminal. If everything goes ok you should have | 214 | webapp.py``` from terminal. If everything goes ok you should have simple API |
| 209 | simple API available via POST method on /api route. | 215 | available via POST method on /api route. |
| 210 | 216 | ||
| 211 | After testing the service with Restlet Client you should be able to view | 217 | After testing the service with Restlet Client you should be able to view your |
| 212 | your data in a database file ```data.db```. | 218 | data in a database file ```data.db```. |
| 213 | 219 | ||
| 214 |  | 220 |  |
| 215 | 221 | ||
| @@ -218,38 +224,38 @@ for SQLite → [DB Browser for SQLite](http://sqlitebrowser.org/). | |||
| 218 | 224 | ||
| 219 |  | 225 |  |
| 220 | 226 | ||
| 221 | Table structure is as simple as it can be. We have ts (timestamp) and | 227 | Table structure is as simple as it can be. We have ts (timestamp) and value |
| 222 | value (value from Arduino). As you can see timestamp is generated on API | 228 | (value from Arduino). As you can see timestamp is generated on API side. If you |
| 223 | side. If you would happen to have atomic clock on Arduino it would be then | 229 | would happen to have atomic clock on Arduino it would be then better to generate |
| 224 | better to generate and send timestamp with the value. This would be | 230 | and send timestamp with the value. This would be particularity useful if we |
| 225 | particularity useful if we would be collecting sensor data at a higher | 231 | would be collecting sensor data at a higher frequency and then sending this data |
| 226 | frequency and then sending this data in bulk to API. | 232 | in bulk to API. |
| 227 | 233 | ||
| 228 | If you will deploy this app with uWSGI and multi-threaded, use | 234 | If you will deploy this app with uWSGI and multi-threaded, use DSN (Data Source |
| 229 | DSN (Data Source Name) url with ```?check_same_thread=False```. | 235 | Name) url with ```?check_same_thread=False```. |
| 230 | 236 | ||
| 231 | Ok, now that we have some sort of a working API with some basic security | 237 | Ok, now that we have some sort of a working API with some basic security so |
| 232 | so unwanted people can not post data to your database can we proceed further | 238 | unwanted people can not post data to your database can we proceed further and |
| 233 | and try to program Arduino to send data to API. | 239 | try to program Arduino to send data to API. |
| 234 | 240 | ||
| 235 | ## Sending data to API with Arduino MKR1000 | 241 | ## Sending data to API with Arduino MKR1000 |
| 236 | 242 | ||
| 237 | First of all you should have MKR1000 module and microUSB cable to proceed. | 243 | First of all you should have MKR1000 module and microUSB cable to proceed. If |
| 238 | If you have ever done any work with Arduino you should know that you also | 244 | you have ever done any work with Arduino you should know that you also need |
| 239 | need [Arduino IDE](https://www.arduino.cc/en/Main/Software). On provided link | 245 | [Arduino IDE](https://www.arduino.cc/en/Main/Software). On provided link you |
| 240 | you should be able to download and install IDE. Once that task is completed | 246 | should be able to download and install IDE. Once that task is completed and you |
| 241 | and you have successfully run blink example you should proceed to the next step. | 247 | have successfully run blink example you should proceed to the next step. |
| 242 | 248 | ||
| 243 | In order to use wireless capabilities of MKR1000 you need to first install | 249 | In order to use wireless capabilities of MKR1000 you need to first install |
| 244 | [WiFi101 library](https://www.arduino.cc/en/Reference/WiFi101) in Arduino IDE. | 250 | [WiFi101 library](https://www.arduino.cc/en/Reference/WiFi101) in Arduino IDE. |
| 245 | Please check before you install, you may already have it installed. | 251 | Please check before you install, you may already have it installed. |
| 246 | 252 | ||
| 247 | Code below is a working example that sends data to API. Before you try to test | 253 | Code below is a working example that sends data to API. Before you try to test |
| 248 | your code make sure you have run Python web application. Then change settings | 254 | your code make sure you have run Python web application. Then change settings |
| 249 | for wifi, api endpoint and api_key. If by some reason code bellow doesn't work | 255 | for wifi, api endpoint and api_key. If by some reason code bellow doesn't work |
| 250 | for you please leave a comment and I'll try to help. | 256 | for you please leave a comment and I'll try to help. |
| 251 | 257 | ||
| 252 | Once you have opened IDE and copied this code try to compile and upload it. | 258 | Once you have opened IDE and copied this code try to compile and upload it. |
| 253 | Then open "Serial monitor" to see if any output is presented by Arduino. | 259 | Then open "Serial monitor" to see if any output is presented by Arduino. |
| 254 | 260 | ||
| 255 | ```c | 261 | ```c |
| @@ -335,50 +341,50 @@ void loop() { | |||
| 335 | } | 341 | } |
| 336 | ``` | 342 | ``` |
| 337 | 343 | ||
| 338 | As seen from example you can notice that Arduino is generating random integer | 344 | As seen from example you can notice that Arduino is generating random integer |
| 339 | between [ 0 .. 1000 ]. You can easily replace this with a temperature sensor | 345 | between [ 0 .. 1000 ]. You can easily replace this with a temperature sensor or |
| 340 | or any other kind of sensor. | 346 | any other kind of sensor. |
| 341 | 347 | ||
| 342 | Now that we have API under the hood and Arduino is sending demo data we can | 348 | Now that we have API under the hood and Arduino is sending demo data we can now |
| 343 | now focus on data visualization. | 349 | focus on data visualization. |
| 344 | 350 | ||
| 345 | ## Data visualization | 351 | ## Data visualization |
| 346 | 352 | ||
| 347 | Before we continue we should examine our project folder structure. Currently | 353 | Before we continue we should examine our project folder structure. Currently we |
| 348 | we only have two files in our project: | 354 | only have two files in our project: |
| 349 | 355 | ||
| 350 | _simple-iot-app/_ | 356 | _simple-iot-app/_ |
| 351 | 357 | ||
| 352 | * _webapp.py_ | 358 | * _webapp.py_ |
| 353 | * _data.db_ | 359 | * _data.db_ |
| 354 | 360 | ||
| 355 | We will now add HTML template that will contain CSS and JavaScript code inline | 361 | We will now add HTML template that will contain CSS and JavaScript code inline |
| 356 | for the simplicity reason. And for the bottle framework to be able to scan root | 362 | for the simplicity reason. And for the bottle framework to be able to scan root |
| 357 | application folder for templates we will add | 363 | application folder for templates we will add ```bottle.TEMPLATE_PATH.insert(0, |
| 358 | ```bottle.TEMPLATE_PATH.insert(0, "./")``` in ```webapp.py```. By default bottle | 364 | "./")``` in ```webapp.py```. By default bottle framework uses ```views/``` |
| 359 | framework uses ```views/``` subfolder to store templates. This is not the ideal | 365 | subfolder to store templates. This is not the ideal situation and if you will |
| 360 | situation and if you will use bottle to develop web applications you should use | 366 | use bottle to develop web applications you should use native behavior and store |
| 361 | native behavior and store templates in it's predefined folder. But for the sake | 367 | templates in it's predefined folder. But for the sake of example we will |
| 362 | of example we will over-ride this. Be careful to fully replace your code with | 368 | over-ride this. Be careful to fully replace your code with new code that is |
| 363 | new code that is provided below. Avoid partially replacing code in file :) Also | 369 | provided below. Avoid partially replacing code in file :) Also new code for |
| 364 | new code for reading data-points is provided in Python example below. | 370 | reading data-points is provided in Python example below. |
| 365 | 371 | ||
| 366 | First we add new route to our web application. It should be trigger when browser | 372 | First we add new route to our web application. It should be trigger when browser |
| 367 | hits root of application ```http://0.0.0.0:5000/```. This route will do nothing | 373 | hits root of application ```http://0.0.0.0:5000/```. This route will do nothing |
| 368 | more than render ```frontend.html``` template. This is done by | 374 | more than render ```frontend.html``` template. This is done by ```return |
| 369 | ```return bottle.template("frontend.html")```. Check code below to further | 375 | bottle.template("frontend.html")```. Check code below to further examine how |
| 370 | examine how exactly this is done. | 376 | exactly this is done. |
| 371 | 377 | ||
| 372 | Now we will expand ```/api``` route and use different methods to write or | 378 | Now we will expand ```/api``` route and use different methods to write or read |
| 373 | read data-points. For writing data-point we will use POST method and for | 379 | data-points. For writing data-point we will use POST method and for reading |
| 374 | reading points we will use GET method. GET method will return JSON object | 380 | points we will use GET method. GET method will return JSON object with latest |
| 375 | with latest readings and historical data. | 381 | readings and historical data. |
| 376 | 382 | ||
| 377 | There is a fantastic JavaScript library for plotting time-series charts | 383 | There is a fantastic JavaScript library for plotting time-series charts called |
| 378 | called [MetricsGraphics.js](https://www.metricsgraphicsjs.org) that is | 384 | [MetricsGraphics.js](https://www.metricsgraphicsjs.org) that is based on |
| 379 | based on [D3.js](https://d3js.org/) library for visualizing data. | 385 | [D3.js](https://d3js.org/) library for visualizing data. |
| 380 | 386 | ||
| 381 | Data schema required by MetricsGraphics.js → to achieve this we need to | 387 | Data schema required by MetricsGraphics.js → to achieve this we need to |
| 382 | transform data from database into this format: | 388 | transform data from database into this format: |
| 383 | 389 | ||
| 384 | ```json | 390 | ```json |
| @@ -394,9 +400,9 @@ transform data from database into this format: | |||
| 394 | ] | 400 | ] |
| 395 | ``` | 401 | ``` |
| 396 | 402 | ||
| 397 | Web application is now complete and we only need ```frontend.html``` that we | 403 | Web application is now complete and we only need ```frontend.html``` that we |
| 398 | will develop now. If you would try to start web app now and go to root app | 404 | will develop now. If you would try to start web app now and go to root app this |
| 399 | this will return error because we don't have frontend.html yet. | 405 | will return error because we don't have frontend.html yet. |
| 400 | 406 | ||
| 401 | ```python | 407 | ```python |
| 402 | # -*- coding: utf-8 -*- | 408 | # -*- coding: utf-8 -*- |
| @@ -477,9 +483,9 @@ if __name__ == "__main__": | |||
| 477 | ) | 483 | ) |
| 478 | ``` | 484 | ``` |
| 479 | 485 | ||
| 480 | And now finally we can implement ```frontend.html```. Create file with this | 486 | And now finally we can implement ```frontend.html```. Create file with this name |
| 481 | name and copy code below. When you are done you can start web application. | 487 | and copy code below. When you are done you can start web application. Steps for |
| 482 | Steps for this part are listed below the code. | 488 | this part are listed below the code. |
| 483 | 489 | ||
| 484 | ```html | 490 | ```html |
| 485 | <!DOCTYPE html> | 491 | <!DOCTYPE html> |
| @@ -572,23 +578,24 @@ Ok, lets now start application and start feeding it data. | |||
| 572 | 2. connect Arduino MKR1000 to power source | 578 | 2. connect Arduino MKR1000 to power source |
| 573 | 3. open browser and go to ```http://0.0.0.0:5000``` | 579 | 3. open browser and go to ```http://0.0.0.0:5000``` |
| 574 | 580 | ||
| 575 | If everything goes well you should be seeing new data-points rendered on | 581 | If everything goes well you should be seeing new data-points rendered on chart |
| 576 | chart every 5 seconds. | 582 | every 5 seconds. |
| 577 | 583 | ||
| 578 | If you navigate to ```http://0.0.0.0:5000``` you should see rendered chart | 584 | If you navigate to ```http://0.0.0.0:5000``` you should see rendered chart as |
| 579 | as shown on picture below. | 585 | shown on picture below. |
| 580 | 586 | ||
| 581 |  | 587 |  |
| 582 | 588 | ||
| 583 | Complete application with all the code is available for [download](/assets/iot-application/simple-iot-application.zip). | 589 | Complete application with all the code is available for |
| 590 | [download](/assets/iot-application/simple-iot-application.zip). | ||
| 584 | 591 | ||
| 585 | ## Conclusion | 592 | ## Conclusion |
| 586 | 593 | ||
| 587 | I hope this clarifies some aspects of IOT application development. Of course | 594 | I hope this clarifies some aspects of IOT application development. Of course |
| 588 | this is a minimal example and is far from what can be done in real life with | 595 | this is a minimal example and is far from what can be done in real life with |
| 589 | some further dive into other technologies. | 596 | some further dive into other technologies. |
| 590 | 597 | ||
| 591 | If you would like to continue exploring IOT world here are some interesting | 598 | If you would like to continue exploring IOT world here are some interesting |
| 592 | resources for you to examine: | 599 | resources for you to examine: |
| 593 | 600 | ||
| 594 | * [Reading Sensors with an Arduino](https://www.allaboutcircuits.com/projects/reading-sensors-with-an-arduino/) | 601 | * [Reading Sensors with an Arduino](https://www.allaboutcircuits.com/projects/reading-sensors-with-an-arduino/) |
| @@ -597,4 +604,3 @@ resources for you to examine: | |||
| 597 | * [Internet of Things (IoT) Tutorials](http://www.tutorialspoint.com/internet_of_things/) | 604 | * [Internet of Things (IoT) Tutorials](http://www.tutorialspoint.com/internet_of_things/) |
| 598 | 605 | ||
| 599 | Any comment or additional ideas are welcomed in comments below. | 606 | Any comment or additional ideas are welcomed in comments below. |
| 600 | |||
