Story

Introduction

The Dash Lid is the Amazon Container with a twist. It is designed to be a very low-friction device and method to use the Amazon Dash Replenishment Service.

The DashLid measures the remaining product in the container daily and requests a replenishment from Amazon if it is close to running out.

When a new shipment arrives from Amazon, the lid is simply placed on the new container.

Why Protein Powder?

Although the lid could work on many containers with a screw-type lid, protein powder makes a good use case right now because it typically takes awhile to deplete, it is easily and reliably measured, and has a higher price range than flour or sugar.

Thisr means that early adopters could find that it is worth the price of the sensor and software to acquire long-term customers.

The Customer's Point of View

The customer will use the lid in two scenarios:

1. Initial Setup

2. Recurring Use

For initial setup the customer will plug their lid into a computer, enter their WIFI credentials, and select the time to check for replenishment. The customer then runs an app that opens a web page to allow the customer to login to Amazon and select their product.

I believe that this is as easy as it can be. The device will likely need to charge before use, so plugging it into a computer has a dual purpose. The USB connection is also more secure than wireless when exchanging data with the device.

After the device is charged (on the MKR1000 the yellow light will stop flashing) it can be put on the new container.

When a new shipment arrives, the customer just moves the lid to the new container, optionally pouring in leftover product.

How it Works

The client application is a Windows Forms application written in C# using Visual Studio. The basic operation is as follows:

The Client presents a form for the user to fill in their WIFI SSID and Password. The user can also choose a time for the device to request replenishment. For example, at 3:00 AM the device can ping the product level and if it is below a predefined ordering level a replenishment request will be sent.

The user is then prompted to plug in their device to charge it and set it up.

The next step is to request the device specifications, such as Model and Serial Number.

Arduino Code FilesArduino

Arduino Code for DashLidArduino

DashLid Code

