Tanny - Tan Manager

by Ben Hur Creations in Circuits > Arduino

36 Views, 0 Favorites, 0 Comments

Tanny - Tan Manager

Tanny.jpg
Tanny 3.jpg
Tanny 2.jpg
Tanny Final Video

Tanny is your IoT personal tan manager.
It helps you maintain beautiful tanned skin color as long as possible!

Tanny uses smart sensors and integrated technologies to scan your skin color and measure how tanned you are, according to your previous skin color.

Tanny will then send personalized notifications on sunny days, based on real-time weather forecast.

Tanny will also make sure you don't forget to take your sunglasses with you, it is eye friendly ;)

Tanny is energy effective, and while you will be outside home it will automatically enter sleep mode.

Enjoy using Tanny & have a wonderful time in the sun!

Supplies

1. Adafruit Circuit Playground (CPX) x 3

2. Wifi connection

3. Blynk mobile-app

4. Integromat mobile-app

5. Integromat web-app

Envrionment

meeny.jpg
Integromat 111.png
ardu.jpg
Blynkie.png

This the first step and is meant to prepare the development environment before starting to code. As the french say, this is our "mise en place":

1. Open integromat account - https://www.integromat.com/

2. Download & install arduino - https://www.arduino.cc/en/software

3. Download Blynk mobile app using your appstore

4. Download Integromat app

5. Get 3 CPXs (Circuit Playground Express) - https://www.adafruit.com/product/3333
- 1 for sensing the skin color
- 1 as a smart sunglasses stand
- 1 as a door open detection sensor

We are all set to continue. Now we're going to create Tanny's setup!

Setup - Libraries, Constants + Global Variables

setup.jpg

Following is the code regarding the project setup. Copy it to your arduino project.
The code is documented in detail for your convenience:

#define BLYNK_PRINT SerialUSB
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h><br>#include <Adafruit_CircuitPlayground.h>
char auth[] = "Your_Blynk_Auth_Code";
char ssid[] = "Your_Wifi_Name";
char pass[] = "Your_Wifi_Password";
#define EspSerial Serial1
#define ESP8266_BAUD 9600
ESP8266 wifi(&EspSerial);
// Init clock variables
int second=0, minutes=0, hours=7;

// Sleep mode variable
int isHome = 1;
// #1.1 Proximity sensors init
#include 

#define SUNGLASSES_THRESHOLD 80
#define DOOR_THRESHOLD 80
#define HAND_THRESHOLD 85
Proximity sunglassesProximity;
Proximity doorProximity;
Proximity prox;
#if !defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS)
#error "Infrared support is only for the Circuit Playground Express, it doesn't work with other boards."
#endif
// #2 Init tan index parameters  
#define TAN_INDEX_LEVEL 15
int sumTanChange = 0;
int tanIndex = 1;
// #2 Init Color parameters  
 uint8_t currentColor[3];
 uint8_t prevColor[3];
 uint8_t basicColor[3];
 bool triggerToGetBaseColor = false;
 bool firstRun = true;
// #3
String recommendTanning[] = {"Go out to the sun Mr. Ashkenazi!", "You're more yoghurt than sexy", 
                              "Take your frisbi and get some sun!", "You're very close to perfect tan, almost there!",
                              "Beautiful tan lines, go get your D vitamin"};
String recommendation;
String weatherStatus;
void setup() {
    // Blynk + wifi connection init
    CircuitPlayground.begin();
    SerialUSB.begin(9600);
    // Wifi connection
    EspSerial.begin(ESP8266_BAUD);
    delay(10);
    Blynk.begin(auth, wifi, ssid, pass);
    
    // Proximity thresholds init
    sunglassesProximity.begin(SUNGLASSES_THRESHOLD);
    doorProximity.begin(DOOR_THRESHOLD);
    prox.begin(HAND_THRESHOLD);
    
    // Reset blynk skin color leds
    Blynk.virtualWrite(V2,0);
    Blynk.virtualWrite(V3,0);
    Blynk.virtualWrite(V4,0);
}

Functions & Main Loop

למדריך.jpg

Following is the code regarding the project main loop & different functions. Copy it to your arduino project.
The code is documented in detail for your convenience:

