Jason Cowinforrest123Jordan Stanton
Published © CC BY-NC

Hot Cocoa 3000

Oh the weather outside is frightful, but hot cocoa is so delightful, so let's order some hot cocoa on the go! An Arduino MKR1000 project.

AdvancedFull instructions providedOver 4 days3,978

Things used in this project

Hardware components

Arduino MKR1000
Arduino MKR1000
×1
Water Pump
NOTE! THIS AND MANY OF THE OTHER PARTS ARE LINKED, BUT MUST CLICK SHOPPING CART TO FOLLOW LINK.
×1
Silicone Tubing
×1
Solenoid Valve
×2
Silicone RTV - Food Grade
×1
SparkFun Easy Driver
×1
Mounting Bracket Stepper Motor
×1
Coupler
×1
NEMA 17 Stepper Motor
OpenBuilds NEMA 17 Stepper Motor
×1
Nalgene Water Bottle
×1
1" Auger
×1
Power MOSFET N-Channel
Power MOSFET N-Channel
Used the NTE2987, however it doesn't work well with the 3.3V logic of the MKR1000 so would recommend finding a better one.
×1
Generic Power Supply 12
×1
Generic Voltage Regulator 12VDC to 5VDC
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Wire Generic
Larger gauges like 18 - 22 for motors other higher current applications.
×1
Solid State Relay
Solid State Relay
×1
PCB Prototyping Board
×1
PVC Piping / T Adaptor
×1
Zipties
×40
Crimp On Connectors
×40
Heat Shrink
Bought in 4 foot sections
×2
Screws
×40
Wood
For housing, used pine and about 12'x12"x3/4" and 6'x6"x3/4"
×1
AA Batteries
AA Batteries
Used, but not needed if the correct MOSFET is used.
×1
AA Battery Holder (1 AA)
×1
Hot Water Pot (Generic)
×1
Spice Container
Used for Hot Cocoa Hopper
×1
Extension Cord (Generic)
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Chop Saw
Hand Drill
Band Saw
Wire Cutters
Wire Strippers
Vice Grips
Used for crimping connectors, not recommended, crimpers work way better.
Bench Top Power Supply
Used for testing, very useful.

Story

Read more

Schematics

Circuit Diagram - Image

Circuit Diagram - Fritzing File

Code

Hot Cocoa 3000 - MKR1000 Code

Arduino
This code is functional, but has certain parts hard coded now for ease of testing and troubleshooting.
An order for a cup of hot cocoa is immediately ordered in this version.
#include <SPI.h>
#include <WiFi101.h>

// -------- Definitions ------  //
#define waitForOrder 0
#define waitToStart 1
#define waitForWater 2
#define waitForPump 3
#define addPowder 4
#define mix 5
#define dispense 6
#define fillingLeft 7
#define fillingRight 8
#define checkOrder 9
#define orderFilled 10
#define drinksTaken 11

#define hotWaterOff 0
#define heatingWater 1
#define waterHot 2

#define hotWaterPotPin 6
#define mixerPin 2
#define leftValve 4
#define rightValve 5
#define pumpPin 3

#define DISTANCE 5000
#define stepperPin1 8
#define stepperPin2 9


// Global Variables //


// -- Wifi Variables -- //
char ssid[] = "your network info";      //  your network SSID (name)
char pass[] = "your network info";   // your network password
int keyIndex = 0;                 // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
WiFiServer server(80);  // "Creates a server that listens for incoming connections on the specified port" - arduino website
int bodyNum = 0; // which body to send to client.
WiFiClient client = false; // making client a global variable

// Global Variables - Order
bool orderRec = true; // True when order placed via web interface
int orderCup = 2; // The number of cups ordered
int orderTime = 0; // The time in min from now the Hot Cocoa is desired

