├── .gitignore ├── 01_Intro ├── .gitignore ├── HttpButton │ ├── HttpButton.ino │ └── config.h ├── MqttButton │ ├── MqttButton.ino │ └── config.h ├── MqttNeoPixel │ ├── MqttNeoPixel.ino │ └── config.h ├── form.html ├── mosquitto-cli.md └── rpi-mqtt-example │ ├── README.md │ ├── requirements.txt │ ├── rgb_sensor.py │ ├── test_led.py │ └── test_rgb.py ├── 02_Arduino ├── README.md ├── arduino │ ├── .gitignore │ ├── AWS │ │ ├── AWS.ino │ │ └── config.h │ ├── GenerateCSR │ │ └── GenerateCSR.ino │ ├── HardwareTest │ │ └── HardwareTest.ino │ ├── HttpClient │ │ ├── HttpClient.ino │ │ └── config.h │ ├── HttpClientBasicAuth │ │ ├── HttpClientBasicAuth.ino │ │ └── config.h │ ├── LED │ │ ├── LED.ino │ │ └── config.h │ ├── LoRa │ │ ├── LoRaButton │ │ │ └── LoRaButton.ino │ │ ├── LoRaPacketLogger │ │ │ └── LoRaPacketLogger.ino │ │ ├── LoRaPacketLoggerLED │ │ │ └── LoRaPacketLoggerLED.ino │ │ ├── MKR1300-ENV │ │ │ ├── MKR1300-ENV.ino │ │ │ └── arduino_secrets.h │ │ └── MKR1300-LoRaSender │ │ │ └── MKR1300-LoRaSender.ino │ ├── MKRGSM1400 │ │ ├── MKRGSM1400.ino │ │ └── config.h │ ├── MacAddress │ │ └── MacAddress.ino │ ├── NetworkTest │ │ ├── NetworkTest.ino │ │ └── arduino_secrets.h │ ├── SoilSensorAnalogRead │ │ └── SoilSensorAnalogRead.ino │ ├── TemperatureHumidity │ │ ├── TemperatureHumidity.ino │ │ └── config.h │ └── TemperatureHumidityLED │ │ ├── TemperatureHumidityLED.ino │ │ └── config.h ├── exercises │ ├── README.md │ ├── exercise1.md │ ├── exercise2.md │ ├── exercise3.md │ ├── exercise4.md │ ├── exercise5.md │ ├── exercise6.md │ ├── exercise7.md │ ├── exercise8.md │ ├── exercise9.md │ └── images │ │ ├── ArduinoIDE-just-download.png │ │ ├── ArduinoIDE.png │ │ ├── BoardsManager-menu.png │ │ ├── BoardsManager.png │ │ ├── ManageLibraries.png │ │ ├── aws-config-1.png │ │ ├── aws-config-2.png │ │ ├── certificate-update.png │ │ ├── chart.png │ │ ├── dashboard-developer-tools.png │ │ ├── dashboard-device-dropdown.png │ │ ├── dashboard-device.png │ │ ├── dashboard.png │ │ ├── disconnect-bad-topic.png │ │ ├── generate-csr.png │ │ ├── get-cert-1.png │ │ ├── get-cert-2.png │ │ ├── hardware-test.png │ │ ├── http-arduino.png │ │ ├── http-server.png │ │ ├── http-start-server.png │ │ ├── https-arduino-glitch.png │ │ ├── https-server-glitch.png │ │ ├── library-arduinobearssl-dependencies.png │ │ ├── library-arduinobearssl.png │ │ ├── library-arduinohttpclient.png │ │ ├── library-arduinomqttclient.png │ │ ├── library-sht31-dependencies.png │ │ ├── library-sht31.png │ │ ├── library-wifinina.png │ │ ├── logo.svg │ │ ├── mkr1010-wiring.fzz │ │ ├── mkr1010-wiring_bb.png │ │ ├── network-test.png │ │ ├── openssl-cert-out.png │ │ ├── openssl-csr-out.png │ │ ├── shiftr.png │ │ ├── subscribe.png │ │ ├── switch.png │ │ ├── temperature-humidity-2.png │ │ ├── temperature-humidity.png │ │ ├── wireshark.png │ │ ├── wiring.fzz │ │ └── wiring_bb.png ├── node │ ├── blink.js │ ├── config.js │ ├── listen.js │ ├── off.js │ ├── on.js │ └── package.json ├── server │ ├── package.json │ ├── server.js │ ├── server2.js │ └── server3.js └── www │ ├── chart │ ├── chart.js │ ├── index.html │ └── mqtt.js │ ├── subscribe │ ├── index.html │ └── index.js │ └── switch │ ├── index.html │ └── index.js ├── 03_RelationalDatabases ├── README.md ├── function_owner.sql ├── img │ ├── aggregate-error-no-group-by.png │ ├── aws-rds-dashboard.png │ ├── count-sensor-data.png │ ├── date-one-row.png │ ├── dates-in-est.png │ ├── desc-mqtt-message.png │ ├── desc-sensor-data-text.png │ ├── desc-sensor-data.png │ ├── distinct-device.png │ ├── distinct-measurement-numeric.png │ ├── distinct-measurement-text.png │ ├── e-create-temp-with-query.png │ ├── e-filter-color.png │ ├── e-filter.png │ ├── e-interval.png │ ├── e-query-imported.png │ ├── e-self-join.png │ ├── f-cast-date.png │ ├── f-count-by-device.png │ ├── f-count-by-measurement.png │ ├── f-count-select.png │ ├── f-describe.png │ ├── f-distinct-device-measurement.png │ ├── f-group-day-no-round.png │ ├── f-group-day-order.png │ ├── f-group-day.png │ ├── f-group-hour.png │ ├── f-hour-4.png │ ├── f-in.png │ ├── f-math.png │ ├── f-minute-15-drop-date.png │ ├── f-minute-15-format-timestamp.png │ ├── f-minute-15.png │ ├── f-order.png │ ├── f-over-100.png │ ├── f-temp-all-columns.png │ ├── f-temp-some-columns.png │ ├── f-v-daily-temp.png │ ├── f-when.png │ ├── insert-more-records.png │ ├── insert.png │ ├── limit.png │ ├── min-by-device.png │ ├── min-max-by-device.png │ ├── min.png │ ├── pg-cli-install-windows.png │ ├── process-flow.png │ ├── psql-connect.png │ ├── psql-create-table.png │ ├── select-2.png │ ├── select-after-insert.png │ ├── select-empty-table.png │ ├── select-mqtt-count.png │ ├── select-mqtt-distinct.png │ ├── select-mqtt-like.png │ ├── select-mqtt-where.png │ ├── set-timezone.png │ ├── show-timezone.png │ ├── specify-columns-2.png │ ├── specify-columns.png │ ├── too-many-rows.png │ ├── union.png │ └── where-between.png ├── itp-schema.sql ├── person_device_postgres.sql ├── person_device_queries.sql ├── person_device_sqlite.sql ├── postgres.md ├── postgres2.md ├── postgres3.md └── sqlite.md ├── 04_TimeSeries ├── README.md ├── influxdb.md └── timescaledb.md ├── 05_Code ├── README.md ├── graphing-data.md ├── img │ ├── by-day-pretty.png │ ├── by-day.png │ ├── device-list.png │ ├── device_01-temperature-json.png │ ├── device_ef3.png │ ├── devices-browser.png │ ├── devices-curl.png │ ├── hello-express.png │ ├── hello-index.png │ ├── message-count-by-day.png │ ├── message-count-by-device.png │ ├── per-device-1.png │ ├── per-device-2.png │ ├── pm2-list.png │ └── sms-notification.jpg └── processing-mqtt-data.md ├── 06_Tools ├── README.md ├── diagrams.pdf ├── grafana.md ├── img │ ├── 15-min-chart.png │ ├── 15-min-import-file.png │ ├── 15-min-imported-data.png │ ├── 15-min-inline-chart.png │ ├── 15-min-inline-timeline.png │ ├── daily-chart.png │ ├── daily-import-file-dialog.png │ ├── daily-import-file.png │ ├── daily-imported-data.png │ ├── daily-inline.png │ ├── daily-move-to-own-sheet.png │ ├── flow-alert-complete.png │ ├── flow-alert-debug.png │ ├── flow-alert-email-message.png │ ├── flow-alert-email.png │ ├── flow-alert-test.png │ ├── flow-hello-inject-node.png │ ├── flow-hello-unconfigured.png │ ├── flow-hello-world.png │ ├── flow-install-email.png │ ├── flow-led-switch.png │ ├── flow-log-format.png │ ├── flow-mqtt-add-broker.png │ ├── flow-mqtt-debug.png │ ├── flow-mqtt-log-file-config.png │ ├── flow-mqtt-node.png │ ├── flow-new-mqtt-broker.png │ ├── grafana-add-datasource-influx.png │ ├── grafana-add-datasource-timescale.png │ ├── grafana-add-datasource.png │ ├── grafana-before-dashboard.png │ ├── grafana-dashboard-with-gauge.png │ ├── grafana-dashboard.png │ ├── grafana-gauge-query.png │ ├── grafana-home.png │ ├── grafana-influx-config.png │ ├── grafana-influx-config2.png │ ├── grafana-influx-query.png │ ├── grafana-influx.png │ ├── grafana-login.png │ ├── grafana-new-dashboard.png │ ├── grafana-query-inspector.png │ ├── grafana-select-one.png │ ├── grafana-timescale-influx.png │ ├── grafana-timescale-query.png │ ├── grafana-timescale.png │ ├── new-sheet.png │ ├── services-grafana-automatic.png │ ├── services-grafana-manual-startup.png │ ├── services-grafana-properties-menu.png │ ├── services-grafana-properties.png │ ├── services-grafana-startup-menu.png │ ├── services-grafana.png │ ├── tail-log-good.png │ ├── tail-log-payload-only-windows.png │ └── tail-log-payload-only.png ├── node-red.md └── spreadsheet.md ├── 07_AWS ├── README.md ├── aws.md ├── cloud_formation.yml ├── glitch.md ├── grafana.md ├── img │ ├── aws-cloudformation-create-agree.png │ ├── aws-cloudformation-create-stack.png │ ├── aws-cloudformation-stack.png │ ├── aws-cloudformation.png │ ├── aws-iot-custom-endpoint.png │ ├── aws-iot-download-certificate.png │ ├── aws-iot-test-data.png │ ├── aws-iot-test-send.png │ ├── aws-iot-test-subscribe.png │ ├── aws-iot-thing-registered.png │ ├── aws-iot.png │ ├── aws-login.png │ ├── aws-region.png │ ├── aws-services-menu-cloudformation.png │ ├── aws.png │ ├── cloudformation-thing-policy.png │ ├── environment-sensor-output.png │ ├── glitch-env.png │ ├── glitch-ui.png │ ├── grafana-graph.png │ ├── grafana-query.png │ ├── iam-download-key.png │ ├── iam-glitch-user.png │ ├── iam-permissions.png │ ├── iam-review-user.png │ ├── iam-users.png │ ├── influxdb-check-data.png │ ├── lambda-connection-string.png │ ├── lambda-create-success.png │ ├── lambda-create.png │ ├── lambda-home.png │ ├── lambda-select-test-data.png │ ├── lambda-test-event.png │ ├── lambda-test-success.png │ ├── lambda-upload-zip.png │ ├── lambda.png │ ├── log-action.png │ ├── log-firehose-action.png │ ├── log-firehose-create-resource.png │ ├── log-kinesis-new-delivery-stream.png │ ├── log-kinesis-s3-bucket.png │ ├── log-kinesis-s3-buffer-hints.png │ ├── log-kinesis-welcome.png │ ├── log-rule.png │ ├── log-s3-bucket.png │ ├── postgresql-check-data.png │ ├── rds.png │ ├── rule-action-dynamo.png │ ├── rule-action-split-dynamo-v2.png │ ├── rule-create.png │ ├── rule-dynamo-edit.png │ ├── rule-dynamo-items.png │ ├── rule-influxdb-lambda.png │ ├── rule-no-rules.png │ ├── rule-postgresql-lambda.png │ ├── rule-query-action.png │ ├── rule-query.png │ ├── sns-configure-action.png │ ├── sns-create-subscription-email.png │ ├── sns-create-topic.png │ ├── sns-rule-action.png │ ├── sns-rule-query.png │ ├── sns-subscribe-sms.png │ └── sns-test.png ├── influxdb.md ├── logging.md ├── notifications.md ├── postgresql.md └── rules.asciidoc ├── Assignments ├── assignment-week-1.md ├── assignment-week-2.md ├── assignment-week-3.md ├── assignment-week-4.md ├── assignment-week-5.md └── assignment-week-6.md ├── README.md ├── extra └── rpi-mqtt │ ├── README.md │ ├── config.py │ ├── led_test.py │ ├── pi-sht31.jpg │ ├── requirements.txt │ ├── rpi-sht31.fzz │ ├── rpi-sht31_bb.png │ ├── sht31_mqtt.py │ └── sht31_test.py └── setup ├── grafana.md ├── influxdb.md ├── mqtt-broker.md ├── postgresql.md └── timescaledb.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .terraform 4 | terraform.tfstate 5 | terraform.tfstate.backup 6 | -------------------------------------------------------------------------------- /01_Intro/.gitignore: -------------------------------------------------------------------------------- 1 | config.don 2 | -------------------------------------------------------------------------------- /01_Intro/HttpButton/HttpButton.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Wire the button to buttonPin (2) and ground 5 | // When the button is pressed, data is posted via HTTP 6 | 7 | #include "config.h" 8 | 9 | // Use WiFiClient for HTTP, WiFiSSLClient for HTTPS 10 | WiFiClient wifi; 11 | //WiFiSSLClient wifi; 12 | HttpClient client = HttpClient(wifi, SERVER_ADDRESS, SERVER_PORT); 13 | 14 | int wifi_status = WL_IDLE_STATUS; 15 | 16 | const int buttonPin = 2; 17 | int count = 0; 18 | int previousButtonValue = HIGH; 19 | 20 | void setup() { 21 | Serial.begin(9600); 22 | 23 | //configure pin 2 as an input and enable the internal pull-up resistor 24 | pinMode(buttonPin, INPUT_PULLUP); 25 | 26 | // wait for a serial connection for debugging 27 | while (!Serial); 28 | 29 | connectWiFi(); 30 | } 31 | 32 | void loop() { 33 | 34 | // read the state of the button pin into a variable 35 | int buttonValue = digitalRead(buttonPin); 36 | 37 | // see if the value has changed 38 | if (buttonValue != previousButtonValue) { 39 | 40 | // since we're using the internal pullup resistor 41 | // HIGH is released and LOW is pressed 42 | if (buttonValue == LOW) { 43 | Serial.println("The button is pressed, increasing count."); 44 | count++; 45 | sendDataToServer(); 46 | } 47 | previousButtonValue = buttonValue; 48 | } 49 | 50 | } 51 | 52 | void sendDataToServer() { 53 | Serial.println("making POST request"); 54 | String contentType = "application/x-www-form-urlencoded"; 55 | // sensor data 56 | String postData = "count=" + String(count); 57 | // device id 58 | postData += "&device=" + String(DEVICE_ID); 59 | 60 | client.post("/", contentType, postData); 61 | 62 | // read the status code and body of the response 63 | int statusCode = client.responseStatusCode(); 64 | String response = client.responseBody(); 65 | 66 | Serial.print("Status code: "); 67 | Serial.println(statusCode); 68 | Serial.print("Response: "); 69 | Serial.println(response); 70 | } 71 | 72 | void connectWiFi() { 73 | 74 | Serial.print("WiFi firmware version "); 75 | Serial.println(WiFi.firmwareVersion()); 76 | 77 | // attempt to connect to WiFi network 78 | while (wifi_status != WL_CONNECTED) { 79 | Serial.print("Attempting to connect to SSID: "); 80 | Serial.println(WIFI_SSID); 81 | wifi_status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); 82 | 83 | // wait 3 seconds for connection 84 | delay(3000); 85 | } 86 | Serial.println("Connected to WiFi"); 87 | printWiFiStatus(); 88 | 89 | } 90 | 91 | void printWiFiStatus() { 92 | // print your WiFi IP address: 93 | IPAddress ip = WiFi.localIP(); 94 | Serial.print("IP Address: "); 95 | Serial.println(ip); 96 | } 97 | -------------------------------------------------------------------------------- /01_Intro/HttpButton/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = ""; 2 | const char WIFI_PASSWORD[] = ""; 3 | 4 | const char SERVER_ADDRESS[] = "iot-data.glitch.me"; 5 | const int SERVER_PORT = 80; 6 | 7 | const char DEVICE_ID[] = ""; 8 | -------------------------------------------------------------------------------- /01_Intro/MqttButton/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = ""; 2 | const char WIFI_PASSWORD[] = ""; 3 | 4 | const char MQTT_BROKER[] = ""; 5 | const int MQTT_PORT = 1883; 6 | const char MQTT_USER[] = ""; 7 | const char MQTT_PASSWORD[] = ""; 8 | 9 | const String DEVICE_ID = ""; 10 | -------------------------------------------------------------------------------- /01_Intro/MqttNeoPixel/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = ""; 2 | const char WIFI_PASSWORD[] = ""; 3 | 4 | const char MQTT_BROKER[] = ""; 5 | const int MQTT_PORT = 1883; 6 | const char MQTT_USER[] = ""; 7 | const char MQTT_PASSWORD[] = ""; 8 | 9 | const String DEVICE_ID = ""; 10 | -------------------------------------------------------------------------------- /01_Intro/form.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
-------------------------------------------------------------------------------- /01_Intro/mosquitto-cli.md: -------------------------------------------------------------------------------- 1 | # Mosquitto Command Line Examples 2 | 3 | MQTT command line examples from class 4 | 5 | ## Installation 6 | 7 | On macOS, install with `brew install mosquitto`. For Windows, download from https://mosquitto.org/download/. 8 | 9 | ## Subscribing 10 | 11 | Subscribe to all messages 12 | 13 | mosquitto_sub -h dev2db.cloud.shiftr.io -u dev2db -P public -t '#' -v 14 | 15 | Subscribe to the temperature topic for nano33 device 16 | 17 | mosquitto_sub -h dev2db.cloud.shiftr.io -u dev2db -P public -t itp/nano33/temperature -v 18 | 19 | Subscribe to the humidity topic for nano33 device 20 | 21 | mosquitto_sub -h dev2db.cloud.shiftr.io -u dev2db -P public -t itp/nano33/humidity -v 22 | 23 | Subscribe to all the topics for nano33 device 24 | 25 | mosquitto_sub -h dev2db.cloud.shiftr.io -u dev2db -P public -t itp/nano33/+ -v 26 | 27 | Subscribe to the temperature topic for all devices 28 | 29 | mosquitto_sub -h dev2db.cloud.shiftr.io -u dev2db -P public -t itp/+/temperature -v 30 | 31 | The `-h` flag specifies the hostname of the broker. The '-u' flag specifies the user and the '-P' flag specifies the password. The `-t` flag specifies the topic to subscribe to. The `-v` flag is verbose mode which prints the topic along with the payload. 32 | 33 | ### Wildcards 34 | 35 | Wildcards only work for subscribing, not publishing 36 | 37 | The single level wildcard is **+**. 38 | 39 | itp/nano33/+ 40 | itp/+/temperature 41 | 42 | The multi level wildcard is **#**. 43 | 44 | # 45 | itp/# 46 | itp/nano33/# 47 | 48 | ## Publishing 49 | 50 | Write a message to the message topic 51 | 52 | mosquitto_pub -h dev2db.cloud.shiftr.io -u dev2db -P public -t message -m 'hello, world' 53 | 54 | Set default host, user, and password in `~/.config/mosquitto_pub` to save some typing. 55 | 56 | # ~/.config/mosquitto_pub 57 | -h dev2db.cloud.shiftr.io 58 | -u dev2db 59 | -P public 60 | 61 | Turn the LED on the mkr-1010 on 62 | 63 | mosquitto_pub -t itp/mkr-1010/led -m on 64 | 65 | Turn the LED on the mkr-1010 off 66 | 67 | mosquitto_pub -t itp/mkr-1010/led -m off 68 | 69 | Write a message to a long topic 70 | 71 | mosquitto_pub -t itp/foo/bar/baz/ack -m 17 72 | 73 | Change the color of the neopixel lights 74 | 75 | mosquitto_pub -t itp/neopixel/color -m red 76 | mosquitto_pub -t itp/neopixel/color -m green 77 | mosquitto_pub -t itp/neopixel/color -m blue 78 | 79 | Change the color of the neopixel lights using a hex color 80 | 81 | mosquitto_pub -t itp/neopixel/color -m #ff00ff 82 | mosquitto_pub -t itp/neopixel/color -m #00ffff 83 | 84 | 85 | The `-h` flag specifies the hostname of the broker. The '-u' flag specifies the user and the '-P' flag specifies the password. The `-t` flag specifies the topic to publish to. The `-m` flag specifies the message to publish. 86 | 87 | 88 | ## Documentation 89 | 90 | * [mosquitto_sub](https://mosquitto.org/man/mosquitto_sub-1.html) 91 | * [mosquitto_pub](https://mosquitto.org/man/mosquitto_pub-1.html) 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /01_Intro/rpi-mqtt-example/README.md: -------------------------------------------------------------------------------- 1 | Raspberry Pi MQTT example. Uses the Pimorini Enviro Phat to measure illuminance (in lux) and red, green, blue (RGB) light levels. Sends the data to MQTT topics. 2 | 3 | For more info about the Pimorini Enviro Phat see https://learn.pimoroni.com/tutorial/sandyj/getting-started-with-enviro-phat 4 | 5 | 6 | -------------------------------------------------------------------------------- /01_Intro/rpi-mqtt-example/requirements.txt: -------------------------------------------------------------------------------- 1 | paho-mqtt 2 | -------------------------------------------------------------------------------- /01_Intro/rpi-mqtt-example/rgb_sensor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Measure illuminance (in lux) and RGB levels 4 | # Raspberry Pi with Pimorini Enviro Phat 5 | # https://learn.pimoroni.com/tutorial/sandyj/getting-started-with-enviro-phat 6 | 7 | import paho.mqtt.client as mqtt 8 | from envirophat import light 9 | from envirophat import leds 10 | from time import sleep 11 | 12 | device_id="raspberrypi" 13 | 14 | def on_connect(client, userdata, flags, rc): 15 | print("Connected ", str(rc)); 16 | client.publish("presence/connected/" + device_id, device_id) 17 | client.subscribe("itp/" + device_id + "/led") 18 | 19 | # note: when the LED on, it affects the light sensor readings 20 | def on_message(client, userdata, msg): 21 | message = str(msg.payload).lower() 22 | if message == "on": 23 | leds.on() 24 | elif message == "off": 25 | leds.off() 26 | 27 | client = mqtt.Client(device_id) 28 | client.on_connect = on_connect 29 | client.on_message = on_message 30 | client.username_pw_set("dev2db", "public") 31 | client.tls_set() 32 | client.connect("dev2db.cloud.shiftr.io", 8883, 60) 33 | client.loop_start() 34 | 35 | while True: 36 | illuminance = light.light() 37 | red, green, blue = light.rgb() 38 | color = "#%02x%02x%02x" % (red, green, blue) 39 | print(red, green, blue, color) 40 | client.publish("itp/" + device_id + "/red", red) 41 | client.publish("itp/" + device_id + "/green", green) 42 | client.publish("itp/" + device_id + "/blue", blue) 43 | client.publish("itp/" + device_id + "/color", color) 44 | client.publish("itp/" + device_id + "/illuminance", illuminance) 45 | sleep(20) 46 | 47 | 48 | -------------------------------------------------------------------------------- /01_Intro/rpi-mqtt-example/test_led.py: -------------------------------------------------------------------------------- 1 | from envirophat import leds 2 | from time import sleep 3 | 4 | leds.on() 5 | sleep(10) 6 | leds.off() 7 | -------------------------------------------------------------------------------- /01_Intro/rpi-mqtt-example/test_rgb.py: -------------------------------------------------------------------------------- 1 | from envirophat import light 2 | 3 | print(light.light()) 4 | print(light.rgb()) 5 | 6 | -------------------------------------------------------------------------------- /02_Arduino/README.md: -------------------------------------------------------------------------------- 1 | # Arduino and MQTT 2 | 3 | Build an IoT device using Arduino Nano 33 IoT board 4 | 5 | ## Exercises 6 | 7 | * [Exercise 1: Development Environment](exercises/exercise1.md) 8 | * [Exercise 2: Assemble the Hardware](exercises/exercise2.md) 9 | * [Exercise 3: Sending data using HTTP](exercises/exercise3.md) 10 | * [Exercise 4: Sending data using MQTT](exercises/exercise4.md) 11 | * [Exercise 5: Viewing MQTT data](exercises/exercise5.md) 12 | * [Exercise 6: Graphing MQTT Data](exercises/exercise6.md) 13 | * [Exercise 7: Sending Data to Arduino](exercises/exercise7.md) 14 | * [Exercise 8: Connecting using X.509 certs](exercises/exercise8.md) 15 | * [Exercise 8: Viewing Data](exercises/exercise9.md) 16 | -------------------------------------------------------------------------------- /02_Arduino/arduino/.gitignore: -------------------------------------------------------------------------------- 1 | config1000.h 2 | -------------------------------------------------------------------------------- /02_Arduino/arduino/AWS/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = ""; 2 | const char WIFI_PASS[] = ""; 3 | 4 | const char MQTT_BROKER[] = ""; 5 | 6 | const char CERTIFICATE[] = R"( 7 | )"; 8 | -------------------------------------------------------------------------------- /02_Arduino/arduino/GenerateCSR/GenerateCSR.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate a Certificate Signing Request (CSR) 3 | * 4 | * Based on the Arduino ECCX08CSR example 5 | * https://github.com/arduino-libraries/ArduinoECCX08/blob/master/examples/Tools/ECCX08CSR/ECCX08CSR.ino 6 | * 7 | * 8 | * This sketch can be used to generate a CSR for a private key 9 | * generated in an ECC508/ECC608 crypto chip slot. 10 | * 11 | * If the ECC508/ECC608 is not configured and locked it prompts 12 | * the user to configure and lock the chip with a default TLS configuration. 13 | * 14 | * The user is prompted for device name which is stored in the common name (CN of the certificate) 15 | * 16 | * The user can also select a slot number to use for the private key 17 | * A new private key can also be generated in this slot. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | void setup() { 25 | Serial.begin(9600); 26 | while (!Serial); 27 | 28 | if (!ECCX08.begin()) { 29 | Serial.println("No ECCX08 present!"); 30 | while (1); 31 | } 32 | 33 | String serialNumber = ECCX08.serialNumber(); 34 | 35 | Serial.print("ECCX08 Serial Number = "); 36 | Serial.println(serialNumber); 37 | Serial.println(); 38 | 39 | if (!ECCX08.locked()) { 40 | String lock = promptAndReadLine("The ECCX08 on your board is not locked, would you like to PERMANENTLY configure and lock it now? (y/N)", "N"); 41 | lock.toLowerCase(); 42 | 43 | if (!lock.startsWith("y")) { 44 | Serial.println("Unfortunately you can't proceed without locking it :("); 45 | while (1); 46 | } 47 | 48 | if (!ECCX08.writeConfiguration(ECCX08_DEFAULT_TLS_CONFIG)) { 49 | Serial.println("Writing ECCX08 configuration failed!"); 50 | while (1); 51 | } 52 | 53 | if (!ECCX08.lock()) { 54 | Serial.println("Locking ECCX08 configuration failed!"); 55 | while (1); 56 | } 57 | 58 | Serial.println("ECCX08 locked successfully"); 59 | Serial.println(); 60 | } 61 | 62 | Serial.println("Hi there, in order to generate a new CSR for your board, we'll need the following information ..."); 63 | Serial.println(); 64 | 65 | String common = promptAndReadLine("Device Name", serialNumber.c_str()); 66 | String slot = promptAndReadLine("Which slot would you like to use? (0 - 4)", "0"); 67 | String generateNewKey = promptAndReadLine("Would you like to generate a new private key? (Y/n)", "Y"); 68 | 69 | Serial.println(); 70 | 71 | generateNewKey.toLowerCase(); 72 | if (!ECCX08CSR.begin(slot.toInt(), generateNewKey.startsWith("y"))) { 73 | Serial.println("Error starting CSR generation!"); 74 | while (1); 75 | } 76 | 77 | ECCX08CSR.setCommonName(common); 78 | 79 | String csr = ECCX08CSR.end(); 80 | 81 | if (!csr) { 82 | Serial.println("Error generating CSR!"); 83 | while (1); 84 | } 85 | 86 | Serial.println("Here's your CSR, enjoy!"); 87 | Serial.println(); 88 | Serial.println(csr); 89 | } 90 | 91 | void loop() { 92 | // do nothing 93 | } 94 | 95 | String promptAndReadLine(const char* prompt, const char* defaultValue) { 96 | Serial.print(prompt); 97 | Serial.print(" ["); 98 | Serial.print(defaultValue); 99 | Serial.print("]: "); 100 | 101 | String s = readLine(); 102 | 103 | if (s.length() == 0) { 104 | s = defaultValue; 105 | } 106 | 107 | Serial.println(s); 108 | 109 | return s; 110 | } 111 | 112 | String readLine() { 113 | String line; 114 | 115 | while (1) { 116 | if (Serial.available()) { 117 | char c = Serial.read(); 118 | 119 | if (c == '\r') { 120 | // ignore 121 | continue; 122 | } else if (c == '\n') { 123 | break; 124 | } 125 | 126 | line += c; 127 | } 128 | } 129 | 130 | return line; 131 | } 132 | -------------------------------------------------------------------------------- /02_Arduino/arduino/HardwareTest/HardwareTest.ino: -------------------------------------------------------------------------------- 1 | // Hardware Test 2 | // 3 | // Run this sketch to test that your hardware is wired correctly. This code 4 | // blinks the LED and prints the temperature and humidity to the serial console. 5 | // 6 | // Connect the SHT31 to vcc, ground, sda, & scl 7 | // 8 | // Arduino Nano 33 IoT 9 | // - SDA pin is A4 10 | // - SCL pin is A5 11 | // Arduino MRK WiFi 1010 12 | // - SCL pin is 12 13 | // - SDA pin is 11 14 | 15 | 16 | #include 17 | #include "Adafruit_SHT31.h" 18 | 19 | Adafruit_SHT31 sht31 = Adafruit_SHT31(); 20 | const int ledPin = LED_BUILTIN; 21 | 22 | void setup() { 23 | Serial.begin(9600); 24 | 25 | // initialize the ledPin as an output. 26 | pinMode(ledPin, OUTPUT); 27 | 28 | // turn the LED on 29 | digitalWrite(ledPin, HIGH); 30 | 31 | // wait for a serial connection from the computer 32 | while (!Serial); 33 | 34 | Serial.println("Hardware Test"); 35 | 36 | // initialize the SHT31 sensor 37 | if (! sht31.begin(SHT31_DEFAULT_ADDR)) { 38 | Serial.println("Couldn't find SHT31"); 39 | digitalWrite(ledPin, LOW); // turn off LED 40 | while (1) { delay(1); } // wait forever 41 | } 42 | } 43 | 44 | void loop() { 45 | 46 | // read the sensor values 47 | float temperature = sht31.readTemperature(); 48 | float humidity = sht31.readHumidity(); 49 | 50 | // print temperature to the serial monitor 51 | Serial.print("Temperature = "); 52 | Serial.print(temperature); 53 | Serial.println(" °C"); 54 | 55 | // print humidity to the serial monitor 56 | Serial.print("Humidity = "); 57 | Serial.print(humidity); 58 | Serial.println(" %"); 59 | 60 | // print an empty line 61 | Serial.println(); 62 | 63 | // turn LED on 64 | digitalWrite(ledPin, HIGH); 65 | // wait 1 second 66 | delay(1000); 67 | 68 | // turn LED off 69 | digitalWrite(ledPin, LOW); 70 | // wait one second 71 | delay(1000); 72 | } 73 | -------------------------------------------------------------------------------- /02_Arduino/arduino/HttpClient/HttpClient.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Adafruit_SHT31.h" 5 | 6 | #include "config.h" 7 | 8 | Adafruit_SHT31 sht31 = Adafruit_SHT31(); 9 | 10 | // Use WiFiClient for HTTP, WiFiSSLClient for HTTPS 11 | WiFiClient wifi; 12 | //WiFiSSLClient wifi; 13 | HttpClient client = HttpClient(wifi, SERVER_ADDRESS, SERVER_PORT); 14 | 15 | int wifi_status = WL_IDLE_STATUS; 16 | 17 | void setup() { 18 | Serial.begin(9600); 19 | 20 | // wait for a serial connection 21 | while (!Serial); 22 | 23 | // initialize the SHT31 sensor 24 | if (! sht31.begin(SHT31_DEFAULT_ADDR)) { 25 | Serial.println("Couldn't find SHT31"); 26 | while (1) { delay(1); } // wait forever 27 | } 28 | 29 | connectWiFi(); 30 | } 31 | 32 | void loop() { 33 | 34 | // read the sensor values 35 | float temperatureC = sht31.readTemperature(); 36 | float temperatureF = temperatureC * 1.8 + 32; 37 | float humidity = sht31.readHumidity(); 38 | 39 | // print the values for debugging 40 | Serial.print(temperatureC); 41 | Serial.print("°C "); 42 | Serial.print(temperatureF); 43 | Serial.print("°F "); 44 | Serial.print(humidity); 45 | Serial.println("% RH"); 46 | 47 | Serial.println("Sending data to server via HTTP POST"); 48 | String contentType = "application/x-www-form-urlencoded"; 49 | String postData = "temperature=" + String(temperatureF); 50 | postData += "&humidity=" + String(humidity); 51 | postData += "&device=" + String(DEVICE_ID); 52 | 53 | client.post("/", contentType, postData); 54 | 55 | // read the status code and body of the response 56 | int statusCode = client.responseStatusCode(); 57 | String response = client.responseBody(); 58 | 59 | Serial.print("Status code: "); 60 | Serial.println(statusCode); 61 | Serial.print("Response: "); 62 | Serial.println(response); 63 | 64 | Serial.println("Waiting for ten seconds\n"); 65 | delay(10000); 66 | } 67 | 68 | void connectWiFi() { 69 | 70 | Serial.print("WiFi firmware version "); 71 | Serial.println(WiFi.firmwareVersion()); 72 | 73 | // attempt to connect to WiFi network 74 | while (wifi_status != WL_CONNECTED) { 75 | Serial.print("Attempting to connect to SSID: "); 76 | Serial.println(WIFI_SSID); 77 | wifi_status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); 78 | 79 | // wait 3 seconds for connection 80 | delay(3000); 81 | } 82 | Serial.println("Connected to WiFi"); 83 | printWiFiStatus(); 84 | 85 | } 86 | 87 | void printWiFiStatus() { 88 | // print your WiFi IP address: 89 | IPAddress ip = WiFi.localIP(); 90 | Serial.print("IP Address: "); 91 | Serial.println(ip); 92 | } 93 | -------------------------------------------------------------------------------- /02_Arduino/arduino/HttpClient/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = "devicenet"; 2 | const char WIFI_PASSWORD[] = "random-token"; 3 | 4 | //const char SERVER_ADDRESS[] = "192.168.86.32"; 5 | //const int SERVER_PORT = 3000; 6 | const char SERVER_ADDRESS[] = "iot-data.glitch.me"; 7 | const int SERVER_PORT = 443; 8 | 9 | const char DEVICE_ID[] = "device_01"; 10 | -------------------------------------------------------------------------------- /02_Arduino/arduino/HttpClientBasicAuth/HttpClientBasicAuth.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Adafruit_SHT31.h" 5 | 6 | #include "config.h" 7 | 8 | Adafruit_SHT31 sht31 = Adafruit_SHT31(); 9 | 10 | // Use WiFiClient for HTTP, WiFiSSLClient for HTTPS 11 | WiFiClient wifi; 12 | //WiFiSSLClient wifi; 13 | HttpClient client = HttpClient(wifi, SERVER_ADDRESS, SERVER_PORT); 14 | 15 | int wifi_status = WL_IDLE_STATUS; 16 | 17 | void setup() { 18 | Serial.begin(9600); 19 | 20 | // wait for a serial connection 21 | while (!Serial); 22 | 23 | // initialize the SHT31 sensor 24 | if (! sht31.begin(SHT31_DEFAULT_ADDR)) { 25 | Serial.println("Couldn't find SHT31"); 26 | while (1) { delay(1); } // wait forever 27 | } 28 | 29 | connectWiFi(); 30 | } 31 | 32 | void loop() { 33 | 34 | // read the sensor values 35 | float temperatureC = sht31.readTemperature(); 36 | float temperatureF = temperatureC * 1.8 + 32; 37 | float humidity = sht31.readHumidity(); 38 | 39 | // print the values for debugging 40 | Serial.print(temperatureC); 41 | Serial.print("°C "); 42 | Serial.print(temperatureF); 43 | Serial.print("°F "); 44 | Serial.print(humidity); 45 | Serial.println("% RH"); 46 | 47 | Serial.println("Sending data to server via HTTP POST"); 48 | String contentType = "application/x-www-form-urlencoded"; 49 | String postData = "temperature=" + String(temperatureF); 50 | postData += "&humidity=" + String(humidity); 51 | postData += "&device=" + String(DEVICE_ID); 52 | 53 | client.beginRequest(); 54 | client.post("/"); 55 | client.sendBasicAuth(HTTP_USER, HTTP_PASSWORD); 56 | client.sendHeader("Content-Type", "application/x-www-form-urlencoded"); 57 | client.sendHeader("Content-Length", postData.length()); 58 | client.beginBody(); 59 | client.print(postData); 60 | client.endRequest(); 61 | 62 | // read the status code and body of the response 63 | int statusCode = client.responseStatusCode(); 64 | String response = client.responseBody(); 65 | 66 | Serial.print("Status code: "); 67 | Serial.println(statusCode); 68 | Serial.print("Response: "); 69 | Serial.println(response); 70 | 71 | Serial.println("Waiting for ten seconds\n"); 72 | delay(10000); 73 | 74 | } 75 | 76 | void connectWiFi() { 77 | 78 | Serial.print("WiFi firmware version "); 79 | Serial.println(WiFi.firmwareVersion()); 80 | 81 | // attempt to connect to WiFi network 82 | while (wifi_status != WL_CONNECTED) { 83 | Serial.print("Attempting to connect to SSID: "); 84 | Serial.println(WIFI_SSID); 85 | wifi_status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); 86 | 87 | // wait 3 seconds for connection 88 | delay(3000); 89 | } 90 | Serial.println("Connected to WiFi"); 91 | printWiFiStatus(); 92 | 93 | } 94 | 95 | void printWiFiStatus() { 96 | // print your WiFi IP address: 97 | IPAddress ip = WiFi.localIP(); 98 | Serial.print("IP Address: "); 99 | Serial.println(ip); 100 | } 101 | -------------------------------------------------------------------------------- /02_Arduino/arduino/HttpClientBasicAuth/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = "devicenet"; 2 | const char WIFI_PASSWORD[] = "random-token"; 3 | 4 | const char SERVER_ADDRESS[] = "192.168.86.32"; 5 | const int SERVER_PORT = 3000; 6 | const char HTTP_USER[] = ""; 7 | const char HTTP_PASSWORD[] = ""; 8 | 9 | const char DEVICE_ID[] = "device_01"; 10 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LED/LED.ino: -------------------------------------------------------------------------------- 1 | // IoT Workshop 2 | // Control a LED via MQTT 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | 9 | WiFiSSLClient net; 10 | MqttClient mqtt(net); 11 | 12 | String ledTopic = "itp/" + DEVICE_ID + "/led"; 13 | 14 | const int ledPin = LED_BUILTIN; 15 | int status = WL_IDLE_STATUS; 16 | 17 | void setup() { 18 | Serial.begin(9600); 19 | 20 | // Wait for a serial connection 21 | while (!Serial) { } 22 | 23 | // initialize ledPin as an output. 24 | pinMode(ledPin, OUTPUT); 25 | 26 | Serial.println("Connecting WiFi"); 27 | connectWiFi(); 28 | 29 | // define function for incoming MQTT messages 30 | mqtt.onMessage(messageReceived); 31 | } 32 | 33 | void loop() { 34 | if (WiFi.status() != WL_CONNECTED) { 35 | connectWiFi(); 36 | } 37 | 38 | if (!mqtt.connected()) { 39 | connectMQTT(); 40 | } 41 | 42 | // poll for new MQTT messages and send keep alives 43 | mqtt.poll(); 44 | } 45 | 46 | void connectWiFi() { 47 | // Check for the WiFi module 48 | if (WiFi.status() == WL_NO_MODULE) { 49 | Serial.println("Communication with WiFi module failed!"); 50 | // don't continue 51 | while (true); 52 | } 53 | 54 | Serial.print("WiFi firmware version "); 55 | Serial.println(WiFi.firmwareVersion()); 56 | 57 | Serial.print("Attempting to connect to SSID: "); 58 | Serial.print(WIFI_SSID); 59 | Serial.print(" "); 60 | 61 | while (WiFi.begin(WIFI_SSID, WIFI_PASSWORD) != WL_CONNECTED) { 62 | // failed, retry 63 | Serial.print("."); 64 | delay(3000); 65 | } 66 | 67 | Serial.println("Connected to WiFi"); 68 | printWiFiStatus(); 69 | 70 | } 71 | 72 | void connectMQTT() { 73 | Serial.print("Connecting MQTT broker "); 74 | Serial.println(MQTT_BROKER); 75 | mqtt.setId(DEVICE_ID); 76 | mqtt.setUsernamePassword(MQTT_USER, MQTT_PASSWORD); 77 | 78 | while (!mqtt.connect(MQTT_BROKER, MQTT_PORT)) { 79 | Serial.print("Connection error "); 80 | Serial.println(mqtt.connectError()); 81 | Serial.println("Waiting 5 seconds before retrying"); 82 | delay(5000); 83 | // check the wifi before looping again 84 | if (WiFi.status() != WL_CONNECTED) { 85 | connectWiFi(); 86 | } 87 | } 88 | 89 | mqtt.subscribe(ledTopic); 90 | Serial.println("connected."); 91 | } 92 | 93 | void messageReceived(int messageSize) { 94 | String topic = mqtt.messageTopic(); 95 | String payload = mqtt.readString(); 96 | Serial.println("incoming: " + topic + " - " + messageSize + " "); 97 | Serial.println(payload); 98 | if (payload == "ON") { 99 | // turn the LED on 100 | digitalWrite(ledPin, HIGH); 101 | } else if (payload == "OFF") { 102 | // turn the LED off 103 | digitalWrite(ledPin, LOW); 104 | } 105 | } 106 | 107 | void printWiFiStatus() { 108 | // print your WiFi IP address: 109 | IPAddress ip = WiFi.localIP(); 110 | Serial.print("IP Address: "); 111 | Serial.println(ip); 112 | } 113 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LED/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = ""; 2 | const char WIFI_PASSWORD[] = ""; 3 | 4 | const char MQTT_BROKER[] = ""; 5 | const int MQTT_PORT = 8883; 6 | const char MQTT_USER[] = ""; 7 | const char MQTT_PASSWORD[] = ""; 8 | 9 | const String DEVICE_ID = ""; 10 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LoRa/LoRaButton/LoRaButton.ino: -------------------------------------------------------------------------------- 1 | // Send "on" over LoRa when a button is pressed 2 | // Send "off" over LoRa when a button is released 3 | #include 4 | #include 5 | 6 | const int buttonPin = 6; // the number of the pushbutton pin 7 | int buttonState = 0; // current state of the button 8 | int lastButtonState = 0; // previous state of the button 9 | 10 | #define PRESSED LOW 11 | 12 | void setup() { 13 | Serial.begin(9600); 14 | //while (!Serial); 15 | 16 | Serial.println("LoRa Sender"); 17 | 18 | if (!LoRa.begin(915E6)) { 19 | Serial.println("Starting LoRa failed!"); 20 | while (1); 21 | } 22 | 23 | // initialize the pushbutton pin as an input: 24 | pinMode(buttonPin, INPUT_PULLUP); 25 | } 26 | 27 | void loop() { 28 | 29 | // read the pushbutton input pin: 30 | buttonState = digitalRead(buttonPin); 31 | 32 | // compare the buttonState to its previous state 33 | if (buttonState != lastButtonState) { 34 | // if the state has changed, increment the counter 35 | if (buttonState == PRESSED) { 36 | send("on"); 37 | } else { 38 | send("off"); 39 | } 40 | // Delay a little bit to avoid bouncing 41 | delay(50); 42 | } 43 | // save the current state as the last state, for next time through the loop 44 | lastButtonState = buttonState; 45 | } 46 | 47 | void send(String message) { 48 | Serial.print("Sending packet: "); 49 | Serial.println(message); 50 | 51 | // send packet 52 | LoRa.beginPacket(); 53 | LoRa.print(message); 54 | LoRa.endPacket(); 55 | } 56 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LoRa/LoRaPacketLogger/LoRaPacketLogger.ino: -------------------------------------------------------------------------------- 1 | // Log received LoRa packets 2 | #include 3 | #include 4 | 5 | void setup() { 6 | Serial.begin(9600); 7 | while (!Serial); 8 | 9 | Serial.println("LoRa Receiver"); 10 | 11 | if (!LoRa.begin(915E6)) { 12 | Serial.println("Starting LoRa failed!"); 13 | while (1); 14 | } 15 | } 16 | 17 | void loop() { 18 | // try to parse packet 19 | int packetSize = LoRa.parsePacket(); 20 | if (packetSize) { 21 | Serial.println("Received packet"); 22 | 23 | Serial.print(" length : "); 24 | Serial.print(packetSize); 25 | Serial.println(); 26 | 27 | 28 | String s = ""; 29 | Serial.print(" bytes : "); 30 | // read packet 31 | for (int i = 0; i < packetSize; i++) { 32 | byte b = LoRa.read(); 33 | Serial.print(b, HEX); // print byte as hex 34 | Serial.print(" "); 35 | s += (char)b; // append byte to the String 36 | } 37 | Serial.println(); 38 | 39 | // replace linefeeds 40 | s.replace("\r","\\r"); 41 | s.replace("\n","\\n"); 42 | 43 | Serial.print(" string : "); 44 | Serial.println(s); 45 | 46 | Serial.print(" rssi : "); 47 | Serial.println(LoRa.packetRssi()); 48 | Serial.println(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LoRa/LoRaPacketLoggerLED/LoRaPacketLoggerLED.ino: -------------------------------------------------------------------------------- 1 | // Log received LoRa packets 2 | // Turn LED on and off when packet value is on or off 3 | #include 4 | #include 5 | 6 | const int ledPin = 5; 7 | 8 | void setup() { 9 | Serial.begin(9600); 10 | // while (!Serial); 11 | 12 | Serial.println("LoRa Receiver"); 13 | 14 | if (!LoRa.begin(915E6)) { 15 | Serial.println("Starting LoRa failed!"); 16 | while (1); 17 | } 18 | 19 | pinMode(ledPin, OUTPUT); 20 | 21 | } 22 | 23 | void loop() { 24 | // try to parse packet 25 | int packetSize = LoRa.parsePacket(); 26 | if (packetSize) { 27 | Serial.println("Received packet"); 28 | 29 | Serial.print(" length : "); 30 | Serial.print(packetSize); 31 | Serial.println(); 32 | 33 | String s = ""; 34 | Serial.print(" bytes : "); 35 | // read packet 36 | for (int i = 0; i < packetSize; i++) { 37 | byte b = LoRa.read(); 38 | Serial.print(b, HEX); // print byte as hex 39 | Serial.print(" "); 40 | s += (char)b; // append byte to the String 41 | } 42 | Serial.println(); 43 | 44 | // replace linefeeds 45 | s.replace("\r","\\r"); 46 | s.replace("\n","\\n"); 47 | 48 | Serial.print(" string : "); 49 | Serial.println(s); 50 | 51 | Serial.print(" rssi : "); 52 | Serial.println(LoRa.packetRssi()); 53 | Serial.println(); 54 | 55 | // toggle LED 56 | if (s.equalsIgnoreCase("ON")) { 57 | digitalWrite(ledPin, HIGH); 58 | } else if (s.equalsIgnoreCase("OFF")) { 59 | digitalWrite(ledPin, LOW); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LoRa/MKR1300-ENV/MKR1300-ENV.ino: -------------------------------------------------------------------------------- 1 | // Send Temperature and Humidity The Things Network 2 | // Demo for Maker Faire NY 2018 https://github.com/don/mfny2018-lora 3 | // Modified February 2020 to work with MRK ENV Sheild 4 | // (c) 2018-2020 Don Coleman 5 | 6 | // Hardware: 7 | // Arduino MKRWAN 1300 https://store.arduino.cc/usa/mkr-wan-1300 8 | // Arduino MKRENV Shield https://store.arduino.cc/usa/mkr-env-shield 9 | // 10 | // Libraries: 11 | // MKRWAN https://www.arduino.cc/en/Reference/MKRWAN 12 | // MKRENV https://www.arduino.cc/en/Reference/ArduinoMKRENV 13 | // CayenneLPP https://github.com/sabas1080/CayenneLPP 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "arduino_secrets.h" 20 | String appEui = SECRET_APP_EUI; 21 | String appKey = SECRET_APP_KEY; 22 | 23 | LoRaModem modem; 24 | 25 | CayenneLPP lpp(51); 26 | 27 | void setup() { 28 | Serial.begin(9600); 29 | // Uncomment the next line to wait for a serial connection when debugging 30 | // while (!Serial); 31 | 32 | // initialize the shield 33 | if (!ENV.begin()) { 34 | Serial.println("Failed to initialize MKR ENV shield!"); 35 | while (1); 36 | } 37 | 38 | // change this to your regional band (eg. US915, AS923, ...) 39 | if (!modem.begin(US915)) { 40 | Serial.println("Failed to start LoRa"); 41 | while (1) {} 42 | }; 43 | 44 | Serial.print("Your module version is: "); 45 | Serial.println(modem.version()); 46 | Serial.print("Your device EUI is: "); 47 | Serial.println(modem.deviceEUI()); 48 | 49 | Serial.println("Attempting to join LoRaWAN network with OTAA"); 50 | int connected = modem.joinOTAA(appEui, appKey); 51 | if (!connected) { 52 | Serial.println("Something went wrong; are you indoor? Move near a window and retry"); 53 | while (1) {} 54 | } 55 | Serial.println("Join Successful"); 56 | 57 | modem.minPollInterval(60); 58 | } 59 | 60 | void loop() { 61 | // read values from ENV shield 62 | float temperature = ENV.readTemperature(FAHRENHEIT); 63 | float humidity = ENV.readHumidity(); 64 | 65 | // Prepare Cayenne LPP 66 | lpp.reset(); 67 | lpp.addTemperature(1, temperature); 68 | lpp.addRelativeHumidity(2, humidity); 69 | 70 | // Send the data 71 | modem.beginPacket(); 72 | modem.write(lpp.getBuffer(), lpp.getSize()); 73 | int err = modem.endPacket(true); 74 | if (err > 0) { 75 | Serial.println("Message sent."); 76 | } else { 77 | Serial.println("Error sending data."); 78 | } 79 | 80 | // Wait 2.5 minutes between transmissions 81 | delay(2.5 * 60 * 1000); 82 | } 83 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LoRa/MKR1300-ENV/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | // Get the App EUI and App Key from your application in the Things Network Console 2 | // https://console.thethingsnetwork.org/applications 3 | #define SECRET_APP_EUI "" 4 | #define SECRET_APP_KEY "" 5 | -------------------------------------------------------------------------------- /02_Arduino/arduino/LoRa/MKR1300-LoRaSender/MKR1300-LoRaSender.ino: -------------------------------------------------------------------------------- 1 | // Send data from Serial over LoRa (not LoRaWAN) 2 | #include 3 | #include 4 | 5 | void setup() { 6 | Serial.begin(9600); 7 | while (!Serial); 8 | 9 | Serial.println("LoRa Sender"); 10 | 11 | // Configure LoRa module to transmit and receive at 915MHz (915*10^6) 12 | // Replace 915E6 with the frequency you need (eg. 433E6 for 433MHz) 13 | if (!LoRa.begin(915E6)) { 14 | Serial.println("Starting LoRa failed!"); 15 | while (1); 16 | } 17 | } 18 | 19 | void loop() { 20 | // if there's data from serial, send it over LoRa 21 | if (Serial.available() > 0) { 22 | LoRa.beginPacket(); 23 | while (Serial.available() > 0) { 24 | LoRa.write(Serial.read()); 25 | } 26 | LoRa.endPacket(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /02_Arduino/arduino/MKRGSM1400/config.h: -------------------------------------------------------------------------------- 1 | #define MQTT_BROKER "dev2db.com" 2 | #define MQTT_PORT 8883 3 | #define MQTT_USER "device_XX" 4 | #define MQTT_PASSWORD "" 5 | 6 | const String DEVICE_ID = "device_XX"; 7 | -------------------------------------------------------------------------------- /02_Arduino/arduino/MacAddress/MacAddress.ino: -------------------------------------------------------------------------------- 1 | // Print the WiFi firmware version and MAC address for MRK boards 2 | // Based on https://www.arduino.cc/en/Reference/WiFiNINAMACAddress 3 | // and https://forum.arduino.cc/index.php?topic=114568.msg862118#msg862118 4 | 5 | #include 6 | #ifdef ARDUINO_SAMD_MKR1000 7 | #include 8 | #define WIFI_LIB "WiFi101" 9 | #else 10 | #include 11 | #define WIFI_LIB "WiFiNINA" 12 | #endif 13 | 14 | byte mac[6]; 15 | 16 | void setup() { 17 | Serial.begin(9600); 18 | 19 | // Wait for a serial connection 20 | while (!Serial) { } 21 | 22 | Serial.println("Using " + String(WIFI_LIB) + " for WiFi"); 23 | 24 | Serial.print("Firmware version "); 25 | Serial.println(WiFi.firmwareVersion()); 26 | 27 | WiFi.macAddress(mac); 28 | 29 | char address[18] = {0}; 30 | sprintf(address,"%02X:%02X:%02X:%02X:%02X:%02X",mac[5],mac[4],mac[3],mac[2],mac[1],mac[0]); 31 | Serial.print("MAC "); 32 | Serial.println(address); 33 | } 34 | 35 | void loop () {} 36 | -------------------------------------------------------------------------------- /02_Arduino/arduino/NetworkTest/NetworkTest.ino: -------------------------------------------------------------------------------- 1 | // Network Test 2 | // 3 | // Run this sketch to test that your Arduino connects to the network 4 | // 5 | // This code is a modified version of Arturo Guadalupi's WiFiNINA WiFiSSLClient example 6 | 7 | #include 8 | #ifdef ARDUINO_SAMD_MKR1000 9 | #include 10 | #define WL_NO_MODULE WL_NO_SHIELD 11 | #else 12 | #include 13 | #endif 14 | 15 | #include "arduino_secrets.h" 16 | ///////please enter your sensitive data in the Secret tab/arduino_secrets.h 17 | char ssid[] = SECRET_SSID; // your network SSID (name) 18 | char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP) 19 | 20 | int status = WL_IDLE_STATUS; 21 | char server[] = "dev2db.com"; 22 | 23 | // Initialize the Ethernet client library 24 | WiFiSSLClient client; 25 | 26 | void setup() { 27 | //Initialize serial and wait for port to open: 28 | Serial.begin(9600); 29 | while (!Serial) { 30 | ; // wait for serial port to connect. Needed for native USB port only 31 | } 32 | 33 | // check for the WiFi module: 34 | if (WiFi.status() == WL_NO_MODULE) { 35 | Serial.println("Communication with WiFi module failed!"); 36 | // don't continue 37 | while (true); 38 | } 39 | 40 | Serial.print("WiFi firmware version "); 41 | Serial.println(WiFi.firmwareVersion()); 42 | 43 | // attempt to connect to WiFi network: 44 | while (status != WL_CONNECTED) { 45 | Serial.print("Attempting to connect to SSID: "); 46 | Serial.println(ssid); 47 | // Connect to WPA/WPA2 network 48 | status = WiFi.begin(ssid, pass); 49 | 50 | // wait 3 seconds for connection 51 | delay(3000); 52 | } 53 | Serial.println("Connected to wifi"); 54 | printWiFiStatus(); 55 | 56 | Serial.println("\nStarting connection to server..."); 57 | // if you get a connection, report back via serial: 58 | if (client.connect(server, 443)) { 59 | Serial.println("connected to server"); 60 | // Make a HTTP request: 61 | client.println("GET /test.html HTTP/1.1"); 62 | client.print("Host: "); 63 | client.println(String(server)); 64 | client.println("Connection: close"); 65 | client.println(); 66 | } 67 | } 68 | 69 | void loop() { 70 | // if there are incoming bytes available 71 | // from the server, read them and print them: 72 | while (client.available()) { 73 | char c = client.read(); 74 | Serial.write(c); 75 | } 76 | 77 | // if the server's disconnected, stop the client: 78 | if (!client.connected()) { 79 | Serial.println(); 80 | Serial.println("disconnecting from server."); 81 | client.stop(); 82 | 83 | // do nothing forevermore: 84 | while (true); 85 | } 86 | } 87 | 88 | 89 | void printWiFiStatus() { 90 | // print the SSID of the network you're attached to: 91 | Serial.print("SSID: "); 92 | Serial.println(WiFi.SSID()); 93 | 94 | // print your WiFi shield's IP address: 95 | IPAddress ip = WiFi.localIP(); 96 | Serial.print("IP Address: "); 97 | Serial.println(ip); 98 | 99 | // print the received signal strength: 100 | long rssi = WiFi.RSSI(); 101 | Serial.print("signal strength (RSSI):"); 102 | Serial.print(rssi); 103 | Serial.println(" dBm"); 104 | } 105 | -------------------------------------------------------------------------------- /02_Arduino/arduino/NetworkTest/arduino_secrets.h: -------------------------------------------------------------------------------- 1 | #define SECRET_SSID "" 2 | #define SECRET_PASS "" 3 | -------------------------------------------------------------------------------- /02_Arduino/arduino/SoilSensorAnalogRead/SoilSensorAnalogRead.ino: -------------------------------------------------------------------------------- 1 | // Capactive Soil Sensor Test 2 | // 3 | // Attach yellow wire on sensor to pin A0 4 | // Put 3.3 volts to red wire from VCC pin 5 | // Black wire goes to ground 6 | 7 | void setup() { 8 | Serial.begin(9600); 9 | } 10 | 11 | void loop() { 12 | int sensorValue = analogRead(A0); 13 | Serial.print("Soil Moisture "); 14 | Serial.println(sensorValue); 15 | delay(1000); 16 | } 17 | -------------------------------------------------------------------------------- /02_Arduino/arduino/TemperatureHumidity/TemperatureHumidity.ino: -------------------------------------------------------------------------------- 1 | // Send temperature and humidity data to MQTT 2 | // 3 | // WiFiNINA https://www.arduino.cc/en/Reference/WiFiNINA (MKR WiFi 1010) 4 | // Arduino MQTT Client https://github.com/arduino-libraries/ArduinoMqttClient 5 | // Adafruit DHT 6 | 7 | #include 8 | #include 9 | #include 10 | #include "Adafruit_SHT31.h" 11 | 12 | #include "config.h" 13 | 14 | Adafruit_SHT31 sht31 = Adafruit_SHT31(); 15 | 16 | WiFiSSLClient net; 17 | MqttClient mqtt(net); 18 | 19 | String temperatureTopic = "itp/" + DEVICE_ID + "/temperature"; 20 | String humidityTopic = "itp/" + DEVICE_ID + "/humidity"; 21 | 22 | // Publish every 10 seconds for the workshop. Real world apps need this data every 5 or 10 minutes. 23 | unsigned long publishInterval = 10 * 1000; 24 | unsigned long lastMillis = 0; 25 | 26 | void setup() { 27 | Serial.begin(9600); 28 | 29 | // Wait for a serial connection 30 | while (!Serial) { } 31 | 32 | // initialize the SHT31 sensor 33 | if (! sht31.begin(SHT31_DEFAULT_ADDR)) { 34 | Serial.println("Couldn't find SHT31"); 35 | while (1) { delay(1); } // wait forever 36 | } 37 | 38 | Serial.println("Connecting WiFi"); 39 | connectWiFi(); 40 | } 41 | 42 | void loop() { 43 | if (WiFi.status() != WL_CONNECTED) { 44 | connectWiFi(); 45 | } 46 | 47 | if (!mqtt.connected()) { 48 | connectMQTT(); 49 | } 50 | 51 | // poll for new MQTT messages and send keep alives 52 | mqtt.poll(); 53 | 54 | if (millis() - lastMillis > publishInterval) { 55 | lastMillis = millis(); 56 | 57 | // read the sensor values 58 | float temperatureC = sht31.readTemperature(); 59 | float temperatureF = temperatureC * 1.8 + 32; 60 | float humidity = sht31.readHumidity(); 61 | 62 | Serial.print(temperatureC); 63 | Serial.print("°C "); 64 | Serial.print(temperatureF); 65 | Serial.print("°F "); 66 | Serial.print(humidity); 67 | Serial.println("% RH"); 68 | 69 | mqtt.beginMessage(temperatureTopic); 70 | mqtt.print(temperatureF); 71 | mqtt.endMessage(); 72 | 73 | mqtt.beginMessage(humidityTopic); 74 | mqtt.print(humidity); 75 | mqtt.endMessage(); 76 | } 77 | } 78 | 79 | void connectWiFi() { 80 | // Check for the WiFi module 81 | if (WiFi.status() == WL_NO_MODULE) { 82 | Serial.println("Communication with WiFi module failed!"); 83 | // don't continue 84 | while (true); 85 | } 86 | 87 | Serial.print("WiFi firmware version "); 88 | Serial.println(WiFi.firmwareVersion()); 89 | 90 | Serial.print("Attempting to connect to SSID: "); 91 | Serial.print(WIFI_SSID); 92 | Serial.print(" "); 93 | 94 | while (WiFi.begin(WIFI_SSID, WIFI_PASSWORD) != WL_CONNECTED) { 95 | // failed, retry 96 | Serial.print("."); 97 | delay(3000); 98 | } 99 | 100 | Serial.println("Connected to WiFi"); 101 | printWiFiStatus(); 102 | 103 | } 104 | 105 | void connectMQTT() { 106 | Serial.print("Connecting MQTT broker "); 107 | Serial.println(MQTT_BROKER); 108 | mqtt.setId(DEVICE_ID); 109 | mqtt.setUsernamePassword(MQTT_USER, MQTT_PASSWORD); 110 | 111 | while (!mqtt.connect(MQTT_BROKER, MQTT_PORT)) { 112 | Serial.print("Connection error "); 113 | Serial.println(mqtt.connectError()); 114 | Serial.println("Waiting 5 seconds before retrying"); 115 | delay(5000); 116 | // check the wifi before looping again 117 | if (WiFi.status() != WL_CONNECTED) { 118 | connectWiFi(); 119 | } 120 | } 121 | 122 | Serial.println("connected."); 123 | } 124 | 125 | void printWiFiStatus() { 126 | // print your WiFi IP address: 127 | IPAddress ip = WiFi.localIP(); 128 | Serial.print("IP Address: "); 129 | Serial.println(ip); 130 | } 131 | -------------------------------------------------------------------------------- /02_Arduino/arduino/TemperatureHumidity/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = "devicenet"; 2 | const char WIFI_PASSWORD[] = "random-token"; 3 | 4 | const char MQTT_BROKER[] = "dev2db.com"; 5 | const int MQTT_PORT = 8883; 6 | const char MQTT_USER[] = "samantha"; 7 | const char MQTT_PASSWORD[] = "secret"; 8 | 9 | const String DEVICE_ID = "device_00"; 10 | -------------------------------------------------------------------------------- /02_Arduino/arduino/TemperatureHumidityLED/TemperatureHumidityLED.ino: -------------------------------------------------------------------------------- 1 | // Send temperature and humidity data to MQTT 2 | // 3 | // WiFiNINA https://www.arduino.cc/en/Reference/WiFiNINA (Arduino Nano 33 IoT / MKR WiFi 1010) 4 | // Arduino MQTT Client https://github.com/arduino-libraries/ArduinoMqttClient 5 | // Adafruit SHT31 6 | 7 | #include 8 | #include 9 | #include 10 | #include "Adafruit_SHT31.h" 11 | 12 | #include "config.h" 13 | 14 | Adafruit_SHT31 sht31 = Adafruit_SHT31(); 15 | 16 | WiFiSSLClient net; 17 | MqttClient mqtt(net); 18 | 19 | String temperatureTopic = "itp/" + DEVICE_ID + "/temperature"; 20 | String humidityTopic = "itp/" + DEVICE_ID + "/humidity"; 21 | String ledTopic = "itp/" + DEVICE_ID + "/led"; 22 | 23 | // Publish every 10 seconds for the workshop. Real world apps need this data every 5 or 10 minutes. 24 | unsigned long publishInterval = 10 * 1000; 25 | unsigned long lastMillis = 0; 26 | const int ledPin = LED_BUILTIN; 27 | 28 | void setup() { 29 | Serial.begin(9600); 30 | 31 | // initialize ledPin as an output. 32 | pinMode(ledPin, OUTPUT); 33 | 34 | // turn the LED on 35 | digitalWrite(ledPin, HIGH); 36 | 37 | // Wait for a serial connection 38 | while (!Serial) { } 39 | 40 | // turn the LED off 41 | digitalWrite(ledPin, LOW); 42 | 43 | // initialize the SHT31 sensor 44 | if (! sht31.begin(SHT31_DEFAULT_ADDR)) { 45 | Serial.println("Couldn't find SHT31"); 46 | while (1) { delay(1); } // wait forever 47 | } 48 | 49 | Serial.println("Connecting WiFi"); 50 | connectWiFi(); 51 | 52 | // set callback function for incoming MQTT messages 53 | mqtt.onMessage(messageReceived); 54 | } 55 | 56 | void loop() { 57 | if (WiFi.status() != WL_CONNECTED) { 58 | connectWiFi(); 59 | } 60 | 61 | if (!mqtt.connected()) { 62 | connectMQTT(); 63 | } 64 | 65 | // poll for new MQTT messages and send keep alives 66 | mqtt.poll(); 67 | 68 | if (millis() - lastMillis > publishInterval) { 69 | lastMillis = millis(); 70 | 71 | // read the sensor values 72 | float temperatureC = sht31.readTemperature(); 73 | float temperatureF = temperatureC * 1.8 + 32; 74 | float humidity = sht31.readHumidity(); 75 | 76 | Serial.print(temperatureC); 77 | Serial.print("°C "); 78 | Serial.print(temperatureF); 79 | Serial.print("°F "); 80 | Serial.print(humidity); 81 | Serial.println("% RH"); 82 | 83 | mqtt.beginMessage(temperatureTopic); 84 | mqtt.print(temperatureF); 85 | mqtt.endMessage(); 86 | 87 | mqtt.beginMessage(humidityTopic); 88 | mqtt.print(humidity); 89 | mqtt.endMessage(); 90 | } 91 | } 92 | 93 | void connectWiFi() { 94 | // Check for the WiFi module 95 | if (WiFi.status() == WL_NO_MODULE) { 96 | Serial.println("Communication with WiFi module failed!"); 97 | // don't continue 98 | while (true); 99 | } 100 | 101 | Serial.print("WiFi firmware version "); 102 | Serial.println(WiFi.firmwareVersion()); 103 | 104 | Serial.print("Attempting to connect to SSID: "); 105 | Serial.print(WIFI_SSID); 106 | Serial.print(" "); 107 | 108 | while (WiFi.begin(WIFI_SSID, WIFI_PASSWORD) != WL_CONNECTED) { 109 | // failed, retry 110 | Serial.print("."); 111 | delay(3000); 112 | } 113 | 114 | Serial.println("Connected to WiFi"); 115 | printWiFiStatus(); 116 | 117 | } 118 | 119 | void connectMQTT() { 120 | Serial.print("Connecting MQTT broker "); 121 | Serial.println(MQTT_BROKER); 122 | mqtt.setId(DEVICE_ID); 123 | mqtt.setUsernamePassword(MQTT_USER, MQTT_PASSWORD); 124 | 125 | while (!mqtt.connect(MQTT_BROKER, MQTT_PORT)) { 126 | Serial.print("Connection error "); 127 | Serial.println(mqtt.connectError()); 128 | Serial.println("Waiting 5 seconds before retrying"); 129 | delay(5000); 130 | // check the wifi before looping again 131 | if (WiFi.status() != WL_CONNECTED) { 132 | connectWiFi(); 133 | } 134 | } 135 | 136 | mqtt.subscribe(ledTopic); 137 | Serial.println("connected."); 138 | } 139 | 140 | void printWiFiStatus() { 141 | // print your WiFi IP address: 142 | IPAddress ip = WiFi.localIP(); 143 | Serial.print("IP Address: "); 144 | Serial.println(ip); 145 | } 146 | 147 | void messageReceived(int messageSize) { 148 | String topic = mqtt.messageTopic(); 149 | String payload = mqtt.readString(); 150 | Serial.println("incoming: " + topic + " - " + messageSize + " bytes "); 151 | Serial.println(payload); 152 | if (payload.equalsIgnoreCase("ON")) { 153 | digitalWrite(ledPin, HIGH); 154 | } else if (payload.equalsIgnoreCase("OFF")) { 155 | digitalWrite(ledPin, LOW); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /02_Arduino/arduino/TemperatureHumidityLED/config.h: -------------------------------------------------------------------------------- 1 | const char WIFI_SSID[] = "devicenet"; 2 | const char WIFI_PASSWORD[] = "random-token"; 3 | 4 | const char MQTT_BROKER[] = "dev2db.com"; 5 | const int MQTT_PORT = 8883; 6 | const char MQTT_USER[] = "samantha"; 7 | const char MQTT_PASSWORD[] = "secret"; 8 | 9 | const String DEVICE_ID = "device_00"; 10 | -------------------------------------------------------------------------------- /02_Arduino/exercises/README.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | * [Exercise 1: Development Environment](exercise1.md) 4 | * [Exercise 2: Assemble the Hardware](exercise2.md) 5 | * [Exercise 3: Sending data using HTTP](exercise3.md) 6 | * [Exercise 4: Sending data using MQTT](exercise4.md) 7 | * [Exercise 5: Viewing MQTT data](exercise5.md) 8 | * [Exercise 6: Graphing MQTT Data](exercise6.md) 9 | * [Exercise 7: Sending Data to Arduino](exercise7.md) 10 | * [Exercise 8: Connecting using X.509 certs](exercise8.md) 11 | * [Exercise 9: Viewing Data](exercise9.md) -------------------------------------------------------------------------------- /02_Arduino/exercises/exercise1.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 - Development Environment 2 | 3 | Set up your computer for Arduino development. 4 | 5 | ## Arduino IDE 6 | Install the Arduino IDE from https://arduino.cc/en/Main/Software#download 7 | 8 | ![Arduino IDE Download](images/ArduinoIDE.png) 9 | 10 | Click the "Just Download" button 11 | 12 | ![Arduino IDE Just Download](images/ArduinoIDE-just-download.png) 13 | 14 | Run the installer. 15 | 16 | ### Arduino SAMD Board Definitions 17 | Use the Arduino Boards Manager to install the Arduino SAMD Board definitions. Open the Boards Manager using the menu _Tools -> Boards -> Boards Manager_ 18 | 19 | ![Arduino Boards Manager](images/BoardsManager-menu.png) 20 | 21 | Search for "33 IoT" and install the Arduino SAMD Boards (32-bit ARM Cortex-M0+) definitions. 22 | 23 | ![Arduino SAMD Board Definitions](images/BoardsManager.png) 24 | 25 | ### Arduino Libraries 26 | Install the following Arduino Libraries using the Library manager. 27 | 28 | * WiFiNINA library (search for "NINA") 29 | * Arduino MQTT Client library (search for "ArduinoMQTTClient") 30 | * Arduino HTTP Client library (search for "ArduinoHTTPClient") 31 | * Arduino BearSSL library (search for "ArduinoBearSSL") 32 | * Adafruit SHT31 library (search for "Adafruit SHT31") 33 | 34 | Open the library manager using the menu _Tools -> Manage Libraries_ 35 | 36 | ![Arduino Library Manager Menu](images/ManageLibraries.png) 37 | 38 | Search for "NINA" and install the WiFiNINA library. 39 | 40 | ![Arduino Library Manager WiFiNINA](images/library-wifinina.png) 41 | 42 | Search for "ArduinoMQTTClient" and install the Arduino MQTT Client library. 43 | 44 | ![Arduino Library Manager MQTT](images/library-arduinomqttclient.png) 45 | 46 | Search for "ArduinoHTTPClient" and install the Arduino HTTP Client library. 47 | 48 | ![Arduino Library Manager MQTT](images/library-arduinohttpclient.png) 49 | 50 | Search for "ArduinoBearSSL" and install the ArduinoBearSSL library. If you are prompted to install missing dependencies, choose "Install All". 51 | 52 | ![Arduino Library Manager Search BearSSL](images/library-arduinobearssl.png) 53 | 54 | ![Arduino Library Manager Install ECCX](images/library-arduinobearssl-dependencies.png) 55 | 56 | Search for "Adfruit SHT31" and install the Adafruit SHT31 Library. If you are prompted to install missing dependencies, choose "Install All". 57 | 58 | ![Arduino Library Manager Search SHT31](images/library-sht31.png) 59 | 60 | ![Arduino Library Manager Install SHT31 Dependencies](images/library-sht31-dependencies.png) 61 | 62 | Next [Exercise 2: Assemble the Hardware](exercise2.md) -------------------------------------------------------------------------------- /02_Arduino/exercises/exercise4.md: -------------------------------------------------------------------------------- 1 | # Exercise 4: Sending data using MQTT 2 | 3 | In this module, you will publish temperature and humidity data using MQTT messages. 4 | 5 | 1. Open arduino/TemperatureHumdity/TemperatureHumidity.ino in the Arduino IDE 6 | 1. Switch to the config.h tab 7 | 1. Update WiFi ssid and password 8 | 1. Add the MQTT broker URL, username, and password 9 | 1. Add your device id 10 | 1. Deploy the code the Arduino _Sketch -> Upload_ 11 | 1. Open the serial monitor to view the data _Tools -> Serial Monitor_ 12 | 13 | ![Arduino serial monitor with DHT22 output](images/temperature-humidity.png) 14 | 15 | Next [Exercise 5: Viewing MQTT data](exercise5.md) 16 | 17 | -------------------------------------------------------------------------------- /02_Arduino/exercises/exercise5.md: -------------------------------------------------------------------------------- 1 | # Exercise 5: Viewing MQTT data 2 | 3 | In the last module, the Arduino was publishing data using MQTT messages. In this module, we will write code to subscribe to MQTT topics and view the messages. 4 | 5 | 1. Open www/subscribe/index.html in your browser 6 | 1. Enter your username and password 7 | 1. Press connect to view all data using the **#** wildcard 8 | 1. Disconnect 9 | 1. Change the topic name to display the temperature from your device "itp/deviceX/temperature" 10 | 1. Disconnect 11 | 1. Use the **+** wildcard to view all the data from your device "itp/deviceX/+" 12 | 1. Disconnect 13 | 1. Try the **+** wildcard to view everyone’s humidity data "itp/+/humidity" 14 | 1. Experiment with some additional topic using wildcards 15 | 1. Open www/subscribe/index.js and examine the MQTT code 16 | 1. Try adjusting how the data is displayed on the page 17 | 18 | ![Sceenshot of MQTT subscription example](images/subscribe.png) 19 | 20 | Next [Exercise 6: Graphing MQTT Data](exercise6.md) -------------------------------------------------------------------------------- /02_Arduino/exercises/exercise6.md: -------------------------------------------------------------------------------- 1 | # Exercise 6: Graphing MQTT Data 2 | 3 | This exercise displays the data from MQTT in a chart. 4 | 5 | 1. Open www/chart/index.html in your web browser 6 | 1. Enter a username and password 7 | 1. Enter your device id 8 | 1. Press Connect 9 | 1. Data will be plotted on the charts as it is arrives via MQTT 10 | 11 | ![Temperature and Humidity chart](images/chart.png) 12 | 13 | * If you're not seeing data, double check the device id. 14 | * Try breathing on the sensor to change the values. 15 | 16 | 1. Open /www/chart/mqtt.js to see the MQTT connection code. The chart can be modified by editing /www/chart/chart.js. 17 | 1. Open the Javascript console of your browser to see log messages. 18 | 19 | Next [Exercise 7: Sending Data to Arduino](exercise7.md) -------------------------------------------------------------------------------- /02_Arduino/exercises/exercise7.md: -------------------------------------------------------------------------------- 1 | # Exercise 7: Sending Data to Arduino 2 | 3 | In the previous examples, the Arduino published data using MQTT messages. The Arduino can also subscribe to a MQTT topic. 4 | 5 | 1. Open arduino/LED/LED.ino in the Arduino IDE 6 | 1. Switch to the config.h tab 7 | 1. Update WiFi ssid and password 8 | 1. Add the MQTT broker URL, username, and password 9 | 1. Add your device id 10 | 1. Deploy the code the Arduino _Sketch -> Upload_ 11 | 1. Open the serial monitor _Tools -> Serial Monitor_ 12 | 13 | The Arduino should now be connected to the network and waiting for MQTT messages. 14 | 15 | 1. Open www/switch/index.html in your web browser 16 | 1. Enter your username and password 17 | 1. Change deviceX to your device id 18 | 1. Press connect 19 | 1. Use the radio buttons to control the LED on your Arduino 20 | 21 | ![Screenshot of webpage to control the LED](images/switch.png) 22 | 23 | The Arduino can publish and subscribe to MQTT messages at the same time. The [TemperatureHumidityLED sketch](/arduino/TemperatureHumidityLED/TemperatureHumidityLED.ino) demonstrates the Arduino publishing messages to temperature and humidity topics, while subscribing to the LED topic. 24 | 25 | # Challenge: Dimmable LED 26 | 27 | Edit the Arudino code and make the LED dimmable. Add a [range control](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range) to the HTML page. Allow the user to use the radio buttons or the range control to adjust the LED. Note that pin 13 on the Nano 33 IoT does not support [analogWrite](https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/). 28 | 29 | Next [Exercise 8: Connecting using X.509 certs](exercise8.md) 30 | -------------------------------------------------------------------------------- /02_Arduino/exercises/exercise8.md: -------------------------------------------------------------------------------- 1 | # Exercise 8 - Connecting using X.509 certs 2 | 3 | AWS requires client certificates to securely authenticate devices with IoT Core. The Arduino 1010 has a crypto element for generating and storing keys. First we need to run code on the Arduino to generate a private key and a Certificate Signing Request (CSR). 4 | 5 | 1. Open arduino/GenerateCSR/GenerateCSR.ino in the Arduino IDE. 6 | 1. Upload the code to the board _Sketch -> Upload_ 7 | 1. Open the serial monitor _Tools -> Serial Monitor_. 8 | 1. Make sure the new line setting in the bottom of the window is set to "Both NL & CR". 9 | 1. Follow the prompts in the serial monitor. 10 | 1. Enter Y to permanently lock the crypto element. Locking the element is required to generate private keys on the device. 11 | 1. The Common Name is pre-populated with the serial number from your the crypto element. Hit enter to use the serial number as the common name. 12 | 1. Choose slot 0 for the key 13 | 1. Enter Y to generate the private key 14 | 1. A CSR will be printed out. Select and copy this text including the dashes and BEGIN and END request text. 15 | 16 | ![Screenshot of CSR generated by Arduino](images/generate-csr.png) 17 | 18 | Now that we have a CSR, we need to create a device in AWS IoT and get a certificate. This process can be done through the AWS Management Console or with the AWS API. For this workshop, we created a web form to automate the process. 19 | 20 | 1. Open https://itp-arduino-workshop.glitch.me/ 21 | 1. Login with the username and password provided by the instructors 22 | 1. Click Register Thing 23 | 1. Copy the CSR generated by Arduino and paste it into the form 24 | 1. Click register 25 | 1. Keep this browser window open for the next step 26 | 27 | ![Screenshot of entering CSR into form](images/get-cert-1.png) 28 | 29 | ![Screenshot of generated certificate](images/get-cert-2.png) 30 | 31 | ### AWS.ino 32 | 33 | Next, we add the generated certificate to our Arduino code. The ArduinoBearSSL library uses the certificate and private key to authenticate with the AWS server in place of a username and password. 34 | 35 | 1. Open arduino/AWS/AWS.ino in the Arudino IDE. 36 | 1. Switch to the config.h tab 37 | 1. Update the wifi SSID and password 38 | 1. Copy the broker URL from registration form and paste it into config.h 39 | 1. Copy the certificate from registration form and paste it into config.h 40 | 1. Deploy the code the Arduino _Sketch -> Upload_ 41 | 42 | ![Screenshot of empty config.h](images/aws-config-1.png) 43 | 44 | ![Screenshot of config.h with new data](images/aws-config-2.png) 45 | 46 | More information on using the MKR WiFi 1010 with AWS IoT can be found in the following tutorial: [Securely Connecting an Arduino MKR WiFi 1010 to AWS IoT Core](https://create.arduino.cc/projecthub/Arduino_Genuino/securely-connecting-an-arduino-mkr-wifi-1010-to-aws-iot-core-a9f365?ref=search&ref_id=AWS%20IoT%20core&offset=1) 47 | 48 | ### CSR and Certificate 49 | 50 | The CSR and certificate files are printed as Base64 encoded data. You can use [OpenSSL](https://www.openssl.org/) to see more details about these certificates. OpenSSL is installed on macOS. Windows users will need to download and install. For now, Windows users should pair up with someone running macOS. 51 | 52 | * Copy and paste your CSR file into a text file named **csr.txt** 53 | * Copy and paste your certificate into a file named **cert.txt** 54 | * Open a terminal and cd to the directory that contains your files. 55 | 56 | View the CSR using the command 57 | 58 | openssl req -in csr.txt -noout -text 59 | 60 | Your device name is listed as the Common Name (CN) in the Subject of the certificate signing request (CSR). 61 | 62 | ![Screenshot of openssl output for the CSR](images/openssl-csr-out.png) 63 | 64 | View the certificate using the command 65 | 66 | openssl x509 -in cert.txt -noout -text 67 | 68 | The certificate is issued by Amazon Web Services. Your device name is listed as the Common Name (CN) in the Subject field of the certificate. 69 | 70 | ![Screenshot of openssl output for the certificate](images/openssl-cert-out.png) 71 | 72 | 73 | 74 | 75 | Next [Exercise 9: Viewing Data](exercise9.md) -------------------------------------------------------------------------------- /02_Arduino/exercises/exercise9.md: -------------------------------------------------------------------------------- 1 | # Exercise 9: Viewing Data 2 | 3 | Now that the keys and certificate are setup, we are ready to upload the AWS sketch on to our board and connect. 4 | 5 | 1. Open arduino/AWS/AWS.ino in the Arudino IDE (if necessary) 6 | 1. Upload the code to the board _Sketch -> Upload_ 7 | 1. Open the serial monitor to view status updates of what the sketch is doing. 8 | 9 | On boot the sketch configures the temperature sensor and LED, in a similiar way as the previous exercises. It also setups up the crypto element and along the certificate needed for the secure connection to AWS IoT Core. After this is complete it will attempt to connect to the local WiFi network and then once this is succesfully complete, it will attempt to connect to the AWS IoT MQTT broker. 10 | 1. Open https://itp-arduino-workshop.glitch.me/ 11 | 1. Login with the username and password provided by the instructor. 12 | 1. Select your device in the drop down. 13 | 14 | ![Screenshot of Dashboard](images/dashboard-device-dropdown.png) 15 | 16 | 1. You will see a real-time graph of the environment data sent by the sketch. Every 10 seconds, sensor data is sent to the "things/*0123456789ABCDEF01*/environment" topic in JSON format. The cloud infrastructure saves this data to a database. The web page creates the graph with data from the database and then subscribes to the same topic appending new data to the graph as it arrives. 17 | 18 | ![Screenshot of Dashboard](images/dashboard-device.png) 19 | 20 | 1. Use the radio buttons or range slider to control the LED on your device. The program sends a MQTT message to the "things/*0123456789ABCDEF01*/led" topic with a brightness value. 21 | 22 | ## Notes 23 | 24 | ### Viewing MQTT Messages 25 | 26 | Incoming MQTT messages are written to the console. If you open the browser debug console, you can view them. 27 | 28 | ![Screenshot of Dashboard with Developer Tools open](images/dashboard-developer-tools.png) 29 | 30 | ### Device Policies 31 | 32 | The cloud infrastructure has been setup in a restrictive manner. AWS Core IoT has a policy applied to enforce the topics a device a device can access. 33 | 34 | * If you try to change the device id to something that is different from the certificate's common name, the MQTT connection will be refused. 35 | * If you try to publish or subscribe to topics outside "things/device_*XX*/" the MQTT broker will disconnect the connection. 36 | 37 | /* Thing Policy */ 38 | { 39 | "Version": "2012-10-17", 40 | "Statement": [ 41 | { 42 | "Effect": "Allow", 43 | "Action": "iot:Connect", 44 | "Resource": "arn:aws:iot:us-east-1:661516571298:client/${iot:Certificate.Subject.CommonName}" 45 | }, 46 | { 47 | "Effect": "Allow", 48 | "Action": [ 49 | "iot:Publish", 50 | "iot:Receive" 51 | ], 52 | "Resource": "arn:aws:iot:us-east-1:661516571298:topic/things/${iot:ClientId}/*" 53 | }, 54 | { 55 | "Effect": "Allow", 56 | "Action": "iot:Subscribe", 57 | "Resource": "arn:aws:iot:us-east-1:661516571298:topicfilter/things/${iot:ClientId}/*" 58 | } 59 | ] 60 | } 61 | 62 | 63 | ## Server Code 64 | 65 | You can view the server code on Glitch https://glitch.com/edit/#!/itp-arduino-workshop 66 | 67 | ## Bonus 68 | 69 | The device serial number is a great way to manage a large number of devices, but it doesn't generate a friendly device name. Try creating a new X.509 certificate with a different Common Name. Adjust the Arduino code so the device can still connect. Remember there is an AWS policy in place for our Core IoT implementation that requires the client id to match the common name in the certificate. -------------------------------------------------------------------------------- /02_Arduino/exercises/images/ArduinoIDE-just-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/ArduinoIDE-just-download.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/ArduinoIDE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/ArduinoIDE.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/BoardsManager-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/BoardsManager-menu.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/BoardsManager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/BoardsManager.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/ManageLibraries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/ManageLibraries.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/aws-config-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/aws-config-1.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/aws-config-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/aws-config-2.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/certificate-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/certificate-update.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/chart.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/dashboard-developer-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/dashboard-developer-tools.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/dashboard-device-dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/dashboard-device-dropdown.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/dashboard-device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/dashboard-device.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/dashboard.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/disconnect-bad-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/disconnect-bad-topic.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/generate-csr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/generate-csr.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/get-cert-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/get-cert-1.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/get-cert-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/get-cert-2.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/hardware-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/hardware-test.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/http-arduino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/http-arduino.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/http-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/http-server.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/http-start-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/http-start-server.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/https-arduino-glitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/https-arduino-glitch.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/https-server-glitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/https-server-glitch.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/library-arduinobearssl-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/library-arduinobearssl-dependencies.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/library-arduinobearssl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/library-arduinobearssl.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/library-arduinohttpclient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/library-arduinohttpclient.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/library-arduinomqttclient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/library-arduinomqttclient.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/library-sht31-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/library-sht31-dependencies.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/library-sht31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/library-sht31.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/library-wifinina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/library-wifinina.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/mkr1010-wiring.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/mkr1010-wiring.fzz -------------------------------------------------------------------------------- /02_Arduino/exercises/images/mkr1010-wiring_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/mkr1010-wiring_bb.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/network-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/network-test.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/openssl-cert-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/openssl-cert-out.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/openssl-csr-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/openssl-csr-out.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/shiftr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/shiftr.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/subscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/subscribe.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/switch.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/temperature-humidity-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/temperature-humidity-2.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/temperature-humidity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/temperature-humidity.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/wireshark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/wireshark.png -------------------------------------------------------------------------------- /02_Arduino/exercises/images/wiring.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/wiring.fzz -------------------------------------------------------------------------------- /02_Arduino/exercises/images/wiring_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/02_Arduino/exercises/images/wiring_bb.png -------------------------------------------------------------------------------- /02_Arduino/node/blink.js: -------------------------------------------------------------------------------- 1 | var mqtt = require('mqtt') 2 | var config = require('./config'); 3 | 4 | // pass in device_id or get from config 5 | var device = process.argv[2] || config.device; 6 | console.log(`blinking ${device}`); 7 | 8 | var client = mqtt.connect(config.server, { 9 | username: config.username, 10 | password: config.password 11 | }); 12 | 13 | client.on('connect', function () { 14 | on(); 15 | }); 16 | 17 | function on() { 18 | client.publish(`itp/${device}/led`, 'ON'); 19 | setTimeout(off, 1000); 20 | } 21 | 22 | function off() { 23 | client.publish(`itp/${device}/led`, 'OFF'); 24 | setTimeout(on, 1000); 25 | } -------------------------------------------------------------------------------- /02_Arduino/node/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | username: '', 3 | password: '', 4 | server: '', 5 | device: '' 6 | } 7 | -------------------------------------------------------------------------------- /02_Arduino/node/listen.js: -------------------------------------------------------------------------------- 1 | var mqtt = require('mqtt'); 2 | var config = require('./config'); 3 | 4 | var client = mqtt.connect(config.server, { 5 | username: config.username, 6 | password: config.password 7 | }); 8 | 9 | client.on('connect', function () { 10 | console.log("connected"); 11 | // # is the wildcard to match any devices 12 | client.subscribe("\#"); 13 | // + is the wildcard to match part of a topic 14 | // get temperature for workshop devices 15 | //client.subscribe("itp/+/temperature"); 16 | // get all messages for device1 17 | //client.subscribe("itp/device_xx/+"); 18 | }); 19 | 20 | client.on('message', function (topic, message) { 21 | // message is Buffer 22 | console.log(topic, message.toString()); 23 | }); -------------------------------------------------------------------------------- /02_Arduino/node/off.js: -------------------------------------------------------------------------------- 1 | var mqtt = require('mqtt') 2 | var config = require('./config'); 3 | 4 | var client = mqtt.connect(config.server, { 5 | username: config.username, 6 | password: config.password 7 | }); 8 | 9 | client.on('connect', function () { 10 | client.publish(`itp/${config.device}/led`, 'OFF'); 11 | client.end(); 12 | }); 13 | -------------------------------------------------------------------------------- /02_Arduino/node/on.js: -------------------------------------------------------------------------------- 1 | var mqtt = require('mqtt'); 2 | var config = require('./config'); 3 | 4 | var client = mqtt.connect(config.server, { 5 | username: config.username, 6 | password: config.password 7 | }); 8 | 9 | client.on('connect', function () { 10 | client.publish(`itp/${config.device}/led`, 'ON'); 11 | client.end(); 12 | }); 13 | -------------------------------------------------------------------------------- /02_Arduino/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "itp-mqtt-examples", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Node MQTT examples for ITP Device to Database class", 6 | "main": "listen.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Don Coleman", 11 | "license": "ISC", 12 | "dependencies": { 13 | "mqtt": "^4.x" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /02_Arduino/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iot-data", 3 | "version": "0.0.1", 4 | "description": "An express server that accepts IoT data", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "express": "", 11 | "express-basic-auth": "" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /02_Arduino/server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const bodyParser = require("body-parser"); 4 | 5 | app.use(bodyParser.urlencoded({ extended: true })); 6 | app.use(bodyParser.json()); 7 | 8 | app.post("/", function (request, response) { 9 | // posted data is in the body 10 | let data = request.body; 11 | // add a timestamp 12 | let dt = new Date(); 13 | data.timestamp = dt.getTime(); 14 | // add the remote address 15 | let remoteAddress = request.headers['x-forwarded-for'] || request.connection.remoteAddress; 16 | data.remoteAddress = remoteAddress; 17 | 18 | console.log(data); 19 | response.send("OK"); 20 | }); 21 | 22 | // listen for requests :) 23 | const listener = app.listen(process.env.PORT || '3000', '0.0.0.0', function() { 24 | console.log('Server is listening on port ' + listener.address().port); 25 | }); 26 | -------------------------------------------------------------------------------- /02_Arduino/server/server2.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | const bodyParser = require("body-parser"); 5 | app.use(bodyParser.urlencoded({ extended: true })); 6 | app.use(bodyParser.json()); 7 | 8 | const fs = require('fs'); 9 | const logFileName = __dirname + '/data.json'; 10 | 11 | // GET returns the data file 12 | app.get('/', function(request, response) { 13 | response.sendFile(logFileName); 14 | }); 15 | 16 | // POST write data to the log 17 | app.post("/", function (request, response) { 18 | // post data 19 | let data = request.body; 20 | // add a timestamp 21 | let dt = new Date(); 22 | data.timestamp = dt.getTime(); 23 | // add the remote address 24 | let remoteAddress = request.headers['x-forwarded-for'] || request.connection.remoteAddress; 25 | data.remoteAddress = remoteAddress; 26 | 27 | fs.appendFile(logFileName, JSON.stringify(data) + '\n', err => { 28 | if(err) { throw err }; 29 | console.log(data); 30 | response.send("OK"); 31 | }); 32 | }); 33 | 34 | // GET /clear - clears the log file 35 | app.get('/clear', function(request, response) { 36 | fs.writeFile(logFileName, '', err => { 37 | if(err) { throw err }; 38 | response.send('ok\n'); 39 | }); 40 | }); 41 | 42 | // listen for requests :) 43 | const listener = app.listen(process.env.PORT || '3000', '0.0.0.0', function() { 44 | fs.closeSync(fs.openSync(logFileName, 'w')); 45 | console.log('Server is listening on port ' + listener.address().port); 46 | }); 47 | -------------------------------------------------------------------------------- /02_Arduino/server/server3.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | const bodyParser = require("body-parser"); 5 | app.use(bodyParser.urlencoded({ extended: true })); 6 | app.use(bodyParser.json()); 7 | 8 | // username password combinations are in hardcoded in the users object 9 | const basicAuth = require('express-basic-auth'); 10 | app.use(basicAuth({ 11 | challenge: true, 12 | users: { 13 | 'sarah': 'hunter2', 14 | 'mike': 'coffee$', 15 | } 16 | })); 17 | 18 | const fs = require('fs'); 19 | const logFileName = __dirname + '/data.json'; 20 | 21 | // GET returns the data file 22 | app.get('/', function(request, response) { 23 | response.sendFile(logFileName); 24 | }); 25 | 26 | // POST write data to the log 27 | app.post("/", function (request, response) { 28 | // post data 29 | let data = request.body; 30 | // add a timestamp 31 | let dt = new Date(); 32 | data.timestamp = dt.getTime(); 33 | // add the remote address 34 | let remoteAddress = request.headers['x-forwarded-for'] || request.connection.remoteAddress; 35 | data.remoteAddress = remoteAddress; 36 | // add ther user 37 | data.user = request.auth.user; 38 | 39 | fs.appendFile(logFileName, JSON.stringify(data) + '\n', err => { 40 | if(err) { throw err }; 41 | console.log(data); 42 | response.send("OK"); 43 | }); 44 | }); 45 | 46 | // GET /clear - clears the log file 47 | app.get('/clear', function(request, response) { 48 | fs.writeFile(logFileName, '', err => { 49 | if(err) { throw err }; 50 | response.send('ok\n'); 51 | }); 52 | }); 53 | 54 | // listen for requests :) 55 | const listener = app.listen(process.env.PORT || '3000', '0.0.0.0', function() { 56 | fs.closeSync(fs.openSync(logFileName, 'w')); 57 | console.log('Server is listening on port ' + listener.address().port); 58 | }); 59 | -------------------------------------------------------------------------------- /02_Arduino/www/chart/chart.js: -------------------------------------------------------------------------------- 1 | var temperatureContext = document.getElementById("temperatureCanvas").getContext("2d"); 2 | var humidityContext = document.getElementById("humidityCanvas").getContext("2d"); 3 | 4 | var temperatureData = { 5 | labels: [], 6 | datasets: [{ 7 | label: "Temperature (°F)", 8 | backgroundColor: "rgba(0, 100, 0, 0.2)", 9 | borderColor: "rgba(0, 100, 0, 1)", 10 | borderWidth: 1, 11 | pointRadius: 0, 12 | data: [] 13 | }] 14 | }; 15 | 16 | var humidityData = { 17 | labels: [], 18 | datasets: [{ 19 | label: "Humidity (%)", 20 | backgroundColor: "rgba(0, 0, 100, 0.2)", 21 | borderColor: "rgba(0, 0, 100, 1)", 22 | borderWidth: 1, 23 | pointRadius: 0, 24 | data: [] 25 | }] 26 | }; 27 | 28 | var temperatureChart = new Chart.Line(temperatureContext, {data: temperatureData}); 29 | var humidityChart = new Chart.Line(humidityContext, {data: humidityData}); 30 | -------------------------------------------------------------------------------- /02_Arduino/www/chart/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | IoT Workshop 9 | 18 | 19 | 20 |
21 |
22 | MQTT 23 |
24 | 25 | 26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 | 34 |
35 |
36 | 37 | 38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /02_Arduino/www/chart/mqtt.js: -------------------------------------------------------------------------------- 1 | let client; 2 | let broker = 'wss://dev2db.com:8083'; 3 | 4 | function connect() { 5 | console.log('Connecting'); 6 | 7 | if (username.value.match(/device/)) { 8 | alert(`Only the Arduino should use the '${username.value}' account.\nTry your user account instead.`); 9 | return; 10 | } 11 | 12 | client = mqtt.connect(broker, { 13 | username: username.value, 14 | password: password.value 15 | }); 16 | 17 | client.on('connect', function () { 18 | console.log('connected'); 19 | connectButton.hidden = true; 20 | disconnectButton.hidden = false; 21 | document.querySelectorAll('input[type=text],input[type=password]').forEach(e => e.disabled = true); 22 | client.subscribe(`itp/${deviceId.value}/temperature`); 23 | client.subscribe(`itp/${deviceId.value}/humidity`); 24 | }); 25 | 26 | client.on('message', function (topic, message) { 27 | // message is Buffer 28 | console.log(topic, message.toString()); 29 | 30 | let dt = new Date().toLocaleString(); 31 | 32 | if (topic.endsWith("temperature")) { 33 | temperatureData.labels.push(dt); 34 | temperatureData.datasets[0].data.push(parseFloat(message.toString())); 35 | temperatureChart.update(); 36 | 37 | } else if (topic.endsWith("humidity")) { 38 | humidityData.labels.push(dt); 39 | humidityData.datasets[0].data.push(parseFloat(message.toString())); 40 | humidityChart.update(); 41 | } 42 | }); 43 | 44 | client.on('error', function (message) { 45 | alert(message); 46 | client.end(); 47 | }); 48 | 49 | } 50 | 51 | function disconnect() { 52 | if (client) { 53 | client.end(); 54 | } 55 | connectButton.hidden = false; 56 | disconnectButton.hidden = true; 57 | document.querySelectorAll('input[type=text],input[type=password]').forEach(e => e.disabled = false); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /02_Arduino/www/subscribe/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MQTT 6 | 12 | 13 | 14 | 15 |
16 |
17 | MQTT 18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 |

41 |     
42 |     
43 | 
44 | 
45 | 


--------------------------------------------------------------------------------
/02_Arduino/www/subscribe/index.js:
--------------------------------------------------------------------------------
 1 | let client;
 2 | 
 3 | function connect() {
 4 |   console.log('Connecting');
 5 | 
 6 |   if (username.value.match(/device/)) {
 7 |     alert(`Only the Arduino should use the '${username.value}' account.\nTry your user account instead.`);
 8 |     return;
 9 |   }
10 | 
11 |   client = mqtt.connect(broker.value, {
12 |     username: username.value,
13 |     password: password.value 
14 |   });
15 | 
16 |   client.on('connect', function () { 
17 |     pre.innerText = '';   
18 |     console.log('connected');
19 |     connectButton.hidden = true;
20 |     disconnectButton.hidden = false;
21 |     document.querySelectorAll('input[type=text],input[type=password]').forEach(e => e.disabled = true);
22 |     client.subscribe(topic.value);
23 |   })
24 | 
25 |   client.on('message', function (topic, message) {
26 |     // message is Buffer so call toString before logging
27 |     console.log(topic, message.toString()); 
28 |     pre.innerText += '\n' + new Date() + '\t' + topic.padEnd(30, ' ') + '\t' + message;
29 |   })
30 | 
31 |   client.on('error', function (message) {
32 |       alert(message);
33 |       client.end();
34 |   })
35 | }
36 | 
37 | function disconnect() {
38 |   if (client) {
39 |     client.end();
40 |   }
41 |   connectButton.hidden = false;
42 |   disconnectButton.hidden = true;
43 |   document.querySelectorAll('input[type=text],input[type=password]').forEach(e => e.disabled = false);
44 | }
45 | 
46 | 


--------------------------------------------------------------------------------
/02_Arduino/www/switch/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 |     Switch
 6 |     
12 | 
13 | 
14 | 
15 |     
16 |
17 | MQTT 18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 |
37 | LED 38 |
39 | 40 | 41 |
42 |
43 | 44 | 45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /02_Arduino/www/switch/index.js: -------------------------------------------------------------------------------- 1 | let client; 2 | let broker = 'wss://dev2db.com:8083'; 3 | 4 | function connect() { 5 | console.log('Connecting'); 6 | 7 | if (username.value.match(/device/)) { 8 | alert(`Only the Arduino should use the '${username.value}' account.\nTry your user account instead.`); 9 | return; 10 | } 11 | 12 | client = mqtt.connect(broker, { 13 | username: username.value, 14 | password: password.value 15 | }); 16 | 17 | client.on('connect', function () { 18 | console.log('connected'); 19 | connectButton.hidden = true; 20 | disconnectButton.hidden = false; 21 | radioButtons.disabled = false; 22 | document.querySelectorAll('input[type=text],input[type=password]').forEach(e => e.disabled = true); 23 | topic.disabled = false; 24 | }); 25 | 26 | client.on('error', function (message) { 27 | alert(message); 28 | client.end(); 29 | }); 30 | 31 | client.on('message', function (topic, message) { 32 | // message is Buffer 33 | console.log(topic, message.toString()); 34 | }) 35 | } 36 | 37 | function on() { 38 | client.publish(topic.value, 'ON'); 39 | console.log('on'); 40 | } 41 | 42 | function off() { 43 | client.publish(topic.value, 'OFF'); 44 | console.log('off'); 45 | } 46 | 47 | function disconnect() { 48 | if (client) { 49 | client.end(); 50 | } 51 | connectButton.hidden = false; 52 | disconnectButton.hidden = true; 53 | radioButtons.disabled = true; 54 | document.querySelectorAll('input[type=text],input[type=password]').forEach(e => e.disabled = false); 55 | } 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /03_RelationalDatabases/README.md: -------------------------------------------------------------------------------- 1 | * [PostgreSQL Intro](postgres.md) 2 | * [PostgreSQL Sensor Data Queries](postgres2.md) 3 | * [PostgreSQL Additional Queries](postgres3.md) 4 | * [SQLite](sqlite.md) 5 | * [Person Device Schema](person_device_sqlite.sql) 6 | * [Person Device Queries](person_device_queries.sql) 7 | -------------------------------------------------------------------------------- /03_RelationalDatabases/function_owner.sql: -------------------------------------------------------------------------------- 1 | -- PostgreSQL function to get the owner of a device 2 | -- 3 | -- Call the function directly 4 | -- SELECT owner('device_11') 5 | -- 6 | -- Or include in a query 7 | -- SELECT owner(device), device, count(*) FROM sensor_data GROUP BY device; 8 | -- 9 | CREATE OR REPLACE FUNCTION owner(device_name varchar) returns varchar AS $$ 10 | SELECT p.name 11 | FROM person p, device d 12 | WHERE p.id = d.person_id 13 | AND d.name = $1; 14 | $$ 15 | LANGUAGE SQL; 16 | 17 | GRANT EXECUTE ON FUNCTION owner TO public; 18 | -------------------------------------------------------------------------------- /03_RelationalDatabases/img/aggregate-error-no-group-by.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/aggregate-error-no-group-by.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/aws-rds-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/aws-rds-dashboard.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/count-sensor-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/count-sensor-data.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/date-one-row.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/date-one-row.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/dates-in-est.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/dates-in-est.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/desc-mqtt-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/desc-mqtt-message.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/desc-sensor-data-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/desc-sensor-data-text.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/desc-sensor-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/desc-sensor-data.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/distinct-device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/distinct-device.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/distinct-measurement-numeric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/distinct-measurement-numeric.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/distinct-measurement-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/distinct-measurement-text.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/e-create-temp-with-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/e-create-temp-with-query.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/e-filter-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/e-filter-color.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/e-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/e-filter.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/e-interval.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/e-interval.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/e-query-imported.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/e-query-imported.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/e-self-join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/e-self-join.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-cast-date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-cast-date.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-count-by-device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-count-by-device.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-count-by-measurement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-count-by-measurement.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-count-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-count-select.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-describe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-describe.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-distinct-device-measurement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-distinct-device-measurement.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-group-day-no-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-group-day-no-round.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-group-day-order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-group-day-order.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-group-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-group-day.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-group-hour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-group-hour.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-hour-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-hour-4.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-in.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-math.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-math.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-minute-15-drop-date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-minute-15-drop-date.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-minute-15-format-timestamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-minute-15-format-timestamp.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-minute-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-minute-15.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-order.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-over-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-over-100.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-temp-all-columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-temp-all-columns.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-temp-some-columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-temp-some-columns.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-v-daily-temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-v-daily-temp.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/f-when.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/f-when.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/insert-more-records.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/insert-more-records.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/insert.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/limit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/limit.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/min-by-device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/min-by-device.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/min-max-by-device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/min-max-by-device.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/min.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/pg-cli-install-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/pg-cli-install-windows.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/process-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/process-flow.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/psql-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/psql-connect.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/psql-create-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/psql-create-table.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/select-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/select-2.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/select-after-insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/select-after-insert.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/select-empty-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/select-empty-table.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/select-mqtt-count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/select-mqtt-count.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/select-mqtt-distinct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/select-mqtt-distinct.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/select-mqtt-like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/select-mqtt-like.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/select-mqtt-where.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/select-mqtt-where.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/set-timezone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/set-timezone.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/show-timezone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/show-timezone.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/specify-columns-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/specify-columns-2.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/specify-columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/specify-columns.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/too-many-rows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/too-many-rows.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/union.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/union.png -------------------------------------------------------------------------------- /03_RelationalDatabases/img/where-between.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/03_RelationalDatabases/img/where-between.png -------------------------------------------------------------------------------- /03_RelationalDatabases/itp-schema.sql: -------------------------------------------------------------------------------- 1 | -- CREATE DATABASE itp; 2 | -- \c itp 3 | 4 | CREATE TABLE mqtt_message ( 5 | id SERIAL PRIMARY KEY, 6 | topic TEXT NOT NULL, 7 | payload TEXT, 8 | recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE TABLE sensor_data ( 12 | id SERIAL PRIMARY KEY, 13 | device VARCHAR(50), 14 | measurement VARCHAR(50), 15 | reading NUMERIC(6, 2), 16 | recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP 17 | ); 18 | 19 | CREATE TABLE sensor_data_text ( 20 | id SERIAL PRIMARY KEY, 21 | device VARCHAR(50), 22 | measurement VARCHAR(50), 23 | reading TEXT, 24 | recorded_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP 25 | ); 26 | 27 | GRANT SELECT ON TABLE mqtt_message TO public; 28 | GRANT SELECT ON TABLE sensor_data TO public; 29 | GRANT SELECT ON TABLE sensor_data_text TO public; 30 | -------------------------------------------------------------------------------- /03_RelationalDatabases/person_device_postgres.sql: -------------------------------------------------------------------------------- 1 | -- Create and populate tables for people and devices 2 | 3 | -- PostgreSQL Schema 4 | 5 | CREATE TABLE person ( 6 | id SERIAL PRIMARY KEY, 7 | name VARCHAR(50) NOT NULL UNIQUE 8 | ); 9 | 10 | CREATE TABLE device ( 11 | id SERIAL PRIMARY KEY, 12 | name VARCHAR(50) NOT NULL UNIQUE, 13 | person_id INTEGER NOT NULL, 14 | FOREIGN KEY (person_id) REFERENCES person(id) 15 | ); 16 | 17 | GRANT SELECT ON TABLE person TO public; 18 | GRANT SELECT ON TABLE device TO public; 19 | 20 | -- Data 21 | INSERT INTO person (id, name) VALUES (1, 'Ricardo'); 22 | INSERT INTO device (name, person_id) VALUES ('device_10', 1); 23 | INSERT INTO person (id, name) VALUES (2, 'Nicholas'); 24 | INSERT INTO device (name, person_id) VALUES ('device_11', 2); 25 | INSERT INTO person (id, name) VALUES (3, 'Chloe'); 26 | INSERT INTO device (name, person_id) VALUES ('device_12', 3); 27 | INSERT INTO person (id, name) VALUES (4, 'Meijie'); 28 | INSERT INTO device (name, person_id) VALUES ('device_13', 4); 29 | INSERT INTO person (id, name) VALUES (5, 'Andres'); 30 | INSERT INTO device (name, person_id) VALUES ('device_14', 5); 31 | INSERT INTO person (id, name) VALUES (6, 'MK'); 32 | INSERT INTO device (name, person_id) VALUES ('device_15', 6); 33 | INSERT INTO person (id, name) VALUES (7, 'Thomas'); 34 | INSERT INTO device (name, person_id) VALUES ('device_16', 7); 35 | INSERT INTO person (id, name) VALUES (8, 'Nati'); 36 | INSERT INTO device (name, person_id) VALUES ('device_17', 8); 37 | INSERT INTO person (id, name) VALUES (9, 'Gracy'); 38 | INSERT INTO device (name, person_id) VALUES ('device_18', 9); 39 | INSERT INTO person (id, name) VALUES (10, 'Jiyun'); 40 | INSERT INTO device (name, person_id) VALUES ('device_19', 10); 41 | INSERT INTO person (id, name) VALUES (11, 'Don'); 42 | INSERT INTO device (name, person_id) VALUES ('device_00', 11); 43 | -- extra devices 44 | INSERT INTO device (name, person_id) SELECT 'device_01', id FROM person WHERE name = 'Don'; 45 | INSERT INTO device (name, person_id) SELECT 'device_02', id FROM person WHERE name = 'Don'; 46 | INSERT INTO device (name, person_id) SELECT 'device_03', id FROM person WHERE name = 'Don'; 47 | INSERT INTO device (name, person_id) SELECT 'device_04', id FROM person WHERE name = 'Don'; 48 | INSERT INTO device (name, person_id) SELECT 'device_05', id FROM person WHERE name = 'Don'; 49 | INSERT INTO device (name, person_id) SELECT 'device_06', id FROM person WHERE name = 'Don'; 50 | INSERT INTO device (name, person_id) SELECT 'device_07', id FROM person WHERE name = 'Don'; 51 | INSERT INTO device (name, person_id) SELECT 'device_08', id FROM person WHERE name = 'Don'; 52 | INSERT INTO device (name, person_id) SELECT 'device_09', id FROM person WHERE name = 'Don'; 53 | -------------------------------------------------------------------------------- /03_RelationalDatabases/person_device_queries.sql: -------------------------------------------------------------------------------- 1 | -- queries using person and device tables 2 | -- these should work in both SQLite and PostgreSQL 3 | 4 | -- device name and owner name 5 | SELECT device.name AS device, person.name AS owner 6 | FROM device, person 7 | WHERE device.person_id = person.id 8 | ORDER BY device.name; 9 | 10 | -- alternate join syntax. this does the same thing as the first query 11 | SELECT device.name AS device, person.name AS owner 12 | FROM device 13 | JOIN person ON device.person_id = person.id 14 | ORDER BY device.name; 15 | 16 | -- device name and owner name using table aliases 17 | SELECT d.name AS device, p.name AS owner 18 | FROM device d, person p 19 | WHERE d.person_id = p.id 20 | ORDER BY d.name; 21 | 22 | -- devices by owner 23 | SELECT p.name AS owner, d.name AS device 24 | FROM device d, person p 25 | WHERE d.person_id = p.id 26 | ORDER BY p.name; 27 | 28 | -- which device(s) does Don own? 29 | SELECT d.name 30 | FROM device d, person p 31 | WHERE d.person_id = p.id 32 | AND p.name = 'Don'; 33 | 34 | -- who owns device_16 35 | SELECT p.name 36 | FROM device d, person p 37 | WHERE d.person_id = p.id 38 | AND d.name = 'device_16'; 39 | 40 | -- show the person with sensor data 41 | SELECT p.name AS person, s.* 42 | FROM sensor_data s, person p, device d 43 | WHERE s.device = d.name 44 | AND d.person_id = p.id 45 | LIMIT 10; 46 | 47 | -- query sensor data by person name 48 | SELECT p.name AS person, s.* 49 | FROM sensor_data s, person p, device d 50 | WHERE s.device = d.name 51 | AND d.person_id = p.id 52 | AND p.name = 'Gracy' 53 | LIMIT 10; 54 | 55 | -- query sensor data by person name and measurement 56 | SELECT p.name AS person, s.* 57 | FROM sensor_data s, person p, device d 58 | WHERE s.device = d.name 59 | AND d.person_id = p.id 60 | AND p.name = 'Chloe' 61 | AND s.measurement = 'temperature' 62 | LIMIT 10; 63 | 64 | -- when was the last record sent for each person and device 65 | SELECT p.name AS person, d.name AS device, max(s.recorded_at) as last_recorded_at 66 | FROM sensor_data s, person p, device d 67 | WHERE s.device = d.name 68 | AND d.person_id = p.id 69 | AND p.name != 'Don' 70 | GROUP BY person, device 71 | ORDER BY last_recorded_at desc; 72 | 73 | -------------------------------------------------------------------------------- /03_RelationalDatabases/person_device_sqlite.sql: -------------------------------------------------------------------------------- 1 | -- Create and populate tables for people and devices 2 | 3 | -- SQLite Schema 4 | 5 | CREATE TABLE person ( 6 | id INTEGER PRIMARY KEY AUTOINCREMENT, 7 | name VARCHAR(50) NOT NULL UNIQUE 8 | ); 9 | 10 | CREATE TABLE device ( 11 | id INTEGER PRIMARY KEY AUTOINCREMENT, 12 | name VARCHAR(50) NOT NULL UNIQUE, 13 | person_id INTEGER NOT NULL, 14 | FOREIGN KEY (person_id) REFERENCES person(id) 15 | ); 16 | 17 | -- tell sqlite to enforce foreign keys 18 | PRAGMA foreign_keys=1; 19 | 20 | -- Data 21 | INSERT INTO person (id, name) VALUES (1, 'Ricardo'); 22 | INSERT INTO device (name, person_id) VALUES ('device_10', 1); 23 | INSERT INTO person (id, name) VALUES (2, 'Nicholas'); 24 | INSERT INTO device (name, person_id) VALUES ('device_11', 2); 25 | INSERT INTO person (id, name) VALUES (3, 'Chloe'); 26 | INSERT INTO device (name, person_id) VALUES ('device_12', 3); 27 | INSERT INTO person (id, name) VALUES (4, 'Meijie'); 28 | INSERT INTO device (name, person_id) VALUES ('device_13', 4); 29 | INSERT INTO person (id, name) VALUES (5, 'Andres'); 30 | INSERT INTO device (name, person_id) VALUES ('device_14', 5); 31 | INSERT INTO person (id, name) VALUES (6, 'MK'); 32 | INSERT INTO device (name, person_id) VALUES ('device_15', 6); 33 | INSERT INTO person (id, name) VALUES (7, 'Thomas'); 34 | INSERT INTO device (name, person_id) VALUES ('device_16', 7); 35 | INSERT INTO person (id, name) VALUES (8, 'Nati'); 36 | INSERT INTO device (name, person_id) VALUES ('device_17', 8); 37 | INSERT INTO person (id, name) VALUES (9, 'Gracy'); 38 | INSERT INTO device (name, person_id) VALUES ('device_18', 9); 39 | INSERT INTO person (id, name) VALUES (10, 'Jiyun'); 40 | INSERT INTO device (name, person_id) VALUES ('device_19', 10); 41 | INSERT INTO person (id, name) VALUES (11, 'Don'); 42 | INSERT INTO device (name, person_id) VALUES ('device_00', 11); 43 | -- extra devices 44 | INSERT INTO device (name, person_id) SELECT 'device_01', id FROM person WHERE name = 'Don'; 45 | INSERT INTO device (name, person_id) SELECT 'device_02', id FROM person WHERE name = 'Don'; 46 | INSERT INTO device (name, person_id) SELECT 'device_03', id FROM person WHERE name = 'Don'; 47 | INSERT INTO device (name, person_id) SELECT 'device_04', id FROM person WHERE name = 'Don'; 48 | INSERT INTO device (name, person_id) SELECT 'device_05', id FROM person WHERE name = 'Don'; 49 | INSERT INTO device (name, person_id) SELECT 'device_06', id FROM person WHERE name = 'Don'; 50 | INSERT INTO device (name, person_id) SELECT 'device_07', id FROM person WHERE name = 'Don'; 51 | INSERT INTO device (name, person_id) SELECT 'device_08', id FROM person WHERE name = 'Don'; 52 | INSERT INTO device (name, person_id) SELECT 'device_09', id FROM person WHERE name = 'Don'; 53 | -------------------------------------------------------------------------------- /03_RelationalDatabases/postgres3.md: -------------------------------------------------------------------------------- 1 | Addition SQL examples using the `itp` database 2 | 3 | psql -h pg.dev2db.com -U xx 4 | \c itp 5 | set timezone TO 'America/New_York'; 6 | 7 | You can set the timezone to 'Asia/Shanghai' or 'Asia/Seoul' to see local times, but use 'America/New_York' here to compare the results. 8 | 9 | Use `interval` to get data within a time frame. Select records that arrived in the last 30 seconds 10 | 11 | select * from sensor_data where recorded_at > now() - interval '30 seconds'; 12 | 13 | ![sample query results for interval](img/e-interval.png) 14 | 15 | You can use `filter` to create new columns when aggregating data. 16 | 17 | SELECT recorded_at::date AS date, 18 | device, 19 | avg(reading) FILTER (WHERE measurement = 'temperature') AS temperature, 20 | avg(reading) FILTER (WHERE measurement = 'humidity') AS humidity 21 | FROM sensor_data 22 | WHERE measurement IN ('temperature', 'humidity') 23 | GROUP BY date, device 24 | ORDER BY date, device; 25 | 26 | ![sample query results using filter](img/e-filter.png) 27 | 28 | Using filter to combine average red, green, and blue readings into one row. 29 | 30 | SELECT recorded_at::date AS date, 31 | extract(HOUR FROM recorded_at) as hour, 32 | device, 33 | avg(reading) FILTER (WHERE measurement = 'red')::integer AS red, 34 | avg(reading) FILTER (WHERE measurement = 'green')::integer AS green, 35 | avg(reading) FILTER (WHERE measurement = 'blue')::integer AS blue 36 | FROM sensor_data 37 | WHERE measurement IN ('red', 'green', 'blue') 38 | GROUP BY date, hour, device 39 | ORDER BY device, date, hour; 40 | 41 | ![sample query results using filter](img/e-filter-color.png) 42 | 43 | If sensor reading arrive around the same time, you can create a query to combined the multiple readings into one row. 44 | 45 | -- Put red, green, blue all on the same row for a device 46 | -- this assumes the readings all arrived within the same second 47 | SELECT r.device, r.reading AS red, g.reading AS green, b.reading AS blue, date_trunc('second', r.recorded_at) as recorded_at 48 | FROM sensor_data r, sensor_data g, sensor_data b 49 | WHERE r.measurement = 'red' 50 | AND g.measurement = 'green' 51 | AND b.measurement = 'blue' 52 | AND r.device = g.device 53 | AND r.device = b.device 54 | AND date_trunc('second', r.recorded_at) = date_trunc('second', g.recorded_at) 55 | AND date_trunc('second', r.recorded_at) = date_trunc('second', b.recorded_at); 56 | 57 | ![sample query results using self join](img/e-self-join.png) 58 | 59 | ## Exporting to CSV 60 | 61 | psql can export data to a CSV file on your machine using `\copy`. 62 | 63 | \copy sensor_data to '/tmp/sensor_data.csv' DELIMITER ',' CSV HEADER; 64 | 65 | You can also export the results of a query instead of the whole table. 66 | 67 | \copy (select * from sensor_data where device='device_04') to '/tmp/device_04.csv' DELIMITER ',' CSV HEADER; 68 | 69 | ## Create temporary tables 70 | 71 | You can use the results of a query to create a temporary table. Temporary tables are deleted when you disconnect. 72 | 73 | SELECT * 74 | INTO TEMPORARY TABLE device_04 75 | FROM sensor_data 76 | WHERE device = 'device_04'; 77 | 78 | ## Importing from CSV 79 | 80 | Create an empty temporary table for importing data. They query uses the definition of sensor_data to define the columns while the limit 0 keeps the table empty. 81 | 82 | select * into temporary table import_example from sensor_data limit 0; 83 | 84 | ![sample query results of imported data](img/e-create-temp-with-query.png) 85 | 86 | Import CSV - psql can import data to a CSV file on you machine. 87 | 88 | \copy import_example from '/tmp/sensor_data.csv' DELIMITER ',' CSV HEADER; 89 | 90 | After importing, select to see the data. Since this it a temporary table it will be deleted when you disconnect. 91 | 92 | select * from import_example limit 10; 93 | 94 | ![sample query results of imported data](img/e-query-imported.png) 95 | -------------------------------------------------------------------------------- /04_TimeSeries/README.md: -------------------------------------------------------------------------------- 1 | * [TimescaleDB](timescaledb.md) 2 | * [InfluxDB](influxdb.md) 3 | -------------------------------------------------------------------------------- /05_Code/README.md: -------------------------------------------------------------------------------- 1 | * [Processing MQTT Data with Node.js](processing-mqtt-data.md) 2 | * [Graphing TimescaleDB Data with Google Charts](graphing-data.md) 3 | -------------------------------------------------------------------------------- /05_Code/img/by-day-pretty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/by-day-pretty.png -------------------------------------------------------------------------------- /05_Code/img/by-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/by-day.png -------------------------------------------------------------------------------- /05_Code/img/device-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/device-list.png -------------------------------------------------------------------------------- /05_Code/img/device_01-temperature-json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/device_01-temperature-json.png -------------------------------------------------------------------------------- /05_Code/img/device_ef3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/device_ef3.png -------------------------------------------------------------------------------- /05_Code/img/devices-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/devices-browser.png -------------------------------------------------------------------------------- /05_Code/img/devices-curl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/devices-curl.png -------------------------------------------------------------------------------- /05_Code/img/hello-express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/hello-express.png -------------------------------------------------------------------------------- /05_Code/img/hello-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/hello-index.png -------------------------------------------------------------------------------- /05_Code/img/message-count-by-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/message-count-by-day.png -------------------------------------------------------------------------------- /05_Code/img/message-count-by-device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/message-count-by-device.png -------------------------------------------------------------------------------- /05_Code/img/per-device-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/per-device-1.png -------------------------------------------------------------------------------- /05_Code/img/per-device-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/per-device-2.png -------------------------------------------------------------------------------- /05_Code/img/pm2-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/pm2-list.png -------------------------------------------------------------------------------- /05_Code/img/sms-notification.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/05_Code/img/sms-notification.jpg -------------------------------------------------------------------------------- /06_Tools/README.md: -------------------------------------------------------------------------------- 1 | * [Google Sheets](./spreadsheet.md) 2 | * [Node-RED](./node-red.md) 3 | * [Grafana](./grafana.md) 4 | -------------------------------------------------------------------------------- /06_Tools/diagrams.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/diagrams.pdf -------------------------------------------------------------------------------- /06_Tools/img/15-min-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/15-min-chart.png -------------------------------------------------------------------------------- /06_Tools/img/15-min-import-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/15-min-import-file.png -------------------------------------------------------------------------------- /06_Tools/img/15-min-imported-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/15-min-imported-data.png -------------------------------------------------------------------------------- /06_Tools/img/15-min-inline-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/15-min-inline-chart.png -------------------------------------------------------------------------------- /06_Tools/img/15-min-inline-timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/15-min-inline-timeline.png -------------------------------------------------------------------------------- /06_Tools/img/daily-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/daily-chart.png -------------------------------------------------------------------------------- /06_Tools/img/daily-import-file-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/daily-import-file-dialog.png -------------------------------------------------------------------------------- /06_Tools/img/daily-import-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/daily-import-file.png -------------------------------------------------------------------------------- /06_Tools/img/daily-imported-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/daily-imported-data.png -------------------------------------------------------------------------------- /06_Tools/img/daily-inline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/daily-inline.png -------------------------------------------------------------------------------- /06_Tools/img/daily-move-to-own-sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/daily-move-to-own-sheet.png -------------------------------------------------------------------------------- /06_Tools/img/flow-alert-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-alert-complete.png -------------------------------------------------------------------------------- /06_Tools/img/flow-alert-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-alert-debug.png -------------------------------------------------------------------------------- /06_Tools/img/flow-alert-email-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-alert-email-message.png -------------------------------------------------------------------------------- /06_Tools/img/flow-alert-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-alert-email.png -------------------------------------------------------------------------------- /06_Tools/img/flow-alert-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-alert-test.png -------------------------------------------------------------------------------- /06_Tools/img/flow-hello-inject-node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-hello-inject-node.png -------------------------------------------------------------------------------- /06_Tools/img/flow-hello-unconfigured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-hello-unconfigured.png -------------------------------------------------------------------------------- /06_Tools/img/flow-hello-world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-hello-world.png -------------------------------------------------------------------------------- /06_Tools/img/flow-install-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-install-email.png -------------------------------------------------------------------------------- /06_Tools/img/flow-led-switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-led-switch.png -------------------------------------------------------------------------------- /06_Tools/img/flow-log-format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-log-format.png -------------------------------------------------------------------------------- /06_Tools/img/flow-mqtt-add-broker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-mqtt-add-broker.png -------------------------------------------------------------------------------- /06_Tools/img/flow-mqtt-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-mqtt-debug.png -------------------------------------------------------------------------------- /06_Tools/img/flow-mqtt-log-file-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-mqtt-log-file-config.png -------------------------------------------------------------------------------- /06_Tools/img/flow-mqtt-node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-mqtt-node.png -------------------------------------------------------------------------------- /06_Tools/img/flow-new-mqtt-broker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/flow-new-mqtt-broker.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-add-datasource-influx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-add-datasource-influx.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-add-datasource-timescale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-add-datasource-timescale.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-add-datasource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-add-datasource.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-before-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-before-dashboard.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-dashboard-with-gauge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-dashboard-with-gauge.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-dashboard.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-gauge-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-gauge-query.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-home.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-influx-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-influx-config.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-influx-config2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-influx-config2.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-influx-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-influx-query.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-influx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-influx.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-login.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-new-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-new-dashboard.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-query-inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-query-inspector.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-select-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-select-one.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-timescale-influx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-timescale-influx.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-timescale-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-timescale-query.png -------------------------------------------------------------------------------- /06_Tools/img/grafana-timescale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/grafana-timescale.png -------------------------------------------------------------------------------- /06_Tools/img/new-sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/new-sheet.png -------------------------------------------------------------------------------- /06_Tools/img/services-grafana-automatic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/services-grafana-automatic.png -------------------------------------------------------------------------------- /06_Tools/img/services-grafana-manual-startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/services-grafana-manual-startup.png -------------------------------------------------------------------------------- /06_Tools/img/services-grafana-properties-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/services-grafana-properties-menu.png -------------------------------------------------------------------------------- /06_Tools/img/services-grafana-properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/services-grafana-properties.png -------------------------------------------------------------------------------- /06_Tools/img/services-grafana-startup-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/services-grafana-startup-menu.png -------------------------------------------------------------------------------- /06_Tools/img/services-grafana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/services-grafana.png -------------------------------------------------------------------------------- /06_Tools/img/tail-log-good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/tail-log-good.png -------------------------------------------------------------------------------- /06_Tools/img/tail-log-payload-only-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/tail-log-payload-only-windows.png -------------------------------------------------------------------------------- /06_Tools/img/tail-log-payload-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/06_Tools/img/tail-log-payload-only.png -------------------------------------------------------------------------------- /06_Tools/spreadsheet.md: -------------------------------------------------------------------------------- 1 | # Google Sheets 2 | 3 | Use Google spreadsheets to graph data. 4 | 5 | ### TimescaleDB 6 | 7 | Open `Terminal.app` or `cmd.exe` and create a new project. 8 | 9 | mkdir week6 10 | cd week6 11 | 12 | Connect to TimescaleDB 13 | 14 | psql -h timescale.dev2db.com -U your-user-name tsitp 15 | 16 | Set the timezone 17 | 18 | SET timezone TO 'America/New_York'; 19 | 20 | ### Daily Min, Max, and Average Temperatures 21 | 22 | Run a query and export min, max, and average daily temperature for device_01 (or your device) as a CSV file. The `\copy` command can't handle muli-line queries, so for readability, we create a temporary view, then export the view to CSV. 23 | 24 | CREATE TEMPORARY VIEW daily_temperatures AS 25 | SELECT time_bucket('1 day', recorded_at::timestamp)::date AS date, 26 | min(reading) AS min_temp, max(reading) AS max_temp, round(avg(reading)) AS avg_temp 27 | FROM sensor_data 28 | WHERE measurement = 'temperature' 29 | AND device = 'device_01' 30 | GROUP BY date 31 | ORDER BY date; 32 | 33 | Export the data from the view to a CSV file 34 | 35 | \COPY (select * from daily_temperatures) to 'daily_temperatures.csv' WITH (format csv, header) 36 | 37 | Create a new Google spreadsheet https://sheets.new 38 | 39 | ![screenshot of new Google spreadsheet](img/new-sheet.png) 40 | 41 | Import data using `File -> Import -> Upload` 42 | 43 | ![screenshot of spreadsheet import data options](img/daily-import-file-dialog.png) 44 | 45 | ![screenshot of spreadsheet import data screen](img/daily-import-file.png) 46 | 47 | ![screenshot of spreadsheet with imported data](img/daily-imported-data.png) 48 | 49 | Insert a chart using `Insert -> Chart` 50 | 51 | ![screenshot of spreadsheet with chart over data](img/daily-inline.png) 52 | 53 | Move the chart to it's own page. Click top right corner of chart. On the menu choose "Move to own sheet". 54 | 55 | ![screenshot showing "move to own sheet" menu](img/daily-move-to-own-sheet.png) 56 | 57 | ![screenshot showing daily temperature chart on new tab](img/daily-chart.png) 58 | 59 | 60 | ## Average Temperature using 15 Minute Buckets 61 | 62 | Run a 2nd query, exporting average temperature using 15 minute time buckets. 63 | 64 | CREATE TEMPORARY VIEW fifteen_minute AS 65 | SELECT time_bucket('15 minutes', recorded_at::timestamp) AS time, round(avg(reading)::numeric, 2) as temperature 66 | FROM sensor_data 67 | WHERE measurement = 'temperature' 68 | AND device = 'device_01' 69 | GROUP BY time 70 | ORDER BY time; 71 | 72 | Export the data to CSV 73 | 74 | \COPY (SELECT * FROM fifteen_minute) to 'fifteen_minute_avg_temp.csv' WITH (format csv, header true) 75 | 76 | Import data using `File -> Import -> Upload`. Be sure to select "Insert new sheet(s)". 77 | 78 | ![screenshot of spreadsheet import data options](img/15-min-import-file.png) 79 | 80 | ![screenshot of spreadsheet with imported data](img/15-min-imported-data.png) 81 | 82 | Insert a chart using `Insert -> Chart` 83 | 84 | ![screenshot of spreadsheet with chart over data](img/15-min-inline-chart.png) 85 | 86 | Change the Chart Type to Timeline chart (near bottom) 87 | 88 | ![screenshot of spreadsheet as timeline chart](img/15-min-inline-timeline.png) 89 | 90 | Move the chart to a new sheet 91 | 92 | ![screenshot of chart on new spreadsheet tab](img/15-min-chart.png) 93 | -------------------------------------------------------------------------------- /07_AWS/README.md: -------------------------------------------------------------------------------- 1 | ![diagram showing system with Arduino and AWS Overview of system](img/aws.png) 2 | 3 | * [AWS Core IoT](aws.md) 4 | * [Rules](rules.asciidoc) 5 | * [PostgreSQL](postgresql.md) 6 | * [InfluxDB](influxdb.md) 7 | * [Notifications](notifications.md) 8 | * [Logging](logging.md) 9 | * [Glitch & Chart.js](glitch.md) 10 | * [Grafana](grafana.md) 11 | -------------------------------------------------------------------------------- /07_AWS/glitch.md: -------------------------------------------------------------------------------- 1 | # Glitch 2 | 3 | Grafana doesn't natively support DynamoDB. However you can modify the Glitch application from earlier in the semester to visualize data from Dynamo. 4 | 5 | ## IAM 6 | 7 | Before we get to Glitch, we need to create an new AWS user. Open the [Users](https://us-east-1.console.aws.amazon.com/iamv2/home?region=us-east-1#/users) page in AWS Identity and Access Management (IAM). 8 | 9 | ![](img/iam-users.png) 10 | 11 | Create a new user named `glitch`. Choose `Access key` for the AWS credential type. 12 | 13 | ![](img/iam-glitch-user.png) 14 | 15 | AWS uses permissions to determine which resources a user can access. Choose `Attach policies directly`. Type `IoTFull | Dynamo` into the filter box to limit which policies are shown. Check the box next to `AWSIoTFullAccess` and `DynamoReader-environment`. 16 | 17 | ![](img/iam-permissions.png) 18 | 19 | Click `Next: Tags`. Skip the tags page and choose `Next: Review`. Click `Create User`. 20 | 21 | ![](img/iam-review-user.png) 22 | 23 | The final page shows the `Access key ID` and `Secret access key` for the glitch user. These values are like a username and password. They must be protected and should never be shared. The secret access key can not be retrieved once you leave this page. 24 | 25 | ![](img/iam-download-key.png) 26 | 27 | ## Glitch 28 | 29 | Open https://glitch.com/edit/#!/itp-arduino-workshop. Use the button on the top right to "Remix to Edit" to make a copy of the project. Open the `.env` file to configure the server. 30 | 31 | Add a username and password that users must enter to access the website 32 | 33 | USERNAME="itp" 34 | PASSWORD="secret" 35 | 36 | Enter your AWS information. 37 | 38 | THING_POLICY="ThingPolicy" 39 | AWS_REGION="us-east-1" 40 | AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" 41 | AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" 42 | 43 | ![](img/glitch-env.png) 44 | 45 | Once the properties are set, choose "Show -> In a New Window" from the menu at the top. 46 | 47 | ![](img/glitch-ui.png) 48 | 49 | Next [Grafana](grafana.md) -------------------------------------------------------------------------------- /07_AWS/grafana.md: -------------------------------------------------------------------------------- 1 | ## Grafana 2 | 3 | Use Grafana to visualize your sensor data. Log into your local Grafana server or https://grafana.dev2db.com. Add a connection to your PostgreSQL RDS database and your Influx database. Create a graph to display your sensor data. 4 | 5 | ![](img/grafana-query.png) 6 | ![](img/grafana-graph.png) 7 | -------------------------------------------------------------------------------- /07_AWS/img/aws-cloudformation-create-agree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-cloudformation-create-agree.png -------------------------------------------------------------------------------- /07_AWS/img/aws-cloudformation-create-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-cloudformation-create-stack.png -------------------------------------------------------------------------------- /07_AWS/img/aws-cloudformation-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-cloudformation-stack.png -------------------------------------------------------------------------------- /07_AWS/img/aws-cloudformation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-cloudformation.png -------------------------------------------------------------------------------- /07_AWS/img/aws-iot-custom-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-iot-custom-endpoint.png -------------------------------------------------------------------------------- /07_AWS/img/aws-iot-download-certificate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-iot-download-certificate.png -------------------------------------------------------------------------------- /07_AWS/img/aws-iot-test-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-iot-test-data.png -------------------------------------------------------------------------------- /07_AWS/img/aws-iot-test-send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-iot-test-send.png -------------------------------------------------------------------------------- /07_AWS/img/aws-iot-test-subscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-iot-test-subscribe.png -------------------------------------------------------------------------------- /07_AWS/img/aws-iot-thing-registered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-iot-thing-registered.png -------------------------------------------------------------------------------- /07_AWS/img/aws-iot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-iot.png -------------------------------------------------------------------------------- /07_AWS/img/aws-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-login.png -------------------------------------------------------------------------------- /07_AWS/img/aws-region.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-region.png -------------------------------------------------------------------------------- /07_AWS/img/aws-services-menu-cloudformation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws-services-menu-cloudformation.png -------------------------------------------------------------------------------- /07_AWS/img/aws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/aws.png -------------------------------------------------------------------------------- /07_AWS/img/cloudformation-thing-policy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/cloudformation-thing-policy.png -------------------------------------------------------------------------------- /07_AWS/img/environment-sensor-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/environment-sensor-output.png -------------------------------------------------------------------------------- /07_AWS/img/glitch-env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/glitch-env.png -------------------------------------------------------------------------------- /07_AWS/img/glitch-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/glitch-ui.png -------------------------------------------------------------------------------- /07_AWS/img/grafana-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/grafana-graph.png -------------------------------------------------------------------------------- /07_AWS/img/grafana-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/grafana-query.png -------------------------------------------------------------------------------- /07_AWS/img/iam-download-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/iam-download-key.png -------------------------------------------------------------------------------- /07_AWS/img/iam-glitch-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/iam-glitch-user.png -------------------------------------------------------------------------------- /07_AWS/img/iam-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/iam-permissions.png -------------------------------------------------------------------------------- /07_AWS/img/iam-review-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/iam-review-user.png -------------------------------------------------------------------------------- /07_AWS/img/iam-users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/iam-users.png -------------------------------------------------------------------------------- /07_AWS/img/influxdb-check-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/influxdb-check-data.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-connection-string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-connection-string.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-create-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-create-success.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-create.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-home.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-select-test-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-select-test-data.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-test-event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-test-event.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-test-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-test-success.png -------------------------------------------------------------------------------- /07_AWS/img/lambda-upload-zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda-upload-zip.png -------------------------------------------------------------------------------- /07_AWS/img/lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/lambda.png -------------------------------------------------------------------------------- /07_AWS/img/log-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-action.png -------------------------------------------------------------------------------- /07_AWS/img/log-firehose-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-firehose-action.png -------------------------------------------------------------------------------- /07_AWS/img/log-firehose-create-resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-firehose-create-resource.png -------------------------------------------------------------------------------- /07_AWS/img/log-kinesis-new-delivery-stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-kinesis-new-delivery-stream.png -------------------------------------------------------------------------------- /07_AWS/img/log-kinesis-s3-bucket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-kinesis-s3-bucket.png -------------------------------------------------------------------------------- /07_AWS/img/log-kinesis-s3-buffer-hints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-kinesis-s3-buffer-hints.png -------------------------------------------------------------------------------- /07_AWS/img/log-kinesis-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-kinesis-welcome.png -------------------------------------------------------------------------------- /07_AWS/img/log-rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-rule.png -------------------------------------------------------------------------------- /07_AWS/img/log-s3-bucket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/log-s3-bucket.png -------------------------------------------------------------------------------- /07_AWS/img/postgresql-check-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/postgresql-check-data.png -------------------------------------------------------------------------------- /07_AWS/img/rds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rds.png -------------------------------------------------------------------------------- /07_AWS/img/rule-action-dynamo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-action-dynamo.png -------------------------------------------------------------------------------- /07_AWS/img/rule-action-split-dynamo-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-action-split-dynamo-v2.png -------------------------------------------------------------------------------- /07_AWS/img/rule-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-create.png -------------------------------------------------------------------------------- /07_AWS/img/rule-dynamo-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-dynamo-edit.png -------------------------------------------------------------------------------- /07_AWS/img/rule-dynamo-items.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-dynamo-items.png -------------------------------------------------------------------------------- /07_AWS/img/rule-influxdb-lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-influxdb-lambda.png -------------------------------------------------------------------------------- /07_AWS/img/rule-no-rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-no-rules.png -------------------------------------------------------------------------------- /07_AWS/img/rule-postgresql-lambda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-postgresql-lambda.png -------------------------------------------------------------------------------- /07_AWS/img/rule-query-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-query-action.png -------------------------------------------------------------------------------- /07_AWS/img/rule-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/rule-query.png -------------------------------------------------------------------------------- /07_AWS/img/sns-configure-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/sns-configure-action.png -------------------------------------------------------------------------------- /07_AWS/img/sns-create-subscription-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/sns-create-subscription-email.png -------------------------------------------------------------------------------- /07_AWS/img/sns-create-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/sns-create-topic.png -------------------------------------------------------------------------------- /07_AWS/img/sns-rule-action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/sns-rule-action.png -------------------------------------------------------------------------------- /07_AWS/img/sns-rule-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/sns-rule-query.png -------------------------------------------------------------------------------- /07_AWS/img/sns-subscribe-sms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/sns-subscribe-sms.png -------------------------------------------------------------------------------- /07_AWS/img/sns-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/07_AWS/img/sns-test.png -------------------------------------------------------------------------------- /07_AWS/influxdb.md: -------------------------------------------------------------------------------- 1 | # InfluxDB 2 | 3 | In addition to storing data in PostgreSQL, we can also send it to InfluxDB. The CloudFormation script created a 2nd Lambda function `mqtt-to-influxdb`. We need to configure this function to connect to Influx. AWS does not have an InfluxDB service, so we'll use the server from previous weeks, `influx.dev2db.com`. You won't be able to write to the `itp` database because your user has read-only permissions. However, each user has their own database they can write to. For example Samantha logs into InfluxDB with the password *secret*. Samantha would set INFLUXDB_USER to `samantha`, INFLUXDB_DATABASE to `samantha`, and INFLUXDB_PASSWORD to `secret`. The INFLUX_USER and INFLUX_DATABASE are both set to `samantha` because each user has a database with their name. We don't need to create a schema for InfluxDB. Measurements will be created as data is received. 4 | 5 | ![](img/lambda.png) 6 | 7 | * Open the Lambda function `Services -> Lambda` 8 | * Click on the `mqtt-to-influxdb` function 9 | * Edit the environment variables 10 | * Change INFLUXDB_DATABASE to your username 11 | * Change INFLUXDB_USER to your username 12 | * Change INFLUXDB_PASSWORD to your password 13 | 14 | ## Test 15 | 16 | Configure a test event with the following data and ensure you can insert records into Influx. 17 | 18 | { 19 | "device": "test", 20 | "temperature": 72, 21 | "humidity": 42, 22 | "timestamp": 0 23 | } 24 | 25 | ## Rules 26 | 27 | Now that the Lambda function works, we can have the IoT Core send data to it. 28 | 29 | * From the `Services` menu, choose `IoT Core`. 30 | * Choose `Act -> Rules` from the menu on the left. 31 | * Select the `environment` rule. 32 | * Use the `Add action` button to add a new action. 33 | * Choose `Send a message to a Lambda function`. 34 | * Press `Configure Action` at the bottom of the page. 35 | * Select `mqtt-to-influxdb`. 36 | * Press `Add action`. 37 | 38 | ![](img/rule-influxdb-lambda.png) 39 | 40 | Use the influx cli to verify that data is begin written to the database 41 | 42 | influx -host influx.dev2db.com -ssl -username samantha -password secret 43 | use samantha 44 | precision rfc3339 45 | select * from /.*/ where time > now() - 30s; 46 | 47 | ![](img/influxdb-check-data.png) 48 | 49 | 50 | Next [Notifications](notifications.md) 51 | 52 | -------------------------------------------------------------------------------- /07_AWS/logging.md: -------------------------------------------------------------------------------- 1 | # Logging 2 | 3 | We can log all messages to S3 using Kinesis Firehose. AWS IoT Rules have an S3 action, however that creates a new file in S3 for *every* MQTT message. Kinesis Firehose batches records together before writing to S3. 4 | 5 | Create a new AWS IoT rule named `log`. 6 | 7 | Set the query to be `SELECT *, clientId() as clientId, timestamp() as timestamp from '#'` 8 | 9 | ![](img/log-rule.png) 10 | 11 | Choose the send a message to an Amazon Kinesis Firehose stream action. 12 | 13 | ![](img/log-firehose-action.png) 14 | 15 | Create a new resource 16 | 17 | ![](img/log-firehose-create-resource.png) 18 | 19 | 20 | Create a delivery stream. NOTE: Kinesis Firehose is not in the Free Tier. You will be charged $0.029 per gigabyte. 21 | 22 | Choose 23 | 24 | * Source: Direct PUT 25 | * Destination: Amazon S2 26 | 27 | Rename the stream PUT-S3-mqtt-log 28 | 29 | ![](img/log-kinesis-new-delivery-stream.png) 30 | 31 | Skip over the "Transform and convert records" section for this tutorial. You should consider mapping JSON to Apache parquet using AWS Glue or a Lambda function for a real project. 32 | 33 | Press the create button to create a new S3 bucket. S3 bucket names must be globally unique. Try `itp-mqtt-log-{YOUR_NAME}`. After creating the bucket, use the browse button to choose your new bucket as the firehose destination. 34 | 35 | ![](img/log-kinesis-s3-bucket.png) 36 | 37 | Expand the 'Buffer hints, compression, and encryption' section. Leave the buffer size 5 minutes. Decrease the interval to 120 seconds so file are written every two minutes, which is better for testing. 38 | 39 | ![](img/log-kinesis-s3-buffer-hints.png) 40 | 41 | Press the button at the bottom of the form to create the delivery stream. Switch back to the AWS IoT rule tab. Refresh the stream names and choose the `PUT-S3-mqtt-log` stream. Set the separator to newline. Create a new role `MqttLogRole` and press the "Add action" button. 42 | 43 | ![](img/log-action.png) 44 | 45 | Press the "Create rule" button to finish creating the rule. 46 | 47 | After the rule has been deployed and is collecting data we need to wait 120 seconds for Firehose to write a record to the S3 bucket. The log files will be prefixed by year, month, day, and hour. Download a log file to view the contents. 48 | 49 | ![](img/log-s3-bucket.png) 50 | 51 | The log files contain rows of JSON log messages 52 | 53 | {"temperature":68.8,"humidity":57,"clientId":"0123CFBA6DEAD444EE","timestamp":1647738055494} 54 | {"temperature":68.9,"humidity":57,"clientId":"0123CFBA6DEAD444EE","timestamp":1647738065530} 55 | {"temperature":68.8,"humidity":56,"clientId":"0123CFBA6DEAD444EE","timestamp":1647738075560} 56 | 57 | Looking at the log file, we could probably make this better. We are missing the topic and the MQTT payload is mixed in with the metadata. Go back to the log rule and edit the query. Add the topic field and move the MQTT into a nested payload field. 58 | 59 | SELECT topic() as topic, clientId() as clientId, timestamp() as timestamp, * as payload from '#' 60 | 61 | The new log messages look better 62 | 63 | {"topic":"things/0123CFBA6DEAD444EE/environment","clientId":"0123CFBA6DEAD444EE","timestamp":1647738435608,"payload":{"temperature":69,"humidity":56}} 64 | {"topic":"things/0123CFBA6DEAD444EE/environment","clientId":"0123CFBA6DEAD444EE","timestamp":1647738445488,"payload":{"temperature":69,"humidity":56}} 65 | {"topic":"things/0123CFBA6DEAD444EE/environment","clientId":"0123CFBA6DEAD444EE","timestamp":1647738455503,"payload":{"temperature":69.1,"humidity":56}} 66 | 67 | Now that the log is working, you could go back into Firehose configuration and increase the buffer interval from 120 seconds to 300 or 900 seconds so you collect more records in fewer files. 68 | 69 | Next [Glitch](glitch.md) -------------------------------------------------------------------------------- /07_AWS/notifications.md: -------------------------------------------------------------------------------- 1 | # Notifications 2 | 3 | Create rules to notify you when the temperature exceeds a certain limit. 4 | 5 | ## SNS 6 | Log into the AWS console and open the [Simple Notification Service (SNS) dashboard](https://console.aws.amazon.com/sns/v3/home?region=us-east-1#/dashboard). 7 | 8 | Create a new standard topic `high-temperature-notification`. 9 | 10 | ![](img/sns-create-topic.png) 11 | 12 | Create a subscription. Use email for protocol and enter your email address as the endpoint. 13 | 14 | ![](img/sns-create-subscription-email.png) 15 | 16 | Open your email and confirm the subscription. 17 | 18 | ## AWS IoT Rule 19 | 20 | Go back to the AWS IoT dashboard and choose the [act tab](https://console.aws.amazon.com/iot/home?region=us-east-1#/rulehub) to create a new rule. 21 | 22 | Name the rule `high_temperature`. Add the query for the rule 23 | 24 | select topic(2) as device, temperature from 'things/+/environment' where temperature > 80 25 | 26 | ![](img/sns-rule-query.png) 27 | 28 | Add an action that send a message using SNS push notification. 29 | 30 | ![](img/sns-rule-action.png) 31 | 32 | Configure the action. Select high-temperature-notification as the SNS target. The message format should be RAW. Create a new role, HighTemperatureNotificationRole. 33 | 34 | ![](img/sns-configure-action.png) 35 | 36 | Press the create action button then press the create rule button. 37 | 38 | ## Testing 39 | 40 | Use the [IoT Core test page](https://console.aws.amazon.com/iot/home?region=us-east-1#/test) to send a test message with a temperature above 80°F. 41 | 42 | ![](img/sns-test.png) 43 | 44 | Ensure you receive an email from AWS. 45 | 46 | For fancier notification emails, look into [AWS Simple Email Service (SES)](https://aws.amazon.com/ses/). 47 | 48 | ## SMS 49 | 50 | AWS can send also send SMS messages for SNS topics. However, the requirements changed in 2021 and it's no longer feasable to set up a SNS subscription from the demo accounts. If you're in your own AWS account, feel free to configure SMS. 51 | 52 | ![](img/sns-subscribe-sms.png) 53 | 54 | 55 | Next [Logging](logging.md) 56 | -------------------------------------------------------------------------------- /07_AWS/rules.asciidoc: -------------------------------------------------------------------------------- 1 | [[chapter-rules]] 2 | == AWS IoT Core Rules 3 | 4 | We are now successfully sending data from the Arduino to AWS using MQTT. The MQTT broker does not store data, it makes data available to subscribers. Something needs to be listening and processing the data. With AWS, the way we do this is by creating rules. 5 | 6 | If necessary, use the `Services` menu to navigate to `IoT Core`. Click the `Act` menu on the left side and choose [Rules](https://us-east-1.console.aws.amazon.com/iot/home?region=us-east-1#/rulehub) to create a rule. 7 | 8 | image::img/rule-no-rules.png[] 9 | 10 | Name the rule `environment` 11 | 12 | image::img/rule-create.png[] 13 | 14 | Add the rule query statement. 15 | 16 | ---- 17 | SELECT topic(2) as device, timestamp() as timestamp, * FROM 'things/+/environment' 18 | ---- 19 | 20 | image::img/rule-query.png[] 21 | 22 | // TODO is this a sidebar? 23 | 24 | The `topic(2)` function gets the 2nd element from the MQTT topic, which in this case, is the device name. For example device `01237EE` writes to `things/01237EE/environment`. The topic(2) function will return `01237EE`. 25 | 26 | The `timestamp()` function returns the current timestamp. For our data, we can use the time it arrives at the broker rather than the time the data was recorded on the device. 27 | 28 | The wildcard symbol, `*`, selects all the remaining attributes in from the JSON in the payload of the MQTT message. 29 | 30 | [TIP] 31 | ==== 32 | See the https://docs.aws.amazon.com/iot/latest/developerguide/iot-sql-reference.html[AWS IoT SQL Reference] for more details about the types of queries you can write. 33 | ==== 34 | 35 | Press the `Add Action` button. Add an action to split the message into multiple columns in a DynamoDB table. 36 | 37 | // .Rule action - split into multiple columns of DynamoDB table 38 | image::img/rule-action-split-dynamo-v2.png[] 39 | 40 | Choose the `environment` table, which was created by CloudFormation. Select the `iot-core-execution-role`. This role was created by CloudFormation. It includes permissions to write to DynamoDB stream. 41 | 42 | image::img/rule-action-dynamo.png[] 43 | 44 | Your rule should look similar to the screenshot below. Be sure to scroll to the bottom and press the `Create Rule` button. 45 | 46 | //[[figure-create-rule]] 47 | //.Create Rule 48 | image::img/rule-query-action.png[] 49 | 50 | Once the new rule is in place, the DynamoDB table will start being populated. Use the AWS console to https://console.aws.amazon.com/dynamodb/home?region=us-east-1#tables:selected=environment;tab=items[view data in the environment table]. 51 | 52 | . Click `Services -> DynamoDB` 53 | . Choose the `Tables` menu on the left 54 | . Select the `environment` table 55 | . Click the `Explore Table Items` button to view the data 56 | . Click on a row to see more details 57 | 58 | image::img/rule-dynamo-items.png[] 59 | 60 | image::img/rule-dynamo-edit.png[] 61 | 62 | If you'd like a good way to view the data stored in Dynamo, jump ahead to the link:glitch.md[Glitch] page. 63 | 64 | Next link:postgresql.md[PostgreSQL] 65 | 66 | -------------------------------------------------------------------------------- /Assignments/assignment-week-1.md: -------------------------------------------------------------------------------- 1 | # Assignment - Week 1 2 | 3 | * Read [Capable Computing](https://medium.com/@aallan/capable-computing-50867847a8d8) 4 | * Read [IoT: MQTT outperforms 300% over HTTP](https://medium.com/@dearsikandarkhan/mqtt-outperforms-300-over-http-6e2c5d40c603) 5 | * Watch the first 12:30 of [Introduction to MQTT](https://www.youtube.com/watch?v=LKz1jYngpcU) 6 | * Set up your laptop and Arduino for week 2 7 | * [Exercise 1: Development Environment](https://github.com/don/ITP-DeviceToDatabase/blob/main/02_Arduino/exercises/exercise1.md) 8 | * [Exercise 2: Assemble the Hardware](https://github.com/don/ITP-DeviceToDatabase/blob/main/02_Arduino/exercises/exercise2.md) 9 | * If you run into problems, email me so we can get them sorted out before class 10 | * Email me a photo of your Arduino hardware once it's built 11 | -------------------------------------------------------------------------------- /Assignments/assignment-week-2.md: -------------------------------------------------------------------------------- 1 | # Assignment - Week 2 2 | 3 | ## SQL 4 | 5 | Watch the following short videos about SQL. Skip the projects. 6 | 7 | * [Welcome to SQL](https://www.khanacademy.org/computing/computer-programming/sql/sql-basics/v/welcome-to-sql) 8 | * [Creating a table and inserting data](https://www.khanacademy.org/computing/computer-programming/sql/sql-basics/pt/creating-a-table-and-inserting-data) 9 | * [Querying the table](https://www.khanacademy.org/computing/computer-programming/sql/sql-basics/pt/querying-the-table) 10 | * [Aggregating data](https://www.khanacademy.org/computing/computer-programming/sql/sql-basics/pt/aggregating-data) 11 | * [S-Q-L or SEQUEL](https://www.khanacademy.org/computing/computer-programming/sql/sql-basics/v/s-q-l-or-sequel) 12 | 13 | ## Review 14 | 15 | Review the work we did in class for Exercises [3](https://github.com/don/ITP-DeviceToDatabase/blob/main/02_Arduino/exercises/exercise3.md) through 7. Take some time to look at the Arduino, nodejs, html and javascript code. Ask question on Slack if you need more help understanding what's going on. 16 | 17 | ## Sending data with MQTT 18 | 19 | Ensure that your Arduino is running and publishing temperature and humidity data to the MQTT broker, dev2db.com. If possible, keep your device running so we can collect data until the next class. You don't need to send data every 10 seconds. You can adjust the publish interval to send data every 1, 2, or 5 minutes. 20 | 21 | If necessary, refer to [Exercise 4: Sending data using MQTT](https://github.com/don/ITP-DeviceToDatabase/blob/main/02_Arduino/exercises/exercise4.md). Verify your sensor is sending data using [MQTT Explorer](http://mqtt-explorer.com/) or the code from [Exercise 5](https://github.com/don/ITP-DeviceToDatabase/blob/main/02_Arduino/exercises/exercise5.md). Make sure your Arduino is using the mqtt user and password *for the device* you received via email. The `device_id` should be the same as the `mqtt user`. For example `device_32`. If your device is at home, you'll need to adjust the WiFi SSID and password in config.h. 22 | 23 | To keep the Arduino running when it's not connected to your computer, remember to comment out the line that waits for a serial connection in the setup function. 24 | 25 | change 26 | 27 | // wait for a serial connection 28 | while (!Serial); 29 | 30 | to 31 | 32 | // wait for a serial connection 33 | // while (!Serial); 34 | 35 | https://github.com/don/ITP-DeviceToDatabase/blob/4db8c58ddca9900516dff99a97b21926d83c8986/02_Arduino/arduino/HttpClient/HttpClient.ino#L20-L21 36 | 37 | After you make this change, and upload the code, you can plug the Arduino's USB cable into a phone charger. 38 | 39 | ## Additional Sensors 40 | 41 | Think about other sensors and actuators you could add to your Arduino. (This is just a though experiment, but you can actually build if you want.) 42 | * What sensor or actuator would you connect? 43 | * Would it send data? 44 | * Would it receive commands? 45 | * Is HTTP or MQTT a better fit? 46 | 47 | Email or Slack if you have problems with the code, need a better explanation, or need other help. 48 | -------------------------------------------------------------------------------- /Assignments/assignment-week-3.md: -------------------------------------------------------------------------------- 1 | ## Arduino 2 | 3 | Ensure that your Arduino is running and publishing temperature and humidity data to the MQTT broker dev2db.com. Keep your device running so we can collect data until the next class. You don't need to send data every 10 seconds. Refer to [assignment 2](assignment-week-2.md#sending-data-with-mqtt) for more details. 4 | 5 | ## Review 6 | 7 | Review the details of the PostgreSQL queries we covered in class. I recommended that you run the queries against the databases using psql or TablePlus to get a better understanding of how they work. Modify the queries to get different data. 8 | 9 | * [PostgreSQL ITP Queries](../03_RelationalDatabases/postgres.md) Start at "Using SQL to look at Sensor Data" 10 | * [PostgreSQL Sensor Data Queries](../03_RelationalDatabases/postgres2.md) 11 | * [PostgreSQL Additional Queries](../03_RelationalDatabases/postgres3.md) 12 | 13 | ## SQL Questions 14 | 15 | Use SQL to answer the following questions 16 | 17 | 1) Using the `sensor_data` table in the `itp` database: 18 | 19 | a. Select all the data *your device* sent
20 | b. Count the number of records *your device* sent
21 | c. Get the **max**imum temperature reading *your device* sent
22 | 23 | 2) Use the `farm` PostgreSQL database. Write queries to answer the following questions: 24 | 25 | a. When did the outside sensor break and stop sending data?
26 | b. Show the min and max temperature in the root cellar by year
27 | c. What was the lowest temperature recorded in 2019?
28 | 29 | Challenge: Which sensor recorded the lowest temperature 2019 and when? Hint: you need a subquery. 30 | 31 | If you get stuck, please email, slack, and/or sign up for office hours. 32 | 33 | Email me a document with your **queries and results** by Wednesday February 23rd, 6:00 PM EST -------------------------------------------------------------------------------- /Assignments/assignment-week-4.md: -------------------------------------------------------------------------------- 1 | # Assignment - Week 4 2 | 3 | ## Assignment: Read 4 | 5 | Read [InfluxDB Key Concepts](https://docs.influxdata.com/influxdb/v1.8/concepts/key_concepts/). Note that you should be reading the older **v1.8** documentation. 6 | 7 | ## Assignment: Review 8 | 9 | Review the [SQLite material](../03_RelationalDatabases/sqlite.md) material we covered in class. Run the example queries against your farm.db database to get a better understanding of how they work. Modify the queries to get different data. Try and adapt some PostgreSQL queryies. 10 | 11 | ## Assignment: SQL 12 | 13 | 1) Create a SQLite database by exporting the `itp` sensor_data from PostgreSQL. Follow the [instructions](../03_RelationalDatabases/sqlite.md#create-a-database-with-itp-sensor-data) to get started. Document the steps you took to create the database. A screenshot of the terminal or cmd prompt is fine. Make sure your database includes the person and device tables. Optionally create some views. 14 | 15 | 2) Write some queries using your new SQLite database of ITP sensor data. Refer to the [SQLite](../03_RelationalDatabases/sqlite.md) and [PostgreSQL](../03_RelationalDatabases/postgres.md) queries we went over in class. Experiment. Spend some time trying to use queries to answer questions with data. 16 | 17 | 3) Write about your experience about using relational datatbases. Maybe things you learned, or PostgreSQL vs SQLite, or whatever. It doesn't need to be formal, think short blog rather than academic paper. 18 | 19 | **Submit via email by Wednesday March 2rd, 6:00 PM EST** 20 | 21 | * Your SQLite database file 22 | * Your best 2 queries and description of what they're doing 23 | * Writeup or link to your blog 24 | 25 | -------------------------------------------------------------------------------- /Assignments/assignment-week-5.md: -------------------------------------------------------------------------------- 1 | # Assignment - Week 5 2 | 3 | ## Assignment: Code 4 | 5 | ### MQTT to SQLite 6 | 7 | Review the exercises we did in class from [Processing MQTT Data with Node.js](../05_Code/processing-mqtt-data.md) through [Save MQTT messages to SQLite](../05_Code/processing-mqtt-data.md#save-mqtt-messages-to-sqlite). 8 | 9 | Make sure that you are processing MQTT messages and storing them into your SQLite database. 10 | 11 | Optional: [High Temperature Alert](../05_Code/processing-mqtt-data.md#high-temperature-alert) 12 | 13 | ### Google Charts (Optional) 14 | 15 | Go through [Graphing TimescaleDB Data with Google Charts](../05_Code/graphing-data.md). Get the server and client running on your machine. Consider adding another chart to your server. If you'd prefer to just run the code on your machine, email me and I'll send you the code. 16 | 17 | ## Assignment: SQL 18 | 19 | 1) Practice writing SQL queries against the InfluxDB and TimescaleDB databases. Review the examples in the [TimeSeries chapters](../04_TimeSeries). 20 | 21 | 2) Use the `farm` database on **InfluxDB**. Write queries to answer the following questions: 22 | - When did the outside sensor break and stop sending data? 23 | - What was the lowest temperature recorded in 2018? Which sensor recorded this data? 24 | 25 | 3) Find the min and max temperatures by week for the root cellar for the last 3 months of 2018. 26 | - Use InfluxDB and the `farm` database. Hint: use `group by time(interval)` 27 | - Use TimescaleDB and the `tsfarm` database. Hint: use the `time_bucket` function 28 | - Explain the differences between the InfluxDB and TimescaleDB and query results. 29 | 30 | 4) OPTIONAL: Write two queries that use data from the `itp` database on InfluxDB. 31 | 32 | Submit queries & results for SQL parts 2, 3 and 4 via email by **March 9th**, 6:00 PM EST. Make sure queries are formatted so they are easy to understand. The queries should be **text**, so I can run them. It's fine to submit a screenshot of the query results. -------------------------------------------------------------------------------- /Assignments/assignment-week-6.md: -------------------------------------------------------------------------------- 1 | # Assignment - Week 6 2 | 3 | ## Node-RED 4 | 5 | Create a flow using [Node-RED](https://nodered.org/). Be creative. Write a blog post (or document) describing what the flow does. You can also talk about why you created the flow or any challenges getting it to work. Include screenshots of flow and other information showing how it works. Submit an [exported version of the flow](https://nodered.org/docs/user-guide/editor/workspace/import-export) via email if it's not included in your post. 6 | 7 | ## Grafana (Optional) 8 | 9 | Create a new Grafana Dashboard. Submit screnshots of the dashboard and query or include them in your blog post. 10 | 11 | ## Arduino 12 | 13 | Bring your Arduino Nano 33 IoT (or Arduino MRK 1010) to class on March 23rd. We will be connecting to Amazon Web Services (AWS). 14 | 15 | **Submit via email by March 23rd, 6:00 PM EDT** 16 | * Link to Node-RED blog post 17 | * Exported version of the workflow 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NYU ITP | Device to Database | Spring 2022 2 | 3 | http://bit.ly/itp-dtd-22 4 | 5 | checkout this code with 6 | 7 | git clone --depth 1 https://github.com/don/ITP-DeviceToDatabase.git 8 | 9 | Archives 10 | * [Spring-2021](https://github.com/don/ITP-DeviceToDatabase/tree/Spring-2021) 11 | * [Spring-2020](https://github.com/don/ITP-DeviceToDatabase/tree/Spring-2020) 12 | * [Spring-2019](https://github.com/don/ITP-DeviceToDatabase/tree/Spring-2019) 13 | 14 | 15 | -------------------------------------------------------------------------------- /extra/rpi-mqtt/README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi MQTT Example 2 | 3 | Publish temperature and humidity data from SHT31 to MQTT. Subscribe to led topic to allow remote control of the LED. 4 | 5 | * Temperature is published to itp/{device_id}/temperature 6 | * Humidity is published to itp/{device_id}/humidity 7 | * The device subscribes to itp/{device_id}/led and will turn the LED on or off when it receives the payload "on" or "off". 8 | 9 | These instructions assume you are running Raspberry Pi OS bullseye. `cat /etc/os-release` and make sure it says VERSION_ID="11". This was tested on a Raspberry Pi 2 with the [Raspberry Pi USB WiFi Dongle](https://www.raspberrypi.org/products/raspberry-pi-usb-wifi-dongle/). I created a fresh Raspberry Pi OS image of Raspberry Pi OS Lite using the [Raspberry Pi Imager tool](https://www.raspberrypi.org/software/). After booting with the new image, I used `sudo raspi-config` to: set up the wifi, change the password, change the keyboard from UK to US, enable ssh. Then I updated to the latest os with `sudo apt update` and `sudo apt -y dist-upgrade`. Reboot the Raspberry Pi and you're ready to go. 10 | 11 | ## Setup 12 | 13 | Use the command line to enable i2c 14 | 15 | sudo raspi-config nonint do_i2c 0 16 | 17 | Install some additional packages 18 | 19 | sudo apt install -y python3-smbus i2c-tools libgpiod2 python3-pip 20 | 21 | ## Hardware 22 | 23 | Wire the hardware. 24 | 25 | ![SHT31 fritzing for sht31 to the raspberry pi](rpi-sht31_bb.png) 26 | 27 | For the SHT31, run 3.3v and ground to the sensor. Connect SDA and SLC to the Raspberry Pi. 28 | 29 | For the LED, connect pin 18 to the positive lead (anode) of the LED. Connect a 220Ω or 330Ω ohm resistor to the negative lead (cathode) of the LED. Connect the resistor to a ground pin on the Raspberry Pi. 30 | 31 | ![SHT31 wired to the raspberry pi](pi-sht31.jpg) 32 | 33 | ## Code 34 | 35 | Copy the files from your computer to the Raspberry Pi. I used `scp` from my laptop. 36 | 37 | cd ITP-DeviceToDatabase/extra 38 | scp -r rpi-mqtt pi@raspberrypi: 39 | 40 | Alternately you could use a thumb drive to move the files over, or download the files from Github directly to your Raspberry Pi. 41 | 42 | ### Libraries 43 | 44 | On the Raspberry Pi, install the python libraries 45 | 46 | cd ~/rpi-mqtt 47 | pip install -r requirements.txt 48 | 49 | ### Test 50 | 51 | Run the tests to check the hardware is working 52 | 53 | python led_test.py 54 | python sht31_test.py 55 | 56 | 57 | ### Config 58 | Edit config.py and enter the device_id and mqtt_password for your device. 59 | 60 | 61 | ### Run 62 | 63 | Run the program 64 | 65 | python sht31_mqtt.py 66 | 67 | Use MQTT Explorer to verify the broker is recieving the data. 68 | 69 | ### Daemon (Optional) 70 | 71 | If you want the program to run as a background process or deamon, you can use [pm2](https://pm2.keymetrics.io/) 72 | 73 | Install PM2 74 | 75 | sudo apt install -y nodejs npm 76 | sudo npm install -g pm2 77 | sudo pm2 setup 78 | 79 | Start the process 80 | 81 | cd ~/rpi-mqtt 82 | pm2 start sht31_mqtt.py 83 | 84 | Save the pm2 settings, to the process will restart on reboot 85 | 86 | pm2 save 87 | 88 | For a version of this code that uses a DHT22 instead of the SHT31, see the [2021 branch](https://github.com/don/ITP-DeviceToDatabase/tree/Spring-2021/extra/rpi-mqtt). -------------------------------------------------------------------------------- /extra/rpi-mqtt/config.py: -------------------------------------------------------------------------------- 1 | # MQTT configuration 2 | # Use a unique username and password for every device 3 | # (Don't check passwords into version control) 4 | device_id = "" 5 | mqtt_broker = "dev2db.com" 6 | mqtt_port = 8883 7 | mqtt_user = device_id 8 | mqtt_password = "" 9 | -------------------------------------------------------------------------------- /extra/rpi-mqtt/led_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # LED wired to pin 18 3 | 4 | import RPi.GPIO as GPIO 5 | import time 6 | 7 | led_pin = 18 8 | GPIO.setmode(GPIO.BCM) 9 | GPIO.setwarnings(False) 10 | GPIO.setup(led_pin, GPIO.OUT) 11 | 12 | for i in range(10): 13 | print("on") 14 | GPIO.output(led_pin, GPIO.HIGH) 15 | time.sleep(1) 16 | print("off") 17 | GPIO.output(led_pin, GPIO.LOW) 18 | time.sleep(1) 19 | -------------------------------------------------------------------------------- /extra/rpi-mqtt/pi-sht31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/extra/rpi-mqtt/pi-sht31.jpg -------------------------------------------------------------------------------- /extra/rpi-mqtt/requirements.txt: -------------------------------------------------------------------------------- 1 | paho-mqtt 2 | RPI.GPIO 3 | smbus 4 | -------------------------------------------------------------------------------- /extra/rpi-mqtt/rpi-sht31.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/extra/rpi-mqtt/rpi-sht31.fzz -------------------------------------------------------------------------------- /extra/rpi-mqtt/rpi-sht31_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/don/ITP-DeviceToDatabase/d1facf22b6af7d820a165ba4687e727c284c16f2/extra/rpi-mqtt/rpi-sht31_bb.png -------------------------------------------------------------------------------- /extra/rpi-mqtt/sht31_mqtt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Raspberry Pi with 4 | # * SHT31 wired to SDA and SLC 5 | # * LED wired to pin 18 6 | # For MQTT see https://pypi.org/project/paho-mqtt/#usage-and-api 7 | # SHT-31 data sheet https://www.mouser.com/datasheet/2/682/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital-971521.pdf 8 | 9 | 10 | import paho.mqtt.client as mqtt 11 | from time import sleep 12 | import smbus 13 | import RPi.GPIO as GPIO 14 | import config 15 | 16 | led_pin = 18 17 | GPIO.setmode(GPIO.BCM) 18 | GPIO.setwarnings(False) 19 | GPIO.setup(led_pin, GPIO.OUT) 20 | 21 | # Get I2C bus 22 | bus = smbus.SMBus(1) 23 | 24 | def on_connect(client, userdata, flags, result_code): 25 | print("Connected ", str(result_code)) # expecting 0 26 | client.publish(f"presence/connected/{config.device_id}", config.device_id) 27 | client.subscribe(f"itp/{config.device_id}/led") 28 | 29 | def on_message(client, userdata, msg): 30 | message = msg.payload.decode('utf-8').lower() 31 | print(message) 32 | if message == "on": 33 | GPIO.output(led_pin, GPIO.HIGH) 34 | elif message == "off": 35 | GPIO.output(led_pin, GPIO.LOW) 36 | 37 | client = mqtt.Client(config.device_id) 38 | client.on_connect = on_connect 39 | client.on_message = on_message 40 | 41 | client.username_pw_set(config.mqtt_user, config.mqtt_password) 42 | client.tls_set() 43 | client.connect(config.mqtt_broker, config.mqtt_port, keepalive=60) 44 | 45 | client.loop_start() 46 | 47 | while True: 48 | 49 | # SHT31 address, 0x44 50 | bus.write_i2c_block_data(0x44, 0x2C, [0x06]) 51 | 52 | # SHT31 address, 0x44(68) 53 | # Read data back from 0x00(00), 6 bytes 54 | # Temp MSB, Temp LSB, Temp CRC, Humididty MSB, Humidity LSB, Humidity CRC 55 | data = bus.read_i2c_block_data(0x44, 0x00, 6) 56 | 57 | # Convert the data 58 | temp = data[0] * 256 + data[1] 59 | temperature_c = -45 + (175 * temp / 65535.0) 60 | temperature_f = -49 + (315 * temp / 65535.0) 61 | humidity = 100 * (data[3] * 256 + data[4]) / 65535.0 62 | 63 | print( 64 | "Temp: {:.1f} F / {:.1f} C Humidity: {:.1f}% ".format( 65 | temperature_f, temperature_c, humidity 66 | ) 67 | ) 68 | 69 | # format values to 1 decimal place while publishing 70 | client.publish(f"itp/{config.device_id}/temperature", f"{temperature_f:.1f}") 71 | client.publish(f"itp/{config.device_id}/humidity", f"{humidity:.1f}") 72 | 73 | sleep(20) 74 | -------------------------------------------------------------------------------- /extra/rpi-mqtt/sht31_test.py: -------------------------------------------------------------------------------- 1 | # http://www.pibits.net/code/raspberry-pi-sht31-sensor-example.php 2 | 3 | import smbus 4 | import time 5 | 6 | # Get I2C bus 7 | bus = smbus.SMBus(1) 8 | 9 | # SHT31 address, 0x44(68) 10 | bus.write_i2c_block_data(0x44, 0x2C, [0x06]) 11 | 12 | time.sleep(0.5) 13 | 14 | # SHT31 address, 0x44(68) 15 | # Read data back from 0x00(00), 6 bytes 16 | # Temp MSB, Temp LSB, Temp CRC, Humididty MSB, Humidity LSB, Humidity CRC 17 | data = bus.read_i2c_block_data(0x44, 0x00, 6) 18 | 19 | # Convert the data 20 | temp = data[0] * 256 + data[1] 21 | cTemp = -45 + (175 * temp / 65535.0) 22 | fTemp = -49 + (315 * temp / 65535.0) 23 | humidity = 100 * (data[3] * 256 + data[4]) / 65535.0 24 | 25 | # Output data to screen 26 | print("Temperature in Celsius is : %.2f C" %cTemp) 27 | print("Temperature in Fahrenheit is : %.2f F" %fTemp) 28 | print("Relative Humidity is : %.2f %%RH" %humidity) -------------------------------------------------------------------------------- /setup/grafana.md: -------------------------------------------------------------------------------- 1 | # Setting up a Grafana server 2 | 3 | Create a new DigitalOcean virtual machine (or other VM.) I chose Ubuntu 19.10 x64 and the $5/month droplet size. 4 | 5 | * NY data center 6 | * Add a ssh key for logging in 7 | * hostname: grafana 8 | 9 | I have DigitalOcean running DNS for the dev2db.com domain, so I set up a new domain record for grafana.dev2db.com pointing to the new VM. 10 | 11 | Since I set up my SSH key when I created the machine, I can ssh into root@grafana.dev2db.com 12 | 13 | ssh root@grafana.dev2db.com 14 | 15 | Turn on the firewall and restrict incoming traffic to ssh, port 22 16 | 17 | ufw allow ssh 18 | ufw enable 19 | 20 | Update the vm 21 | 22 | apt update 23 | apt dist-upgrade -y 24 | 25 | Reboot, just in case the kernel was upgraded 26 | 27 | shutdown -r now 28 | 29 | SSH back in 30 | 31 | ssh root@grafana.dev2db.com 32 | 33 | Install the nginx 34 | 35 | apt install nginx -y 36 | 37 | Open the firewall for web traffic 38 | 39 | ufw allow http 40 | ufw allow https 41 | 42 | Open your http://grafana.dev2db.com in a web browser to ensure nginx is running 43 | 44 | Install the letsencrypt.org certbot 45 | 46 | apt install python3-certbot-nginx -y 47 | 48 | Get a TLS certificate from letsencrypt.org. Enter your email. Agree to the terms. Answer yes when it asks you to redirect all http traffic to https. 49 | 50 | certbot --nginx -d grafana.dev2db.com 51 | 52 | Now that we have a TLS certificate, we can install Grafana based on https://grafana.com/grafana/download 53 | 54 | sudo apt-get install -y adduser libfontconfig1 55 | wget https://dl.grafana.com/oss/release/grafana_7.4.3_amd64.deb 56 | sudo dpkg -i grafana_7.4.3_amd64.deb 57 | 58 | Start Grafana 59 | 60 | service grafana-server start 61 | 62 | Grafana is running on port 3000. Next we configure Nginx to proxy traffic traffic to Grafana. This allows us to use Nginx for TLS. Exit `/etc/nginx/sites-enabled/default` 63 | 64 | In the SSL server section, comment out (or delete) `location /` 65 | 66 | location / { 67 | # First attempt to serve request as file, then 68 | # as directory, then fall back to displaying a 404. 69 | try_files $uri $uri/ =404; 70 | } 71 | 72 | Example of commenting out root location 73 | 74 | #location / { 75 | # # First attempt to serve request as file, then 76 | # # as directory, then fall back to displaying a 404. 77 | # try_files $uri $uri/ =404; 78 | #} 79 | 80 | Add a new root location that redirects all traffic to Grafana on port 3000 81 | 82 | location / { 83 | proxy_pass http://localhost:3000/; 84 | } 85 | 86 | Note that it's often useful to redirect another url like /grafana to the Grafana server. This requires that you edit grafana.conf. This server is only running Grafana, so I'm redirecting all traffic to Grafana. 87 | 88 | Restart nginx 89 | 90 | service ngnix restart 91 | 92 | Open web browser. Log into https://grafana.dev2db.com as user admin. The default password is admin. Grafana will prompt you to change the password when you login. Use the Granfa control panel to create configure the [database connections](https://grafana.dev2db.com/datasources/new). You should create read only database users to use with the Grafana datasources. Create a [dashboard](https://grafana.dev2db.com/dashboard/new), then add some [user accounts](https://grafana.dev2db.com/org/users). 93 | 94 | 95 | -------------------------------------------------------------------------------- /setup/influxdb.md: -------------------------------------------------------------------------------- 1 | # Setting up the InfluxDB database 2 | 3 | Create a new DigitalOcean virtual machine (or other VM.) I chose Ubuntu 20.04 LTS x64 and the $5/month droplet size. 4 | 5 | * NY data center 6 | * Add a ssh key for logging in 7 | * hostname: influx 8 | 9 | I have DigitalOcean running DNS for the dev2db.com domain, so I set up a new domain record for influx.dev2db.com pointing to the new VM. 10 | 11 | Since I set up my SSH key when I created the machine, I can ssh into root@influx.dev2db.com 12 | 13 | ssh root@influx.dev2db.com 14 | 15 | Turn on the firewall and restrict incoming traffic to ssh, port 22 16 | 17 | ufw allow ssh 18 | ufw enable 19 | 20 | Update the vm 21 | 22 | apt update 23 | apt dist-upgrade -y 24 | 25 | Reboot, just in case the kernel was upgraded 26 | 27 | shutdown -r now 28 | 29 | SSH back in 30 | 31 | ssh root@influx.dev2db.com 32 | 33 | Install the compiler tools and nginx 34 | 35 | apt install build-essential nginx -y 36 | 37 | Open the firewall for web traffic 38 | 39 | ufw allow http 40 | ufw allow https 41 | 42 | Open your http://influx.dev2db.com in a web browser to ensure nginx is running 43 | 44 | Install the letsencrypt.org certbot 45 | 46 | snap install core 47 | snap refresh core 48 | snap install --classic certbot 49 | ln -s /snap/bin/certbot /usr/bin/certbot 50 | 51 | Get a TLS certificate from letsencrypt.org. Enter your email. Agree to the terms. Answer yes when it asks you to redirect all http traffic to https. 52 | 53 | certbot --nginx -d influx.dev2db.com 54 | 55 | Now that we have a TLS certificate, we can install InfluxDB. Install InfluxDB version 1.8.x. 56 | 57 | wget https://dl.influxdata.com/influxdb/releases/influxdb_1.8.10_amd64.deb 58 | sudo dpkg -i influxdb_1.8.10_amd64.deb 59 | 60 | Copy the keys into the the influxdb directory 61 | 62 | cd /etc/influxdb 63 | cp /etc/letsencrypt/live/influx.dev2db.com/fullchain.pem . 64 | cp /etc/letsencrypt/live/influx.dev2db.com/privkey.pem . 65 | 66 | Change the owner of the private key to influxdb 67 | 68 | chown influxdb /etc/influxdb/privkey.pem 69 | 70 | Edit /etc/influxdb/influxdb.conf and adjust the configuration in the appropriate sections 71 | 72 | auth-enabled = true 73 | https-enabled = true 74 | https-certificate = "/etc/influxdb/fullchain.pem" 75 | https-private-key = "/etc/influxdb/privkey.pem" 76 | 77 | Restart influx 78 | 79 | service influxdb restart 80 | 81 | Open the firewall for influx 82 | 83 | ufw allow 8086 84 | 85 | Login into influx and create the `admin` user. Until you do this anyone can login without a password. 86 | 87 | influx -host influx.dev2db.com -ssl 88 | create user admin with password [REDACTED] with all privileges; 89 | exit 90 | 91 | Confirm the username and password work 92 | 93 | influx -host influx.dev2db.com -ssl -username admin -password [REDACTED] 94 | -------------------------------------------------------------------------------- /setup/postgresql.md: -------------------------------------------------------------------------------- 1 | PostgreSQL is running on Amazon Web Services (AWS) Relational Database Service (RSD). Here's how the database was created for this class. 2 | 3 | * Log into AWS management console and go to the RDS dashboard. 4 | * Choose Databases from the menu on the left. 5 | * Press the Create Database button 6 | * Select Easy Create 7 | * Choose PostgreSQL 8 | * Choose Free Tier 9 | * Add an instance identifier, I used `pg` (for postgres) 10 | * Check "Auto generate a password" 11 | * Press the Create Database button 12 | 13 | It will take a some time to create the database. The first time you click on the database details, there will be a button near the top of the screen to "View credential details". Click this button to see the password that was generated for the postgres user. 14 | 15 | The database details will also give you an endpoint to connect to your database that will look something like pg.coce2pzsifqu.us-east-2.rds.amazonaws.com. These names are difficult to type and remember. I created a DNS CNAME pg.dev2db.com that points to pg.coce2pzsifqu.us-east-2.rds.amazonaws.com. for this class. 16 | 17 | The default security restrictions on the database need to be changed to allow users to connect to the database. 18 | 19 | * From the database details page click the Modify button 20 | * Under the Connectivity section, expand Additional Information 21 | * Change Public access to Publicly accessible 22 | * Enable Delete Protection 23 | * Click continue 24 | * Choose Apply Immediately 25 | * Click the orange Modify DB Instance button 26 | 27 | You also need to modify the security group to allow connections 28 | * Click the inbound security group on the database details page 29 | * Choose the inbound tab on the security group detail page 30 | * Press the Edit Inbound Rules button 31 | * Add a rule 32 | * Type: PostgreSQL 33 | * Source: Anywhere 34 | * Choose Save Rules button 35 | 36 | Typically you'd want more restricted access to the database, but allowing connections from anywhere makes things simpler for this class. 37 | 38 | --------------------------------------------------------------------------------