Bartender digital. Proyek Arduino untuk elektronik pemula dewasa. Bagian 1

Saya punya banyak teman. Para pria muda, pria paruh baya, dan tentu saja wanita dari segala usia. Mungkin semuanya. Sulit untuk menentukan usia wanita modern. Dan saya benar-benar tidak mau.
Jadi disini. Karena kemampuan saya, saya membawa anak-anak dari teman dan kenalan saya dengan elektronik. Kami membangun robot kecil, bug dari semua jenis kunang-kunang, dan bahkan lightsabers. Anak-anak hampir selalu berhasil dan mereka, tentu saja, berlari untuk membual kepada orang tua yang telah menghidupkan kembali barang elektronik. Dan seterusnya. Tetapi suatu hari, salah satu teman saya, melihat gelombang kebanggaan pada putrinya, mengatakan - Saya juga ingin mempelajari elektronik ini, dan pemrograman, dan mungkin bahkan menyolder sesuatu. Tidak ada pertanyaan Mari kita tunjukkan bagaimana cara merakit robot pada platform beroda. Akan naik strip di lantai. Atau kita akan membuat game, python, misalnya, atau hanya mengedipkan LED, atau ... Kami telah melalui banyak contoh. Ternyata jika untuk pemula, maka semuanya seperti anak kecil, dan jika tidak untuk anak-anak, maka itu bukan untuk pemula sama sekali. Ada yang salah! Pelajaran diperlukan untuk PEMULA, DEWASA insinyur elektronik. Biarkan saja - bartender digital atau mesin untuk mencampur koktail.

Proyek untuk hari ini belum selesai. Jika Anda perlu menambahkan, tulis langsung di komentar. Di foto ada stand di mana Anda dapat melihat desain bartender ini.



Dan ini adalah diagram dalam gaya proyek Arduino.



Sekarang desain dan logika kerjanya. Menurut skema dan foto.

Tabung penerima empat micropump dijatuhkan ke dalam empat botol. Micropump ini terhubung ke empat tombol yang dipasang pada transistor efek medan MOSFET. Kunci-kunci ini, pada gilirannya, terhubung ke terminal 3,5,6,9 dari setiap Arduina (lebih lanjut jika ditulis pada kesimpulan **, ini berarti kesimpulan dari Arduina). Ini adalah bagian eksekutif. Logika bagian eksekutif ditetapkan oleh lima potensiometer. Agar: dengan potensiometer paling kiri yang terhubung ke terminal A4, atur volume gelas tempat kami akan menyiapkan koktail. Di atasnya Anda melihat skala 16 LED RGB dengan apa yang disebut pixel addressing WS2812b. Ketika kita menghidupkan potensiometer pertama pada skala, LED menyala secara berurutan, menunjukkan volume kaca secara kondisional. Skala ini terhubung ke pin 11.

Empat potensiometer geser berikutnya (terhubung ke terminal A0, A1, A2, A3) mengatur proporsi volume total minuman yang perlu kita tuangkan dari botol tertentu. Di atas slider adalah skala kecil masing-masing 8 LED (terhubung secara seri ke yang panjang). Memindahkan potensiometer, kami memilih proporsi dan skala kecil, dari bawah ke atas, masing-masing dicat dengan warnanya sendiri. Pada saat yang sama, skala panjang kiri, atau lebih tepatnya, bagian awalnya menyala, dicat dengan warna yang sama sebanding dengan yang kecil.

Segera lihat komposisi koktailnya. Kami menekan tombol (pin 7), dan pompa menyala secara berurutan dari kiri ke kanan, menyesuaikan minuman ke dalam gelas.

Dalam sketsa, Anda dapat mengonfigurasi:

1. Warna aktif semua skala, iluminasi latar belakangnya.
2. Kecepatan pompa, dengan modulasi lebar pulsa. Oleh karena itu, pompa tidak termasuk relay, tetapi saklar transistor. Ini diperlukan jika Anda, misalnya, akan menggunakan pompa pencuci kaca mobil. Dia sangat produktif.
3. Volume fisik maksimum kaca. Standarnya adalah 750 ml.
4. Waktu pengisapan cairan sebelum dimasukkannya pembacaan dan indikasi. Ini untuk mengisi tabung kosong saat jeda.
5. Jeda sebelum disajikan dari botol berikutnya.
6. Ukuran timbangan dalam LED. Secara default, ada 16 LED dalam skala besar dan delapan LED dalam yang kecil. Anda dapat berubah sesuai keinginan. Itu akan menjadi indah.
Sesuatu seperti itu.

Ini adalah video demo.



Saya mengusulkan untuk merakit desain ini untuk elektronik pemula. Tuang sketsa ke Arduin dan mainkan dengan pengaturan bartender. Ketika mata Anda terbiasa dengan kode dan Anda menavigasi dalam garis, Anda dapat menambahkan fungsi baru, misalnya, tombol Stop atau tombol Ice. Yang terakhir dapat secara otomatis mengurangi volume total minuman sesuai dengan volume es batu.

Saya berharap bahwa proyek-proyek seperti itu akan memikat programmer elektronik pemula dewasa. Dan mereka perlahan-lahan memilah-milah desain dan kode bersama dengan anak-anak mereka akan meningkatkan harga diri beberapa tingkat lebih.

Semoga akhir pekanmu menyenangkan!

Dan sebagai kesimpulan, juga dalam video, saya akan mencoba membuat komentar dan saran. Saya akan mempertimbangkan ini dalam kelanjutan dan dalam proyek yang sebenarnya.

Kode (sketsa) dapat diunduh di sini . Atau lihat:

Anda memerlukan Adafruit_NeoPixel.h - perpustakaan untuk LED dan PinChangeInt.h - perpustakaan untuk interupsi.

#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; } 

Source: https://habr.com/ru/post/id403607/


All Articles