Project in progress
Internet of Toiletries

Internet of Toiletries © LGPL

Making your toiletries smart, automatically reorder them when they are under 1/4 of the weight.

  • 1,149 views
  • 1 comment
  • 5 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)

Apps and online services

About this project

Demo of Auto Reorder

Demo for Amazon Dash Replenishment ordering

What is Internet of Toiletries?

Internet of Toiletries is a project that makes all of our toiletries smart, it focuses on problems of refilling toiletries such as toothpaste, contact lens solution, lotions, and many other items. Users can track their usage over the phone, and Amazon RDS helps to reorder once it reaches 1/4 of the item weight. The current proof of concept design can hold 2 different items, which we will use to demonstrate the ordering system for Tooth Paste and Contact Lens solution.

Why did I build this?

I've always find myself out of contact lens solutions and toothpastes, ordering on Amazon generally takes 2 days to ship and running to super market can be a hassle when it's cold outside. These are also items I don't usually buy extra for storage, mainly due to the fact they have expiration date on them. For the proof of concept we are doing 2 items because that's only 2 items I've personally find always missing on my own life. But we can easily expand this to 3 to 4 item units depending on the situation.

How it works

The Toiletries uses weight sensor to sense the product weight and sending it to the cloud, this allows user can check their items in real time. When the toiletries reach threshold of 1/4 of the weight it would automatically use Amazon RDS for reordering, allows users to seamlessly getting their toiletries. The trigger would be 4am in the morning for the order checked once a day, this allows users not to have to worry about taking items out of the Unit.

Design of the unit

We've been taking a lot of consideration for the design of the unit including the aesthetic appeal as well as the simplicity for the use, the top unit is high enough to hold items like toothpaste, contact lens solutions as well as many other items. The scale sits in the middle, the hole under the scale plate is used to do the wiring to the bottom portion which holds the battery and PLCs.

The bottom portion is used to hold the chip, the sensor wiring and the battery, the POC was made a little larger on purpose to accommodate different sizes of chips as well as battery, the next version is likely much smaller.

How it was built

There are 3 components that making Internet of Toiletries work, the code for the chip, the node.js server, and consumer facing Android app.

Arduino 101

The IoT chip senses the weight data, then sending it to the cloud every 5 seconds (on POC), this way it allows users to monitor them at the real time. The code is being open sourced and attached below, Arduino 101 is using Wifi Shield, 2x Hx711 Weight Weighing Load Cell Conversion Module Sensors

MediaTek LinkIt™

Alternatively, we were also able to get this working using LinkIt™ Smart 7688 Duo because the MediaTek LinkIt™ Smart 7688 Duo contains the wifi without the shield. The Media kit uses A0, A1 for the first scale, and A2, A3 for the second scale. The mechanism works as MediaTek will upload the sensor data every 5 minutes. The code for MediaTek LinkIt™ using Arduino IDE is being open sourced and attached below.

Node.js server and mysql

The server uses mysql for data storage, which includes updating the weight value as well as getting the data.

Amazon Dash Replenishment

Following the walkthrough of Amazon Dash Replenishment, I was able to setup LWA, SNS and ADR Device. Creating 2 slots on the unit.

Android

The Android app does 3 things. First is allowing user to login with Amazon through LWA, creating an environment allowing user to make purchases.

Second is retrieving and displaying data of the current unit. In the Proof of Concept prototype we've already connected with the device as it is the only device available. Login through RDS and setup the slot items, then loading the weight to see how much does it have left.

Third is where when item is below threshold, android would automatically make a purchase. Amazon Dash Replenishment Service will automatically place the order at 4am in the morning if it is below threshold of 25%.

In the following demo we are showing how the functionalities work with Amazon Dash Replenishment. The reordering system happens at 4am, we just comment a line for the trigger on the demo for the automatic ordering.

Next Step

Current proof of concept is fully functional and being tested by myself, the very near future I will be improving the design of the casing and sensors and see if there is a market for this item.

Code

