J'ai beaucoup d'amis. Jeunes hommes, hommes d'âge moyen et bien sûr dames de tous âges. Probablement tout le monde. Il est difficile de déterminer l'âge d'une femme moderne. Oui, et je ne veux pas vraiment.
Alors voilà. En raison de mes capacités, je porte des enfants de mes amis et des connaissances en électronique. Nous construisons de petits robots, des insectes de toutes sortes de lucioles et même des sabres laser. Les enfants réussissent presque toujours et, bien sûr, ils se vantent auprès de parents qui ont relancé l'électronique. Et ainsi de suite. Mais un jour, un de mes amis, regardant une autre vague de fierté envers sa fille, dit - je veux aussi me plonger dans cette électronique et cette programmation, et peut-être même souder quelque chose. Pas question. Voyons comment assembler un robot sur une plate-forme à roues. Va monter une bande sur le sol. Ou nous allons faire un jeu, un python, par exemple, ou simplement faire clignoter les LED, ou ... Nous sommes passés par de nombreux exemples. Il s'avère que pour les débutants, tout est plutôt enfantin, et sinon pour les enfants, ce n'est pas du tout pour les débutants. Quelque chose ne va pas! Des leçons sont nécessaires pour les débutants, les ingénieurs électroniques ADULTES. Que ce soit - un barman numérique ou une machine pour mélanger des cocktails.
Le projet d'aujourd'hui n'est pas terminé. Si vous avez besoin d'ajouter, écrivez directement dans les commentaires. Sur la photo, il y a un stand où vous pouvez voir le design de ce barman.

Et ceci est un schéma dans le style des projets Arduino.

Maintenant, la conception et la logique du travail. Selon le schéma et la photo.
Les tubes récepteurs de quatre micropompes sont déposés dans quatre flacons. Ces micropompes sont connectées à quatre touches assemblées sur des transistors à effet de champ MOSFET. Les clés, à leur tour, sont connectées aux bornes 3,5,6,9 de n'importe quel Arduina (en outre, si cela est écrit à la conclusion **, cela signifie la conclusion d'Arduina). Ceci est la partie exécutive. La logique de la partie exécutive est réglée par cinq potentiomètres. Dans l'ordre: avec le potentiomètre le plus à gauche connecté à la borne A4, réglez le volume du verre dans lequel nous préparerons le cocktail. Au-dessus, vous voyez une échelle de 16 LED RVB avec le soi-disant adressage de pixels WS2812b. Lorsque nous tournons le premier potentiomètre sur une échelle, les LED sont allumées séquentiellement, indiquant conditionnellement le volume du verre. Cette balance est connectée à la broche 11.
Avec les quatre potentiomètres à glissière suivants (connectés aux bornes A0, A1, A2, A3), nous définissons la proportion du volume total de la boisson que nous devons verser à partir d'une bouteille particulière. Au-dessus des curseurs se trouvent de petites échelles de 8 LED chacune (connectées en série à une longue). En déplaçant les potentiomètres, nous sélectionnons la proportion et les petites échelles, de bas en haut, chacune peinte avec sa propre couleur. Dans le même temps, l'échelle longue gauche, ou plutôt sa partie éclairée à l'origine, est peinte dans les mêmes couleurs proportionnellement aux petites.
Voir immédiatement la composition du cocktail. Nous appuyons sur le bouton (broche 7), et les pompes s'allument séquentiellement de gauche à droite, ajustant les boissons dans le verre.
Dans l'esquisse, vous pouvez configurer:
1. Couleurs actives de toutes les échelles, leur éclairage de fond.
2. La vitesse des pompes, par modulation de largeur d'impulsion. Par conséquent, les pompes ne comprennent pas de relais, mais des commutateurs à transistors. Cela est nécessaire si, par exemple, vous utilisez une pompe de lave-glace de voiture. Elle est très productive.
3. Le volume physique maximum du verre. La valeur par défaut est 750 ml.
4. Le temps d'aspiration du fluide avant l'inclusion de la lecture et de l'indication. Il s'agit de remplir le tube vide lors d'une pause.
5. Faites une pause avant de servir à partir de la bouteille suivante.
6. La taille des échelles dans les LED. Par défaut, il y a 16 LED à grande échelle et huit LED dans les petites. Vous pouvez changer comme vous le souhaitez. Ce sera magnifique.
Quelque chose comme ça.
Ceci est une vidéo de démonstration.
Je propose d'assembler ce design pour l'électronique débutante. Versez l'esquisse dans Arduin et jouez avec les paramètres du barman. Lorsque vos yeux s'habituent au code et que vous naviguez dans les lignes, vous pouvez ajouter de nouvelles fonctions, par exemple le bouton Stop ou le bouton Ice. Ce dernier peut automatiquement réduire le volume total de la boisson proportionnellement au volume du glaçon.
J'espère que de tels projets captiveront les programmeurs électroniques débutants adultes. Et ils ont lentement trié la conception et le code avec leurs enfants augmenteront l'estime de soi de plusieurs niveaux.
Bon week-end!
Et en conclusion, ainsi que dans la vidéo, je vais essayer de faire des commentaires et des suggestions. J'en tiendrai compte dans la suite et dans le projet actuel.
Le code (croquis) peut être téléchargé
ici . Ou voyez:
Vous avez besoin d'Adafruit_NeoPixel.h - une bibliothèque pour les LED et
PinChangeInt.h - une bibliothèque pour les interruptions.
#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; }