This is a guide on how to start a coffee machine with an SMS message using Laserfiche and an Arduino micro-controller. I built this system about a year ago but haven't had a chance to write about it until now. If you're looking to "get your nerd on" over the holidays, it could be a fun project.
What is Needed
- 1 coffee machine with a flippable on/off switch (I used this thing)
- 1 Arduino or similar microcontroller (I used an Arduino Uno)
- 1 PowerTail Switch, basically a relay to interface with AC voltage
- A gateway computer with an Internet connection
- Arduino IDE (used to develop programs that run on the Arduino)
- Processing (used to send signals to the Arduino via the USB port)
- A web server
- A web service on the web server that can receive webhooks and initiate workflows
- A Twilio account with a phone number
- Laserfiche Workflow
How It Works
- User sends an SMS message containing the word "on" or "off" to a phone number (acquired from Twilio)
- Twilio receives the text message and sends an HTTP POST to an endpoint on the web server
- The endpoint runs a simple application that is basically a wrapper around the workflow rest API. The application initiates a workflow with the SMS message content as input parameters
- Based on whether the SMS says "on" or "off", Workflow changes the contents of a text file on the web server to 1 or 0, respectively
- A program that runs on the gateway computer periodically (i.e. every 5 seconds) polls a virtual directory on the web server
- The endpoint serves the contents of the text file to the program
- The gateway computer sends the relevant signal to the Arduino based on the response
- Arduino turns the PowerTail Switch relay on or off, which turns the coffee machine on or off
Twilio Setup
This part is pretty straightforward. We create a Twilio account, buy a phone number (they're super cheap), then create a programmable SMS Messaging Service on Twilio and configure it to send webhooks to an endpoint on our web server.
Webhook Processor to Initiate Workflows
The webserver endpoint runs a simple application that receives the HTTP POST and starts a workflow with it. For me this is an ASP.NET application with a single controller:
using Webhooks.WorkflowRestAPI; using System.Web.Mvc; using System.Web.Configuration; using System.IO; namespace Webhooks.Controllers { public class WorkflowsController : Controller { // // POST: /workflows/coffee/ public string coffee() { var config = WebConfigurationManager.OpenWebConfiguration("~"); string endpoint = config.AppSettings.Settings["WorkflowWebURL"].Value + "/api/RestWorkflowAPI.svc"; System.ServiceModel.EndpointAddress address = new System.ServiceModel.EndpointAddress(endpoint); Stream req = Request.InputStream; req.Seek(0, System.IO.SeekOrigin.Begin); string json = new StreamReader(req).ReadToEnd(); using (WorkflowAPIBaseClient WorkflowService = new WorkflowAPIBaseClient()) { WorkflowService.Endpoint.Address = address; InstanceCreationData creationData = new InstanceCreationData(); creationData.Initiator = new InstanceUserData(); creationData.Initiator.InitiatorName = "Admin"; InstanceParameterData webhookPayload = new InstanceParameterData(); webhookPayload.Name = "payload"; webhookPayload.Value = json; creationData.ParameterCollection = new InstanceParameterCollection(); creationData.ParameterCollection.Add(webhookPayload); InstanceCreationResultData results = WorkflowService.CreateWorkflowInstance("Process Webhook", creationData); return results.instanceId; } } } }
You'll note that it uses the Workflow Rest API.
The "Process Webhook" Workflow
This is the workflow that is initiated by the web service. Note that it receives "payload" as an input parameter.
The conditional branches check the contents of the payload. The scripts inside the conditional branches change the contents of the text file on the hard drive to "0" or "1":
namespace WorkflowActivity.Scripting.Changetextfileto1 { using System.Text; using System.IO; public class Script1 : ScriptClass90 { protected override void Execute() { File.WriteAllText(@"C:\My IIS Websites\Arduino\coffee.txt", "1"); } } }
Serving the Text File Contents on the Web Server
Next we make the contents of the text file available to requesters. This is also very easy. Simply create a virtual directory and map it to the folder containing the text file, and specify the Virtual Path:
We need to have Directory Browsing enabled and also Anonymous Access enabled, otherwise we won't be able to access specific files (i.e. the text file) via the web.
Code Running on the Gateway Computer
The gateway computer is responsible for periodically checking the text file on the web server and communicating the appropriate action to the Arduino that is connected to it via USB. There are many ways to do this. In my case I'm using an open-source development tool called Processing and I'm running the following Java program:
import processing.serial.*; Serial ComPort; String input[]; //Set up the serial port at 9600 baud void setup() { String portName = Serial.list()[0]; ComPort = new Serial(this, portName, 9600); ComPort.bufferUntil('\n'); } //Check a text file on the server and send its value to the serial port void draw() { input = loadStrings("https://hostname.mydomain.com/Arduino/coffee.txt"); if (input != null) { if(input.length != 0) { println(input[0]); int status = Integer.parseInt(input[0]); ComPort.write(status); } } delay(5000); //Check the text file every five seconds }
Every 5 seconds, this program checks the text file on the server and sends the response as an integer to the serial port.
The setup() function has to set up the serial port at 9600 baud, since that's the bit rate the Arduino expects (seen in the next section).
Code Running on the Arduino
// control power socket const int powerPin = A0; int incomingByte; //a variable to read incoming serial data void setup() { //initialize the Arduino by initiating serial communication at 9600 baud: Serial.begin(9600); Serial.println(F("starting up")); pinMode(powerPin, OUTPUT); digitalWrite(powerPin, HIGH); } //This function runs continuously void loop() { // read serial messages if (Serial.available() > 0) { int key = Serial.read(); if (key == 1) { //Turn on the PowerTail Switch if text file on the server is "1" digitalWrite(powerPin, LOW); Serial.println(F("ON")); } else { //Turn off the PowerTail Switch if text file on the server is "0" digitalWrite(powerPin, HIGH); Serial.println(F("OFF")); } //read all the bytes in the serial buffer while (Serial.available()) incomingByte = Serial.read(); } }
Arduino has the above code running on it. It listens to the serial port and adjusts the power pin's voltage accordingly.
PowerSwitch Tail Relay
Lastly, the Arduino itself is connect to the PowerSwitch Tail relay, which turns the circuit on or off depending on the voltage on the connected Arduino socket. Here are a couple of pictures of my setup:
Final Thoughts
Obviously this system is a lot more complex than it needs to be. After all you can just buy an IoT coffee machine these days and use that instead of dealing with micro-controllers and circuits and web services! Still though, this was a lot of fun to develop and I learned a lot in the process too.
The nice thing about the whole thing is that you can replace the whole Twilio/SMS part with a simple Laserfiche form that kicks off the workflow. Then you also wouldn't need to deal with the web service and the workflow rest API. But SMS is probably more user-friendly in this case. You may also be able to ditch the gateway computer completely if you get an Arduino with built-in wifi.
Anyways, my coworkers initially got quite excited at the prospect of having fresh coffee ready for them in the morning. Unfortunately we realized someone would need to reload fresh coffee into the coffee machine after each batch. I need to figure out how to automate that part next. Time to buy some mechanical parts...
Happy holidays!