Arduino Code For LinkIt Smart 7688 Duo and and Arduino 101Arduino
This is the code used for Arduino C which uploads the IoT data to the cloud every 5 seconds (for the demo, in reality it would be much less)

Server code and Android code will be posted separately on github
#include "HX711.h"
#include <SPI.h>
#include <WiFi.h>

WiFiClient client;
char clientServer[] = "shrouded-citadel-97257.herokuapp.com";
IPAddress ip(192,168,2,3);

char ssid[] = "";     //  your network SSID (name) 
char pass[] = "";    // your network password
int status = WL_IDLE_STATUS;

HX711 scale1;//(A1,A0);
HX711 scale2;//(A3,A2);

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

  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present"); 
    // don't continue:
    while(true);
  } 
  
  // attempt to connect to Wifi network:
  while ( status != WL_CONNECTED) { 
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:    
    status = WiFi.begin(ssid, pass);
//    status = WiFi.begin(ssid);

    // wait 10 seconds for connection:
    delay(20000);
  }   
  printWifiStatus();


    // parameter "gain" is ommited; the default value 128 is used by the library
  // HX711.DOUT  - pin #A1
  // HX711.PD_SCK - pin #A0
  scale1.begin(A1, A0);
  scale2.begin(A3, A2);

  scale1.set_scale(2280.f);                      // this value is obtained by calibrating the scale with known weights; see the README for details
  scale2.tare();               // reset the scale to 0
  scale1.set_scale(2280.f);                      // this value is obtained by calibrating the scale with known weights; see the README for details
  scale2.tare();      
}


void loop() {
  Serial.print("one reading:\t");
  Serial.print(scale1.get_units(10), 1);
  Serial.print("two reading:\t");
  Serial.println(scale2.get_units(10), 1);

  
  String PostData = "{\"product1weight\":" + String(scale1.get_units(10), 1) + ",\"product2weight\":" + String(scale2.get_units(10), 1) + " }";
    Serial.println(PostData);

  if (client.connect(clientServer, 80)) {  
    Serial.println("connected");
    Serial.println("POST /updatevalue HTTP/1.1");
    client.println("POST /updatevalue HTTP/1.1");
    client.println("Host: shrouded-citadel-97257.herokuapp.com");
    client.println("User-Agent: Internet of Toiltries");
    client.println("Content-Type: application/json");
    client.print("Content-Length: ");
    client.println(PostData.length());
    client.println();
    client.println(PostData);
    client.println();
    client.println("Connection: close");
  
    while(client.connected() && !client.available()) delay(1); //waits for data
    while (client.connected() && client.available()) { //connected or data available
      char c = client.read(); //gets byte from ethernet buffer
      Serial.print(c); //prints byte to serial monitor 
    }
    Serial.println();
    Serial.println("disconnecting.");
    Serial.println("==================");
    Serial.println();
    client.stop(); //stop client
  }

  scale1.power_down();             // put the ADC in sleep mode
  scale2.power_down();             // put the ADC in sleep mode
  delay(5000);
  scale1.power_up();
  scale2.power_up();
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
  
    // check firmware version
  Serial.print("Firmware version: ");
  Serial.println(WiFi.firmwareVersion());
}
Android codeJava
This is the main portion for Android
package hack.internetoftoiletries;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.amazon.identity.auth.device.AuthError;
import com.amazon.identity.auth.device.api.Listener;
import com.amazon.identity.auth.device.api.authorization.AuthCancellation;
import com.amazon.identity.auth.device.api.authorization.AuthorizationManager;
import com.amazon.identity.auth.device.api.authorization.AuthorizeListener;
import com.amazon.identity.auth.device.api.authorization.AuthorizeRequest;
import com.amazon.identity.auth.device.api.authorization.AuthorizeResult;
import com.amazon.identity.auth.device.api.authorization.ProfileScope;
import com.amazon.identity.auth.device.api.authorization.Scope;
import com.amazon.identity.auth.device.api.authorization.User;
import com.amazon.identity.auth.device.api.workflow.RequestContext;
import com.amazon.identity.auth.device.AuthError;
import com.amazon.identity.auth.device.authorization.api.AmazonAuthorizationManager;
import com.amazon.identity.auth.device.authorization.api.AuthorizationListener;
import com.amazon.identity.auth.device.authorization.api.AuthzConstants;