#include<ArduinoJson.h>#include<RTCZero.h>#include"AmazonDRS.h"//JSON Parsing -------------------------------------------------------------------------------StaticJsonBuffer<2000>jsonBuffer;StringreadString;//--------------------------------------------------------------------------------------------//DRS ----------------------------------------------------//WiFi creds ----------------------------------------------------------------------------------//char ssid[] = ""; // your network SSID (name)//char pass[] = ""; // your network password (use for WPA, or use as key for WEP)//------------------------------------------------------------------------------------------------------#define slotNumber 1 //This will vary for multi slot devices - dash buttons typically only serve one product/slotconstintdashButton=5;//DIO number of the pushbutton pinstaticlongbuttonHigh=0;//millis of last button push for switch debouncingstaticStringslotStatus="";//boolean which depicts if slot is available for replenishmentstaticStringslotId="";//unique slot id ex: 0a5038b7-7609-4b81-b87e-3e291f386324 //Enter your device InfocharjsonDeviceInfo[]="{\"model\":\"Dry_Goods_Retro_Container\",\"serial\":\"dgrc01\"}";AmazonDRSDRS=AmazonDRS();//RTC ----------------------------------------------------RTCZerortc;//rtc RTCZero instanceboolawake=false;//true = running, false = sleep mode//Values to set the current initial date and time byteseconds;byteminutes;bytehours;byteday;bytemonth;byteyear;bytereplenishhour;//Ultrasonic ---------------------------------------------#define echoPin 7 // Echo Pin#define trigPin 8 // Trigger Pin#define LEDPin 6 // Onboard LEDintmaximumRange=200;// Maximum range neededintminimumRange=5;// Minimum range neededintreplenishRange=20;//20 cm. or greater => Replenishlongduration,distance;// Duration used to calculate distance//Setup ---------------------------------------------------voidsetup(){Serial.begin(9600);delay(10000);//delay to measure current draw. Also needed to be able to re-program MKR1000 when using sleep mode//Exchange Data with Client ----------------------------------------------------------------Serial.println(jsonDeviceInfo);while(!Serial.available()){}//Wait for the serial portwhile(Serial.available()){delay(3);//delay to allow buffer to fillif(Serial.available()>0){charc=Serial.read();//gets one byte from serial bufferreadString+=c;//append to readString}}//if (readString.length() >0) Serial.println(readString); //see what was receivedJsonObject&root=jsonBuffer.parseObject(readString);// Test if parsing failedif(!root.success()){Serial.println("parseObject() failed");return;//We have a problem}StringssidString=root["wifissid"];charssid[ssidString.length()];ssidString.toCharArray(ssid,ssidString.length()+1);StringpassString=root["wifipassword"];charpass[passString.length()];passString.toCharArray(pass,passString.length()+1);StringrefreshToken=root["refreshtoken"];DRS.setRefreshToken(refreshToken);StringaccessToken=root["accesstoken"];DRS.setAccessToken(accessToken);Serial.print("==========> Refresh Token: ");Serial.println(refresh_token);Serial.print("==========> Access Token: ");Serial.println(access_token);seconds=root["second"];minutes=root["minute"];hours=root["hour"];year=root["year"];month=root["month"];day=root["day"];replenishhour=root["replenishhour"];Serial.print("SSID: ");Serial.println(ssid);//Serial.print("PASS: ");//Serial.println(pass);//------------------------------------------------------------------------------------------DRS.begin(ssid,pass);//Startup DRSpinMode(LEDPin,OUTPUT);//set LED pin to outputdigitalWrite(LEDPin,LOW);//turn LED offrtc.begin();//Start RTC, this is where the clock source is initializedrtc.setTime(hours,minutes,seconds);//set timertc.setDate(day,month,year);//set datertc.setAlarmTime(replenishhour,0,0);Serial.print("================> Replenish Hour: ");Serial.println(replenishhour);Serial.print("RTC Date/Time: ");PrintDateTime();//rtc.enableAlarm(rtc.MATCH_SS);//set alarm time to go off on matching secondsrtc.enableAlarm(rtc.MATCH_HHMMSS);//set alarm to go off at a specific timertc.attachInterrupt(ISR);//creates an interrupt that wakes the SAMD21 which is triggered by a FTC alarm//Replenish(); //Replenish immediately for testingrtc.standbyMode();//Puts the SAMD chip in standby (low power) mode. USB Serial port will not work (disconnects from USB). Comment out for testing.}//Loop -----------------------------------------------voidloop(){if(awake){Serial.println("awake in loop");ping();awake=false;}}voidISR(){//Serial.println("awake in ISR");awake=true;PrintDateTime();}voidping(){Serial.println("awake in ping");//Setup Ultrasonic SensorpinMode(trigPin,OUTPUT);pinMode(echoPin,INPUT);//Ping Ultrasonic Sensor and show distance/* The following trigPin/echoPin cycle is used to determine the distance of the nearest object by bouncing soundwaves off of it. *///digitalWrite(trigPin, LOW); delayMicroseconds(2);digitalWrite(trigPin,HIGH);delayMicroseconds(10);digitalWrite(trigPin,LOW);duration=pulseIn(echoPin,HIGH);//Calculate the distance (in cm) based on the speed of sound.floatdistance=duration/58.2;if(distance>=maximumRange||distance<=minimumRange){//Ignore//Serial.println("out of range");}else{//We have a valid distance reading, see if we need to replenishdigitalWrite(LEDPin,HIGH);delay(1000);digitalWrite(LEDPin,LOW);delay(1000);if(distance>=replenishRange)Replenish();/* Send the distance to the computer using Serial protocol, and turn LED OFF to indicate successful reading. */Serial.print("Distance: ");Serial.print(distance,3);Serial.print(" cm Duration: ");Serial.println(duration);}}voidReplenish(){Serial.println("Requesting replenishment");DRS.retrieveSubscriptionInfo();//check slot statusesslotStatus=DRS.getSlotStatus(slotNumber);slotId=DRS.getSlotId(slotNumber);//Replenish if(slotStatus=="true")//if the product in slot are available {//we have a match! replenish the products associated with that slot! Serial.println("Trying to replenish!!!");DRS.requestReplenishmentForSlot(slotId);}else{Serial.print("Sorry, slot ");Serial.print(slotId);Serial.println(" is not available at this time");}}voidprint2digits(intnumber){if(number<10){Serial.print("0");// print a 0 before if the number is < than 10}Serial.print(number);}voidPrintDateTime(){// Print date...print2digits(rtc.getMonth());Serial.print("/");print2digits(rtc.getDay());Serial.print("/");print2digits(rtc.getYear());Serial.print(" ");// ...and timeprint2digits(rtc.getHours());Serial.print(":");print2digits(rtc.getMinutes());Serial.print(":");print2digits(rtc.getSeconds());Serial.println();}