void loop() {
    Blynk.run();
    basicClock();

    // Activate only when user is at home (saving battery life), triggered by user's Integromat GPS location
    if(isHome){
        // Is the sunglasses on stand
        Serial.println("-----------       Sunglasses      -----------");
        Serial.println(sunglassesProximity.lastDist()); // For testing treshold
        if(sunGlassOnStand()){
            Serial.println("On stand");
        }
        else{
            Serial.println("Not on stand");
        }
        
        // #1 When you forget to take your sunglasses notification
        if(sunGlassOnStand() && doorIsOpen() && sunnyDay()){
            takeSunglassesNotification();
        }
        
        //********************************               #2 Color Scanner                  ******************************//
        // Take base color
        if(CircuitPlayground.rightButton()){
            Serial.println("-----------       Right Button      -----------");
            Serial.print("Rightbutton: ");
            Serial.println(CircuitPlayground.rightButton());
            buttonPressed();
            triggerToGetBaseColor = true;
        }
    
        // Update current color when the user wants to  
        if(prox.close() && triggerToGetBaseColor){
            if(prox.lastDist() > 90 && prox.lastDist() < 98){
                firstSkinScan(); 
            }
        }
        else if(prox.close() && !triggerToGetBaseColor && !firstRun){
            if(prox.lastDist() > 90 && prox.lastDist() < 98){
                scanSkinColor(); 
            }
        }
    
        //********************************               #2 Color Scanner                  ******************************//
    
        // Update the change in the tanIndex
        tanCompare(currentColor, prevColor);
    }
    
    // #3 Send tan alerts when it's a sunny day, every two hours
    if(sunnyDay() && second == 0 && minutes == 0 && hours % 2 == 0){
        sunAlert(); 
    }
}


// #1.1 Check if the sunglasses are on the stand
bool sunGlassOnStand(){
    if(sunglassesProximity.close()){
        return true;
    }
    else{
        return false;
    }
}


//********************************        #1.2 Check if the the door is open from the inside        ******************************//
bool doorIsOpen()
{
    int num;
    //Check if amount of IR light is greater than threshold
    if (doorProximity.close()) {
        //Say the threshold and that it recieved more IR light than the threshold
        Serial.println("-----------       Door      -----------");
        Serial.print("Close to object, threshold is ");
        Serial.print(String(doorProximity.getThreshold()));
        Serial.print(", distance is ");
        Serial.println(String(doorProximity.lastDist()));
        return true;
    } else {
        //Say the threshold and that it recieved less IR light than the threshold
        Serial.println("-----------       Door      -----------");
        Serial.print("Not close to object, threshold is ");
        Serial.print(String(doorProximity.getThreshold()));
        Serial.print(", distance is ");
        Serial.println(String(doorProximity.lastDist()));
        return false;
    }
    //If user sent data
    if (Serial.available()) {
        //Get number that user sent
        num = Serial.parseInt();
        //Set new threshold
        doorProximity.setThreshold(num);
        //Say the new threshold
        Serial.println("-----------       Door      -----------");
        Serial.print("Changed threshold to ");
        Serial.println(num);
    }
}
//********************************        #1.2 Check if the the door is open from the inside        ******************************//


// #1.3 Checks if there is sun outside
bool sunnyDay(){
    // checks if night time
    if(!dayTime(hours))
    {
        return false;
    }
    Blynk.virtualWrite(V5,99);
    if(weatherStatus.equals("Clear")){
        return true;
    }
    return false;
}


// #1.3 Return true if day time
bool dayTime(int hours) 
{
    if(6 < hours && hours < 18)
    {
        return true;
    }
    return false;
}


// #1.4 Reminds you to take the sunglasses on a sunny day
void takeSunglassesNotification(){
    // Send reminder to take sunglasses
    Blynk.virtualWrite(V0,99);
    
    // Activate Door CPX
    for(int i = 0; i < 9; i++){
        CircuitPlayground.setPixelColor(i, 0, 70, 158); 
        CircuitPlayground.setPixelColor(i+1, 0, 70, 158); 
        delay(20);
        CircuitPlayground.setPixelColor(i, 0, 0, 0); 
    }
    
    CircuitPlayground.playTone(1000,50); 
    CircuitPlayground.playTone(800,50); 
    CircuitPlayground.playTone(900,50); 
    delay(150);
    CircuitPlayground.clearPixels();
}


