claude garrett
Published

Dash Lid

The Dash Lid is an easy to use, low friction replenishment device. You just use it like a lid.

IntermediateFull instructions provided6 hours5,230
Dash Lid

Things used in this project

Hardware components

Arduino MKR1000
Arduino MKR1000
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×1
Adafruit 3.7V LiPo Battery
×1

Software apps and online services

Visual Studio 2015
Microsoft Visual Studio 2015
Arduino IDE
Arduino IDE

Story

Read more

Schematics

DashLid Schematic

Code

Visual Studio DashLid Client Solution

C#
This code is run to setup the DashLid Device.
No preview (download only).

Visual Studio DashLid Client Code

C#
Used to set up the Dash Lid
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using RestSharp;
using System.IO.Ports;

//Watin problems?
//Add reference to Interop.SHDocVw
//Set CopyLocal to true, or copy the dll into the Release/Debug folders

namespace DRS
{
    public partial class SetupForm : Form
    {
        SerialPort serialPort;
        string returnURL;
        string code;
        Tokens tokens;
        string wifiPassword;
        string wifiSsid;

        //Enter your client info here.
        //Go to https://developer.amazon.com/iba-sp/overview.html -> APPS & SERVICES -> Security Profile (in sub-header) and choose your security profile.
        const string client_id = "";
        const string client_secret = "";
        const string slot_id = ""; //APPS & SERVICES -> Dash Replenishment Service  (sub-header)

        DeviceData deviceData;
        DeviceSpecifications deviceSpecifications;
        string URL;
        byte replenishHour;

        public SetupForm()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //Save the settings from the form
            replenishHour = (Byte) cbRefreshHour.SelectedIndex;
            wifiSsid = tbSSID.Text;
            wifiPassword = tbPassword.Text;

            GetDRSDeviceInfo();
            BuildURL();
            UseWatinBrowser();
            ExtractCodeFromReturnURL();
            ObtainDRSTokens();
            SendDeviceInfo();
            ReadDeviceDebuggingInfo();
        }

        private void GetDRSDeviceInfo()
        {
            while (SerialPort.GetPortNames().Count() == 0); //Wait for a COM port to appear - when MKR1000 is connected
            string[] ports = SerialPort.GetPortNames();
            string port = ports[0];
            
            Console.WriteLine("Opening port " + port);

            serialPort = new SerialPort(port, 9600, Parity.None, 8, StopBits.One);
            serialPort.Open();
            string deviceInfo = serialPort.ReadLine();
            System.Diagnostics.Debug.WriteLine(deviceInfo);
            deviceSpecifications = Json.JsonParser.Deserialize<DeviceSpecifications>(deviceInfo);
        }

        private void BuildURL()
        {
            string scopeData = "{\"dash:replenish\":{\"device_model\":\"" + deviceSpecifications.model + "\",\"serial\":\"" + deviceSpecifications.serial + "\",\"is_test_device\":\"true\"}}";
            string urlEncodedScopeData = WebUtility.UrlEncode(scopeData); 
            URL = "https://www.amazon.com/ap/oa?client_id=" + client_id + "&scope=dash%3Areplenish&scope_data=" + urlEncodedScopeData + "&response_type=code&redirect_uri=https%3A%2F%2Fwww.getpostman.com%2Foauth2%2Fcallback";   
        }

        private void UseWatinBrowser()
        {
            //Check url until the code is returned
            WatiN.Core.IE ie = new WatiN.Core.IE(URL);
            bool OkToContinue = true;
            while (OkToContinue)
            {
                string currentURL = ie.Url;
                System.Diagnostics.Debug.WriteLine(currentURL);
                if (currentURL.Contains("code="))
                {
                    returnURL = currentURL;
                    OkToContinue = false;
                    ie.ForceClose();
                }
                else
                    System.Threading.Thread.Sleep(1000);
            }
        }

        private void ExtractCodeFromReturnURL()
        {
            //returnURL example: "https://app.getpostman.com/oauth2/callback?code=cccccccooooooooodddddddeeeeee&scope=dash%3Areplenish";
            int startString = returnURL.IndexOf("?code") + 6;
            int endString = returnURL.IndexOf("&scope");
            code = returnURL.Substring(startString, endString - startString);
            //System.Diagnostics.Debug.WriteLine(code);
        }

