Tengo muchos amigos Chicos jóvenes, hombres de mediana edad y, por supuesto, damas de todas las edades. Probablemente todos. Es difícil determinar la edad de una mujer moderna. Y realmente no quiero hacerlo.
Entonces aquí. Debido a mis habilidades, llevo hijos de mis amigos y conocidos con la electrónica. Construimos pequeños robots, insectos de todo tipo de luciérnagas e incluso sables de luz. Los niños casi siempre tienen éxito y, por supuesto, corren para presumir ante los padres que han revivido la electrónica. Y así sucesivamente. Pero un día, una de mis amigas, al mirar otra oleada de orgullo por su hija, dice: también quiero profundizar en esta electrónica y programación, y tal vez incluso soldar algo. No hay duda Vamos a mostrar cómo ensamblar un robot en una plataforma con ruedas. Montará una tira en el suelo. O haremos un juego, una pitón, por ejemplo, o simplemente parpadearemos los LED, o ... Pasamos por muchos ejemplos. Resulta que si es principiante, entonces todo es infantil, y si no es para niños, entonces no es para principiantes. Algo esta mal! Se necesitan lecciones para PRINCIPIANTES, ADULTOS, ingenieros electrónicos. Déjalo ser: un barman digital o una máquina para mezclar cócteles.
El proyecto de hoy no está terminado. Si necesita agregar, escriba directamente en los comentarios. En la foto hay un stand donde puedes ver el diseño de este cantinero.

Y este es un diagrama al estilo de los proyectos de Arduino.

Ahora el diseño y la lógica del trabajo. Según el esquema y la foto.
Los tubos receptores de cuatro microbombas se colocan en cuatro botellas. Estas microbombas están conectadas a cuatro teclas ensambladas en transistores de efecto de campo MOSFET. Las teclas, a su vez, están conectadas a los terminales 3,5,6,9 de cualquier Arduina (además si está escrito en la conclusión **, esto significa la conclusión de Arduina). Esta es la parte ejecutiva. La lógica de la parte ejecutiva está establecida por cinco potenciómetros. En orden: con el potenciómetro más a la izquierda conectado al terminal A4, ajuste el volumen del vaso en el que prepararemos el cóctel. Arriba, verá una escala de 16 LED RGB con el llamado direccionamiento de píxeles WS2812b. Cuando activamos el primer potenciómetro en una escala, los LED se iluminan secuencialmente, mostrando condicionalmente el volumen del vidrio. Esta escala está conectada al pin 11.
Con los siguientes cuatro potenciómetros deslizantes (conectados a los terminales A0, A1, A2, A3) establecemos la proporción del volumen total de la bebida que necesitamos verter de una botella en particular. Por encima de los controles deslizantes hay pequeñas escalas de 8 LED cada una (conectadas en serie a una larga). Moviendo potenciómetros, seleccionamos la proporción y las escalas pequeñas, de abajo hacia arriba, cada una pintada con su propio color. Al mismo tiempo, la escala larga izquierda, o mejor dicho, su parte originalmente iluminada, está pintada en los mismos colores en proporción a los pequeños.
Inmediatamente ver la composición del cóctel. Presionamos el botón (pin 7), y las bombas se encienden secuencialmente de izquierda a derecha, colocando las bebidas en el vaso.
En el boceto, puede configurar:
1. Colores activos de todas las escalas, su iluminación de fondo.
2. La velocidad de las bombas, por modulación de ancho de pulso. Por lo tanto, las bombas no incluyen relés, sino interruptores de transistores. Esto es necesario si, por ejemplo, usará una bomba de lavado de vidrio para automóviles. Ella es muy productiva.
3. El volumen físico máximo del vidrio. El valor predeterminado es 750 ml.
4. El tiempo de succión de fluido antes de la inclusión de lectura e indicación. Esto es para llenar el tubo vacío durante una pausa.
5. Haga una pausa antes de servir de la próxima botella.
6. El tamaño de las escalas en los LED. Por defecto, hay 16 LED en la escala grande y ocho LED en los pequeños. Puedes cambiar como quieras. Será hermoso
Algo asi.
Este es un video de demostración.
Propongo ensamblar este diseño para principiantes electrónicos. Vierte el boceto en Arduin y juega con la configuración del barman. Cuando sus ojos se acostumbran al código y navega en las líneas, puede agregar nuevas funciones, por ejemplo, el botón Detener o el botón Hielo. Este último puede reducir automáticamente el volumen total de la bebida en proporción al volumen del cubo de hielo.
Espero que tales proyectos cautiven a los programadores electrónicos novatos adultos. Y lentamente resolvieron el diseño y el código, junto con sus hijos elevarán la autoestima en varios niveles más.
¡Que tengan un buen fin de semana!
Y en conclusión, así como en el video, intentaré hacer comentarios y sugerencias. Tendré esto en cuenta en la continuación y en el proyecto real.
El código (boceto) se puede descargar
aquí . O ver:
Necesita Adafruit_NeoPixel.h , una biblioteca para LED y
PinChangeInt.h , una biblioteca para interrupciones.
#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; }