// #2.1 UX for the user - can scan his skin now
void buttonPressed(){
    for(int i = 0; i < 9; i += 2){
        CircuitPlayground.setPixelColor(i, 0, 128, 128);
        if(i > 0){
            CircuitPlayground.setPixelColor(i-1, 0, 0, 0);
        }
        CircuitPlayground.playTone(100*i+200,50);
        delay(20);
        CircuitPlayground.setPixelColor(i+1,  16, 65, 98); 
        CircuitPlayground.playTone(100*i+200,50);
        delay(20);
        CircuitPlayground.setPixelColor(i,  0, 0, 0);
    }
    CircuitPlayground.clearPixels();
    delay(1000);
second++; CircuitPlayground.setPixelColor(5, 200, 200, 200); } // #2.2 Gets the basic tan color and the daily tan color and return tanIndex void tanCompare(uint8_t* currentColor, uint8_t* prevColor){ sumTanChange = (prevColor[0] - currentColor[0]) + (prevColor[1] - currentColor[1]) + (prevColor[2] - currentColor[2]); int tanChangeLevel = sumTanChange / TAN_INDEX_LEVEL; // Determine the level of tan tanIndex += tanChangeLevel; // If got tanned if(sumTanChange > 0){ sumTanChange -= tanChangeLevel * TAN_INDEX_LEVEL; // Keep the sheerit of the tan change } else{ sumTanChange += tanChangeLevel * TAN_INDEX_LEVEL; // Keep the sheerit of the tan change } // Set min/max tan levels if(tanIndex < 1){ tanIndex = 1; } else if(tanIndex > 15){ tanIndex = 15; } } // #2.3 When the user scans his basic skin color (tan free, e.g. waist area) void firstSkinScan(){ // Reset previous colors Blynk.virtualWrite(V2,0); Blynk.virtualWrite(V3,0); Blynk.virtualWrite(V4,0); takeSkinColor(basicColor); prevColor[0] = basicColor[0]; prevColor[1] = basicColor[1]; prevColor[2] = basicColor[2]; triggerToGetBaseColor = false; Serial.println("----------- Colors -----------"); Serial.print("basic RED color="); Serial.println(basicColor[0]); Serial.print("basic GREEN color="); Serial.println(basicColor[1]); Serial.print("basic BLUEcolor="); Serial.println(basicColor[2]); Serial.print("prevColor: "); Serial.println(*prevColor); // Set the led blynk widget at skin's scanned color ledActivator(basicColor[0],basicColor[1],basicColor[2], 3); firstRun = false; CircuitPlayground.clearPixels(); } // #2.4 Scan the skin color based when the hand is positioned next to the cpx void scanSkinColor(){ takeSkinColor(currentColor); // Set the led blynk widget at skin's scanned color ledActivator(currentColor[0],currentColor[1],currentColor[2], 2); delay(750);
second++; ledActivator(prevColor[0],prevColor[1],prevColor[2], 4); Serial.println("----------- Colors -----------"); Serial.print("prev RED color="); Serial.println(prevColor[0]); Serial.print("prev GREEN color="); Serial.println(prevColor[1]); Serial.print("prev BLUEcolor="); Serial.println(prevColor[2]); Serial.print("current RED color="); Serial.println(currentColor[0]); Serial.print("current GREEN color="); Serial.println(currentColor[1]); Serial.print("current BLUEcolor="); Serial.println(currentColor[2]); prevColor[0] = currentColor[0]; prevColor[1] = currentColor[1]; prevColor[2] = currentColor[2]; } // #2.5 Scan the skin color and display it on blynk UI led widget void takeSkinColor(uint8_t color[3]) { int num; // Say the threshold and that it recieved more IR light than the threshold Serial.print("Close to object, threshold is "); Serial.print(String(prox.getThreshold())); Serial.print(", distance is "); Serial.println(String(prox.lastDist())); CircuitPlayground.senseColor(color[0], color[1], color[2]); CircuitPlayground.playTone(100,50); CircuitPlayground.playTone(800,50); delay(1000);
second++; //If user sent data if(Serial.available()){ //Get number that user sent num = Serial.parseInt(); //Set new threshold prox.setThreshold(num); //Say the new threshold Serial.print("Changed threshold to "); Serial.println(num); } } // #2.6 Activate the blynk led according to the skin's current color void ledActivator(int r, int g, int b, int ledNum){ String rH = decToHexa(r); Serial.print("rH now: "); Serial.println(rH); String gH = decToHexa(g); Serial.print("gH now: "); Serial.println(gH); String bH = decToHexa(b); Serial.print("bH now: "); Serial.println(bH); String num = "#" + rH + gH + bH; Serial.print("Concatenated num: "); Serial.println(num); if(ledNum == 2){ Blynk.virtualWrite(V2, 255); Blynk.setProperty(V2, "color", num); } else if(ledNum == 3){ Blynk.virtualWrite(V3, 255); Blynk.setProperty(V3, "color", num); } else if(ledNum == 4){ Blynk.virtualWrite(V4, 255); Blynk.setProperty(V4, "color", num); } delay(1000);
second++; } // #2.7 Convert decimal to hexadecimal represented by a string String decToHexa(int n) { // Char array to store hexadecimal number char hexaDeciNum[2] = {'0','0'}; // counter for hexadecimal number array int i = 0; while(n != 0) { // Temporary variable to store remainder int temp = 0; // storing remainder in temp variable. temp = n % 16; // check if temp < 10 if(temp < 10) { hexaDeciNum[i] = temp + 48; i++; } else { hexaDeciNum[i] = temp + 55; i++; } n = n/16; } Serial.println("----------- DecToHexa -----------"); Serial.print("Real Number "); Serial.print(hexaDeciNum[1]); Serial.print(" "); Serial.println(hexaDeciNum[0]); char hexNum[2]; hexNum[1] = hexaDeciNum[0]; hexNum[0] = hexaDeciNum[1]; Serial.print("After char transform "); Serial.println(hexNum); return hexNum; } // #3 Send sun alert to the user based on his tanIndex void sunAlert(){ // Choose the appropriate tan recommendation int tanRecommend = tanIndex / 3; recommendation = recommendTanning[tanRecommend]; // Send to blynk webhook to trigger android notification Blynk.virtualWrite(V1, recommendation); } // Reads the weather status from virtual pin V5 (gets the information from integromat) BLYNK_WRITE(V5){ // Reading the weather status weatherStatus = param[0].asString(); Serial.println("----------- Weather -----------"); Serial.print("Weather status is: "); Serial.println( weatherStatus); } // Checks if the user left or entered his home to activate / deactivate the system BLYNK_WRITE(V6){ // Reading the value isHome = param[0].asInt(); } // Clock function void basicClock(){ // Add one second every loop second++; // Add one minute every 60 minutes if(second>=60) { second=0; minutes++; } // Add one hour every 60 minutes if(minutes>=60) { second=0; minutes=0; hours++; } // Verify if hours more then 24 if(hours>=24) { second=0; minutes=0; hours=0; } Serial.println("----------- Clock -----------"); Serial.print("Time : "); //print the time Serial.print(hours); //Serial.print(" minutes: "); Serial.print(":"); Serial.print(minutes); Serial.print(":"); Serial.println(second); }<br>

