TVShow S01E20 Arduino IoT Shower (w/ Arduino MKR1000)
06:50.21 347-15 OTC



Annotation; this was/is a first prototype.

Collaborating water and electricity seems as almost common sense as pairing a young Petit Chablis with a spaghetti alla vongole. As an alternative to the several thousand dollar pre-installed branded electronically controlled shower, a few attempts at creating a DIY automated shower have appeared over the years, though, applied with immense exchange of existing components and not best utilizing the newer era of IoT. Here, we have sympathetically married abundantly common pre-existing shower valves with the yet to be released Arduino MKR1000 to create an automated, connected and subsequently more efficient bathing experience.

Through either the Windows 10 or Android application, you may preset an optimal temperature and begin the shower. The MKR1000 will then monitor and regulate the temperature and shower valve to create a luxurious escapade in your dirt ass poor bath room. The amount of time and temperature variation will be sent in real time to ThingSpeak where it can be monitored for display and personal analysis of water consumption.

Additional notation; the popsicle sticks were a late second amendment to gain leverage.

Written, Directed and Moistened by TVMiller
Filmed, Rinsed and Repeated on a Droid Razr

Arduino Sketch
// Arduino IoT Shower V1.0 @TVMiller
// Hackster.io Arduino MKR1000 Beta

// A0 Temp Tub
// A1 Temp Head
// 0 Cap 10M
// 1 Cap 1K
// 2 3 4 5 11 12 LCD
// 6 LED
// 7 Hot
// 8 Valve
// 9 Cold

// Libraries
#include <SPI.h>
#include <WiFi101.h>
#include <LiquidCrystal.h>
#include <Servo.h>
#include <CapacitiveSensor.h>

// WiFi
char ssid[] = "NETWORK";
char pass[] = "PASSWORD";
int keyIndex = 0;
int status = WL_IDLE_STATUS;
WiFiServer server(80);

// ThingSpeak
char thingSpeakAddress[] = "api.thingspeak.com";
String APIKey = "XXXXXXXX"; // API
const int updateThingSpeakInterval = 10 * 1000; // Interval
long lastConnectionTime = 0;
boolean lastConnected = false;
WiFiClient client;

// LCD
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Servos
Servo hot;
Servo valve;
Servo cold;
int openturn = 0;
int closeturn = 180;
int noturn = 90;

// Servo Position Delays
int hotdelay = 450;
int valvedelay = 700;
int colddelay = 300;

// Temperature
int temptub = A0;
int temphead = A1;
int templow = 0;
int temphigh = 0;
int tempc = 0;
int pretempf = 0;

// Temperature Variable
int settemp = 42;
int preptemp = 0;
int tempvarlow = 0;
int tempvarhigh = 0;

// Map Variables
int lowvol = 400;
int highvol = 1005;
int lowc = 56;
int highc = 131;

// On Off Button
CapacitiveSensor onoffbutton = CapacitiveSensor(0, 1);
int touchpeak = 150;

// State
// 0 = Off
// 1 = Turn On
// 2 = On
// 3 = Shut Off
int state = 0;

void setup() {
Serial.begin(9600);

// Servos
hot.attach(7);
valve.attach(8);
cold.attach(9);
hot.write(90);
valve.write(90);
cold.write(90);

// Temperature
pinMode(temptub, INPUT);
pinMode(temphead, INPUT);

// LCD
lcd.begin(16, 2);
lcd.clear();

// WIFI Connection
while ( status != WL_CONNECTED) {
lcd.setCursor(0, 0);
lcd.print("Connecting...");
lcd.setCursor(0, 1);
lcd.print(ssid);
Serial.print("Connecting Network: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
delay(10000);
}
server.begin();
lcd.clear();
printWifiStatus();
delay(3000);
lcd.clear();
}

void loop() {

// WiFi Server Page
if (state == 0) {
WiFiClient client = server.available();
if (client) {
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (c == '\n') {
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
client.print("<body bgcolor=\"#9999FF\">");
client.print("<div align=\"center\">");
client.print("<table cellspacing=\"0\" cellpadding=\"0\"
border=\"0\" style=\" border:1px solid #000000;background-color:#FFFFFF;\">
<tr><td style=\"padding:5px;font:50px tahoma;\">");
client.print(settemp);
client.print("</td></tr></table>");
client.print("<a href=\"/U\"><img src=\"
http://yoursite.com/images/uparrow.png\" border=\"0\"></a>
<a href=\"/D\"><img src=\"http://yoursite.com/images/
downarrow.png\" border=\"0\"></a><br />");
client.print("<br />");
client.print("<a href=\"/S\"><img src=\"http://yoursite.
com/images/showeron.png\" border=\"0\"></a>");
client.print("</div>");
client.println();
break;
}
else {
currentLine = "";
}
}
else if (c != '\r') {
currentLine += c;
}

if (currentLine.endsWith("GET /U")) {
settemp = settemp + 1;
}
if (currentLine.endsWith("GET /D")) {
settemp = settemp - 1;
}
if (currentLine.endsWith("GET /S")) {
state = 1;
}
}
}
client.stop();
}
}