import org.codeandmagic.android.gauge.GaugeView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getName();
    public static final MediaType JSON
            = MediaType.parse("application/json; charset=utf-8");
    public static final MediaType STRING
            = MediaType.parse("application/x-www-form-urlencoded");
    public String DRS_URL = "https://dash-replenishment-service-na.amazon.com/replenish/";

    private TextView mProfileText;
    private TextView mProfileText2;
    private TextView mLogoutTextView;
    private ProgressBar mLogInProgress;
    private RequestContext requestContext;
    private boolean mIsLoggedIn;
    private View mLoginButton;
    private ViewGroup mWrapperProfile;
    private GaugeView mGaugeView1;
    private GaugeView mGaugeView2;

    private TextView product1;
    private TextView product2;

    private ScheduledFuture<?> future;
    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    OkHttpClient client = new OkHttpClient();

    private String slot1 = "f76a78ff-d73b-4c64-967f-d28f54645c9d";
    private String slot2 = "59a29d6d-39f3-4adb-8b88-96fc5e2261cc";

        private AmazonAuthorizationManager mAuthManager;

    private boolean started = false;
    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAuthManager = new AmazonAuthorizationManager(this, Bundle.EMPTY);

        requestContext = RequestContext.create(this);

        /*
        requestContext.registerListener(new AuthorizeListener() {
            @Override
            public void onSuccess(AuthorizeResult authorizeResult) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // At this point we know the authorization completed, so remove the ability to return to the app to sign-in again
                        setLoggingInState(true);
                    }
                });
                fetchUserProfile();
            }

            @Override
            public void onError(AuthError authError) {
                Log.e(TAG, "AuthError during authorization", authError);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        showAuthToast("Error during authorization.  Please try again.");
                        resetProfileView();
                        setLoggingInState(false);
                    }
                });
            }

            @Override
            public void onCancel(AuthCancellation authCancellation) {
                Log.e(TAG, "User cancelled authorization");
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        showAuthToast("Authorization cancelled");
                        resetProfileView();
                    }
                });
            }
        });*/


        setContentView(R.layout.activity_main);
        initializeUI();

        //new HttpGetRequestRefill().execute("");

        start();

        //Schedule for every 24 hours, this is just for demo, will be used in the future
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                    SharedPreferences sharedPref = MainActivity.this.getPreferences(Context.MODE_PRIVATE);
                    if(sharedPref.contains("access_token") && sharedPref.contains("refresh_token"))
                    {
                        new HttpGetRequestRefill().execute("");
                    }
            }
        };

        //schedule this once every day
        future = scheduler.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.DAYS);

    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            new HttpGetRequest().execute("");
            start();
        }
    };

    public void start() {
        started = true;
        handler.postDelayed(runnable, 2000);
    }

    @Override
    protected void onResume() {
        super.onResume();
        requestContext.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        SharedPreferences sharedPref = MainActivity.this.getPreferences(Context.MODE_PRIVATE);
        if(sharedPref.contains("refresh_token") && sharedPref.contains("access_token"))
        {
            mLoginButton.setVisibility(View.GONE);
            mWrapperProfile.setVisibility(View.VISIBLE);
        }
        else
        {
            mLoginButton.setVisibility(View.VISIBLE);
            mWrapperProfile.setVisibility(View.GONE);
        }

        /*
        Scope[] scopes = {ProfileScope.profile(), ProfileScope.postalCode()};
        AuthorizationManager.getToken(this, scopes, new Listener<AuthorizeResult, AuthError>() {
            @Override
            public void onSuccess(AuthorizeResult result) {
                if (result.getAccessToken() != null) {

                    fetchUserProfile();
                    mAccessToken = result.getAccessToken();
                } else {

                }
            }

            @Override
            public void onError(AuthError ae) {

            }
        });*/
    }


    private void fetchUserProfile() {
        User.fetch(this, new Listener<User, AuthError>() {

            /* fetch completed successfully. */
            @Override
            public void onSuccess(User user) {
                final String name = user.getUserName();
                final String email = user.getUserEmail();
                final String account = user.getUserId();
                final String zipCode = user.getUserPostalCode();

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        updateProfileData(name, email, account, zipCode);
                    }
                });
            }

            /* There was an error during the attempt to get the profile. */
            @Override
            public void onError(AuthError ae) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        setLoggedOutState();
                        String errorMessage = "Error retrieving profile information.\nPlease log in again";
                        Toast errorToast = Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_LONG);
                        errorToast.setGravity(Gravity.CENTER, 0, 0);
                        errorToast.show();
                    }
                });
            }
        });
    }

    private void updateProfileData(String name, String email, String account, String zipCode) {
        StringBuilder profileBuilder = new StringBuilder();
        profileBuilder.append(String.format("Welcome, %s!\n", name));
        profileBuilder.append(String.format("Your email is %s\n", email));
        profileBuilder.append(String.format("Your zipCode is %s\n", zipCode));
        final String profile = profileBuilder.toString();
        Log.d(TAG, "Profile Response: " + profile);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                updateProfileView(profile);
                setLoggedInState();
            }
        });
    }

    /**
     * Initializes all of the UI elements in the activity
     */
    private void initializeUI() {

        mWrapperProfile = (ViewGroup)findViewById(R.id.wrapperProfile);
        mLoginButton = findViewById(R.id.login_with_amazon);
        mLoginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Bundle options = new Bundle();
                // device_model is generated by the wizard when you create a device - replace "modelX"; serial is something you provide and should be visible to the customer - replace "serialY".
                String scope_data = "{\"dash:replenish\":{\"device_model\":\"Internet_of_Toiletries\", \"serial\":\"0\", \"is_test_device \":\"true\"} }";
                options.putString(AuthzConstants.BUNDLE_KEY.SCOPE_DATA.val, scope_data);

                // Request the authorization code instead of an access token
                options.putBoolean(AuthzConstants.BUNDLE_KEY.GET_AUTH_CODE.val, true);
                // Plain = code verifier; S256 uses a Base64url encoding of the code verifier's hash
                options.putString(AuthzConstants.BUNDLE_KEY.CODE_CHALLENGE.val, "e9598da04c204deaf2dff8892efdd9cb0e180b44f406c31ee916175a99511231");
                // Set code challenge method - "plain" or "S256"
                options.putString(AuthzConstants.BUNDLE_KEY.CODE_CHALLENGE_METHOD.val, "plain");

                mAuthManager.authorize(new String []{"dash:replenish"}, options, new AuthorizeListener());

                /*
                AuthorizationManager.authorize(
                        new AuthorizeRequest.Builder(requestContext)
                                .addScopes(ProfileScope.profile(), ProfileScope.postalCode())
                                .build()
                );*/
            }
        });

        mGaugeView1 = (GaugeView)findViewById(R.id.gauge_view1);
        mGaugeView1.setTargetValue(0);
        mGaugeView1.initDrawingRects();

        mGaugeView2 = (GaugeView)findViewById(R.id.gauge_view2);
        mGaugeView2.setTargetValue(0);
        mGaugeView2.initDrawingRects();

        product1 = (TextView)findViewById(R.id.product1);
        product2 = (TextView)findViewById(R.id.product2);

        // Find the button with the logout ID and set up a click handler
        View logoutButton = findViewById(R.id.logout);
        logoutButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                AuthorizationManager.signOut(getApplicationContext(), new Listener<Void, AuthError>() {
                    @Override
                    public void onSuccess(Void response) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                setLoggedOutState();
                            }
                        });
                    }

                    @Override
                    public void onError(AuthError authError) {
                        Log.e(TAG, "Error clearing authorization state.", authError);
                    }
                });
            }
        });

        String logoutText = getString(R.string.logout);
        mProfileText = (TextView) findViewById(R.id.profile_info);
        mProfileText2 = (TextView) findViewById(R.id.profile_info_2);
        mLogoutTextView = (TextView) logoutButton;
        mLogoutTextView.setText(logoutText);
        mLogInProgress = (ProgressBar) findViewById(R.id.log_in_progress);
    }

    /**
     * Sets the text in the mProfileText {@link TextView} to the value of the provided String.
     *
     * @param profileInfo the String with which to update the {@link TextView}.
     */
    private void updateProfileView(String profileInfo) {
        Log.d(TAG, "Updating profile view");
        mProfileText.setText(profileInfo);
        mProfileText2.setText(profileInfo);
    }

    /**
     * Sets the text in the mProfileText {@link TextView} to the prompt it originally displayed.
     */
    private void resetProfileView() {
        setLoggingInState(false);
        mProfileText.setText(getString(R.string.default_message));
    }

    /**
     * Sets the state of the application to reflect that the user is currently authorized.
     */
    private void setLoggedInState() {
        mLoginButton.setVisibility(Button.GONE);
        setLoggedInButtonsVisibility(Button.VISIBLE);
        mIsLoggedIn = true;
        setLoggingInState(false);
    }

    /**
     * Sets the state of the application to reflect that the user is not currently authorized.
     */
    private void setLoggedOutState() {
        mLoginButton.setVisibility(Button.VISIBLE);
        setLoggedInButtonsVisibility(Button.GONE);
        mIsLoggedIn = false;
        resetProfileView();
    }

    /**
     * Changes the visibility for both of the buttons that are available during the logged in state
     *
     * @param visibility the visibility to which the buttons should be set
     */
    private void setLoggedInButtonsVisibility(int visibility) {
        mLogoutTextView.setVisibility(visibility);
    }

    /**
     * Turns on/off display elements which indicate that the user is currently in the process of logging in
     *
     * @param loggingIn whether or not the user is currently in the process of logging in
     */
    private void setLoggingInState(final boolean loggingIn) {
        if (loggingIn) {
            mLoginButton.setVisibility(Button.GONE);
            setLoggedInButtonsVisibility(Button.GONE);
            mLogInProgress.setVisibility(ProgressBar.VISIBLE);
            mProfileText.setVisibility(TextView.GONE);
            mWrapperProfile.setVisibility(View.GONE);
        } else {
            if (mIsLoggedIn) {
                setLoggedInButtonsVisibility(Button.VISIBLE);
                mProfileText.setVisibility(TextView.VISIBLE);
                mWrapperProfile.setVisibility(View.VISIBLE);
            } else {
                mLoginButton.setVisibility(Button.VISIBLE);
                mProfileText.setVisibility(TextView.GONE);
                mWrapperProfile.setVisibility(View.GONE);
            }
            mLogInProgress.setVisibility(ProgressBar.GONE);
        }
    }

    private void showAuthToast(String authToastMessage) {
        Toast authToast = Toast.makeText(getApplicationContext(), authToastMessage, Toast.LENGTH_LONG);
        authToast.setGravity(Gravity.CENTER, 0, 0);
        authToast.show();
    }


    public class DashAuthorize extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... params){
            String result = null;
            try {
                if(params.length == 0 || params[0] == null)
                {
                    return null;
                }

                RequestBody body = RequestBody.create(STRING, params[0]);

                Request AMZNrequest = new Request.Builder()
                        .url("https://api.amazon.com/auth/O2/token")
                        .post(body)
                        .build();
                Response AMZNresponse = client.newCall(AMZNrequest).execute();
                String responseBody = AMZNresponse.body().string();
                Log.e("responseBody", responseBody);

                JSONObject authData = new JSONObject(responseBody);
                SharedPreferences sharedPref = MainActivity.this.getPreferences(Context.MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPref.edit();
                if(authData.has("access_token"))
                {
                    editor.putString("access_token", authData.getString("access_token"));
                    editor.commit();
                }
                if(authData.has("refresh_token"))
                {
                    editor.putString("refresh_token", authData.getString("refresh_token"));
                    editor.commit();
                }
                return responseBody;
            }
            catch(IOException e){
                e.printStackTrace();
                result = null;
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return result;
        }
        protected void onPostExecute(String result){
            super.onPostExecute(result);
            if(result != null)
            {
                try {
                    JSONObject authData = new JSONObject(result);
                    if(authData.has("refresh_token")) {
                        mLoginButton.setVisibility(View.GONE);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                Log.e("stuff", result);

            }
        }
    }


    public class DashRefresh extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... params){
            String result = null;
            try {

                SharedPreferences sharedPref = MainActivity.this.getPreferences(Context.MODE_PRIVATE);
                if(!sharedPref.contains("access_token") || !sharedPref.contains("refresh_token") || !sharedPref.contains("client_id"))
                {
                    return null;
                }

                String bodyraw = "grant_type=refresh_token&refresh_token=" + sharedPref.getString("sharedPref", "")
                        + "&client_id=" + sharedPref.getString("client_id", "");

                RequestBody body = RequestBody.create(STRING, bodyraw);

                Request AMZNrequest = new Request.Builder()
                        .url("https://api.amazon.com/auth/O2/token")
                        .post(body)
                        .build();
                Response AMZNresponse = client.newCall(AMZNrequest).execute();
                String responseBody = AMZNresponse.body().string();
                Log.e("responseBody", responseBody);

                JSONObject authData = new JSONObject(responseBody);
                SharedPreferences.Editor editor = sharedPref.edit();
                if(authData.has("access_token"))
                {
                    editor.putString("access_token", authData.getString("access_token"));
                    editor.commit();
                }
                if(authData.has("refresh_token"))
                {
                    editor.putString("refresh_token", authData.getString("refresh_token"));
                    editor.commit();
                }

                return responseBody;
            }
            catch(IOException e){
                e.printStackTrace();
                result = null;
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return result;
        }
        protected void onPostExecute(String result){
            super.onPostExecute(result);
            super.onPostExecute(result);
            if(result != null)
            {
                try {
                    JSONObject authData = new JSONObject(result);
                    if(authData.has("refresh_token")) {
                        mLoginButton.setVisibility(View.GONE);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                Log.e("stuff", result);
                new HttpGetRequestRefill().execute("");

            }
        }
    }

    public class HttpGetRequest extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... params){
            String result;
            String inputLine;
            try {
                Request request = new Request.Builder()
                        .url("https://shrouded-citadel-97257.herokuapp.com/getproduct")
                        .build();
                Response response = client.newCall(request).execute();
                if (!response.isSuccessful()) {
                    return null;
                }

                return response.body().string();
            }
            catch(IOException e){
                e.printStackTrace();
                result = null;
            }
            return result;
        }
        protected void onPostExecute(String result){
            super.onPostExecute(result);
            if(result != null)
            {
                try {
                    JSONArray data = new JSONArray(result);
                    if(data.length() > 0)
                    {
                        JSONObject item = data.getJSONObject(0);
                        String product1_desc = item.optString("Product1Name", "");
                        String product2_desc = item.optString("Product2Name", "");
                        float product_ratio1 = (float) (item.getDouble("Product1weight") / item.getDouble("Product1Max"));
                        float product_ratio2 = (float) (item.getDouble("Product2weight") / item.getDouble("Product2Max"));

                        product1.setText(product1_desc);
                        product2.setText(product2_desc);
                        mGaugeView1.setTargetValue(product_ratio1 * 100.0f);
                        mGaugeView2.setTargetValue(product_ratio2 * 100.0f);

                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //Amazon Dash
    public class HttpGetRequestRefill extends AsyncTask<String, Void, String> {
        @Override
        protected String doInBackground(String... params){
            String result = null;
            try {

                Request request = new Request.Builder()
                        .url("https://shrouded-citadel-97257.herokuapp.com/getproduct")
                        .build();
                Response response = client.newCall(request).execute();
                if (!response.isSuccessful())
                {
                    return null;
                }
                String jsonresult = response.body().string();
                Log.e("result", jsonresult);
                JSONArray data = new JSONArray(jsonresult);
                if(data.length() > 0)
                {
                    JSONObject item = data.getJSONObject(0);
                    float product_ratio1 = (float) (item.getDouble("Product1weight") / item.getDouble("Product1Max"));
                    float product_ratio2 = (float) (item.getDouble("Product2weight") / item.getDouble("Product2Max"));
                    String url = "";
                    //If first product is smaller than 25%
                    //if(product_ratio1 * 100.0f < 25.0f)
                    //{
                    url = DRS_URL + slot1;
                    //}
                    //If second product is smaller than 25%
                    if(product_ratio2 * 100.0f < 25.0f)
                    {
                        url = DRS_URL + slot2;
                    }
                    SharedPreferences sharedPref = MainActivity.this.getPreferences(Context.MODE_PRIVATE);
                    RequestBody body = RequestBody.create(JSON, "");
                    Request AMZNrequest = new Request.Builder()
                            .addHeader("Authorization", "Bearer " + sharedPref.getString("access_token", ""))
                            .addHeader("x-amzn-accept-type", "com.amazon.dash.replenishment.DrsReplenishResult@1.0")
                            .addHeader("x-amzn-type-version", "com.amazon.dash.replenishment.DrsReplenishInput@1.0")
                            .url(url)
                            .post(body)
                            .build();
                    Response AMZNresponse = client.newCall(AMZNrequest).execute();
                    String responseBody = AMZNresponse.body().string();
                    return responseBody;
                }
            }
            catch(IOException e){
                e.printStackTrace();
                result = null;
            }
            catch (JSONException e) {
                e.printStackTrace();
                result = null;

            }
            return result;
        }
        protected void onPostExecute(String result){
            super.onPostExecute(result);
            if(result != null)
            {
                JSONObject order = null;
                try {
                    order = new JSONObject(result);
                    if(!order.has("eventInstanceId"))
                    {
                        new DashRefresh().execute("");
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                Log.e("result", result);
            }
        }
    }

    public class AuthorizeListener implements AuthorizationListener {

        /* Authorization was completed successfully. */
        @Override
        public void onSuccess(Bundle response) {

            try {
                String authorizationCode = response.getString(AuthzConstants.BUNDLE_KEY.AUTHORIZATION_CODE.val);
                String clientId = mAuthManager.getClientId();
                String redirectUri = mAuthManager.getRedirectUri();


                SharedPreferences sharedPref = MainActivity.this.getPreferences(Context.MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPref.edit();
                editor.putString("client_id", clientId);
                editor.commit();
                /*
                RequestBody body = new FormBody.Builder()
                        .add("grant_type", "authorization_code")
                        .add("code", authorizationCode)
                        .add("redirect_uri", redirectUri)
                        .add("client_id", clientId)
                        .add("code_verifier", "e9598da04c204deaf2dff8892efdd9cb0e180b44f406c31ee916175a99511231")
                        .build();*/


                String body = "grant_type=authorization_code&code=" + authorizationCode + "&redirect_uri=" + redirectUri
                        + "&client_id=" + clientId + "&code_verifier=e9598da04c204deaf2dff8892efdd9cb0e180b44f406c31ee916175a99511231";
                Log.e("doh", body);

                //Getting authorized
                new DashAuthorize().execute(body);
            } catch (AuthError authError) {
                authError.printStackTrace();
            }
        }
        /* There was an error during the attempt to authorize the application. */
        @Override
        public void onError(AuthError ae) {
            String errorResponse = ae.getMessage();
            if(errorResponse != null)
            {
                Log.e("doh", errorResponse);
            }
        }
        /* Authorization was cancelled before it could be completed. */
        @Override
        public void onCancel(Bundle cause) {
        }
    }

}
server portionJavaScript
This is the server portion where the activity is handled via node.js
var express = require('express');
var mysql = require('mysql');
var app = express();
var bodyParser = require('body-parser');
var jsonParser = bodyParser.json();

require('dotenv').config()

var connection = mysql.createConnection({
  host     : process.env.RDS_HOSTNAME,
  user     : process.env.RDS_USERNAME,
  password : process.env.RDS_PASSWORD,
  port     : process.env.RDS_PORT,
  database : process.env.RDS_DB
});

app.set('port', (process.env.PORT || 5000));

app.use(express.static(__dirname + '/public'));

// views is directory for all template files
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

app.get('/', function(request, response) {
	console.log(process.env.RDS_HOSTNAME);

  	response.render('pages/index');
});

connection.connect(function(err) {
  if (err) {
    console.error('error connecting: ' + err.stack);
    return;
  }

  console.log('connected as id ' + connection.threadId);
});

app.get('/getproduct', function(request, response) {
		connection.query('Select * from products where id = 0', function(err, results) {
			if(err) throw err;
			console.log(results);
			response.send(results);
		});
});

//just updating demo unit for now, in the future we will update it by id
//this portion requires the service to allow me to change items on the slots
app.post('/updateproduct', jsonParser, function(request, response) {
    if (!request.body) return response.sendStatus(400);

	connection.query('UPDATE products SET Product1 = ?, Product2 = ?, Product1Max= ?, Product2Max = ?, Product1Name = ?, Product2Name =? WHERE id = 0',
	[request.body.product1, request.body.product2, request.body.product1max, request.body.product2max, request.body.product1name, request.body.product2name], function(err, results) {
	    if(err) throw err;
		console.log("success");

	});

  	response.sendStatus(200);
});

//just updating demo unit for now, in the future we will update it by id
app.post('/updatevalue', jsonParser, function(request, response) {
    if (!request.body) return response.sendStatus(400);
	connection.query('UPDATE products SET Product1weight = ?, Product2weight = ? WHERE id = 0',
		[request.body.product1weight, request.body.product2weight], function(err, results) {
	                	if(err) throw err;
	                	console.log("success");
	                });
  	response.sendStatus(200);
});


app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});
Internet of Toiletries source code
Github that contains all the project files

Custom parts and enclosures

Unit Scale
Scale attached to the Unit, we are using 2 of these, the electrical scale is attached to the unit
Top portion
Top portion of the Unit
Top part lock
Locking mechanism for the top portion to attach to the bottom portion
Bottom portion
Bottom portion of Internet of Toiletries, this portion holds all the components as well as the battery.
Bottom Lock
Bottom lock for Internet of Toiletries, this part locks the bottom which contains all the components

Schematics

Wiring for Internet of Toiletries
This is the wiring for Internet of Toiletries from a photo perspective.
Img 20170225 183712 qgssvycheb
Wiring using Arduino 101
This is the wiring of Internet of Toiletries using Arduino 101
Img 20170226 012045 ftrccff0vx
Wiring between HX711 to Arduino
This is how HX711 connects to Arduino
61bliydhhbl  sl1001  1 b7fkv52qsb
HX711 diagram
How the scales are attached to HX711
510ihq5hshl  sl1038  ogv2giop9m

Comments

Similar projects you might like

Air Quality License Plate Holder

Project tutorial by Team Air Defender

  • 3,933 views
  • 1 comment
  • 16 respects

Voronoi101: Light Your Lamp!

Project tutorial by Gregory O. Voronin

  • 8,509 views
  • 2 comments
  • 25 respects

Traffic Light Information System

Project tutorial by Joppe Smeets and Pieter Luyten

  • 18,088 views
  • 26 comments
  • 39 respects

"Mini-Vintage" Internet Radio

Project tutorial by Guilherme Schallenbach

  • 28,869 views
  • 24 comments
  • 135 respects

AutoHome - Internet of Things (IoT) for Home Automation

Project showcase by Team AutoHome

  • 2,369 views
  • 0 comments
  • 5 respects
Add projectSign up / Login