Nowadays there are countless coffee machines however with limited to no smart functionality. With this new way of doing work remotely buying a cup of coffee for a colleague, friend or family is not possible.
The ideaThis Home Automation Smart Coffee Machine Add-on was designed and prototyped by me using KiCad. Is of easy installation on any home or office coffee machine, and enables any vintage coffee machine connectivity to the internet (or a personal network).
The PCB uses an ESP32 S3 packed with Bluetooth, BLE, and WiFi compatible with major software vendors such as Apple Home, Google Home, Matter/Zigbee, Home Assistant, and many others.
The custom firmware coded includes the functionality of someone sending for a cup of coffee (or other) request from the Telegram Messenger App. In return, it sends a receipt as proof a coffee was indeed brewed on the coffee machine.
Join the WhatsApp group here.
Functionalities available:
- Control water temperature;
- Low water detection in the water tank;
- Order a cup of coffee (with the possibility to control the quantity of coffee in a cup);
- RGB LED;
- Magnetic Buzzer;
- This PCB can be powered using 220V AC or regular 5V DC.
- Control of the coffee machine is made using a 220V relay or alternatively any other 3.3V switch.
The OEM version of the firmware code can be found in the folder firmware code. It has by default OTA updates, meaning the smart coffee machine add-on device automatically updates itself when newer updated versions are made available here.
This code uses my own ESP32 C++ class libraries to expedite the development of code for ESP32 microcontrollers. The repository is located here for anyone to use.
This smart device add-on Is able to connect to a WIFI network for handling message requests from the Telegram Messaging App. Below is the full code for the smart_cofee_machine.ino file . The remainder of the code can be found on my repository.
#define uS_TO_S_FACTOR 1000000
//----------------------------------------------------------------------------------------
// Components Testing **************************************
bool SCAN_I2C_BUS = true;
bool TEST_FINGERPRINT_ID_IC = false;
//----------------------------------------------------------------------------------
#include <math.h>
#include <cmath>
#include "SPI.h"
#include <semphr.h>
#include "esp32-hal-psram.h"
// #include "rom/cache.h"
extern "C"
{
#include <esp_himem.h>
#include <esp_spiram.h>
}
// custom includes **********************************
#include "nvs_flash.h" //preferences lib
// External sensor moeasurements
#include "telegram.h"
TELEGRAM_CLASS* telegram = new TELEGRAM_CLASS();
// custom functions
#include "src/m_file_functions.h"
// Interface class ******************************
#include "src/interface_class.h"
INTERFACE_CLASS* interface = new INTERFACE_CLASS();
#define DEVICE_NAME "Smart Coffee Machine"
// GBRL commands ***************************
#include "src/gbrl.h"
GBRL gbrl = GBRL();
// Onboard sensors *******************************
#include "src/onboard_sensors.h"
ONBOARD_SENSORS* onBoardSensors = new ONBOARD_SENSORS();
// unique figerprint data ID
#include "src/m_atsha204.h"
// serial comm
#include <HardwareSerial.h>
HardwareSerial UARTserial(0);
#include "src/mserial.h"
mSerial* mserial = new mSerial(true, &UARTserial);
// File class
#include <esp_partition.h>
#include "FS.h"
#include <LittleFS.h>
#include "src/m_file_class.h"
FILE_CLASS* drive = new FILE_CLASS(mserial);
// WIFI Class
#include <ESP32Ping.h>
#include "src/m_wifi.h"
M_WIFI_CLASS* mWifi = new M_WIFI_CLASS();
// Certificates
#include "src/cert/github_cert.h"
// Coffee Machine
#include "coffee_machine.h"
COFFEE_MACHINE_CLASS* coffeeMachine = new COFFEE_MACHINE_CLASS();
/********************************************************************/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
BLECharacteristic *pCharacteristicTX, *pCharacteristicRX;
BLEServer *pServer;
BLEService *pService;
bool BLE_advertise_Started = false;
bool newValueToSend = false;
String $BLE_CMD = "";
bool newBLESerialCommandArrived = false;
SemaphoreHandle_t MemLockSemaphoreBLE_RX = xSemaphoreCreateMutex();
float txValue = 0;
String valueReceived = "";
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
mWifi->setBLEconnectivityStatus (true);
mserial->printStr("BLE connection init ", mserial->DEBUG_BOTH_USB_UART);
interface->onBoardLED->led[0] = interface->onBoardLED->LED_BLUE;
interface->onBoardLED->statusLED(100, 1);
String dataStr = "Connected to the Smart Concrete Maturity device (" + String(interface->firmware_version) + ")" + String(char(10)) + String(char(13)) + "Type $? or $help to see a list of available commands" + String(char(10));
dataStr += String(interface->rtc.getDateTime(true)) + String(char(10)) + String(char(10));
if (mWifi->getNumberWIFIconfigured() == 0 ) {
dataStr += "no WiFi Networks Configured" + String(char(10)) + String(char(10));
}
//interface->sendBLEstring(dataStr, mserial->DEBUG_TO_BLE);
}
void onDisconnect(BLEServer* pServer) {
mWifi->setBLEconnectivityStatus (false);
interface->onBoardLED->led[0] = interface->onBoardLED->LED_BLUE;
interface->onBoardLED->statusLED(100, 0.5);
interface->onBoardLED->led[0] = interface->onBoardLED->LED_RED;
interface->onBoardLED->statusLED(100, 0.5);
interface->onBoardLED->led[0] = interface->onBoardLED->LED_BLUE;
interface->onBoardLED->statusLED(100, 0.5);
pServer->getAdvertising()->start();
}
};
class pCharacteristicTX_Callbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String txValue = String(pCharacteristic->getValue().c_str());
txValue.trim();
mserial->printStrln("Transmitted TX Value: " + String(txValue.c_str()) , mserial->DEBUG_BOTH_USB_UART);
if (txValue.length() == 0) {
mserial->printStr("Transmitted TX Value: empty ", mserial->DEBUG_BOTH_USB_UART);
}
}
void onRead(BLECharacteristic *pCharacteristic) {
mserial->printStr("TX onRead...", mserial->DEBUG_BOTH_USB_UART);
//pCharacteristic->setValue("OK");
}
};
class pCharacteristicRX_Callbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
delay(10);
String rxValue = String(pCharacteristic->getValue().c_str());
rxValue.trim();
mserial->printStrln("Received RX Value: " + String(rxValue.c_str()), mserial->DEBUG_BOTH_USB_UART );
if (rxValue.length() == 0) {
mserial->printStr("Received RX Value: empty " , mserial->DEBUG_BOTH_USB_UART);
}
$BLE_CMD = rxValue;
mWifi->setBLEconnectivityStatus(true);
xSemaphoreTake(MemLockSemaphoreBLE_RX, portMAX_DELAY);
newBLESerialCommandArrived = true; // this needs to be the last line
xSemaphoreGive(MemLockSemaphoreBLE_RX);
delay(50);
}
void onRead(BLECharacteristic *pCharacteristic) {
mserial->printStr("RX onRead..." , mserial->DEBUG_BOTH_USB_UART);
//pCharacteristic->setValue("OK");
}
};
// ********************************************************
// ************************* == SETUP == *****************
// ********************************************************
static uint8_t taskCoreZero = 0;
static uint8_t taskCoreOne = 1;
long int prevMeasurementMillis;
void setup() {
ESP_ERROR_CHECK(nvs_flash_erase());
nvs_flash_init();
// Firmware Build Version / revision ______________________________
interface->firmware_version = "1.0.0";
// Serial Communication Init ______________________________
interface->UARTserial = &UARTserial;
mserial->DEBUG_TO = mserial->DEBUG_TO_UART;
mserial->DEBUG_EN = true;
mserial->DEBUG_TYPE = mserial->DEBUG_TYPE_VERBOSE; // DEBUG_TYPE_INFO;
mserial->start(115200);
// .............................................................................
// .......................... START OF IO & PIN CONFIGURATIO .................
// ........................................................................
// I2C IOs __________________________
interface->I2C_SDA_IO_PIN = 9;
interface->I2C_SCL_IO_PIN = 8;
// Power Saving ____________________________________
interface->LIGHT_SLEEP_EN = false;
// ________________ Onboard LED _____________
interface->onBoardLED = new ONBOARD_LED_CLASS();
interface->onBoardLED->LED_RED = 36;
interface->onBoardLED->LED_BLUE = 34;
interface->onBoardLED->LED_GREEN = 35;
interface->onBoardLED->LED_RED_CH = 8;
interface->onBoardLED->LED_BLUE_CH = 6;
interface->onBoardLED->LED_GREEN_CH = 7;
// ___________ MCU freq ____________________
interface-> SAMPLING_FREQUENCY = 240;
interface-> WIFI_FREQUENCY = 80; // min WIFI MCU Freq is 80-240
interface->MIN_MCU_FREQUENCY = 10;
interface-> SERIAL_DEFAULT_SPEED = 115200;
// _____________________ coffee machine ________________________
/* for IO assignment edit the coffee machine class constructor */
coffeeMachine->coffeeMachineBrand = "Philips Senseo";
// _____________________ TELEGRAM _____________________________
telegram->OWNER_CHAT_ID = "1435561519";
// Initialize Telegram BOT
telegram->BOTtoken = "5813926838:AAFwC1cV_QghdZiVUP8lAwbg9mNvkWc27jA"; // your Bot Token (Get from Botfather)
// ..........................................................................
// .......................... END OF IO & PIN CONFIGURATION..................
// ..........................................................................
interface->onBoardLED->init();
interface->onBoardLED->led[0] = interface->onBoardLED->LED_RED;
interface->onBoardLED->statusLED(100, 0);
//init storage drive ___________________________
drive->partition_info();
if (drive->init(LittleFS, "storage", 2, mserial, interface->onBoardLED ) == false)
while (1);
//init interface ___________________________
interface->init(mserial, true); // debug EN ON
interface->settings_defaults();
if ( !interface->loadSettings() ) {
interface->onBoardLED->led[0] = interface->onBoardLED->LED_RED;
interface->onBoardLED->led[0] = interface->onBoardLED->LED_GREEN;
interface->onBoardLED->statusLED(100, 2);
}
// init onboard sensors ___________________________
onBoardSensors->init(interface, mserial);
if (SCAN_I2C_BUS) {
onBoardSensors->I2Cscanner();
}
if (TEST_FINGERPRINT_ID_IC) {
mserial->printStrln("Testing the Unique FingerPrind ID for Sensor Data Measurements");
mserial->printStr("This Smart Device Serial Number is : ");
mserial->printStrln(CryptoICserialNumber(interface));
mserial->printStrln("Testing Random Genenator: " + CryptoGetRandom(interface));
mserial->printStrln("");
mserial->printStrln("Testing Sensor Data Validation hashing");
mserial->printStrln( macChallengeDataAuthenticity(interface, "TEST IC"));
mserial->printStrln("");
}
mserial->printStrln("\nMicrocontroller specifications:");
interface->CURRENT_CLOCK_FREQUENCY = getCpuFrequencyMhz();
mserial->printStr("Internal Clock Freq = ");
mserial->printStr(String(interface->CURRENT_CLOCK_FREQUENCY));
mserial->printStrln(" MHz");
interface->Freq = getXtalFrequencyMhz();
mserial->printStr("XTAL Freq = ");
mserial->printStr(String(interface->Freq));
mserial->printStrln(" MHz");
interface->Freq = getApbFrequency();
mserial->printStr("APB Freq = ");
mserial->printStr(String(interface->Freq / 1000000));
mserial->printStrln(" MHz");
interface->setMCUclockFrequency( interface->WIFI_FREQUENCY);
mserial->printStrln("setting Boot MCU Freq to " + String(getCpuFrequencyMhz()) +"MHz");
mserial->printStrln("");
// init BLE
BLE_init();
//init wifi
mWifi->init(interface, drive, interface->onBoardLED);
mWifi->OTA_FIRMWARE_SERVER_URL = "https://github.com/aeonSolutions/AeonLabs-Home-Automation-Smart-Coffee-MAchine-Addon/releases/download/openFirmware/firmware.bin";
mWifi->add_wifi_network("TheScientist", "angelaalmeidasantossilva");
mWifi->ALWAYS_ON_WIFI=true;
mWifi->WIFIscanNetworks();
mWifi->start(10000,5);
// check for firmwate update
mWifi->startFirmwareUpdate();
interface->onBoardLED->led[0] = interface->onBoardLED->LED_RED;
interface->onBoardLED->statusLED(100, 0);
// initialize the coffee machine
coffeeMachine->init(interface);
// initialize Telegram
telegram->init(interface, mWifi, coffeeMachine);
//Init GBRL
gbrl.init(interface, mWifi);
interface->onBoardLED->led[0] = interface->onBoardLED->LED_BLUE;
interface->onBoardLED->statusLED(100, 0);
interface->$espunixtimePrev = millis();
interface->$espunixtimeStartMeasure = millis();
mWifi->$espunixtimeDeviceDisconnected = millis();
prevMeasurementMillis = millis();
mserial->printStr("\nStarting MCU cores... ");
MemLockSemaphoreBLE_RX = xSemaphoreCreateMutex();
mserial->printStrln("done. ");
mserial->printStrln("Free memory: " + addThousandSeparators( std::string( String(e sp_get_free_heap_size() ).c_str() ) ) + " bytes");
mserial->printStrln("=========================================================");
mserial->printStrln("Setup is completed. You may start using the " + String(DEVICE_NAME) );
mserial->printStrln("Type $? for a List of commands.");
mserial->printStrln("=======================================================\n");
interface->onBoardLED->led[0] = interface->onBoardLED->LED_GREEN;
interface->onBoardLED->statusLED(100, 1);
}
// ********END SETUP *********************************************************
void GBRLcommands(String command, uint8_t sendTo) {
if (gbrl.commands(command, sendTo) == false) {
if ( onBoardSensors->gbrl_commands(command, sendTo ) == false) {
if (mWifi->gbrl_commands(command, sendTo ) == false) {
if ( command.indexOf("$") > -1) {
interface->sendBLEstring("$ CMD ERROR \r\n", sendTo);
} else {
// interface->sendBLEstring("$ CMD UNK \r\n", sendTo);
}
}
}
}
}
// ************************************************************
void BLE_init() {
// Create the BLE Device
BLEDevice::init(String("LDAD " + interface->config.DEVICE_BLE_NAME).c_str()); // max 29 chars
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristicTX = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristicTX->addDescriptor(new BLE2902());
pCharacteristicRX = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pCharacteristicTX->setCallbacks(new pCharacteristicTX_Callbacks());
pCharacteristicRX->setCallbacks(new pCharacteristicRX_Callbacks());
interface->init_BLE(pCharacteristicTX);
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
}
// *********************************************************************************
//******************************* == LOOP == *************************************
unsigned long lastMillisWIFI = 0;
int waitTimeWIFI = 0;
String dataStr = "";
long int eTime;
long int statusTime = millis();
long int beacon = millis();
// adc_power_release()
unsigned int cycle = 0;
uint8_t updateCycle = 0;
// ********************* == Core 1 : Data Measurements Acquisition == ***********
void loop2 (void* pvParameters) {
//measurements->runExternalMeasurements();
delay(2000);
}
//************************** == Core 2: Connectivity WIFI & BLE == ******************
void loop(){
if (millis() - beacon > 60000) {
beacon = millis();
mserial->printStrln("(" + String(beacon) + ") Free memory: " + addThousandSeparators( std::string( String(esp_get_free_heap_size() ).c_str() ) ) + " bytes\n", mSerial::DEBUG_TYPE_VERBOSE, mSerial::DEBUG_ALL_USB_UART_BLE);
}
if ( (millis() - statusTime > 10000)) { //10 sec
statusTime = millis();
interface->onBoardLED->led[1] = interface->onBoardLED->LED_GREEN;
interface->onBoardLED->statusLED(100, 0.04);
} else if (millis() - statusTime > 10000) {
statusTime = millis();
interface->onBoardLED->led[1] = interface->onBoardLED->LED_RED;
interface->onBoardLED->statusLED(100, 0.04);
}
// .............................................................................
// disconnected for at least 3min
// change MCU freq to min
if ( mWifi->ALWAYS_ON_WIFI == false && mWifi->getBLEconnectivityStatus() == false && ( millis() - mWifi->$espunixtimeDeviceDisconnected > 180000) && interface->CURRENT_CLOCK_FREQUENCY >= interface->WIFI_FREQUENCY) {
mserial->printStrln("setting min MCU freq.");
btStop();
//BLEDevice::deinit(); // crashes the device
WiFi.disconnect(true);
delay(100);
WiFi.mode(WIFI_MODE_NULL);
interface->setMCUclockFrequency(interface->MIN_MCU_FREQUENCY);
mWifi->setBLEconnectivityStatus(false);
interface->onBoardLED->led[0] = interface->onBoardLED->LED_RED;
interface->onBoardLED->led[1] = interface->onBoardLED->LED_GREEN;
interface->onBoardLED->statusLED(100, 2);
}
// ..............................................................................
// Ligth Sleeep
eTime = millis() - prevMeasurementMillis;
if ( mWifi->getBLEconnectivityStatus() == false && interface->LIGHT_SLEEP_EN) {
mserial->printStr("Entering light sleep....");
interface->onBoardLED->turnOffAllStatusLED();
//esp_sleep_enable_timer_wakeup( ( (measurements->config.MEASUREMENT_INTERVAL - eTime) / 1000) * uS_TO_S_FACTOR);
delay(100);
esp_light_sleep_start();
mserial->printStrln("wake up done.");
}
prevMeasurementMillis = millis();
// .....................................................................
// Telegram
telegram->runTelegramBot();
// .............................................................................
if (mserial->readSerialData()){
GBRLcommands(mserial->serialDataReceived, mserial->DEBUG_TO_USB);
}
// ..............................................................................
if (mserial->readUARTserialData()){
GBRLcommands(mserial->serialUartDataReceived, mserial->DEBUG_TO_UART);
}
// ..............................................................................
if (newBLESerialCommandArrived){
xSemaphoreTake(MemLockSemaphoreBLE_RX, portMAX_DELAY);
newBLESerialCommandArrived=false; // this needs to be the last line
xSemaphoreGive(MemLockSemaphoreBLE_RX);
GBRLcommands($BLE_CMD, mserial->DEBUG_TO_BLE);
}
//................................................................................
// OTA Firmware
if ( mWifi->forceFirmwareUpdate == true )
mWifi->startFirmwareUpdate();
// ---------------------------------------------------------------------------
if (millis() - lastMillisWIFI > 60000) {
xSemaphoreTake(interface->MemLockSemaphoreCore2, portMAX_DELAY);
waitTimeWIFI++;
lastMillisWIFI = millis();
xSemaphoreGive(interface->MemLockSemaphoreCore2);
}
}
// -----------------------------------------------------------------
Try it right now on TelegramIs now #official. Anyone can "buy me a coffee" on Telegram. Just start a conversation with MiguelCoffeeMachineBot here and send a message /start
to view available commands. (office hours only , CET)
/start
: to view available commands on the Coffee Machine I have at my home/coffee
: to "buy" Miguel a cup of Coffee/tea
: to "buy" Miguel a cup of Tea/cappuccino
: to buy a cup of Cappuccino/decaf
: to buy a cup of decaf. coffee/accept
[short/normal/long]
:: to accept an offer made by another person/status
: to view the current status of the Philips Senseo Coffee Machine
Try it out is free and is #FUN. No money is asked! Great for a meeting or a conversation online. No excuses not to have a cup of coffee with colleagues while working remotely. ( online during office hours, CET )
Testing the firmware codeBrewing the very first coffee request on the Philips #senseo coffee machine. How #cool 😎 😍 is that? And testing the cup/mug presence sensor. I've used Arduino Studio 1.8 and also MS Visual Code for coding the firmware. For debugging, I used the coolTerm Windows app for UART 2 USB serial communication.
Mod Installation OverviewTo make this work it was necessary to install a magnetic sensor behind the water tank and also a DS18b20 temperature sensor on the white boiler. The water pump is activated using the already-existent Philips PCB electronics.
Below is possible to see in greater detail the installation of a cup/mug sensor. I utilized the VL6180x sensor which also allows measurement while filling the cup. Short, medium, and full cup options are possible with this smart add-on.
I've created a Dashboard on Adafruit IO with some simple usage statistics check it out here.
- Apple Home
- Google Home
- Home Assistant
- Matter / Zigbee
#openInnovation #DIY
one final note about the author
The PCB Design Files I provide on GitHub for anyone to use are free. If you like this Smart Device or use it, please consider buying me a cup of coffee, a slice of pizza or a book to help me study, eat and think to work on new PCB design files.
Make a donation on PayPal and get a TAX refund*.
Comments
Please log in or sign up to comment.