در این پروژه یاد میگیریم که چگونه یک ماشین وندینگ با آردوینو بسازیم و ما کل روند ساختن آن، از بریدن و مونتاژ کردن قطعات MDF تا متصل کردن تمام بخشهای الکترونیکی به یکدیگر و نوشتن کد آردینو را به شما نشان خواهیم داد.
شمای کلی
به طور کلی دستگاه فروش از بخشهای اصلی که در ادامه نام برده میشوند تشکیل شده است.
- چهار واحد تخلیه که توسط چهار سروو موتور چرخشی کنترل میشوند.
- یک سیستم حامل که توسط موتورهای پلهای کنترل میشوند.
- یک نمایشگر.
- چهار کلید برای انتخاب آیتم مورد نظر و یک تشخیص دهندهی سکه.
ممکن است فکر کنید که نگهدارنده آیتم برای این ماشین فروش مفید به نظر نمیرسد و بله! حق با شماست. اما ما به دلیل اینکه میخواستیم این پروژه مقداری سختتر شود تا بتوانید از آن مطالب بیشتری یاد بگیرید، آن بخش را در نظر گرفتیم.
فکر میکنم این پروژه میتواند به عنوان پروژه نهایی برای دانشجویان گرایشهای الکترونیک و مکاترونیک و همچنین هر شخص مشتاق دیگری به یادگیری آردوینو ایده بسیار خوبی باشد.
ساختن ماشین فروش
با بریدن صفحههای MDF به اندازههای 8 میلیمتر شروع میکنیم.
قبل از شروع کار، ما یک مدل سه بُعدی از ماشین ساختیم و تمام اندازه گیریها را با توجه به آن مدل مشخص کردیم. شما میتوانید مدل سه بُعدی را از سایت آخر نوشته دانلود کنید.
(DIY Vending Machine 3D Model Solidworks Files)
(DIY Vending Machine 3D Model STEP File)
برای برش دادن MDF از یک ارّه گرد استفاده کردیم. در واقع چیزی که در تصویر میبینید یک میز کار خانگی با ارّه مدور، یک مسیریاب و همچنین یک ارّه مویی است که توسط همکارم Marija ساخته شده و یک ویدئو برای اینکه خودتان آن را بسازید در کانال یوتیوب همکارم با عنوان Creativity Hero موجود است.
پس از بریدن تمام صفحهها با استفاده از ارّه مدوّر، شروع به بریدن بخشهای باز بعضی از صفحهها با استفاده از ارّه مویی معکوس میکنیم.
در واقع اگر شما ارّه مدوّر ندارید، میتوانید برای برشهای بخش قبل نیز از ارّه مویی استفاده کنید. همچنین برای بریدن بخشهای کوچکتر که برشهای متعددی داشتند نیز از ارّه مویی استفاده کردهایم. هر چند این ابزارها، بسیار خطرناک هستند و باید در هنگام کار با آنها بسیار مراقب باشید.
به محض آماده شدن قطعات MDF، شروع به مونتاژ آنها با نوعی چسب چوب و تعدادی پیچ میکنیم. همچنین برای محکم کردن پنلها از گیرههای 90 درجه استفاده میکنیم. ابتدا با استفاده از یک دریل بیسیم، سوراخهای مشابه پایلوت را میسازیم، سپس خزینه (Counter sink) را میسازیم و پیچهای 3 میلیمتری را در محل پیچ میکنیم. به همین صورت برای مونتاژ تمام پنلها از همین روش استفاده میکنیم و برای تعدادی از آنها از برخی گیرههای F شکل نیز استفاده میکنیم.
سیستم ریل
این بخش از مونتاژ را با ساختن سیستم ریلی ادامه میدهیم. به همین جهت از تیوبهای آلومینیومی استفاده شده، که به وسیله یک ارّه فلزی به اندازه دلخواه بریده شدهاند. قطر تیوب برای ریل افقی 16 میلیمتر و برای ریل عمودی برابر 20 میلیمتر است. سپس روی یک تخته چوبی 18 میلیمتری شکافهایی برای لولهها ایجاد میکنیم و به وسیله یک مته گازور (frostner bit) آنها را به تخته متصل میکنیم.
ریل افقی از دو لوله به طول 27 سانتیمتر و ریل عمودی از سه لوله به طول 45 سانتیمتر ساخته شده است.
سپس روند ساخت لغزندهها را با هم دنبال میکنیم. ما از یک تخته چوبی با ابعاد 21 در 21 سانتیمتر استفاده کردهایم که روی آن حفرههای 8 میلیمتری ایجاد شده است.
سپس میلههای نخ دار 8 میلیمتری را درون این حفرهها نصب میکنیم و با استفاده از واشر و مهره یاتاقانهای 22 میلیمتری را محکم میکنیم. برای بخش کشویی افقی، از روش مشابه استفاده میکنیم و تنها تفاوت آن اندازه یاتاقانها که 16 میلیمتر برای قطر خارجی است، میباشد.
پس از اتصال لغزنده کشویی بین ریلهای لولهای، متوجه شدیم که مقداری سست است. برای حل این مشکل باید فاصله بین ریلها را کم کنیم. در نتیجه، ابتدا تعداد شکافهای لولهها را افزایش دادیم. شیارهایی به صورت عمود روی لولهها ایجاد کردیم و در نهایت با استفاده از یک میله نخ دار دو ریل لولهای را نزدیک یکدیگر محکم کردیم. پس از این کار، کشو دیگر سست نبوده و به خوبی کار میکند.
هرچند، حالا باید ریلها را به جهت اضافه کردن دیگر المانها دوباره از هم جدا کنیم. ابتدا یک پیچ 5 میلیمتری روی طرف چپ ریلهایی که بر روی آن یک قرقره برای کمربند زمان افقی وصل میکنیم، اضافه میشود و همچنین دو یاتاقان دیگر که روی ریل عمودی سمت چپ میچرخند.
در طرف دیگر، سمت راست ریل باید یک موتور پلهای جهت کنترل حرکت افقی قرار میدهیم. ابتدا موتور را روی تختهی امدیاف 8 میلیمتری میبندیم و سپس یک قطعه چوب برای حمایت از آن اضافه میکنیم. در نهایت با استفاده از چسب چوب و دو عدد پیچ این مجموعه را به لغزنده عمودی متصل میکنیم.
سپس کار را با اضافه کردن مخزن نگهدارنده به لغزنده افقی ادامه میدهیم. برای این کار از تکههای بسیار کوچک چوب که با چسب چوب به هم وصل کرده بودیم، استفاده میکنیم. به محض اینکه این بخش تمام شد، آماده مونتاژ کردن سیستم ریل میشویم. در شکاف ریلها از نوعی چسب پلاستیکی (اپوکسی) استفاده میکنیم و یک تخته چوبی اضافی به یک طرف ریل متصل میکنیم تا سیستم ریل مستحکمتر شود.
در قدم بعدی، این مجموعه مونتاژ شده را بین ریلهای افقی متصل کرده و آنها را در مکان خودشان محکم میکنیم. نتیجهی نهایی این است که لغزندهها و سیستم ریلها بسیار عالی عمل میکند.
سپس کمربند زمانی افقی را نصب میکنیم. مقدار طول مورد نیاز را اندازهگیری کرده و به همان میزان برش میدهیم و به کمک یک بست کمربندی آن را به لغزنده متصل میکنیم. به همین صورت برای لغزنده عمودی، با استفاده از یک قطعه MDF و چند پیچ و مهره، موتور پلهای را بالای ماشین متصل میکنیم.
در بخش پایینی یک قرقره نصب کرده و به همان صورت کمربند زمانی را اضافه میکنیم.
واحدهای تخلیه
سپس به بخش تخلیه میپردازیم. به این صورت که از سیم فلزی با ضخامت 3 میلیمتر، یک سیم پیچ فلزی درست میکنیم و آن را به دور یک قوطی اسپری به قطر 7 سانتیمتر بهم میپیچیم.
پس از آن با یک چسب تفنگی آن را به سروو موتور چرخشی مداوم متصل میکنیم.
پنل جلویی
در مرحله بعد، پنل جلویی را با لولاهای ساده به ماشین فروش اضافه کرده و برای قابلیت قفل شدن آن، از یک گیره درب مغناطیسی استفاده میکنیم. سپس از یک آکریلیک به ضخامت 5 میلیمتر برای پوشاندن ورودی بزرگ و برای باز کردن ورودی کوچکتر در سمت راست از یک صفحه آلومینیومی بسیار کوچک استفاده میکنیم. در اینجا با استفاده از دریل و اره برقی، چهار حفره برای کلیدها و همچنین یک ورودی برای سکهها و بخش دیگری هم برای نمایشگر LCD ایجاد میکنیم. به محض اینکه بخشهای الکترونیکی را به پنل آلومینیومی متصل کردیم، این مجموعه را با پیچ و مهرههای 5 میلیمتری به در جلویی اضافه میکنیم.
جهت قرار دادن حامل در موقعیت اولیهاش، دو عدد میکروسوئیچ نصب کرده و برای سکهها یک راهنما که آنها را به سمت پایین ماشین هدایت میکند، قرار میدهیم.
سکهیاب یک سنسور مجاورت مادون قرمز ساده است و به محض اینکه یک سکه از نزدیک آن عبور کند، یک بازخورد مثبت به ما میدهد.
مدار شماتیک
حالا به بخش جالب کار میرسیم، اتصال تمام بخشهای الکترونیکی به برد آردوینو.
مدار شماتیک کامل برای این پروژه ماشین فروش به صورت زیر است.
با توجه به شکل، به یک منبع تغذیه 12 ولتی و همچنین حدقل 2 عدد تقویت کننده نیاز داریم. ما برای موتورهای پلهای و همچنین LEDهای نواری که در ادامه به در جلویی متصل خواهیم کرد، به منبع تغذیهی 12 ولتی نیاز داریم. هرچند از آنجایی که برای تمامی دیگر اجزا به منبع تغذیهی 5 ولت نیاز داریم، از یک مبدل 5 ولت به 12 ولت استفاده میکنیم تا ولتاژ مورد نیاز را تامین کند.
موتورهای چرخشی DS04-NFC با تغذیه 5 ولت راه اندازی شده و توسط سیگنال PWM که از برد آردوینو فرستاده شده، کنترل میشوند. در حالی که موتورهای پلهای توسط درایورهای A4988 کنترل میشوند.
چهار کلید فشاری و دو میکرو سوئیچ استفاده شده به زمین الکتریکی و پینهای دیجیتال آردوینو متصل میشوند. در نتیجه با استفاده از مقاومتهای بالاکش داخلی برد آردوینو، به سادگی میتوانیم فشرده شدن آنها را تشخیص دهیم.
شما میتوانید قطعات مورد نیاز برای این پروژه آردوینو را از طریق لینکهای زیر تهیه کنید.
- مبدل DC-DC LM2596
- LCD 16 * 2
- سروو موتور چرخشی 360 درجه
- موتور پلهای NEMA 17
- موتور درایور A4988
- سنسور مجاورت IR
- کلید فشاری
- میکرو سوئیچ
- برد آردوینو
مقاله مفید مرتبط: فروشگاه های اینترنتی قطعات الکترونیک
حالا با استفاده از تعدادی سیم، قطعات الکترونیکی را به یکدیگر متصل میکنیم. ظاهر کار به خاطر زیاد بودن تعداد سیمها مقداری به هم ریخته به نظر میرسد اما عملکرد آن کاملا درست است. در آخر دو نوار LED به پنل در ماشین متصل میکنیم تا داخل ماشین فروش را روشن و نورانی کند.
کد آردوینو
حالا تنها چیزی که باقی مانده، دادن کد برنامه مورد نظر به آردوینو است که آن را در اینجا مشاهده میکنید. در زیر توصیفی از این کد آورده شده است.
/* DIY Vending Machine - Arduino based Mechatronics Project by Dejan Nedelkovski, www.HowToMechatronics.com */ #include <LiquidCrystal.h> // includes the LiquidCrystal Library #include <Servo.h> LiquidCrystal lcd(27, 26, 25, 24, 23, 22); // Creates an LC object. Parameters: (rs, enable, d4, d5, d6, d7) Servo servo1, servo2, servo3, servo4; // DS04-NFC motors // Stepper motors pins #define dirPinVertical 0 #define stepPinVertical 1 #define dirPinHorizontal 2 #define stepPinHorizontal 3 #define coinDetector 9 #define button1 13 #define button2 12 #define button3 11 #define button4 10 #define microSwitchV 15 #define microSwitchH 14 int buttonPressed; void setup() { lcd.begin(16, 2); // Initializes the interface to the LCD screen, and specifies the dimensions (width and height) of the display servo1.attach(4); servo2.attach(5); servo3.attach(6); servo4.attach(7); pinMode(dirPinVertical, OUTPUT); pinMode(stepPinVertical, OUTPUT); pinMode(dirPinHorizontal, OUTPUT); pinMode(stepPinHorizontal, OUTPUT); pinMode(coinDetector, INPUT); // Activating the digital pins pull up resistors pinMode(button1, INPUT_PULLUP); pinMode(button2, INPUT_PULLUP); pinMode(button3, INPUT_PULLUP); pinMode(button4, INPUT_PULLUP); pinMode(microSwitchV, INPUT_PULLUP); pinMode(microSwitchH, INPUT_PULLUP); // Vertical starting position digitalWrite(dirPinVertical, HIGH); // Set the stepper to move in a particular direction while (true) { if (digitalRead(microSwitchV) == LOW) { // If the micro switch is pressed, move the platfor a little bit up and exit the while loop moveUp(70); break; } // Move the carrier up until the micro switch is pressed digitalWrite(stepPinVertical, HIGH); delayMicroseconds(300); digitalWrite(stepPinVertical, LOW); delayMicroseconds(300); } // Horizontal starting position digitalWrite(dirPinHorizontal, LOW); while (true) { if (digitalRead(microSwitchH) == LOW) { moveLeft(350); break; } digitalWrite(stepPinHorizontal, HIGH); delayMicroseconds(300); digitalWrite(stepPinHorizontal, LOW); delayMicroseconds(300); } } void loop() { // Print "Insert a coin!" on the LCD lcd.clear(); lcd.setCursor(0, 0); lcd.print("Insert a coin!"); // Wait until a coin is detected while (true) { if (digitalRead(coinDetector) == LOW) { // If a coin is detected, exit the from the while loop break; } } delay(10); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Select your item"); lcd.setCursor(0, 1); lcd.print(" 1, 2, 3 or 4?"); // Wait until a button is pressed while (true) { if (digitalRead(button1) == LOW) { buttonPressed = 1; break; } if (digitalRead(button2) == LOW) { buttonPressed = 2; break; } if (digitalRead(button3) == LOW) { buttonPressed = 3; break; } if (digitalRead(button4) == LOW) { buttonPressed = 4; break; } } // Print "Delivering..." lcd.clear(); lcd.setCursor(0, 0); lcd.print("Delivering..."); // Depending on the pressed button, move the carrier to that position and discharge the selected item switch (buttonPressed) { case 1: // Move the container to location 1 moveUp(4900); // Move up 4900 steps (Note: the stepper motor is set in Quarter set resolution) delay(200); moveLeft(1700); // Move left 1700 steps delay(300); // Rotate the helical coil, discharge the selected item servo1.writeMicroseconds(2000); // rotate delay(950); servo1.writeMicroseconds(1500); // stop delay(500); // Move the container back to starting position moveRight(1700); delay(200); moveDown(4900); break; case 2: // Move the container to location 2 moveUp(4900); delay(200); // Rotate the helix, push the selected item servo2.writeMicroseconds(2000); // rotate delay(950); servo2.writeMicroseconds(1500); // stop delay(500); moveDown(4900); break; case 3: // Move the container to location 3 moveUp(2200); delay(200); moveLeft(1700); delay(300); // Rotate the helix, push the selected item servo3.writeMicroseconds(2000); // rotate delay(950); servo3.writeMicroseconds(1500); // stop delay(500); // Move the container back to starting position moveRight(1700); delay(200); moveDown(2200); break; case 4: // Move the container to location 4 moveUp(2200); // Move verticaly 4800 steps delay(200); // Rotate the helix, push the selected item servo4.writeMicroseconds(2000); // rotate delay(950); servo4.writeMicroseconds(1500); // stop delay(500); moveDown(2200); break; } lcd.clear(); // Clears the display lcd.setCursor(0, 0); lcd.print("Item delivered!"); // Prints on the LCD delay(2000); } // == Custom functions == void moveUp (int steps) { digitalWrite(dirPinVertical, LOW); for (int x = 0; x < steps; x++) { digitalWrite(stepPinVertical, HIGH); delayMicroseconds(300); digitalWrite(stepPinVertical, LOW); delayMicroseconds(300); } } void moveDown (int steps) { digitalWrite(dirPinVertical, HIGH); for (int x = 0; x < steps; x++) { digitalWrite(stepPinVertical, HIGH); delayMicroseconds(300); digitalWrite(stepPinVertical, LOW); delayMicroseconds(300); } } void moveLeft (int steps) { digitalWrite(dirPinHorizontal, HIGH); for (int x = 0; x < steps; x++) { digitalWrite(stepPinHorizontal, HIGH); delayMicroseconds(300); digitalWrite(stepPinHorizontal, LOW); delayMicroseconds(300); } } void moveRight (int steps) { digitalWrite(dirPinHorizontal, LOW); for (int x = 0; x < steps; x++) { digitalWrite(stepPinHorizontal, HIGH); delayMicroseconds(300); digitalWrite(stepPinHorizontal, LOW); delayMicroseconds(300); } }
توضیح کد مرجع
در ابتدا نیاز است که دو کتابخانه Servo و LiquidCrystal را اضافه کرده و پینهای LCD را تعریف کنیم. همچنین چهار سروو موتور، پینهای موتور پلهای، سکهیاب و همچنین چهار کلید فشاری و دو میکرو سوئیچ را به همین ترتیب تعریف میکنیم.
در بخش تنظیمات، حالت کاری پینها را برای تمامی پینهای ذکر شده در بالا تنظیم میکنیم. قابل ذکر است که برای کلیدهای فشاری و همچنین میکروسوئیچها، مقاومتهای بالاکش داخلی را فعال نمودهایم، این به آن معناست که مقدار منطقی در این پینها، همواره و به صورت پیشفرض برابر 1 یا مقدار بالای سیگنال تعریف شده است و به محض اینکه ما آنها را بفشاریم مقدار منطقی آنها برابر صفر خواهد شد.
قبل از آنکه وارد حلقه main شویم، حامل را هم در موقعیت شروع اولیهاش که توسط دو میکروسوئیچ تعیین شده است، قرار میدهیم.
پس توسط حلقه while میتوانیم حامل را مجددا در موقعیت اولیهاش قرار دهیم و به محض اینکه دو میکروسوئیچ فشرده شدند، موتور متوقف شده و به موقعیت شروع انتخاب شده، منتقل میشود.
// Vertical starting position digitalWrite(dirPinVertical, HIGH); // Set the stepper to move in a particular direction while (true) { if (digitalRead(microSwitchV) == LOW) { // If the micro switch is pressed, move the platfor a little bit up and exit the while loop moveUp(70); break; } // Move the carrier up until the micro switch is pressed digitalWrite(stepPinVertical, HIGH); delayMicroseconds(300); digitalWrite(stepPinVertical, LOW); delayMicroseconds(300); } // Horizontal starting position digitalWrite(dirPinHorizontal, LOW); while (true) { if (digitalRead(microSwitchH) == LOW) { moveLeft(350); break; } digitalWrite(stepPinHorizontal, HIGH); delayMicroseconds(300); digitalWrite(stepPinHorizontal, LOW); delayMicroseconds(300); }
برنامه اصلی با نمایش پیغام «یک سکه وارد کنید» روی LCD شروع میشود و سپس در حلقه while قرار گرفته و در آن میماند. به محض اینکه یک سکه از نزدیکی سنسور سکهیاب بگذرد، حالت منطقی در سکهیاب به مقدار صفر یا سطح پایین سیگنال تغییر کرده و در این شرایط با کمک عبارت break از حلقه while خارج میشود.
// Wait until a coin is detected while (true) { if (digitalRead(coinDetector) == LOW) { // If a coin is detected, exit the from the while loop break; } }
سپس پیغام «گزینه مورد نظر را انتخاب کنید» روی LCD چاپ شده و در حلقهی while دیگری قرار میگیریم.
// Wait until a button is pressed while (true) { if (digitalRead(button1) == LOW) { buttonPressed = 1; break; } if (digitalRead(button2) == LOW) { buttonPressed = 2; break; } if (digitalRead(button3) == LOW) { buttonPressed = 3; break; } if (digitalRead(button4) == LOW) { buttonPressed = 4; break; } }
این حلقه while، منتظر میماند تا ما یکی از چهار دکمه را بفشاریم و به محض انجام این کار، از آن بیرون آمده و عبارت «در حال تحویل» نمایش داده میشود.
حالا بسته به کلید فشرده شده، یکی از حالتهای تعریف شده در ساختار switch case زیر اجرا می شود. در حالتی که کلید اول را فشرده باشیم، حامل توسط تابع «()move up» شروع به حرکت میکند.
switch (buttonPressed) { case 1: // Move the container to location 1 moveUp(4900); // Move up 4900 steps (Note: the stepper motor is set in Quarter set resolution) delay(200); moveLeft(1700); // Move left 1700 steps delay(300); // Rotate the helical coil, discharge the selected item servo1.writeMicroseconds(2000); // rotate delay(950); servo1.writeMicroseconds(1500); // stop delay(500); // Move the container back to starting position moveRight(1700); delay(200); moveDown(4900); break; }
اگر به این تابع دقت کنیم، میبینیم که به سادگی حرکت موتور پلهای را در جهت خاصی تنظیم میکند و تعداد گامهای مورد نیاز را با توجه به آرگومان ورودی که ما تعیین کردیم، انتخاب میکند.
void moveUp (int steps) { digitalWrite(dirPinVertical, LOW); for (int x = 0; x < steps; x++) { digitalWrite(stepPinVertical, HIGH); delayMicroseconds(300); digitalWrite(stepPinVertical, LOW); delayMicroseconds(300); } }
در اینجا بهتر است توجه کنیم که ما درایور پلهای A4988 را در وضعیت وضوح مرحله چهارم تنظیم کردیم و با انجام تعدادی آزمایش، به این نتیجه رسیدیم که برای اینکه حامل در موقعیت بالا قرار گیرد به 4900 گام نیاز است. به همین صورت حامل را به سمت چپ منتقل میکنیم تا به موقعیت شماره 1 برسد.
دقیقا بعد از این مرحله، موتور چرخشی را به اندازه 950 میلیثانیه میچرخانیم تا کویل مارپیچ یک دور کامل را طی کند.
// Rotate the helical coil, discharge the selected item servo1.writeMicroseconds(2000); // rotate delay(950); servo1.writeMicroseconds(1500); // stop
حالا در اینجا مقدار این متغیرها میتواند بسته به خود موتور تغییر کند. با استفاده از توابع سفارشی ()moveRight و ()moveDown، حامل را به مکان اولیهاش برمیگردانیم و به همین صورت میتوانیم هر یک از چهار آیتم را تخلیه کنیم و در انتهای کار پیغام «آیتم تحویل داده شد» نمایش داده میشود.
به همین سادگی بود. امیدوارم لذت برده باشید و همچنین نکات جدیدی را از این آموزش یاد گرفته باشید.
منبع: ترجمه از سایت howtomechatronics.com
امیدواریم این آموزش براتون مفید واقع شده باشه. صرفا جهت اطلاع دستگاه/ماشین وندینگ در ایران تولید میشه و شاید شما هم بتوانید یک کارگاه تولیدی دستگاه وندینگ خاص مثلا قهوه درست کنید. کامنت یادتون نره.
اگر این نوشته برایتان مفید بود لطفا کامنت بنویسید.
خیلی عالی بود.
خدا خیرتون بده