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.

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

The end of Hot Cocoa 3000

Intro

Oh the weather outside is frightful, but hot cocoa is so delightful, so let’s order some hot cocoa on the go! After a night out in the cold and snowy holiday weather, wouldn’t it be nice to have a hot beverage waiting when you got home? The Hot Cocoa 3000 makes this dream a reality.

The Hot Cocoa 3000 is capable of preparing two cups of steaming hot cocoa ready as soon as you walk in your front door. Use the simple graphical user interface (GUI) available from any internet connected device to send in your order to the Hot Cocoa 3000.

Hot Cocoa 3000 – Overview

System Overview

The Hot Cocoa 3000 can be broken down into four systems:

  • The water works
  • The chocolate express
  • The power plant
  • The electric brain

The water works heats, and delivers the water through out the process. The chocolate express is responsible for delivering and mixing the cocoa powder. The power plant delivers power to all the systems. The electric brain includes the Arduino MKR1000 and the code it executes.

The Water Works

The water system begins with a modified hot water pot. The hot water pot switch is bypassed with the power cord leads directly soldered to the heating element. The heating element uses 10 Amps at 120 Volts and gives off 1200 Watts of heating power. The power comes from the wall and then is switched by a solid state relay.

 

The next step is pumping the water from the hot water pot to the mixing chamber. We use a 12 Volt diaphragm pump with 3/8″ inside diameter food grade silicone tubing. Home brew shops are a great place to find food grade tubing. Two main reasons for using this style of pump are 1) it is self priming and 2) it doesn’t burn up if it runs out of water. The power to the pump is switched using a MOSFET transistor.

MThe mixing chamber is an narrow necked Nalgene bottle. The bottle is mounted upside down. The bottom is cut off which allows the water and powder to come in. The exit hose is sealed in a hole in with food grade silicone RTV.

Once the water and cocoa powder are done mixing, the fresh made hot cocoa is ready to be dispensed. The hot cocoa flows out of the mixing chamber in 1/4″ outside diameter food grade silicone tubing. The reason for using 1/4″ tubing is due to the solenoid valves use this size of tubing. The only larger valves can easily purchase need pressurized liquid to open the valve, while these valves work fine with gravity feed. The logic in the program chooses which valve to open and the corresponding cup fills up ready to be enjoyed.

The Chocolate Express

Hot Cocoa 3000 – The Chocolate Express

The chocolate express adds the hot cocoa powder and mixes it together with the water. The hot cocoa powder begins in the hopper which is just a modified plastic spice bottle. The powder then falls into a PVC T joint where an auger drill bit is waiting. The drill bit turns using a stepper motor connected to the SparkFun EasyDriver. The hot cocoa power then falls gracefully into the mixing chamber to do its job.

The mixing motor was taken from a milk frother, which we found for only $2.79 at Ikea. The great part about using the milk frother is that the motor is already attached to a great mixing stick. The frother is also very easily dissembled. The power for this motor is switched from a MOSFET transistor. The prototype uses 5 volts straight. For longevity future versions should step the voltage down to around 3 volts with pulse width modulation or a voltage regulator. During final assembly the mixing motor was broken likely due too high of voltage for an extended period of time.

The Power Plant

Hot Cocoa 3000 – The Power Plant

The 120 watt power supply is able to provide 8.33 amps. A portion of this is used for our 5 volt circuit. The 5 volt circuit is able to provide a maximum of 3 amps. It uses a buck converter to reduce the input voltage from 12 to 5 VDC. A key feature of the power supply system is that the two different voltages have different gender pigtails. This prevents plugging in the wrong voltages and destroying circuitry.

While taking a break during testing, the power was left on with a buggy version code on the Arduino. After the break, there was a slight smell of smoke, and a MOSFET was extremely hot. We may never know what exactly happened but what we found was a trail of destruction. The buck converter failed possibly due to to high of current draw from faulty code. The failed converter then provided 12 volts instead of 5 volts, compounding the problem. This definitely killed the MKR1000, but thankfully we had a backup. The mixing motor also connect to this circuit never worked again after this disaster. During final testing we used a bench top power supply to power the 5 volt circuitry and skipped the mixing step. Lesson learned, always power off during breaks!

The Electric Brain

The electric brain of this machine is an Arduino MKR1000. The MKR1000 controls all of the parts of the machine as well as acts as a simple web server waiting for orders. The code for this project is based off modeling the Hot Cocoa 3000 as a Finite State Machine (FSM). For those not familiar with FSMs, they provide a nice visual way to model complex systems. They also are very easy to turn into code once a diagram has been created. What you need to know to read a FSM diagram:

  • The circles are the states
  • The arrows connect states
  • The “equation” contains the logic to follow the arrow
  • Above the line is what must be true to follow the arrow
  • Below the line is what to do when the arrow is followed

One thing to note about this diagrams is that the current machine code doesn’t completely follow this diagrams due to some simplifications due to time constraints. This is a great visual of the overall structure of the code though. The main simplification was using a timer instead of a thermometer to control the hot water pot. It will likely be implemented in the future, but had issues this time around.

The HTML server page which is where orders can be placed currently doesn’t allow for any options. All the choices are hard coded due to time constraints. It will however allow the user to monitor what state of the FSM the Hot Cocoa 3000 is in. We used port forwarding to test ordering hot cocoa when not on the local network. It worked great but port forwarding is outside the scope of this write up, but there are plenty of tutorials out there on this subject. It is fairly easy to do but requires configuring your WiFi router.

The complete code for the Hot Cocoa 3000 can be found at this Arduino Web Editor Link or as an attachment at the end of this project.

The circuitry of the Hot Cocoa isn’t too complex. It has 1 Solid State Relay Controlling the hot water pot. It has 4 MOSFET transistors Controlling the Pump, 2 Solenoid Valves, and the Mixer Motor. Sadly the MOSFETs used don’t follow the way I interpreted the datasheet. The threshold voltage for the gate is listed at 2.5V but really needs about 4-4.5V to work in this situation. This is more than the Arduino MKR1000 can provide so I placed a 1.5V battery in series with each MOSFET gate to bump each signal from 3.3V to 4.8V which has solved the problem. Eventually new MOSFETs that work with 3.3V will be incorporated into the design.

Construction

The enclosure for this prototype is made out of 3/4″ pine. This decision to use pine is due to the availability, easy of construction and low cost. We have access to a very well equipped wood shop that makes woodworking easy. The enclosure measures approximately 1’x1.5’x2′. ( ‘ Symbol means Feet.) These dimensions work well and allow for easy access to the components inside. There is potential for shrinking as there is a lot of empty space inside. Building the Hot Cocoa 3000 takes almost a solid week of time.

Schematics

Code

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

Source : Hot Cocoa 3000


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top