Juan Pablo Risso
Published © MIT

🍸 DIY MyBar 🍹 - Cocktail Mixing Machine

DIY MyBAR (Bartender Assistant Robot) makes the perfect cocktail, time after time! With a simple app discover and share like never before.

IntermediateFull instructions provided3 hours3,898
🍸 DIY MyBar 🍹 - Cocktail Mixing Machine

Things used in this project

Hardware components

DIY MyBar Board
×1
Espressif ESP32 Development Board
×1
Peristaltic Pump
×9
Wire (ft)
×25
Tubing (ft)
×105
3D Printed Parts
×6
Plastic Connectors
×36
Rubber Legs Pads
×4
Wooden Case
×1
Acrylic Front
×1
Acrylic Grill
×1
DC Jack Connector
×1
Power Supply 9v 2Amps
×1
Distance sensor (GY-53)
×1
Screws 3M 4mm
×6
Screws 3M 10mm
×14
Spacers 3M
×4
Nuts 3M
×14
Screws 3M 6mm
×4

Software apps and online services

MyBar App

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

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.

Code

DIY MyBar Firmware

C/C++
This is the code that goes in the ESP32
/*
  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");
          }
        }      
      }
    }
  }
}

DIY Bar - Firmware

To flash the firmware into the ESP32 board you will need to install the Arduino IDE, the ESP32 boards manager and all the required libraries.

Credits

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.
Contact

Comments

Please log in or sign up to comment.