// Global Variables - Finite State Machines
int mainState = waitToStart;
int waterHeaterState = hotWaterOff; 
unsigned long msCount = 0; // going to us millis() functionality
// 24 hrs in ms is 8.6 million, this is plenty big enough for this use. (Remove note later?)
bool hotWaterWanted = false; //If hot water is wanted
bool hotWaterReady = false;  //If the water is ready
float waterTemp = 0; // Temp of the water in degrees F
bool pumpWater = false; // If water should be pumped
bool waterPumped = false; // If water has been pumped
unsigned long mixTime = 2; // The desired mix time in seconds
bool cupLeftFull = false; // false = empty , true = full 
bool cupRightFull = false; // false = empty , true = full 
unsigned long fillTime = 10; // The time in seconds to dispense the cocoa (time valve is open) 
bool orderReady = false; // If the order is ready for pickup
bool buttonPress = false; // The button pressed to signal drinks taken (TODO change name)
bool refillButton = false; // The button pressed to signal a refill is wanted
unsigned long waitTime = 0; // ------------------------------- TODO ------------ need to address wait time
bool conversionStart = false; // tempSensor if the conversion has started yet. 
int waterHotWaitTime = 5; // Time to heat water in seconds
int waterPumpWaitTime = 5;// Time to pump in seconds

// Global Variables - Stepper Motor
int StepCounter = 0;
int Stepping = false;
int Powder = 0;

void setup() {

// ------------------------- Wifi Setup -------------------- //

// TODO static IP address... not currently working may need to update IDE? though unlikely.
//IPAddress desiredIP = (192, 168, 11, 23);
//WiFi.config(desiredIP);
  
  while ( status != WL_CONNECTED) {
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);    
    // wait 10 seconds for connection:
    delay(10000);
  }
  server.begin(); // start the web server on port 80 (defined above around line 36)
  
// ----------------------- Powder Delivery Setup -------------- //
  pinMode(stepperPin1, OUTPUT);    
  pinMode(stepperPin2, OUTPUT);
  digitalWrite(stepperPin1, LOW);
  digitalWrite(stepperPin2, LOW);

  
// Finite State Machine Setup //
  pinMode(hotWaterPotPin, OUTPUT);
  digitalWrite(hotWaterPotPin, LOW);
  pinMode(mixerPin, OUTPUT);
  digitalWrite(mixerPin, LOW);
  pinMode(leftValve, OUTPUT);
  digitalWrite(leftValve, LOW);
  pinMode(rightValve, OUTPUT);
  digitalWrite(rightValve, LOW);
  
}

