Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 2 | |||
![]() |
| × | 3 | |||
![]() |
| × | 2 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 |
I built a Motion-sensing faucet system that takes a hot and cold water supply and it combines them to output the temperature set by the user when the user places their hands below the faucet. The desired temperature can be set either via the touch buttons on the OPLA or through the cloud.
Some of the challenges were:
One, the temperature sensor's response time proved to be too slow and hence it lagged behind the actual water temperature.
The sensor problem was over come by using a complementary filter to fuse the sensor's temperature reading with a theoretical prediction derived from the percentage of hot and cold water being let in by the motor-driven valves.
Two, the valves used are plastic and are prone to breakage if the motor shaft overrun the taps. But, I couldn't use 4 limit switches (One each for min and max positions for the 2 valves), since I had only 2 digital pins available for the switches.
I had to work with just 2 limit switches to identify when the maximum and minimum positions were reached. It was a challenge but I managed to get it work efficiently with the help of some extra code work
I also wanted to use the display feature on the Opla to make it more user friendly. But it turned out that I needed to disable the display to have enough pins to operate the stepper. Operating the stepper motors on a different controller to leave the display free is the next step for this project. Below is the video of the implementation of my temperature display feature.
#include "thingProperties.h"
#include<AccelStepper.h>
#include <DS18B20.h>
#include <Arduino_MKRIoTCarrier.h>
#include <ezButton.h>
float tempCold = 25;
float prevTempDesired;
float tempHot = 48;
//~~~~~~~~~~Status Messages~~~~~~~~~~//
String stringOne = "Desired: ";
String stringTwo = " Curr: ";
String stringThree = "Desired Temperaure is out of Scope.";
String stringFour = "Desired Temp Reached.";
String stringFive = "No Motion Detected.";
String stringSix = "°";
String stringSeven = " Sensor(s) Missing";
String stringEight = " Current Temp: ";
//~~~~~~<END>Status Messages~~~~~~//
//~~~~Define MKR Carrier Sensors and Hardware~~~~//
#define PIR A5
MKRIoTCarrier carrier;
extern bool CARRIER_CASE;
int Val = 0;
//~~<END>Setup MKR Carrier Sensors and Hardware~~//
//~~~~~~~~Create the Motor Instances~~~~~~~~//
long positions[2];
long zeroPos[2];
long mid[2] = {0,80};
AccelStepper stepperCold(1,1,0);
AccelStepper stepperHot(1,4,3);
float Vd;
bool dirCold = 0;
bool dirHot = 0;
float positionCold, positionHot;
//~~~~~<END>Create the Motor Instances~~~~~//
//~~Create Instances for Temperature Sensors~~//
DS18B20 Temps(21);
uint8_t Sensor2Add[8] = {40, 59, 22, 70, 146, 9, 2, 107};
uint8_t Sensor3Add[8] = {40, 47, 17, 210, 11, 0, 0, 250};
float Uqr = 0;
float pqr = 0;
//~~<END>create instances for temperature sensors~~//
//~~~~~~~~~~Define Limit Switches~~~~~~~~~~//
ezButton ls_hot(7);
ezButton ls_cold(8);
int stateHot, stateCold;
bool fin = false;
float Th_1 = 0;
//~~~~~~~<END>Define Limit Switches~~~~~~~~//
//~~~~~~~~~~~~~~~~Display features~~~~~~~~~~~~~~~//
int ht = 0;
//~~~~~~~~~~~~~<END> Display Features~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~SETUP~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
void setup() {
//~~~~~~~Initialize serial~~~~~~//
Serial.begin(9600);
delay(6000);
//~~~~<END>Initialize serial~~~~//
// Setup and Initialize Arduino Cloud
// Defined in thingProperties.h
initProperties();
// Connect to Arduino IoT Cloud
ArduinoCloud.begin(ArduinoIoTPreferredConnection);
/*
The following function allows you to obtain more information
related to the state of network and IoT Cloud connection and errors
the higher number the more granular information you’ll get.
The default is 0 (only errors).
Maximum is 4
*/
ArduinoCloud.addCallback(ArduinoIoTCloudEvent::CONNECT, doThisOnConnect);
setDebugMessageLevel(2);
ArduinoCloud.printDebugInfo();
tempDesired = 36;
// <END>Setup and Initialize Arduino Cloud
//~~~~~Setup MKR Carrier and Sensors~~~~//
CARRIER_CASE = true;
carrier.begin();
pinMode (PIR, INPUT);
//~~<END>Setup MKR Carrier and Sensors~~//
//~~~~~~~~Initialize Motor Settings~~~~~~~~//
stepperCold.setAcceleration(80);
stepperCold.setMaxSpeed(1000);
stepperHot.setAcceleration(80);
stepperHot.setMaxSpeed(1000);
//~~~~~<END>Initialize Motor Settings~~~~~~//
//~~~~~~~~~~Limit Switch Settings~~~~~~~~~~//
ls_hot.setDebounceTime(50);
ls_cold.setDebounceTime(50);
//~~~~~~~<END>Limit Switch Settings~~~~~~~~//
//~~~~~~~~Setup Temperature Sensors~~~~~~~~//
float Th_1 = 0;
int p = 1;
int n = Temps.getNumberOfDevices();
Serial.println(n);
if(n < 2){//Verify if all snesors are pressent
int see = 2 - n;
String errr = see+stringSeven;
Serial.println(errr);
}
//~~~~~~~<END>Setup Temperature Sensors~~~~~~~//
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~LOOP~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int check = 1;
int imp = 0;
int prevTemp = 0;
void loop() {
ArduinoCloud.update();
// carrier.display.setRotation(0);
//~~~~~~~~~Check Touch Buttons~~~~~~~~~~//
TouchButtonsCheck();
//~~~~~~~~~<END>Check Touch Buttons~~~~~//
//~~~~~~~~~~~~~~Run Flow Checks~~~~~~~~~~~~~//
Val = digitalRead(PIR);
//~~~~~~~~~~~~No Motion Detected?~~~~~~~~~~~//
while(Val == LOW){
ArduinoCloud.update();
for(int r = 0; r < 5; r++){
carrier.leds.setPixelColor(r, 0, 0, 0);
carrier.leds.show();
}
Status = stringFive;
//~~~~~~~~~~~Check Touch Buttons~~~~~~~~~~~~~//
TouchButtonsCheck();
//~~~~~~~~~<END>Check Touch Buttons~~~~~~~~~~//
motorHot = stepperHot.currentPosition();
motorCold = stepperCold.currentPosition();
Val = digitalRead(PIR);
}
//~~~~~~~~~~~~~~~Motion Detected~~~~~~~~~~~~~//
//~~~~~~~~~~~~Check Touch Buttons~~~~~~~~~~~//
TouchButtonsCheck();
//~~~~~~~~~<END>Check Touch Buttons~~~~~~~~~//
//~~~~~~~~~~<END>Run Flow Checks~~~~~~~~~~~~//
//~~~~~~~~~~Wash Em' Hands Folks~~~~~~~~~~~~//
float flT = millis();
while((millis() - flT) <= 10000){
tempOut = getT(2);
Status = stringEight + tempOut+stringSix;
ArduinoCloud.update();
for(int r = 0; r < 5; r++){
if (r % 2 == 0){carrier.leds.setPixelColor(r, 50, 0, 0);}
else{carrier.leds.setPixelColor(r, 0, 50, 0);}
carrier.leds.show();
}
//~~~~~~~~~~~Check Touch Buttons~~~~~~~~~~~~//
TouchButtonsCheck();
//~~~~~~~~<END>Check Touch Buttons~~~~~~~~~~//
//~~~~~~~~~~~~Run Motor Control~~~~~~~~~~~~~//
if(tempDesired != prevTempDesired){
TempDesiredChange();
motors(positions);
prevTempDesired = tempDesired;
}
else{
motors(positions);
}
motorHot = stepperHot.currentPosition();
motorCold = stepperCold.currentPosition();
}
//~~~~~~~~<END> Run Motor Control~~~~~~~~~~~//
motorsZero();
}
float Kalman(float x){
float rq = 0.00001; float qq = 0.000005; float hq = 1;
float kq = pqr*hq/(hq*pqr*hq+rq);
Uqr = Uqr+kq*(x-hq*Uqr);
pqr = (hq-kq*hq)*pqr+qq;
return Uqr;
}
float getT(int num){
float tTheo;
switch(num){
case 1:
// Temps.select(Sensor1Add);
// return Temps.getTempC();
break;
case 2:
Temps.select(Sensor2Add);
if(stepperCold.currentPosition() != 0 || stepperHot.currentPosition() != 0){
if(stepperCold.currentPosition() != 0 && stepperHot.currentPosition() == 0){
tTheo = tempCold;
}
if(stepperCold.currentPosition() == 0 && stepperCold.currentPosition() != 0){
tTheo = tempHot;
}
if(stepperCold.currentPosition() != 0 && stepperHot.currentPosition() != 0){
tTheo = tempCold + (tempHot-tempCold)*(stepperHot.currentPosition()/198)/(stepperCold.currentPosition()/204+positions[1]/198);
}
return 0.7 * tTheo + 0.3 * Kalman(Temps.getTempC());
break;
}
case 3:
Temps.select(Sensor3Add);
return Temps.getTempC();
break;
}
}
void TempDesiredChange(){
Vd = tempDesired / 100;
positions[1] = (long)(Vd * 198);
if (Vd <= 0.25){
positions[0] = 200;
}
if (Vd <= 0.5 && Vd > 0.25){
positions[0] = 140;
}
if (Vd <= 0.75 && Vd > 0.5){
positions[0] = 90;
}
if (Vd <= 1 && Vd > 0.75){
positions[0] = 30;
}
}
void TouchButtonsCheck(){
carrier.Buttons.update();
if (carrier.Buttons.onTouchDown(TOUCH0)) {
tempDesired = 0;
carrier.leds.setPixelColor(0, 0, 50, 0);
carrier.leds.show();
}
if(carrier.Buttons.onTouchUp(TOUCH0)){
carrier.leds.setPixelColor(0, 0);
carrier.leds.show();
}
if (carrier.Buttons.onTouchDown(TOUCH1)) {
tempDesired = 50;
carrier.leds.setPixelColor(1, 0, 50, 0);
carrier.leds.show();
}
if(carrier.Buttons.onTouchUp(TOUCH1)){
carrier.leds.setPixelColor(1, 0);
carrier.leds.show();
}
if (carrier.Buttons.onTouchDown(TOUCH2)) {
tempDesired = 76;
carrier.leds.setPixelColor(2, 0, 50, 0);
carrier.leds.show();
}
if(carrier.Buttons.onTouchUp(TOUCH2)){
carrier.leds.setPixelColor(2, 0);
carrier.leds.show();
}
if (carrier.Buttons.onTouchDown(TOUCH3)) {
if(tempDesired < 102){
tempDesired += 2;
}
carrier.leds.setPixelColor(3, 50, 0, 0);
carrier.leds.show();
}
if (carrier.Buttons.onTouchUp(TOUCH3)) {
carrier.leds.setPixelColor(3, 0);
carrier.leds.show();
}
if (carrier.Buttons.onTouchDown(TOUCH4)) {
if(tempDesired > -2){
tempDesired -= 2;
}
carrier.leds.setPixelColor(4, 0, 0, 50);
carrier.leds.show();
}
if (carrier.Buttons.onTouchUp(TOUCH4)) {
carrier.leds.setPixelColor(4, 0);
carrier.leds.show();
}
}
void motors(long psn[2]){
int j = 1; int k = 1;
stepperCold.moveTo(psn[0]);
stepperHot.moveTo(psn[1]);
while(j == 1 || k == 1){
ls_cold.loop();
ls_hot.loop();
stateCold = ls_cold.getState();
stateHot = ls_hot.getState();
if(j == 1){
stepperCold.run();
}
if(k == 1){
stepperHot.run();
}
if(ls_cold.isPressed() || stepperCold.distanceToGo() == 0){
if(j == 1){
stepperCold.stop();
j++;
}
}
if(ls_hot.isPressed() || stepperHot.distanceToGo() == 0){
if(k == 1){
stepperHot.stop();
k++;
}
}
}
positionHot = stepperHot.currentPosition();
positionCold = stepperCold.currentPosition();
}
void motorsZero(){
int j = 1; int k = 1;
while(j == 1 || k == 1){
ls_cold.loop();
ls_hot.loop();
stateCold = ls_cold.getState();
stateHot = ls_hot.getState();
if(j == 1){
stepperCold.moveTo(stepperCold.currentPosition() - 20);
stepperCold.run();
}
if(k == 1){
stepperHot.moveTo(stepperHot.currentPosition() - 20);
stepperHot.run();
}
if(ls_cold.isPressed()){
if(j == 1){
stepperCold.stop();
Serial.println("1 pressed");
stepperCold.setCurrentPosition(0);
j++;
}
}
if(ls_hot.isPressed()){
if(k == 1){
stepperHot.stop();
Serial.println("2 pressed");
stepperHot.setCurrentPosition(0);
k++;
}
}
}
positionHot = stepperHot.currentPosition();
positionCold = stepperCold.currentPosition();
}
Comments
Please log in or sign up to comment.