// On Off Button
long touch = onoffbutton.capacitiveSensor(30);
if (touch > touchpeak) {
if (state == 0) {
state = 1;
}
else if (state == 2) {
state = 3;
}
}

// Update ThingSpeak
if (state == 1 || state == 2) {
temphigh = analogRead(temphead);
pretempf = map(temphigh, lowvol, highvol, lowc, highc);
tempc = (pretempf - 32) * 5 / 9;
String headtemperature = String(tempc, DEC);
String runtime = String(state, DEC);
if (millis() - lastConnectionTime > updateThingSpeakInterval) {
updateThingSpeak("field1=" + runtime + "&field2=" + headtemperature);
}
}

// State Operation
if (state == 1) {
turnon();
}
else if (state == 2) {
shower();
}
else if (state == 3) {
turnoff();
}

}

void printWifiStatus() {
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
IPAddress ip = WiFi.localIP();
Serial.print("http://");
Serial.println(ip);
long rssi = WiFi.RSSI();
Serial.print("RSSI: ");
Serial.print(rssi);
Serial.println(" dBm");
lcd.setCursor(0, 0);
lcd.print(WiFi.SSID());
lcd.setCursor(0, 1);
lcd.print(ip);
}

void updateThingSpeak(String tsData) {
if (client.connect(thingSpeakAddress, 80)) {
client.print("POST /update HTTP/1.1\n");
client.print("Host: api.thingspeak.com\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: " + APIKey + "\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print(tsData.length());
client.print("\n\n");
client.print(tsData);
lastConnectionTime = millis();
}
}

void turnon() {

// LCD Temperature Display
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Temp:");
lcd.setCursor(10, 0);
lcd.print(settemp);
lcd.setCursor(0, 1);
lcd.print("Current:");
lcd.setCursor(9, 1);
lcd.print("0");

// Hot Open Valve Tub Cold Closed
hot.write(180);
delay(hotdelay);
hot.write(90);

delay(2000);

preptemp = settemp - 2;

// Wait For Temp
while (preptemp > tempc) {
templow = analogRead(temptub);
pretempf = map(templow, lowvol, highvol, lowc, highc);
tempc = (pretempf - 32) * 5 / 9;
lcd.setCursor(9, 1);
lcd.print(tempc);
String headtemperature = String(tempc, DEC);
if (millis() - lastConnectionTime > updateThingSpeakInterval) {
updateThingSpeak("&field2=" + headtemperature);
}
}

// Hot Open Valve Tub Cold Open
cold.write(180);
delay(colddelay);
cold.write(90);

// Delay For While Statement
delay(2000);

// Wait For Temp
while (preptemp > tempc) {
templow = analogRead(temptub);
pretempf = map(templow, lowvol, highvol, lowc, highc);
tempc = (pretempf - 32) * 5 / 9;
lcd.setCursor(9, 1);
lcd.print(tempc);
String headtemperature = String(tempc, DEC);
if (millis() - lastConnectionTime > updateThingSpeakInterval) {
updateThingSpeak("&field2=" + headtemperature);
}
}

// Hot Open Valve Head Cold Open
valve.write(180);
delay(valvedelay);
valve.write(90);

// Set State On
state = 2;

// Update State ThingSpeak
String runtime = String(state, DEC);
updateThingSpeak("field1=" + runtime);

}

void shower() {

// Get Temp Shower Head
temphigh = analogRead(temphead);
pretempf = map(temphigh, lowvol, highvol, lowc, highc);
tempc = (pretempf - 32) * 5 / 9;

// Print Shower Temp
lcd.setCursor(9, 1);
lcd.print(tempc);

// Set Temperature Leeway
tempvarlow = settemp - 2;
tempvarhigh = settemp + 2;

// Adjust Temperature
if (tempc < tempvarlow) {
cold.write(0);
delay(100);
cold.write(90);
colddelay = colddelay - 100;
delay(2000);
}
else if (tempc > tempvarhigh) {
cold.write(180);
delay(100);
cold.write(90);
colddelay = colddelay + 100;
delay(2000);
}

}

void turnoff() {

// Get Temp Shower Head
temphigh = analogRead(temphead);
pretempf = map(temphigh, lowvol, highvol, lowc, highc);
tempc = (pretempf - 32) * 5 / 9;

// Print Shower Temp
lcd.setCursor(9, 1);
lcd.print(tempc);

// Close Valve
valve.write(0);
delay(valvedelay);
valve.write(90);

// Close Hot
hot.write(0);
delay(hotdelay);
hot.write(90);

// Close Cold
cold.write(0);
delay(colddelay);
cold.write(90);

// Reset All States
state = 0;
templow = 0;
temphigh = 0;
tempc = 0;
colddelay = 400;

delay(1000);

// Update ThingSpeak FINAL
String headtemperature = String(tempc, DEC);
String runtime = String(state, DEC);
updateThingSpeak("field1=" + runtime + "&field2=" + headtemperature);

delay(5000);

// Clear LCD
lcd.clear();
}

Archive
HackADay - Start your day the Arduino way with this IoT shower controller
Hackster.io - Arduino IoT Shower
HackADay.io - Arduino IoT Shower
Adafruit - Automate Your Shower with Arduino and IoT
Atmel - Connect and Automate Your 'Dumb' Shower with Arduino MKR1000
Simple Condiment Packet Waste Solution
17:59.51 330-15 OTC


TVShow S01E19 Gate Jedi
20:30.2 324-15 OTC



Having discovered several spare Midichlorians in my liquor cabinet, I trained and applied them to opening a large cumbersome gate. The FORCE motion travels through my inner what-nots and is translated by the Pebble Classic accelerometer toggling a command sent to the (Particle) Cloud (City) which returns to the Particle Photon triggering a TIP120 to fire a button on an existing RF transceiver.

May the ridiculous hand gestures be with you, always.

Written, Directed and Hate Flowed Through by TVMiller
Filmed through 12 parsecs and not the Droid Razr you were looking for

Particle Photon Code
// Gate Jedi @TVMiller

int sig = D7;
int command = 0;

void setup() {
pinMode(sig, OUTPUT);
Spark.function("gate",gateJedi);
digitalWrite(sig, LOW);
}

void loop() {
}

int gateJedi(String command) {
if (command=="open") {
digitalWrite(sig,HIGH);
delay(3000);
digitalWrite(sig,LOW);
command = 0;
}
else {
digitalWrite(sig,LOW);
}
}

Pebble.js Gate Jedi APP
// Gate Jedi Peddle APP (Version 1.3) @TVMiller 
// Pebble Blink APP @Jack-Dangerfield

var Accel = require('ui/accel');
Accel.init();
var UI = require('ui');
var ajax = require('ajax');
var Vector2 = require('vector2');

// Create Window
var main_window = new UI.Window();

// Open Button and Display
var txtOnLabel = new UI.Text({
position: new Vector2(0, 50),
size: new Vector2(144, 30),
font: 'Gothic 28 Bold',
text: 'GATE JEDI',
textAlign: 'center',
color: 'white'
});

// Display Main Window
main_window.backgroundColor('black');
main_window.add(txtOnLabel);
main_window.show();

// URL To Particle Cloud
function Toggle(function_name,function_value){
var URL = 'https://api.particle.io/v1/devices/DEVICE
ID/' + function_name +'?access_token=TOKEN';

ajax(
{
url: URL,
method: 'post',
type: 'json',
data: { "args": function_value}
}
);
}

// Accelerometer Poll and Function (Default 100Hz 25)
Accel.on('data', function(e) {
console.log(e.accel.x);
if (e.accel.x > 900) {
console.log(e.accel.y);
if (e.accel.y > 900) {
Toggle('gate','open');
}}
});

// Button Function and Photon Parameters
main_window.on('click', 'up', function() {
Toggle('gate','open');
});

Archive
HackADay - Open Sesame, from a Galaxy Far, Far Away
Hackster.io - Gate Jedi
HackADay.io - Gate Jedi
Airtronics Servo 94322 Continuous Rotation Hack
07:11.55 310-15 OTC


This is my servo.
There are many like it,
but this one is mine.

There are numerous tutorials online for numerous models of servos and now there
is one for the Airtronics 94322 which did not previously exist.

HackADay.io - Airtronics Servo 94322 Continuous Rotation Hack
TVShow S01E18 The Butterfly Effect
06:23.2 308-15 OTC


Written, Directed and Brewed by TVMiller
Filmed through a Stained Art House Coffee Pot and Droid Razr

Several butterflies were harmed in the making of my editing migraine.
Matte editors, we salute you.

Wikipedia - The Butterfly Effect