Ich habe viele Freunde. Junge Männer, Männer mittleren Alters und natürlich Damen jeden Alters. Wahrscheinlich jeder. Es ist schwierig, das Alter einer modernen Frau zu bestimmen. Ja und nicht wirklich wollen.
Also. Aufgrund meiner Fähigkeiten trage ich Kinder meiner Freunde und Bekannte mit Elektronik. Wir bauen kleine Roboter, Käfer aller Arten von Glühwürmchen und sogar Lichtschwerter. Kinder haben fast immer Erfolg und sie prahlen natürlich mit Eltern, die die Elektronik wiederbelebt haben. Und so weiter und so fort. Aber eines Tages sagt eine meiner Freundinnen, die einen weiteren Anstieg des Stolzes auf ihre Tochter betrachtet - ich möchte mich auch mit dieser Elektronik und Programmierung befassen und vielleicht sogar etwas löten. Keine Frage. Lassen Sie uns zeigen, wie ein Roboter auf einer Plattform mit Rädern montiert wird. Fährt einen Streifen auf dem Boden. Oder wir machen ein Spiel, zum Beispiel eine Python, oder blinken einfach die LEDs oder ... Wir haben viele Beispiele durchgesehen. Es stellt sich heraus, dass für Anfänger alles kindisch ist, und wenn nicht für Kinder, dann ist es überhaupt nicht für Anfänger. Stimmt etwas nicht! Für ANFÄNGER, ERWACHSENE Elektronikingenieure werden Lektionen benötigt. Lassen Sie es sein - ein digitaler Barkeeper oder eine Maschine zum Mixen von Cocktails.
Das Projekt für heute ist noch nicht abgeschlossen. Wenn Sie hinzufügen müssen, schreiben Sie direkt in die Kommentare. Auf dem Foto gibt es einen Stand, an dem Sie das Design dieses Barkeepers sehen können.

Und dies ist ein Diagramm im Stil von Arduino-Projekten.

Nun das Design und die Logik der Arbeit. Nach Schema und Foto.
Die Aufnahmeröhrchen von vier Mikropumpen werden in vier Flaschen fallen gelassen. Diese Mikropumpen sind mit vier Schlüsseln verbunden, die auf MOSFET-Feldeffekttransistoren montiert sind. Die Schlüssel sind wiederum mit den Terminals 3,5,6,9 einer Arduina verbunden (wenn dies zum Abschluss ** geschrieben wird, bedeutet dies den Abschluss von Arduina). Dies ist der ausführende Teil. Die Logik des ausführenden Teils wird durch fünf Potentiometer festgelegt. In der Reihenfolge: Stellen Sie mit dem Potentiometer ganz links an Klemme A4 das Volumen des Glases ein, in dem der Cocktail zubereitet werden soll. Darüber sehen Sie eine Skala von 16 RGB-LEDs mit der sogenannten Pixeladressierung WS2812b. Wenn wir das erste Potentiometer auf einer Skala drehen, leuchten die LEDs nacheinander und zeigen bedingt das Volumen des Glases an. Diese Waage ist mit Pin 11 verbunden.
Die nächsten vier Schieberpotentiometer (angeschlossen an die Klemmen A0, A1, A2, A3) stellen den Anteil des Gesamtvolumens des Getränks ein, den wir aus einer bestimmten Flasche einfüllen müssen. Über den Schiebereglern befinden sich kleine Skalen mit jeweils 8 LEDs (in Reihe zu einer langen geschaltet). Mit beweglichen Potentiometern wählen wir die Proportionen und kleinen Maßstäbe von unten nach oben aus, die jeweils mit ihrer eigenen Farbe bemalt sind. Gleichzeitig wird die linke lange Skala oder vielmehr ihr ursprünglich beleuchteter Teil im Verhältnis zu den kleinen Farben in denselben Farben gemalt.
Sehen Sie sofort die Zusammensetzung des Cocktails. Wir drücken den Knopf (Pin 7) und die Pumpen schalten sich nacheinander von links nach rechts ein und passen die Getränke in das Glas.
In der Skizze können Sie Folgendes konfigurieren:
1. Aktive Farben aller Skalen, deren Hintergrundbeleuchtung.
2. Die Drehzahl der Pumpen durch Pulsweitenmodulation. Daher enthalten die Pumpen keine Relais, sondern Transistorschalter. Dies ist erforderlich, wenn Sie beispielsweise eine Autoglaswaschpumpe verwenden. Sie ist sehr produktiv.
3. Das maximale physikalische Volumen des Glases. Der Standardwert ist 750 ml.
4. Der Zeitpunkt des Absaugens der Flüssigkeit vor dem Einbeziehen von Anzeige und Anzeige. Dies dient dazu, das leere Rohr während einer Pause zu füllen.
5. Pause vor dem Servieren ab der nächsten Flasche.
6. Die Größe der Skalen in den LEDs. Standardmäßig gibt es 16 LEDs im großen Maßstab und acht LEDs im kleinen Maßstab. Sie können nach Belieben ändern. Es wird schön sein.
Irgendwie so.
Dies ist ein Demo-Video.
Ich schlage vor, dieses Design für Anfängerelektronik zusammenzubauen. Gießen Sie die Skizze in Arduin und spielen Sie mit den Einstellungen des Barkeepers. Wenn sich Ihre Augen an den Code gewöhnt haben und Sie in den Zeilen navigieren, können Sie neue Funktionen hinzufügen, z. B. die Schaltfläche Stopp oder die Schaltfläche Eis. Letzteres kann das Gesamtvolumen des Getränks automatisch proportional zum Volumen des Eiswürfels reduzieren.
Ich hoffe, dass solche Projekte erwachsene elektronische Programmieranfänger in ihren Bann ziehen werden. Und sie sortierten langsam das Design und den Code zusammen mit ihren Kindern, um das Selbstwertgefühl um mehrere Stufen zu steigern.
Ein schönes Wochenende!
Abschließend werde ich ebenso wie im Video versuchen, Kommentare und Vorschläge zu machen. Ich werde dies in der Fortsetzung und im eigentlichen Projekt berücksichtigen.
Der Code (Skizze) kann hier heruntergeladen
werden . Oder sehen Sie:
Sie benötigen Adafruit_NeoPixel.h - eine Bibliothek für LEDs und
PinChangeInt.h - eine Bibliothek für 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) // typedef struct { 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]; void setup() { 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(); } //---------------------------------------- void loop() { 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; } else if (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; } } //---------------------------------------- void START_ISR() { State |= PROCESS; }