Digital barman. Arduino project for adult beginners electronics engineers. Part 1

  • Tutorial
I have many friends. Young guys, middle-aged men and of course ladies of all ages. Probably everyone. It is difficult to determine the age of a modern woman. Yes, and not really want.
So here. Children of my friends and acquaintances because of their capabilities, I fascinate electronics. We build small robots, fireflies of all sorts and even light swords. Children almost always succeed and they, of course, run to show off their revived electronics to their parents. And so time after time. But one day, one of my friends, looking at another surge of pride in his daughter, said - I also want to delve into this electronics, and programming, and maybe even something to solder. No problem. Let me show you how to assemble a robot on a wheeled platform. Will ride on a strip on the floor. Either we will make a game, a python for example, or just blink LEDs, or ... Went through many examples. It turns out if for beginners, then all some kind of childish, and if not childish, then it is not at all for beginners. Something is wrong! Looking for lessons for BEGINNERS, ADULT electronics engineers.

The project for today is not finished. If you need to add something, write directly in the comments. In the photo there is a stand where you can see the construction of this bartender.



And this is a scheme in the style of Arduino projects.



Now the design and logic of work. According to the scheme and photo.

The four tubes of four micropumps are lowered into four bottles. These micropumps are connected to four keys assembled on MOSFET field-effect transistors. The keys, in turn, are connected to the conclusions of 3,5,6,9 of any Arduin (further if written to the conclusion **, this means the conclusion of Arduin). This is the executive part. The logic of the executive part is set by five potentiometers. In order: the leftmost potentiometer connected to the A4 output, set the volume of the glass in which we will prepare the cocktail. Above it, you see a scale of 16 RGB LEDs with the so-called WS2812b pixel addressing. When we turn the first potentiometer on the scale, the LEDs conditionally indicate the volume of the glass. This scale is connected to pin 11.

The next four slide potentiometers (connected to the pins A0, A1, A2, A3) set the proportion of the total volume of the drink that we need to pour from a particular bottle. Above the sliders are small scales of 8 LEDs each (connected in series to the long). Moving potentiometers, we select the proportion and small scales, from bottom to top, each colored with its own color. At the same time, the left long scale, or rather its originally lit part, is painted in the same colors proportionally small.

Immediately see the composition of the cocktail. We press the button (pin 7), and the pumps are sequentially switched from left to right, pushing the drinks into the glass.

In the sketch you can customize:

1. The active colors of all scales, their backlighting.
2. The speed of the pump, by pulse width modulation. Therefore, the pumps do not include relays, but transistor switches. This is necessary if you, for example, will use the glass pump car wash pump. It is very productive.
3. The maximum physical volume of the glass. The default is 750 ml.
4. Time of fluid aspiration before turning on the counting and indication. This is to fill the empty tube during a pause.
5. A pause before serving from the next bottle.
6. The size of the scales in the LEDs. The default is 16 LEDs in a large scale and eight LEDs in small ones. You can change as you wish. It will be beautiful.
Something like this.

This is a video demonstration.



I propose to assemble this design novice electronics engineers. Pour a sketch into Arduin and play as a bartender. When your eyes get used to the code and you will be guided in the lines, you can add new functions, such as the Stop button or the Ice button. The latter can automatically reduce the total volume of the drink in proportion to the volume of the ice cube.

I hope that such projects will attract adult novice electronics programmers. And they are slowly sorting out the design and code with their children will raise respect for themselves even at several levels.

Have a good weekend!

And in the conclusion as well as in the video, I wish to make comments and suggestions. I will take this into account in the continuation and in the actual project.

The code (sketch) can be downloaded here.. Or see:

It will take Adafruit_NeoPixel.h - a library for LEDs and PinChangeInt.h - a library for interrupts.

