Making cocktails at home will never be the same!
- Never forget a recipe.
- Make the perfect cocktail over and over again.
- Have a dedicated Bartender for your parties.
- Share your favorite cocktails with your friends.
With a simple and intuitive App this DIY Bartender Assistant Robot can make drinks by mixing up to 9 ingredients together at the same time. Just enter the bottles connected to your Bar into the mobile app and the list of possible cocktails will be populated.
Can't find a bottle or drink? Don't panic, you can add your own bottles and cocktails
But why to stop there, we also integrated it with Alexa, Google Assistant and Bixby.
Check out the tutorial below to see how it works and how we built it:
DIY Bar - Hardware
3D Printer files:
This section includes all the necessary files to 3D print every plastic part needed for this project.
Laser Cutter files:
Here you will find all the necessary files to laser cut both the wooden and acrylic parts of the case.
Custom Board files:
Last but not less the Gerber files will allow you to order or make your own board if you fell like SMD soldering is your thing.
/*
DIY MyBar - Cocktail Machine
ESP32 Firmware
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini, with some additional code by pcbreflux
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
Other Libreries:
Adafruit VL53L0X Time of Flight Micro-LIDAR Distance Sensor Breakout
https://learn.adafruit.com/adafruit-vl53l0x-micro-lidar-distance-sensor-breakout/arduino-code
QueueList Library For Arduino
https://playground.arduino.cc/Code/QueueList
*/
const bool debug = false;
const bool debugLoop = false;
const int numMotors = 9;
const int maxMotorsRunning = 4;
const int minGlassDistance = 80;
const int blinkTimes = 10; //Number if blinks if there is no glass
int fadeAmount = 10; // how many points to fade the LED by
// use first channel of 16 channels (started from zero)
#define LEDC_CHANNEL_0 0
// use 13 bit precission for LEDC timer
#define LEDC_TIMER_13_BIT 13
// use 5000 Hz as a LEDC base frequency
#define LEDC_BASE_FREQ 5000
// fade LED PIN (replace with LED_BUILTIN constant for built-in LED)
#define LED_PIN 12
bool inProgress = false;
bool backwardsOn = false;
bool read_distance = true;
int glassDistance = 100;
int blinkedTimes = 0;
int brightness = 0;
bool ledOn = false;
bool turnLedOn = false;
bool turnLedOff = false;
bool ledBlink = false;
// Arduino like analogWrite
// value has to be between 0 and valueMax
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
// calculate duty, 8191 from 2 ^ 13 - 1
uint32_t duty = (8191 / valueMax) * min(value, valueMax);
// write duty to LEDC
ledcWrite(channel, duty);
}
#include <QueueList.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include "L9110Driver.h"
#include <vector>
#include "Adafruit_VL53L0X.h"
Adafruit_VL53L0X lox = Adafruit_VL53L0X();
VL53L0X_RangingMeasurementData_t measure;
QueueList <String> motorsQueue;
std::vector<L9110_Motor> motor(numMotors);
int timeToCompletion[numMotors]={0,0,0,0,0,0,0,0,0};
int motorsRunning = 0;
BLECharacteristic *pCharacteristic;
BLEDescriptor *pDescriptor;
bool deviceConnected = false;
bool deviceNotifying = false;
String notification;
void sendBTNotification(String message) {
if (deviceConnected && deviceNotifying) {
char charBuf[10];
String(message).toCharArray(charBuf, 10);
pCharacteristic->setValue(charBuf);
pCharacteristic->notify();
}
}
// 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"
void startLedBlink() {
brightness = 0;
blinkedTimes = 0;
ledBlink = true;
}
// setMotors() Command String = motor_number-dirrection-duration
// motor 0 - 8 for pumps.
// direction f (forward) or b (backward).
// duration in miliseconds, if duration = 0 it will run until a stop command is send.
void setMotors(String command) {
bool motorStarted = false;
int index = command.indexOf('-');
int secondIndex = command.indexOf('-', index + 1);
int motorNumber = atoi(command.substring(0, index).c_str());
String motorDirection = command.substring(index + 1, secondIndex);
long duration = atoi(command.substring(secondIndex + 1).c_str());
if (motorNumber < numMotors) {
if (motorDirection == "s"){
motor[motorNumber].run (BRAKE);
if (motorsRunning > 0) {
motorsRunning = motorsRunning - 1;
}
sendBTNotification("stop:" + String(motorNumber));
if (debug) {
Serial.printf("Motor %d stopped\n", motorNumber);
}
} else if (motorsRunning < maxMotorsRunning) {
if (motorDirection == "b") {
motor[motorNumber].run (BACKWARD | RELEASE);
motorStarted = true;
} else {
if (glassDistance < minGlassDistance) {
motor[motorNumber].run (FORWARD | RELEASE);
motorStarted = true;
} else {
sendBTNotification("noGlass");
if (debug) {
Serial.println("Glass not ready");
}
if (!ledBlink) {
startLedBlink();
}
}
}
if (motorStarted) {
if (motorsRunning == 0) {
sendBTNotification("start");
}
motorsRunning += 1;
if (duration > 0) {
unsigned long currentMillis = millis();
timeToCompletion[motorNumber] = duration + currentMillis;
}
sendBTNotification("start:" + String(motorNumber));
if (debug) {
Serial.printf("Motor %d started\n", motorNumber);
}
}
} else {
//Add pending command to a queue
motorsQueue.push(command);
}
} else {
sendBTNotification("noMotorNum");
if (debug) {
Serial.println("The motor number doesn't exist");
}
}
}
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
notification = "";
for (int i = 0; i < rxValue.length(); i++) {
notification = notification + rxValue[i];
}
if (debug) {
Serial.print("Received Value: ");
Serial.print(notification);
Serial.println();
}
if (notification == "lockOn") {
inProgress = true;
} else if (notification == "backwardsOn"){
backwardsOn = true;
} else {
if (inProgress == false) {
setMotors(notification);
} else {
sendBTNotification("lockOn");
if (debug) {
Serial.println("Lock is on!");
}
}
}
}
}
};
class MyDisCallbacks: public BLEDescriptorCallbacks {
void onWrite(BLEDescriptor *pDescriptor) {
uint8_t* rxValue = pDescriptor->getValue();
if (pDescriptor->getLength() > 0) {
if (rxValue[0]==1) {
deviceNotifying=true;
if (debug) {
Serial.println("Notifications enabled");
}
} else {
deviceNotifying=false;
if (debug) {
Serial.println("Notifications disabled");
}
}
}
}
};
void setup() {
if (debug) {
Serial.begin(115200);
// wait until serial port opens for native USB devices
while (! Serial) {
delay(1);
}
}
Serial.println("Bar v1.0");
motor[0].initialize(32, 33);
motor[1].initialize(25, 26);
motor[2].initialize(27, 14);
motor[3].initialize(13, 23);
motor[4].initialize( 1, 3);
motor[5].initialize(19, 18);
motor[6].initialize( 5, 17);
motor[7].initialize(16, 4);
motor[8].initialize(15, 2);
if (!lox.begin()) {
Serial.println(F("Failed to boot VL53L0X"));
while(1);
}
ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
ledcAttachPin(LED_PIN, LEDC_CHANNEL_0);
for (int bright = 255; bright >= 0; bright--) {
ledcAnalogWrite(LEDC_CHANNEL_0, bright);
delay(10);
}
// engage the motor's brake
for (int i = 0; i <= numMotors; ++i) {
motor[i].run (BRAKE);
}
// Create the BLE Device
BLEDevice::init("DIYBar");
// Create the BLE Server
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
pDescriptor = new BLE2902();
pCharacteristic->addDescriptor(pDescriptor);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new MyCallbacks());
pDescriptor->setCallbacks(new MyDisCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
if (debug) {
Serial.println("Waiting a client connection to notify...");
}
}
void loop() {
if (debug && debugLoop) {
Serial.printf("Motors running: %d\n", motorsRunning);
Serial.printf("Glass Distance: %d mm\n", glassDistance);
}
unsigned long currentMillis = millis();
// If motors are in queue, start one at a time
if ((motorsRunning < maxMotorsRunning) && (!motorsQueue.isEmpty())) {
setMotors(motorsQueue.pop());
}
if (turnLedOn) {
ledcAnalogWrite(LEDC_CHANNEL_0, 255);
ledOn = true;
turnLedOn = false;
}
if (turnLedOff) {
ledcAnalogWrite(LEDC_CHANNEL_0, 0);
ledOn = false;
turnLedOff = false;
if (!(backwardsOn)) {
if (motorsRunning > 0) {
// engage the motor's brake
for (int i = 0; i < numMotors; ++i) {
motor[i].run (BRAKE);
timeToCompletion[i] = 0;
}
motorsRunning = 0;
sendBTNotification("motorsStp");
}
while (!motorsQueue.isEmpty())
motorsQueue.pop();
}
}
if (ledBlink) {
// change the brightness for next time through the loop:
brightness = brightness + fadeAmount;
ledcAnalogWrite(LEDC_CHANNEL_0, brightness);
// reverse the direction of the fading at the ends of the fade:
if (brightness <= 0 || brightness >= 255) {
fadeAmount = -fadeAmount;
blinkedTimes += 1;
}
if (blinkedTimes >= blinkTimes) {
ledBlink = false;
ledOn = false;
ledcAnalogWrite(LEDC_CHANNEL_0, 0);
brightness = 0;
}
delay(10);
} else {
if (!(backwardsOn)) {
lox.rangingTest(&measure, debug); // pass in 'true' to get debug data printout!
if (measure.RangeStatus != 4 && read_distance) { // phase failures have incorrect data
glassDistance = measure.RangeMilliMeter;
} else {
delay(50);
}
delay(80);
}
}
if (glassDistance < minGlassDistance) {
if (!(ledOn)) turnLedOn = true;
} else {
if (ledOn or (motorsRunning > 0)) turnLedOff = true;
}
if (inProgress && motorsRunning == 0) {
inProgress = false;
backwardsOn = false;
}
if (inProgress && motorsRunning != 0) {
// Stop motors as time is up
for (int thisMotor = 0; thisMotor < numMotors; thisMotor++) {
bool lastMotor = false;
if (timeToCompletion[thisMotor] > 0 && timeToCompletion[thisMotor] <= currentMillis) {
motor[thisMotor].run (BRAKE);
if (motorsRunning > 0) {
motorsRunning = motorsRunning - 1;
if (motorsRunning == 0) {
lastMotor = true;
}
}
timeToCompletion[thisMotor] = 0;
sendBTNotification("stop:" + String(thisMotor));
if (debug) {
Serial.printf("Motor %d stopped\n", thisMotor);
}
if (lastMotor) {
startLedBlink();
sendBTNotification("finish");
if (debug) {
Serial.printf("All Motors stopped");
}
}
}
}
}
}
Juan Pablo Risso
6 projects โข 33 followers
I am a geek, technology Innovator, Software Engineer and Maker. I work at Samsung / SmartThings propelling the Internet of Things.
Comments