Setup - Integromat (mobile + Web)

1 - Integromat init (2).JPG
4 - Integromat.JPG
4.1 - Integromat.JPG
4.2 - Integromat.JPG
3 - Integromat.JPG
3.1 - Integromat.JPG
3.2 - Integromat.JPG
2- Integromat.JPG
2.1 - Integromat.JPG
2.2 - Integromat.JPG
2.3 - Integromat.JPG
11111111111.JPG
222222222222.JPG
33333333333.JPG
44444444444444444.JPG
55555555555555.JPG
6666666666666666.JPG
77777777777777777777.JPG
WhatsApp Image 2021-02-28 at 19.58.13 (1).jpeg
WhatsApp Image 2021-02-28 at 19.58.13 (2).jpeg
WhatsApp Image 2021-02-28 at 19.58.13.jpeg

Create four scenarios in Integromat web app:

1. #1 scenario: "Sunglasses reminder"- send a notification to the user's phone when he leaves the house on a sunny day without his sunglasses.

2. #2 scenario: "It's a sunny day alert"- send a personalized notification to the user's phone (based on the user tan index) when it is a sunny day

3. #3 scenario: "Get weather forecast"- get the current weather status and send it to the CPX to determine if it is a sunny day.

4. #4 scenario: "Sleep mode"- detects when the user enters / leaves home area to put the system in sleep mode and save energy. Using the integromat app and the phone GPS to do so.


Add to each scenario the relevant services:

#1 Webhook (custom webhook), Android (send a push notification)

#2 Webhook (custom webhook), Android (send a push notification)

#3 Webhook (custom webhook), Weather (get daily weather forecast), Http (send a request)

#4 Android (watch entrances to / exits from geofence area) , Router (with the relevant conditions), Http (send a request)