        private void ObtainDRSTokens()
        {
            var client = new RestClient("https://api.amazon.com/auth/o2/token");
            var request = new RestRequest(Method.POST);
            request.AddHeader("cache-control", "no-cache");
            request.AddHeader("content-type", "application/x-www-form-urlencoded");
            request.AddParameter("application/x-www-form-urlencoded", "grant_type=authorization_code&code=" + code + "&client_id=" + client_id + "&client_secret=" + client_secret + "&redirect_uri=https%3A%2F%2Fwww.getpostman.com%2Foauth2%2Fcallback", ParameterType.RequestBody);
            IRestResponse response = client.Execute(request);

            //System.Diagnostics.Debug.WriteLine(response.Content);
            tokens = Json.JsonParser.Deserialize<Tokens>(response.Content);
        }

        private void SendDeviceInfo()
        {
            deviceData = new DeviceData();
            deviceData.AccessToken = tokens.access_token;
            deviceData.RefreshToken = tokens.refresh_token;
            deviceData.WifiPassword = wifiPassword;
            deviceData.WifiSsid = wifiSsid;
            deviceData.ClientId = client_id;
            deviceData.ClientSecret = client_secret;
            deviceData.SlotId = slot_id;
            deviceData.ReplenishHour = replenishHour;

            DateTime dateTime = System.DateTime.Now;
            deviceData.Year = (byte)(dateTime.Year - 2000);
            deviceData.Month = (byte)dateTime.Month;
            deviceData.Day = (byte)dateTime.Day;
            deviceData.Hour = (byte)dateTime.Hour;
            deviceData.Minute = (byte)dateTime.Minute;
            deviceData.Second = (byte)dateTime.Second;
            string json = Json.JsonParser.Serialize<DeviceData>(deviceData);
            System.Diagnostics.Debug.WriteLine(json);
            serialPort.WriteLine(json);
        }


        private void ReadDeviceDebuggingInfo()
        {
            while (true)
            {
                if (serialPort.BytesToRead > 0)
                {
                    System.Diagnostics.Debug.WriteLine(serialPort.ReadLine());
                }
            }
        }


        // FIN
























        private void TestSendingDeviceInfo()
        {
            //SerialPort testPort = new SerialPort("COM5", 9600, Parity.None, 8, StopBits.One);
            //testPort.Open();
            //string line = testPort.ReadLine();
            //System.Diagnostics.Debug.WriteLine(line);

            deviceData = new DeviceData();
            deviceData.AccessToken = "Atza|testAccessToken";
            deviceData.RefreshToken = "Atzr|testRefreshToken";
            deviceData.WifiPassword = wifiPassword;
            deviceData.WifiSsid = wifiSsid;
            deviceData.ClientId = client_id;
            deviceData.ClientSecret = client_secret;
            deviceData.SlotId = slot_id;
            deviceData.ReplenishHour = 3;

            DateTime dateTime = System.DateTime.Now;
            deviceData.Year = (byte)(dateTime.Year - 2000);
            deviceData.Month = (byte)dateTime.Month;
            deviceData.Day = (byte)dateTime.Day;
            deviceData.Hour = (byte)dateTime.Hour;
            deviceData.Minute = (byte)dateTime.Minute;
            deviceData.Second = (byte)dateTime.Second;
            string json = Json.JsonParser.Serialize<DeviceData>(deviceData);
            //testPort.WriteLine(json);
        }




        private void ParseTokensTest()
        {
            string tokens = "{\"access_token\":\"Atza|testAccessToken\",\"refresh_token\":\"Atzr|testRefreshToken\",\"token_type\":\"bearer\",\"expires_in\":3585}";
            System.Diagnostics.Debug.WriteLine(tokens);
            Tokens t = Json.JsonParser.Deserialize<Tokens>(tokens);
        }

        private void flowLayoutPanel1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void label3_Click(object sender, EventArgs e)
        {

        }
    }
}

Arduino Code Files

Arduino
MKR1000 Code
No preview (download only).

Arduino Code for DashLid

Arduino
DashLid Code
#include <ArduinoJson.h>
#include <RTCZero.h>
#include "AmazonDRS.h"

//JSON Parsing -------------------------------------------------------------------------------
StaticJsonBuffer<2000> jsonBuffer;
String readString;
//--------------------------------------------------------------------------------------------