void loop() {

// ------ Wifi Server ------- //

  client = server.available();  // listen for incoming clients
    if(client){
      String currentLine = "";    // String to hold incoming data from the client
      while(client.connected()){  // loop while the client is connected
        if(client.available()){   // if there are bytes to read from the client
          char c = client.read(); // read a byte
          if(c == '\n') {         // if the byte is a newline character
          
            // if the current line is blank, you got two newline characters in a row.
            // that's the end of the client HTTP request, so send a response:
            if (currentLine.length() == 0) {
               // HTTP Response
               sendHeader();
               sendBody(bodyNum); // 1 is the initial body
               sendEnd();
               // break out of the while loop when done
               break;
             }
             else {      // if you got a newline, then clear currentLine:
               currentLine = "";
             }
           } // end if(c == '\n')
           else if (c != '\r') {    // if you got anything else but a carriage return character,
             currentLine += c;      // add it to the end of the currentLine
           }
           // Check the client request:
           if (currentLine.endsWith("GET /order")) {
              orderRec = true;               // order recived!
              orderTime = 0; // hard coded now for example - TODO make it a variable
              orderCup = 2;   // hard coded now for example - TODO make it a variable
           }
           if (currentLine.endsWith("GET /state")) {
             bodyNum = 1;
           }         
         }      
       }
    } // end if(client)
    // close the connection:
    client.stop();

// ------- Main Finite State Machine --------- //
  switch (mainState)
  {
    case waitForOrder:
      if(orderRec == true){
        mainState = waitToStart;
        msCount = millis();
        waitTime = (unsigned long)orderTime * 60 * 1000; // convert ordertime (min) to waitTime (ms) 
        // max is 4.2 billion = which means the max order time is about 49 days. (plenty!)
        // FYI (unsigned long) is used for type casting 
      }
      break;
    case waitToStart:
      if((millis() - msCount) >= (waitTime)){  // TODO diagram typo msCounter -> msCount
        mainState = waitForWater;
        hotWaterWanted = true;
        digitalWrite(hotWaterPotPin, HIGH);
        msCount = millis();
      }
      break;
    case waitForWater:
      if((millis() - msCount) >= (waterHotWaitTime * 1000)){
        mainState = waitForPump;
        pumpWater = true;
        digitalWrite(hotWaterPotPin, LOW);
        digitalWrite(pumpPin, HIGH);
        msCount = millis();
      }
      break;
    case waitForPump:
      if((millis() - msCount) >= (waterPumpWaitTime * 1000)){
        mainState = addPowder;
        digitalWrite(mixerPin, HIGH);
        digitalWrite(pumpPin,LOW);
        dispensePowder();
      }
      break;
    case addPowder:
      if(true){
        dispensePowder();
        mainState = mix;
        msCount = millis();
      }
      break;
    case mix:
      if((millis() - msCount) >= (mixTime * 1000)){ // (TODO *1000 on diagram)
        mainState = dispense;
        digitalWrite(mixerPin, LOW);
      }
      break;
    case dispense:
      if(cupLeftFull == false){
        mainState == fillingLeft;
        digitalWrite(leftValve, HIGH);
        msCount = millis();                   // (TODO add msCount = 0)
      } else if (cupRightFull == false) {
        mainState == fillingRight;
        digitalWrite(rightValve, HIGH);
        msCount = millis();                   // (TODO add msCount = 0)
      }
      break;
    case fillingLeft:
      if((millis() - msCount) >= fillTime){
        mainState = checkOrder;
        cupLeftFull = true;
        digitalWrite(leftValve, LOW);
      }
      break;      
    case fillingRight:
      if((millis() - msCount) >= fillTime){
        mainState = checkOrder;
        cupRightFull = true;
        digitalWrite(rightValve, LOW);
      }
      break;      
    case checkOrder:
      if(orderCup == 1){
        mainState = orderFilled;
        orderReady = true;
      } else if ( orderCup == 2 && cupRightFull == true){ // (TODO fix diagram cupRightFull!!!)
        mainState = orderFilled;
        orderReady = true;  
      } else if ( orderCup == 2 && cupRightFull == false){
        mainState = waitForWater;
      }
      break;
    case orderFilled:
      if(buttonPress == true){
        mainState = drinksTaken;
        clearOrder();
      }
      break;  
    case drinksTaken:
      if(refillButton == true){
        mainState = waitForWater;
        orderCup == 1; // (TODO Add to diagram)
      }
      break;  
  }  // End Main FSM


  

} // End Loop

void dispensePowder(){

if (Stepping == false)
  {
    Stepping = true;
  }

  while(Stepping == true)
  {
    digitalWrite(9, HIGH);
    delay(1);         
    digitalWrite(9, LOW);
    delay(1);

    StepCounter = StepCounter + 1;

    if (StepCounter == DISTANCE)
    {
      StepCounter = 0;
      Stepping = false;
      Powder=0;
      break; // optional , but does work as well like this.
    }
  }
  
}


// This function clears the current order //
void clearOrder(){

  orderRec = false;
  orderCup = 0;
  orderTime = 0;
  
} // End clearOrder



// ----- WIFI Functions ------//
void sendHeader(){
  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
  // and a content-type so the client knows what's coming, then a blank line:
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();
            
} // End sendHeader

void sendBody(int num){
  switch(num){
    case 0: // Initial body
      client.print("Welcome to the Hot Cocoa 3000<br>");
      client.print("Click <a href=\"/order\">here</a> to order some hot cocoa<br>");
      client.print("Click <a href=\"/state\">here</a> to check the order status<br>");
      break;
    case 1: // State Check body
      client.print("Welcome to the Hot Cocoa 3000<br>");
      client.print("The machine is currently in state:");
      client.print(mainState);
      client.print("<br>");
      client.print("Click <a href=\"/state\">here</a> to check the order status<br>");
      break;  
  }
   

}  // End sendBody

void sendEnd(){
  client.println();
} // end sendEnd

Credits

Jason Cowin

Jason Cowin

3 projects • 11 followers
Mechatronics Degree 2014 - Happily Married in 2015 - Maintain Robots for Work
forrest123

forrest123

2 projects • 0 followers
Jordan Stanton

Jordan Stanton

1 project • 4 followers
Thanks to Amanda Cowin.

Comments