Configure each service as demonstrated in the images. Special notes:
1. Http service- in the url line use your blynk authorization id and your blynk IP. This is the following pattern: http://Your_Blynk_IP/Your_Blynk_Auth_Code/update/Your_Desired_VirtualPin?value=Your_Value

(Blynk IP - to get it simply follow the instructions on the following link- https://osoyoo.com/2021/01/15/how-to-install-a-local-blynk-server-in-your-pc-and-get-a-local-blynk-token/)

2. Android service- to connect your phone to integromat follow this guide -

https://support.integromat.com/hc/en-us/articles/360019584834-Android OR integromat's built-in instructions

3. Run #2 scenario once while uploading the arduino code to access the 'value' field (the notification content)
4. Webhook service- to activate each url copy & paste it in a different tab. Once you see 'Accepted' text displayed in this tab, the webhook is activated.

Configure the inegromat app:

1. In settings enter GPS

2. Tick the "Entered/left the area" box and enter "manage areas"

3. Choose your home location and configure it's settings

Setup - Blynk Screen

WhatsApp Image 2021-02-28 at 18.17.16 (2).jpeg
WhatsApp Image 2021-02-28 at 05.43.19 (1).jpeg
WhatsApp Image 2021-02-28 at 05.43.19 (3).jpeg
WhatsApp Image 2021-02-28 at 05.43.19.jpeg
WhatsApp Image 2021-02-28 at 18.17.16 (3).jpeg
WhatsApp Image 2021-02-28 at 18.17.16.jpeg
WhatsApp Image 2021-02-28 at 18.17.16 (1).jpeg

Blynk Configuration:
1.
Enter blynk app and open a new project.
2. Copy the blynk project auth code to the line from the previous step

char auth[] = "Your_Blynk_Auth_Code";<br>

3. Add three led widgets & 3 webhook widgets.

4. Configure their names and pins as showns in the images.

5. Copy the relevant url from each integromat scenario.

6. Configure the webhooks 'method' and 'content type' fields as shown in the images

6. Pass the tan recommendation data by adding '/pin/' to "Tan recommendation" webhook configuration


The Blynk interface is in charge of three main actions:

1. Showing our real time skin color in the led widget. Every time we will scan our skin color - it will automatically display it in the screen. Three colors will be displayed- current, previous & base.

2. Sending the personalized tan recommendation to the user's phone. The message is sent via the CPX and to Integromat webhook widget. Blynk is the mediator

3. Connecting the door CPX and the "Sunglasses reminder" scenario in integromat interface

Setup is finished, now let's build & run the system!

Building Tanny

WhatsApp Image 2021-02-28 at 20.17.17.jpeg
WhatsApp Image 2021-02-28 at 20.20.40.jpeg
WhatsApp Image 2021-02-28 at 20.17.16.jpeg

1. Activate the door CPX and place it in the house entrance
Set the DOOR_THRESHOLD to the right value after testing the door CPX functionality

2. Activate the sunglasses CPX and place it in the sunglasses stand
Set the SUNGLASSES_THRESHOLD to the right value after testing the sunglasses CPX functionality

3. Place the tan scanner CPX in an accesible place.
Set the HAND_THRESHOLD to the right value after testing the tan CPX functionality

*For your convenience we used plenty of serial prints along the code for debugging and to define the thresholds.
Every threshold is printed with the relevant proximity sensor name above it.

Activating Tanny

#1 Skin Scanner
#2 Skin Scanner
#3 Skin Scanner
Sun Alert
Sun Alert

Before Runtime:

1. Play integromat different sceces

2. Activate blynk's UI

3. Run arduino's program


During Runtime:

1. Activate the first scan by pressing and holding the right button.
- Blue neopixels will runin a circle in the cpx for indication
- White neopixel will light constantly till the first scan
(1st video)

2. Place your hand near the scanner to activate it. To maintain constant color detecting, the activation will occur only when your hand is within a pre-defined range.
(2nd + 3rd videos)

*To scan your base color use a usually covered body part, such as the waists area or the back of your hand

3. Get notified whenever there is sun outside. The notifications you get are based on how tanned you are!

(4th video)

4. If you got outside your house and didn't take the sunglasses - don't worry the door cpx will notify by turning on lights & sound, and you will also get a phone notification
(5th video)

5. Enjoy your beautiful tanned color.

6. Sleep mode- integromat will indicate when you're not at home and will automatically stop entering the different loop functions

We hope you enjoyed Tanny the tan manager, and we wish you beatiful colors all year long!
Yoav & Gal

~ The End ~