//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/slot

const int dashButton = 5;     //DIO number of the pushbutton pin
static long buttonHigh = 0;    //millis of last button push for switch debouncing
static String slotStatus = ""; //boolean which depicts if slot is available for replenishment
static String slotId = "";     //unique slot id ex: 0a5038b7-7609-4b81-b87e-3e291f386324 
//Enter your device Info
  char jsonDeviceInfo[] ="{\"model\":\"Dry_Goods_Retro_Container\",\"serial\":\"dgrc01\"}";
  
AmazonDRS DRS = AmazonDRS();
  
//RTC ----------------------------------------------------
RTCZero rtc; //rtc RTCZero instance
bool awake = false; //true = running, false = sleep mode
//Values to set the current initial date and time 
byte seconds;
byte minutes;
byte hours;
byte day;
byte month;
byte year;
byte replenishhour;

//Ultrasonic ---------------------------------------------
#define echoPin 7 // Echo Pin
#define trigPin 8 // Trigger Pin
#define LEDPin 6 // Onboard LED

int maximumRange = 200; // Maximum range needed
int minimumRange = 5; // Minimum range needed
int replenishRange = 20; //20 cm. or greater => Replenish
long duration, distance; // Duration used to calculate distance

//Setup ---------------------------------------------------
void setup() {
 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 port
  while (Serial.available()) {
    delay(3);  //delay to allow buffer to fill
    if (Serial.available() >0) {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //append to readString
    }
  }

  //if (readString.length() >0) Serial.println(readString); //see what was received

  JsonObject& root = jsonBuffer.parseObject(readString);

  // Test if parsing failed
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return; //We have a problem
  }

  
  String ssidString = root["wifissid"];
  char ssid[ssidString.length()]; 
  ssidString.toCharArray(ssid, ssidString.length()+1); 

  String passString = root["wifipassword"];
  char pass[passString.length()]; 
  passString.toCharArray(pass, passString.length()+1);

  String refreshToken = root["refreshtoken"];
  DRS.setRefreshToken(refreshToken);

  String accessToken = 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 DRS
  
  pinMode(LEDPin, OUTPUT); //set LED pin to output
  digitalWrite(LEDPin, LOW); //turn LED off

  rtc.begin(); //Start RTC, this is where the clock source is initialized

  rtc.setTime(hours, minutes, seconds); //set time
  rtc.setDate(day, month, year); //set date
  rtc.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 seconds
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm to go off at a specific time
  rtc.attachInterrupt(ISR); //creates an interrupt that wakes the SAMD21 which is triggered by a FTC alarm

  
  //Replenish(); //Replenish immediately for testing
  rtc.standbyMode(); //Puts the SAMD chip in standby (low power) mode. USB Serial port will not work (disconnects from USB). Comment out for testing.
}

//Loop -----------------------------------------------
void loop() {
 if (awake)
 { 
  Serial.println("awake in loop");
  ping();
  awake = false;
 }
}

void ISR()
{
  //Serial.println("awake in ISR");
  awake = true;
  PrintDateTime();
}

void ping()
{
  Serial.println("awake in ping");
 //Setup Ultrasonic Sensor
 pinMode(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.
 float distance = 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 replenish
   digitalWrite(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);
 }
}

void Replenish()
{
  Serial.println("Requesting replenishment");
    DRS.retrieveSubscriptionInfo();  //check slot statuses

  slotStatus = 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");
        }
}

void print2digits(int number) 
{
  if (number < 10) {
    Serial.print("0"); // print a 0 before if the number is < than 10
  }
  Serial.print(number);
}

void PrintDateTime()
{
    // Print date...
  print2digits(rtc.getMonth());
  Serial.print("/");
  print2digits(rtc.getDay());
  Serial.print("/");
  print2digits(rtc.getYear());
  Serial.print(" ");

  // ...and time
  print2digits(rtc.getHours());
  Serial.print(":");
  print2digits(rtc.getMinutes());
  Serial.print(":");
  print2digits(rtc.getSeconds());

  Serial.println();
}

An Arduino library for WiFi101 connected devices implementing the Amazon Dash Replenishment API

RTCZero

Credits

claude garrett

claude garrett

11 projects • 10 followers

Comments