#include<Adafruit_NeoPixel.h>#include<PinChangeInt.h>#define   START_PIN            7#define   LEDS_PIN             11#define   VOLUME_1_PIN         A0#define   VOLUME_2_PIN         A1#define   VOLUME_3_PIN         A2#define   VOLUME_4_PIN         A3#define   TOTAL_VOLUME_PIN     A4#define   ACT_1_PIN            3#define   ACT_2_PIN            5#define   ACT_3_PIN            6#define   ACT_4_PIN            9#define   DRINKS_NUM           4#define   PIXEL_IN_STICK       8#define   PIXEL_IN_DRINK       PIXEL_IN_STICK#define   PIXEL_IN_VOLUME      (2 * PIXEL_IN_STICK)#define   PIXEL_NUM            (DRINKS_NUM * PIXEL_IN_DRINK + PIXEL_IN_VOLUME)#define   VOLUME_START_PIXEL        0#define   DRINKS_START_PIXEL        (VOLUME_START_PIXEL + PIXEL_IN_VOLUME)#define   DRINK_START_PIXEL(DRINK)  (DRINKS_START_PIXEL + DRINK * PIXEL_IN_DRINK)#define   BACKGROUND_COLOUR      ((uint32_t) 0x000001)#define   SHADOW_1_COLOUR        ((uint32_t) 0x000100)#define   SHADOW_2_COLOUR        ((uint32_t) 0x000100)#define   SHADOW_3_COLOUR        ((uint32_t) 0x000100)#define   SHADOW_4_COLOUR        ((uint32_t) 0x000100)#define   PROCESS_1_COLOUR       ((uint32_t) 0xFF0000)#define   PROCESS_2_COLOUR       ((uint32_t) 0x0100ff)#define   PROCESS_3_COLOUR       ((uint32_t) 0x111100)#define   PROCESS_4_COLOUR       ((uint32_t) 0xFF00FF)#define   VOLUME_PROCESS_COLOUR  ((uint32_t) 0x888888)#define   DataThreshold        ((uint16_t) (1024/PIXEL_IN_DRINK))#define   DataThresholdVol     ((uint16_t) (1024/PIXEL_IN_VOLUME))#define   PROCESS              (1 << 0)#define   mlToTimeCoef         10 //время на подачу 1 мл жидксти, мс#define   MIN_VOLUME           1 //минимальный учитываемый объем, мл#define   MAX_VOLUME           ((uint32_t) 750) //мл#define   mlForLED             ((float)((float)MAX_VOLUME / (float)PIXEL_IN_VOLUME))#define   PREPROCESS_DELAY     ((uint32_t) 2000) //мс#define   PUMP_POWER           ((uint16_t) 255) //от 0 (выключен) до 255 (максимум)#define   WaitShowDelay        ((uint16_t) 300) //2 * WaitShowDelay - период моргания ожидающей жидкости#define   WaitCycle            3 //циклы ожидания время ожидания = WaitCycle * 2 * WaitShowDelay#define   EndDelay            ((uint16_t) 2500) //пауза в концеtypedefstruct
{uint8_t  VolPin;
  uint8_t  ActPin;
  uint16_t Volume; //показания АЦПuint32_t ProcColour;
  uint32_t ShadColour;
  float    mlVol; //мл в зависимости от общего объема и доли напиткаuint8_t  LEDsNum;
  uint8_t  LEDsPos;
}  DrinkType;
Adafruit_NeoPixel LEDS = Adafruit_NeoPixel(PIXEL_NUM, LEDS_PIN, NEO_GRB + NEO_KHZ800);
uint8_t State = 0;
uint16_t TotalVolume = 0;
float TotalVolml = 0;
uint8_t TotVolActLEDs = 0;
bool ColourMix = false;
uint32_t NewColour = 0;
bool SystemChange = false;
DrinkType Drinks[DRINKS_NUM];
voidsetup(){
  digitalWrite(START_PIN, HIGH);
  uint8_t VolPINs[DRINKS_NUM] = {VOLUME_1_PIN, VOLUME_2_PIN, VOLUME_3_PIN, VOLUME_4_PIN};
  uint8_t ActPINs[DRINKS_NUM] = {ACT_1_PIN, ACT_2_PIN, ACT_3_PIN, ACT_4_PIN};
  uint32_t ProcCOLOURs[DRINKS_NUM] = {PROCESS_1_COLOUR, PROCESS_2_COLOUR, PROCESS_3_COLOUR, PROCESS_4_COLOUR};
  uint32_t ShadCOLOURs[DRINKS_NUM] = {SHADOW_1_COLOUR, SHADOW_2_COLOUR, SHADOW_3_COLOUR, SHADOW_4_COLOUR};  
  for(uint8_t i = 0; i < DRINKS_NUM; i++)
  {
    Drinks[i].VolPin = VolPINs[i];
    Drinks[i].ActPin = ActPINs[i];
    pinMode(Drinks[i].ActPin, OUTPUT);
    digitalWrite(Drinks[i].ActPin, LOW);
    Drinks[i].Volume = 0;
    Drinks[i].ProcColour = ProcCOLOURs[i];
    Drinks[i].ShadColour = ShadCOLOURs[i];
    Drinks[i].LEDsPos = 0;
  }
  PCintPort::attachInterrupt(START_PIN, &START_ISR, FALLING);
  LEDS.begin();
  for(byte i=0; i < PIXEL_NUM; i++)
      LEDS.setPixelColor(i, BACKGROUND_COLOUR);
  LEDS.show();
}
//----------------------------------------voidloop(){
  if ((State & PROCESS) != PROCESS)
  {
    uint16_t Data;
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
    {
      Data = analogRead(Drinks[cup].VolPin);
      if (abs(Data - Drinks[cup].Volume) >= DataThreshold)
      {
        Drinks[cup].Volume = Data;
        if (Drinks[cup].Volume > 975)
          Drinks[cup].Volume = 1024;
        uint8_t StartPixel = DRINK_START_PIXEL(cup);
        for(byte i = StartPixel; i < (StartPixel + PIXEL_IN_DRINK); i++)
          LEDS.setPixelColor(i, Drinks[cup].ShadColour);
        for(byte i = StartPixel; i < (StartPixel + (Drinks[cup].Volume / DataThreshold)); i++)
          LEDS.setPixelColor(i, Drinks[cup].ProcColour);
        SystemChange = true;
      }
    }
    Data = analogRead(TOTAL_VOLUME_PIN);
    if (abs(Data - TotalVolume) >= DataThresholdVol)
    {
      TotalVolume = Data;
      if (TotalVolume > 975)
        TotalVolume = 1024;
      TotVolActLEDs = TotalVolume / DataThresholdVol;
      for(byte i = VOLUME_START_PIXEL; i < (VOLUME_START_PIXEL + PIXEL_IN_VOLUME); i++)
        LEDS.setPixelColor(i, BACKGROUND_COLOUR);
      SystemChange = true;
    }
    if (SystemChange)
    {
      TotalVolml = (float)((TotalVolume * MAX_VOLUME) / 1024);
    uint16_t MinVol = 1025;
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
    {
      if ((Drinks[cup].Volume < MinVol) && (Drinks[cup].Volume != 0))
        MinVol = Drinks[cup].Volume;
    }
    float OnePartVol = 0;
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
    {
      Drinks[cup].mlVol = (float)Drinks[cup].Volume / (float)MinVol;
      OnePartVol += Drinks[cup].mlVol;
    }
    OnePartVol = TotalVolml / OnePartVol;
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
    {
      Drinks[cup].mlVol *= OnePartVol;
      Drinks[cup].LEDsNum = Drinks[cup].mlVol / mlForLED;
      if ((Drinks[cup].mlVol > 0) && (Drinks[cup].LEDsNum < 1))
        Drinks[cup].LEDsNum = 1;
    }
    uint8_t LEDsSum = 0;
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
      LEDsSum += Drinks[cup].LEDsNum;
    if ((LEDsSum > 0) && (LEDsSum <= TotVolActLEDs))
    {
      uint8_t LedsNumMAX = Drinks[0].LEDsNum;
      uint8_t LedsNumMAXPos = 0;
        for(uint8_t cup = 1; cup < DRINKS_NUM; cup++)
        {
          if (Drinks[cup].LEDsNum > LedsNumMAX)
          {
            LedsNumMAX = Drinks[cup].LEDsNum;
            LedsNumMAXPos = cup;
          }
        }
      Drinks[LedsNumMAXPos].LEDsNum += TotVolActLEDs - LEDsSum;
      Drinks[0].LEDsPos = VOLUME_START_PIXEL;
      for(uint8_t cup = 1; cup < DRINKS_NUM; cup++)
        Drinks[cup].LEDsPos = Drinks[cup - 1].LEDsPos + Drinks[cup - 1].LEDsNum;
      ColourMix = false;
    }
    elseif (LEDsSum > TotVolActLEDs)
    {
      for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
      {
        Drinks[cup].LEDsNum = TotVolActLEDs;
        Drinks[cup].LEDsPos = VOLUME_START_PIXEL;
      }
      ColourMix = true;
      NewColour = 0;
      for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
        NewColour |= Drinks[cup].ProcColour;
    }
    bool EmptyCup = true;
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
    {
      if (Drinks[cup].LEDsNum != 0)
      {
        EmptyCup = false;
        break;
      } 
    }
    if (EmptyCup)
    {
      for(byte i = VOLUME_START_PIXEL; i < (VOLUME_START_PIXEL + TotVolActLEDs); i++)
        LEDS.setPixelColor(i, VOLUME_PROCESS_COLOUR);
    }
    else
    {
      if (ColourMix)
      {
        for(byte i = VOLUME_START_PIXEL; i < (VOLUME_START_PIXEL + TotVolActLEDs); i++)
          LEDS.setPixelColor(i, NewColour);
      }
      else
      {
        for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
        {
          if (Drinks[cup].LEDsNum != 0)
          {
            for(byte i = Drinks[cup].LEDsPos; i < (Drinks[cup].LEDsPos + Drinks[cup].LEDsNum); i++)
              LEDS.setPixelColor(i, Drinks[cup].ProcColour);
          }
        }
      }
    }
      SystemChange = false;
    }
    LEDS.show();
  }
  else
  {
    uint8_t LEDSPos[DRINKS_NUM];
    uint8_t LEDSNum[DRINKS_NUM];
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
    {
      LEDSPos[cup] = Drinks[cup].LEDsPos;
      LEDSNum[cup] = Drinks[cup].LEDsNum;
    }
    for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
    {
      if (Drinks[cup].LEDsNum != 0)
      {
      float Volume = Drinks[cup].mlVol;
      uint16_t VolCnt = 0;
      uint16_t mlPerLEDCoef = Volume / LEDSNum[cup];
      analogWrite(Drinks[cup].ActPin, PUMP_POWER);//вкл
      delay(PREPROCESS_DELAY);
      while (Volume >= MIN_VOLUME)
      {
        delay(mlToTimeCoef * MIN_VOLUME);
        Volume -= MIN_VOLUME;
        VolCnt += MIN_VOLUME;
        {
          if (VolCnt >= mlPerLEDCoef)
          {
            if (ColourMix)
            {
            }
            else
            {
              if (LEDSNum[cup] != 0)
              {
                for(byte i = VOLUME_START_PIXEL; i < (VOLUME_START_PIXEL + PIXEL_IN_VOLUME); i++)
                  LEDS.setPixelColor(i, BACKGROUND_COLOUR);
                if (LEDSNum[cup] > 0)
                  LEDSNum[cup]--;
                for(uint8_t i = 0; i < DRINKS_NUM; i++)
                {
                  if (LEDSPos[i] > 0)
                    LEDSPos[i]--;
                }
                for(uint8_t i = 0; i < DRINKS_NUM; i++)
                {
                  if (Drinks[i].LEDsNum != 0)
                  {
                    for(byte j = LEDSPos[i]; j < (LEDSPos[i] + LEDSNum[i]); j++)
                      LEDS.setPixelColor(j, Drinks[i].ProcColour);
                  }
                }
              }
            }
            LEDS.show();
            VolCnt = 0;
          }
        }
      }
      analogWrite(Drinks[cup].ActPin, 0);//выклif (cup < (DRINKS_NUM - 1))
      {
        uint8_t StickNum = cup + 1;
                uint8_t StartPixel = DRINK_START_PIXEL(StickNum);
      for (uint8_t BlinkTime = 0; BlinkTime < WaitCycle; BlinkTime++)
      {
        for(byte i = StartPixel; i < (StartPixel + PIXEL_IN_DRINK); i++)
          LEDS.setPixelColor(i, Drinks[cup+1].ShadColour);
        LEDS.show();
        delay(WaitShowDelay);
        for(byte i = StartPixel; i < (StartPixel + (Drinks[cup+1].Volume / DataThreshold)); i++)
          LEDS.setPixelColor(i, Drinks[cup+1].ProcColour);
        LEDS.show();
        delay(WaitShowDelay);
      }
      }
    }
    }
    delay(EndDelay);
    if (ColourMix)
    {
      for(byte i = VOLUME_START_PIXEL; i < (VOLUME_START_PIXEL + TotVolActLEDs); i++)
        LEDS.setPixelColor(i, NewColour);
    }
    else
    {
      for(uint8_t cup = 0; cup < DRINKS_NUM; cup++)
      {
        if (Drinks[cup].LEDsNum != 0)
        {
          for(byte i = Drinks[cup].LEDsPos; i < (Drinks[cup].LEDsPos + Drinks[cup].LEDsNum); i++)
            LEDS.setPixelColor(i, Drinks[cup].ProcColour);
        }
      }
    }
    State &= ~PROCESS;
  }
}
//----------------------------------------voidSTART_ISR(){
  State |= PROCESS;
}

Also popular now: