Ryan HodgePhilip Arnold
Published © GPL3+

Automated Coffee Maker

A coffee maker that can be controlled and temperature monitored over the cloud using a Particle Photon.

BeginnerShowcase (no instructions)6 hours1,276
Automated Coffee Maker

Things used in this project

Hardware components

Photon
Particle Photon
×2
Servos (Tower Pro MG996R)
×1
Modulo Temperature Probe
Modulo Temperature Probe
×1
LED (generic)
LED (generic)
×1
Resistor 4.75k ohm
Resistor 4.75k ohm
×1
Resistor 221 ohm
Resistor 221 ohm
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

particle.io
Google Sheets
Google Sheets
Maker service
IFTTT Maker service

Story

Read more

Schematics

Servo Motor and Temperature Sensor Circuit Schematic

This schematic shows the servo motor and temperature sensor wired up to the particle photon on the breadboard. The sensor shown is an TMP36 temperature sensor. We used the waterproof temperature sensor in the maker kit, which is interchangeable with the wiring.

LED Circuit Schematic

This schematic shows the simple set up for the LED which lights up when the LED function is called on by IFTTT.

Code

Servo Motor and Temperature Sensor Code

C/C++
This code controls the servo motor used to turn on the coffee maker and the temperature sensor used to monitor the coffee temperature as well as send a function to the photon with the LED wired to it.
Servo myservo;  // create servo object to control a servo
                // a maximum of eight servo objects can be created

int pos = 0;    // variable to store the servo position


int gong(String command)   // when "gong" is called from the cloud, it will
{                          // be accompanied by a string.
    if(command == "on")   // if the string is "on", ring the gong once.
    {  

        myservo.write(0);       // move servo to 0∞ - ding!
        digitalWrite(D7, HIGH); // flash the LED (as an indicator)
        delay(150);             // wait 100 ms
        myservo.write(90);      // move servo to 25∞
        digitalWrite(D7, LOW);  // turn off LED
        
        return 1;               // return a status of "1"
        
        
    }
   
    
}
int analogvalue = 0;
double tempF = 0;

// This #include statement was automatically added by the Particle IDE.
#include "OneWire/OneWire.h"

/************************************************************************
This sketch reads the temperature from a 1-Wire device and then publishes
to the Particle cloud. From there, IFTTT can be used to log the date,
time, and temperature to a Google Spreadsheet. Read more in our tutorial
here: http://docs.particle.io/tutorials/topics/maker-kit

This sketch is the same as the example from the OneWire library, but
with the addition of three lines at the end to publish the data to the
cloud.

Use this sketch to read the temperature from 1-Wire devices
you have attached to your Particle device (core, p0, p1, photon, electron)

Temperature is read from: DS18S20, DS18B20, DS1822, DS2438

Expanding on the enumeration process in the address scanner, this example
reads the temperature and outputs it from known device types as it scans.

I/O setup:
These made it easy to just 'plug in' my 18B20 (note that a bare TO-92
sensor may read higher than it should if it's right next to the Photon)

D3 - 1-wire ground, or just use regular pin and comment out below.
D4 - 1-wire signal, 2K-10K resistor to D5 (3v3)
D5 - 1-wire power, ditto ground comment.

A pull-up resistor is required on the signal line. The spec calls for a 4.7K.
I have used 1K-10K depending on the bus configuration and what I had out on the
bench. If you are powering the device, they all work. If you are using parisidic
power it gets more picky about the value.
************************************************************************/

OneWire ds = OneWire(D4);  // 1-wire signal on pin D4

unsigned long lastUpdate = 0;

float lastTemp;

void setup() {
  Serial.begin(9600);
  // Set up 'power' pins, comment out if not used!
  pinMode(D3, OUTPUT);
  pinMode(D5, OUTPUT);
  digitalWrite(D3, LOW);
  digitalWrite(D5, HIGH);

    Particle.function("coffie", gong);  // create a function called "gong" that
                                      // can be called from the cloud
                                      // connect it to the gong function below

    myservo.attach(D0);   // attach the servo on the D0 pin to the servo object
    myservo.write(0);    // test the servo by moving it to 25∞
    pinMode(D7, OUTPUT);  // set D7 as an output so we can flash the onboard LED

  Particle.variable("analogvalue", analogvalue);
  Particle.variable("temp", tempF);
  
  pinMode(D4, INPUT);
    }
    

// up to here, it is the same as the address acanner
// we need a few more variables for this example

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
  
    // Read the analog value of the sensor (TMP36)
  analogvalue = analogRead(D4);
  //Convert the reading into degree celcius
  tempF = (((((analogvalue * 3.3)/4095) - 0.5) * 100)*1.8)+32;
  delay(200);

  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
    
  }

  // The order is changed a bit in this example
  // first the returned address is printed

  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  // second the CRC is checked, on fail,
  // print error and just return to try again

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();

  // we have a good address at this point
  // what kind of chip do we have?
  // we will set a type_s value for known types or just return

  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS1820/DS18S20");
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    case 0x26:
      Serial.println("  Chip = DS2438");
      type_s = 2;
      break;
    default:
      Serial.println("Unknown device type.");
      return;
  }

  // this device has temp so let's read it

  ds.reset();               // first clear the 1-wire bus
  ds.select(addr);          // now select the device we just found
  // ds.write(0x44, 1);     // tell it to start a conversion, with parasite power on at the end
  ds.write(0x44, 0);        // or start conversion in powered mode (bus finishes low)

  // just wait a second while the conversion takes place
  // different chips have different conversion times, check the specs, 1 sec is worse case + 250ms
  // you could also communicate with other devices if you like but you would need
  // to already know their address to select them.

  delay(1000);     // maybe 750ms is enough, maybe not, wait 1 sec for conversion

  // we might do a ds.depower() (parasite) here, but the reset will take care of it.

  // first make sure current values are in the scratch pad

  present = ds.reset();
  ds.select(addr);
  ds.write(0xB8,0);         // Recall Memory 0
  ds.write(0x00,0);         // Recall Memory 0

  // now read the scratch pad

  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE,0);         // Read Scratchpad
  if (type_s == 2) {
    ds.write(0x00,0);       // The DS2438 needs a page# to read
  }

  // transfer and print the values

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s == 2) raw = (data[2] << 8) | data[1];
  byte cfg = (data[4] & 0x60);

  switch (type_s) {
    case 1:
      raw = raw << 3; // 9 bit resolution default
      if (data[7] == 0x10) {
        // "count remain" gives full 12 bit resolution
        raw = (raw & 0xFFF0) + 12 - data[6];
      }
      celsius = (float)raw * 0.0625;
      break;
    case 0:
      // at lower res, the low bits are undefined, so let's zero them
      if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
      if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
      if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
      // default is 12 bit resolution, 750 ms conversion time
      celsius = (float)raw * 0.0625;
      break;

    case 2:
      data[1] = (data[1] >> 3) & 0x1f;
      if (data[2] > 127) {
        celsius = (float)data[2] - ((float)data[1] * .03125);
      }else{
        celsius = (float)data[2] + ((float)data[1] * .03125);
      }
  }

  // remove random errors
  if((((celsius <= 0 && celsius > -1) && lastTemp > 5)) || celsius > 125) {
      celsius = lastTemp;
  }

  fahrenheit = celsius * 1.8 + 32.0;
  lastTemp = celsius;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
  

  // now that we have the readings, we can publish them to the cloud
  String temperature = String(fahrenheit); // store temp in "temperature" string
  Particle.publish("temperature", temperature, PRIVATE);// publish to cloud
  delay(10000); // 5 second delay
  
  
  
}

LED Code

C/C++
This code is used to control the LED attached to the photon. This code is called upon by "If This Then That" when the coffee reaches a certain temperature enabling the LED to illuminate.
int led1 = D0;
int led2 = D7;

// Last time, we only needed to declare pins in the setup function.
// This time, we are also going to register our Particle function

void setup()
{

   // Here's the pin configuration, same as last time
   pinMode(led1, OUTPUT);
   pinMode(led2, OUTPUT);

   // We are also going to declare a Particle.function so that we can turn the LED on and off from the cloud.
   Particle.function("led",ledToggle);
   Particle.function("ledoff",ledToggleoff);

   // This is saying that when we ask the cloud for the function "led", it will employ the function ledToggle() from this app.

   // For good measure, let's also make sure both LEDs are off when we start:
   digitalWrite(led1, LOW);
   digitalWrite(led2, LOW);

}


/* Last time, we wanted to continously blink the LED on and off
Since we're waiting for input through the cloud this time,
we don't actually need to put anything in the loop */

 void loop(){  // Nothing to do here
}

// We're going to have a super cool function now that gets called when a matching API request is sent
// This is the ledToggle function we registered to the "led" Particle.function earlier.
int ledToggle(String command){
    /* Particle.functions always take a string as an argument and return an integer.
    Since we can pass a string, it means that we can give the program commands on how the function should be used.
    In this case, telling the function "on" will turn the LED on and telling it "off" will turn the LED off.
    Then, the function returns a value to us to let us know what happened.
    In this case, it will return 1 for the LEDs turning on, 0 for the LEDs turning off,
    and -1 if we received a totally bogus command that didn't do anything to the LEDs.
    */


{
        digitalWrite(led1,HIGH);
        digitalWrite(led2,HIGH);
      
        return 1;
        
    }
    
  
    }
int ledToggleoff(String command){
    {digitalWrite(led1,LOW);
    digitalWrite(led2,LOW);
    
    return 0;
    }
    
}

Credits

Ryan Hodge

Ryan Hodge

1 project • 0 followers
Philip Arnold

Philip Arnold

1 project • 0 followers

Comments