پروژه و ساخت

ساخت دستگاه فروش خودکار (وندینگ) با آردوینو

ساخت دستگاه فروش خودکار (وندینگ ماشین) هوشمند با آردوینو و پرداخت آنلاین

مقدمه

دستگاه‌های فروش خودکار (Vending Machines) در سال‌های اخیر به‌دلیل افزایش نیاز به فروش بدون ملاقات انسانی، بسیار محبوب شده‌اند. این دستگاه‌ها در فروش انواع کالاها از تنقلات و نوشیدنی گرفته تا قطعات الکترونیکی و تجهیزات کوچک کاربرد دارند.
با استفاده از آردوینو و ماژول‌های ارتباطی WiFi می‌توان یک وندینگ‌ماشین کاملاً هوشمند ساخت که توانایی مدیریت موجودی، کنترل فرآیند فروش، ذخیره گزارشات و پرداخت آنلاین از طریق درگاه پرداخت را داشته باشد.

این مقاله یک راهنمای کامل، عملی و حرفه‌ای از صفر تا صد ساخت چنین دستگاهی است.

معرفی کامل دستگاه فروش خودکار

وندینگ ماشین هوشمند چیست؟

یک دستگاه قابل اتکا که بدون نیاز به نیروی انسانی می‌تواند:

  • کالا را نمایش دهد

  • هزینه را دریافت کند

  • تراکنش را بررسی کند

  • کالا را تحویل دهد

  • گزارش عملکرد را ذخیره و ارسال کند

چرا ساخت وندینگ با آردوینو؟

  • هزینه بسیار کمتر از نمونه‌های تجاری

  • انعطاف‌پذیری بالا در انتخاب قطعات

  • امکان سفارشی‌سازی پرداخت، سیستم مانیتورینگ و UI

  • مناسب برای کارآفرینی و پروژه‌های استارتاپی

  • قابلیت یکپارچه‌سازی کامل با IoT

اجزای اصلی دستگاه کدام ها هستند ؟

برد آردوینو

بهترین گزینه‌ها:

  • Arduino Mega (به دلیل پین‌های زیاد و حافظه بیشتر)

  • Arduino Uno (برای نسخه‌های ساده‌تر)

البته برای کار حرفه ای تر میتوان از برد های ESP32 یا حتی از برد های رزبری پای کمک گرفت.

ماژول ارتباطی WiFi

  • ESP8266: گزینه اقتصادی و قابل اعتماد

  • ESP32: سریع‌تر، دو هسته‌ای و دارای Bluetooth

  • ماژول وای فای آردوینو

موتورهای مکانیکی

برای رهاسازی کالا:

  • استپر موتور به همراه درایور راه انداز

  • سروو موتور SG90 یا MG90S (برای درب‌ها یا سیستم‌های خطی)

نمایشگر

برای نشان دادن:

  • قیمت

  • QR پرداخت

  • وضعیت سفارش
    بهترین انتخاب‌: LCD 20×4 OLED 1.3

سنسورها

  • سنسور نوری فتودیود (Photodiode / IR Break Beam) برای تشخیص خروج کالا

  • سنسور وزن لودسل برای تشخیص موجودی

  • میکروسوئیچ‌ها برای ایمنی مکانیکی

منبع تغذیه

  • 12 ولت برای موتور

  • 5 ولت برای آردوینو و ماژول WiFi

  • استفاده از Buck Converter توصیه می‌شود

طراحی مکانیزم رهاسازی کالا

روش‌های تخصصی مکانیکی

سیستم حلزونی (Spiral System)

پرکاربردترین روش در وندینگ‌های حرفه‌ای.
ویژگی‌ها:

  • هزینه ساخت کم

  • مناسب برای تنقلات و بسته‌های کوچک

  • قابلیت کنترل دقیق با موتور استپر

سیستم Drop Door با سروو

مناسب برای کالاهای بزرگ‌تر

  • یک درب با سروو باز می‌شود

  • کالا از فضای شیب‌دار سقوط می‌کند

سیستم Push-Arm

بازوی مکانیکی کالا را به بیرون هل می‌دهد

  • مناسب برای کالاهای مستطیلی شکل

اتصال قطعات الکترونیکی

اتصال موتور به آردوینو

موتور استپر → درایور → Arduino

  • استفاده از A4988 و DRV8825 توصیه می‌شود

  • جریان موتور باید Set شود

  • اضافه کردن خنک‌کننده ضروری است

اتصال نمایشگر

LCD با I2C فقط دو پین لازم دارد:

  • SDA

  • SCL

اتصال ماژول WiFi

دو روش وجود دارد:

  • اتصال ESP8266 از طریق Serial

  • استفاده از ESP32 به عنوان برد اصلی

اتصال سنسورها

  • سنسور نوری در مسیر خروج کالا

  • میکروسوئیچ برای Limit

برنامه‌نویسی کامل دستگاه

وظایف اصلی برنامه

  • مدیریت کالاها

  • اتصال به اینترنت

  • ارتباط با سرور پرداخت

  • رهاسازی کالا

  • آپدیت موجودی

  • ثبت گزارش فروش

ساختار نرم‌افزار پیشنهادی

 

Setup:
- Init Display
- Connect WiFi
- Load Products
- Start Sensors

Loop:
- Wait for selection
- Generate order ID
- Request payment link
- Display QR
- Check payment status
- If success: Release product
- Log transaction

کتابخانه‌های ضروری

  • WiFiClient

  • HTTPClient

  • ArduinoJson

  • Servo / AccelStepper

  • Wire

اتصال به اینترنت و مدیریت شبکه

اتصال WiFi

  • ذخیره رمز شبکه در EEPROM

  • بررسی Connection در Loop

  • ارسال پینگ دوره‌ای برای بررسی ارتباط

ارتباط با سرور

ارسال درخواست‌های HTTP:

  • POST برای ایجاد سفارش

  • GET برای بررسی پرداخت

  • POST برای ثبت گزارش فروش

تمام داده‌ها باید JSON باشند.

 در ادامه یک فایل برنامه کامل و کاربردی (برای برد ESP32) برای پیاده‌سازی منطق اصلی یک دستگاه فروش خودکار با پرداخت آنلاین را آماده کردم. این برنامه شامل:

  • اتصال به وای‌فای

  • ساخت شناسه سفارش و ارسالِ درخواست ایجاد سفارش به سرور شما

  • نمایش لینک پرداخت و تولید و نمایش QR Code روی نمایشگر OLED

  • بررسی وضعیت پرداخت (Polling) از سرور

  • رهاسازی کالا با موتور استپر (A4988/DRV8825)

  • بررسی خروج کالا با سنسور نوری

  • ثبت لاگ ساده در سریال

نکته مهم: برنامه فرض می‌کند که شما یک سرور واسط دارید که با درگاه پرداخت ایرانی (مثلاً زرین‌پال، آیدی‌پی و غیره) ارتباط برقرار می‌کند. سرور باید APIهای زیر را پیاده‌سازی کند:

  1. POST /create_order که order_id یکتا و payment_url برمی‌گرداند.

  2. GET /order_status?order_id=... که وضعیت تراکنش (pending, paid, failed) را برمی‌گرداند.
    سرور همچنین می‌تواند از وبهوک درگاه برای تأیید سریع‌تر استفاده کند؛ این نمونه از Polling استفاده می‌کند تا ساده باشد.


پیش‌نیازها / کتابخانه‌ها

در Arduino IDE یا PlatformIO نصب کنید:

  • WiFi.h (در ESP32 SDK هست)

  • HTTPClient.h

  • ArduinoJson (برای JSON)

  • Adafruit SSD1306 و Adafruit GFX (برای OLED 128x64)

  • AccelStepper (برای استپر) یا Servo اگر از سروو استفاده می‌کنید

  • qrcode یا qrcodegen (کتابخانه تولید QR به ماتریس) — در این کد از یک نسخه سادهٔ qrcode برای تولید ماتریس QR استفاده شده است. اگر در IDE ندارید، می‌توانید از کتابخانه‌های متن‌باز مشابه استفاده کنید.


اتصالات سخت‌افزاری (پیشنهادی)

  • ESP32 VIN/GND → منبع تغذیه 5V/0V (مطابق برد)

  • OLED (I2C) SDA → GPIO21، SCL → GPIO22

  • استپر درایور (A4988/DRV8825): STEP → GPIO 26، DIR → GPIO 27، ENABLE → GPIO 14

  • سنسور IR (Break-beam) خروج کالا → دیجیتال GPIO 34 (ورودی)

  • دکمه انتخاب کالا (یا کیپد) → GPIO 32، 33 ... (در کد نمونه از 1 دکمه ساده استفاده شده)

  • (اختیاری) لامپ/LED/رله وضعیت → GPIO 13

جریان و تغذیه موتور را جداگانه تأمین کنید و GND مشترک بین منبع موتور و ESP32 را برقرار کنید.

/* Vending Machine - ESP32 (WiFi + Payment + Stepper + OLED + QR)
   - Requires: ArduinoJson, Adafruit_SSD1306, Adafruit_GFX, AccelStepper, qrcode (or qrcodegen)
   - Hardware assumptions: ESP32, SSD1306 128x64 I2C, stepper driver (STEP/DIR), IR sensor for product detection.
   - Note: Replace SERVER_URL and API_KEY placeholders with your backend info.
*/

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#include <AccelStepper.h>

// QR code generator: include your QR library here.
// Example uses a simple qrcodegen library interface - adjust if your library differs.
#include "qrcodegen.h" // If using the qrcodegen library by Nayuki (rename appropriately)

//////////////////////
// Configuration
//////////////////////
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";

const char* SERVER_BASE = "https://your-backend.example.com"; // بدون اسلش انتهایی
const char* API_CREATE_ORDER = "/create_order";
const char* API_ORDER_STATUS = "/order_status"; // expect GET ?order_id=...

const char* DEVICE_ID = "vending_esp32_01"; // شناسه دستگاه برای سرور

// Hardware pins
#define OLED_SDA 21
#define OLED_SCL 22
#define OLED_RST -1 // -1 اگر RST متصل نیست

#define STEPPER_STEP_PIN 26
#define STEPPER_DIR_PIN 27
#define STEPPER_ENABLE_PIN 14

#define IR_SENSOR_PIN 34 // ورودی آنالوگ/دیجیتال خواندنی (اینجا دیجیتال)
#define SELECT_BUTTON_PIN 32
#define LED_STATUS_PIN 13

// Stepper configuration
#define STEPS_PER_PRODUCT 200 // عدد مراحل لازم برای تحویل یک کالا - باید تنظیم شود
#define MAX_SPEED 1000
#define ACCEL 500

// OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST);

// Stepper
AccelStepper stepper(AccelStepper::DRIVER, STEPPER_STEP_PIN, STEPPER_DIR_PIN);

// Product definitions (نمونه ساده)
struct Product {
  const char* id;
  const char* name;
  int price; // تومان یا واحد مورد نظر
  int stepsToRelease; // میزان حرکت استپر برای آزادسازی این محصول
  int stock;
};

Product products[] = {
  {"A1", "شکلات", 8000, STEPS_PER_PRODUCT, 10},
  {"A2", "چیپس", 7000, STEPS_PER_PRODUCT, 10},
  {"A3", "آب", 5000, STEPS_PER_PRODUCT, 10},
};

const int productCount = sizeof(products)/sizeof(products[0]);

// Runtime state
String currentOrderId = "";
int selectedProductIndex = 0;
bool waitingForPayment = false;
unsigned long paymentRequestTime = 0;
const unsigned long PAYMENT_TIMEOUT_MS = 120000; // 2 دقیقه

//////////////////////
// Utility Functions
//////////////////////

void showMessage(const char* line1, const char* line2 = NULL, const char* line3 = NULL) {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0);
  display.println(line1);
  if (line2) display.println(line2);
  if (line3) display.println(line3);
  display.display();
}

void showProductList() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0);
  for (int i=0;i<productCount;i++) {
    if (i == selectedProductIndex) display.print("> ");
    else display.print("  ");
    display.print(i+1);
    display.print(". ");
    display.print(products[i].name);
    display.print(" - ");
    display.print(products[i].price);
    display.println("ت");
  }
  display.display();
}

void setupStepper() {
  pinMode(STEPPER_ENABLE_PIN, OUTPUT);
  digitalWrite(STEPPER_ENABLE_PIN, HIGH); // disable initially (active low usually)
  stepper.setMaxSpeed(MAX_SPEED);
  stepper.setAcceleration(ACCEL);
}

void enableStepper(bool en) {
  digitalWrite(STEPPER_ENABLE_PIN, en ? LOW : HIGH); // depends on your driver; LOW = enabled for many
}

String httpPostCreateOrder(const char* productId, int price) {
  // Builds JSON: { device_id, product_id, price }
  // Sends POST to SERVER_BASE + API_CREATE_ORDER
  HTTPClient http;
  String url = String(SERVER_BASE) + String(API_CREATE_ORDER);
  http.begin(url);
  http.addHeader("Content-Type", "application/json");
  // Optional: add API key header
  // http.addHeader("Authorization", "Bearer YOUR_KEY");

  StaticJsonDocument<256> doc;
  doc["device_id"] = DEVICE_ID;
  doc["product_id"] = productId;
  doc["price"] = price;

  String payload;
  serializeJson(doc, payload);

  int httpCode = http.POST(payload);
  if (httpCode != 200) {
    Serial.printf("CreateOrder HTTP error: %d\n", httpCode);
    http.end();
    return "";
  }

  String resp = http.getString();
  http.end();

  // Parse response: expect { order_id: "...", payment_url: "..." }
  StaticJsonDocument<512> respDoc;
  DeserializationError err = deserializeJson(respDoc, resp);
  if (err) {
    Serial.println("JSON parse error on createOrder");
    return "";
  }

  const char* orderId = respDoc["order_id"] | "";
  const char* paymentUrl = respDoc["payment_url"] | "";

  if (strlen(orderId) == 0 || strlen(paymentUrl) == 0) {
    Serial.println("Invalid response from server createOrder");
    return "";
  }

  // Save order id in EEPROM or RAM; here just return paymentUrl but store orderId global
  currentOrderId = String(orderId);

  // For convenience, store the payment URL in a file or global; we'll return it
  return String(paymentUrl);
}

String httpGetOrderStatus(const char* orderId) {
  HTTPClient http;
  String url = String(SERVER_BASE) + String(API_ORDER_STATUS) + "?order_id=" + String(orderId);
  http.begin(url);
  int httpCode = http.GET();
  if (httpCode != 200) {
    Serial.printf("GetOrderStatus HTTP error: %d\n", httpCode);
    http.end();
    return "error";
  }
  String resp = http.getString();
  http.end();

  StaticJsonDocument<256> respDoc;
  DeserializationError err = deserializeJson(respDoc, resp);
  if (err) {
    Serial.println("JSON parse error on orderStatus");
    return "error";
  }
  const char* status = respDoc["status"] | "error";
  return String(status);
}

//////////////////////
// QR Display Helpers (using qrcodegen library by Nayuki as example)
//////////////////////

void drawQRCodeOnOLED(const String &text) {
  // adjust to the QR library you have. Here uses qrcodegen::QrCode
  using qrcodegen::QrCode;
  using qrcodegen::QrSegment;

  QrCode qr = QrCode::encodeText(text.c_str(), QrCode::Ecc::LOW);
  int size = qr.getSize(); // e.g., 21, 25, ... matrix size
  int scale = SCREEN_WIDTH / (size + 2); // leave margin
  if (scale <= 0) scale = 1;
  int margin = (SCREEN_WIDTH - size*scale) / 2;

  display.clearDisplay();
  // Draw QR
  for (int y = 0; y < size; y++) {
    for (int x = 0; x < size; x++) {
      bool module = qr.getModule(x, y); // black or white
      if (module) {
        int rx = margin + x*scale;
        int ry = margin + y*scale;
        display.fillRect(rx, ry, scale, scale, SSD1306_WHITE);
      }
    }
  }
  // Optional: print short URL below if fits
  display.display();
}

//////////////////////
// Product release + sensor
//////////////////////

bool waitForProductDrop(unsigned long timeoutMs) {
  unsigned long start = millis();
  while (millis() - start < timeoutMs) {
    int val = digitalRead(IR_SENSOR_PIN);
    // sensor logic: assume LOW when beam broken (product passed)
    if (val == LOW) {
      Serial.println("Product detected by IR sensor.");
      return true;
    }
    delay(50);
  }
  return false;
}

bool releaseProduct(int stepsToMove) {
  enableStepper(true);
  // rotate in appropriate direction
  stepper.moveTo(stepper.currentPosition() + stepsToMove);
  while (stepper.distanceToGo() != 0) {
    stepper.run();
  }
  enableStepper(false);
  return true;
}

//////////////////////
// Setup & Loop
//////////////////////

void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting to WiFi");
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 30) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  Serial.println();
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("Connected. IP: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("WiFi not connected.");
  }
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP);
  pinMode(IR_SENSOR_PIN, INPUT_PULLUP);
  pinMode(LED_STATUS_PIN, OUTPUT);
  digitalWrite(LED_STATUS_PIN, LOW);

  // OLED init
  Wire.begin(OLED_SDA, OLED_SCL);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("SSD1306 allocation failed");
    for(;;); // stop
  }
  display.clearDisplay();
  display.display();

  showMessage("Vending Machine", "Initializing...");

  // Stepper
  setupStepper();

  // WiFi
  connectWiFi();

  delay(500);
  showProductList();
  Serial.println("Setup complete.");
}

unsigned long lastPollTime = 0;
const unsigned long POLL_INTERVAL_MS = 3000;

void loop() {
  // Simple UI: use SELECT_BUTTON_PIN to cycle selection and long press to buy
  static unsigned long btnLast = 0;
  static bool btnPrevState = HIGH;
  bool btnState = digitalRead(SELECT_BUTTON_PIN);
  if (btnState == LOW && btnPrevState == HIGH) {
    // button pressed - short press = next product
    btnLast = millis();
  }
  if (btnState == HIGH && btnPrevState == LOW) {
    unsigned long pressLen = millis() - btnLast;
    if (pressLen < 800) {
      // short press
      selectedProductIndex = (selectedProductIndex + 1) % productCount;
      showProductList();
    } else {
      // long press => buy
      if (products[selectedProductIndex].stock <= 0) {
        showMessage("محصول ناموجود است", products[selectedProductIndex].name);
        delay(1500);
        showProductList();
      } else {
        // Start purchase flow
        Serial.printf("Start purchase: %s\n", products[selectedProductIndex].id);
        digitalWrite(LED_STATUS_PIN, HIGH);
        showMessage("در حال ایجاد سفارش...", products[selectedProductIndex].name);
        String paymentUrl = httpPostCreateOrder(products[selectedProductIndex].id, products[selectedProductIndex].price);
        if (paymentUrl.length() == 0) {
          showMessage("خطا در ایجاد سفارش", "لطفا دوباره تلاش کنید");
          digitalWrite(LED_STATUS_PIN, LOW);
          delay(2000);
          showProductList();
        } else {
          // Show QR and URL
          showMessage("لطفا پرداخت کنید", "لینک در OLED نمایش داده شده");
          delay(800);
          drawQRCodeOnOLED(paymentUrl); // show big QR
          // Also print URL to serial
          Serial.println("Payment URL:");
          Serial.println(paymentUrl);
          waitingForPayment = true;
          paymentRequestTime = millis();
          // Polling loop for payment
          while (waitingForPayment) {
            if (millis() - paymentRequestTime > PAYMENT_TIMEOUT_MS) {
              showMessage("زمان پرداخت تمام شد", "سفارش لغو شد");
              waitingForPayment = false;
              currentOrderId = "";
              digitalWrite(LED_STATUS_PIN, LOW);
              delay(1500);
              showProductList();
              break;
            }
            if (millis() - lastPollTime >= POLL_INTERVAL_MS) {
              lastPollTime = millis();
              if (currentOrderId.length() == 0) {
                Serial.println("No order id? abort.");
                waitingForPayment = false;
                break;
              }
              String status = httpGetOrderStatus(currentOrderId.c_str());
              Serial.printf("Order status: %s\n", status.c_str());
              if (status == "paid") {
                // Release product
                showMessage("پرداخت موفق!", "در حال تحویل کالا...");
                bool released = releaseProduct(products[selectedProductIndex].stepsToRelease);
                if (!released) {
                  showMessage("خطا در تحویل", "لطفا تماس بگیرید");
                } else {
                  // wait sensor
                  bool dropped = waitForProductDrop(10000); // 10s
                  if (dropped) {
                    showMessage("تحویل با موفقیت انجام شد", products[selectedProductIndex].name);
                    products[selectedProductIndex].stock -= 1;
                  } else {
                    showMessage("تحویل ناموفق", "بازگشت وجه یا کمک");
                    // Optionally trigger refund flow via server
                  }
                }
                waitingForPayment = false;
                currentOrderId = "";
                digitalWrite(LED_STATUS_PIN, LOW);
                delay(1500);
                showProductList();
                break;
              } else if (status == "failed") {
                showMessage("پرداخت ناموفق", "لطفا دوباره تلاش کنید");
                waitingForPayment = false;
                currentOrderId = "";
                digitalWrite(LED_STATUS_PIN, LOW);
                delay(1500);
                showProductList();
                break;
              } else {
                // still pending - show waiting message maybe with countdown
                // we already display QR; update can be skipped
              }
            }
            delay(100);
          } // end polling while
        } // end if paymentUrl
      } // end else stock
    } // end long press branch
  } // end button release handling

  btnPrevState = btnState;

  // small delay to debounce
  delay(50);
}

توضیحات تکمیلی و نکات مهم

  1. سرور واسط لازم است — این کد خود درگاه پرداخت را صدا نمی‌زند؛ برای امنیت و سادگی باید از یک backend استفاده کنید که با درگاه بانکی قرارداد دارد و payment_url را تولید می‌کند.

  2. کتابخانهٔ QR — من در کد از qrcodegen (نسخهٔ Nayuki) به‌عنوان مثال استفاده کردم. ممکن است در محیط Arduino IDE لازم باشد نسخه‌ای از کتابخانه QR را نصب یا متناسب‌سازی کنید. برخی کتابخانه‌ها خروجی بایت‌مپ می‌دهند که باید روی OLED رسم شود (همان‌طور که در تابع drawQRCodeOnOLED نشان داده‌شده).

  3. تنظیمات استپر — مقدار STEPS_PER_PRODUCT و تنظیمات MAX_SPEED و ACCEL باید براساس مکانیک شما تنظیم شوند. همچنین بسیاری از درایورها برای فعال/غیرفعال کردن ENABLE حساسیت متفاوتی دارند (پایه فعال-پایین یا فعال-بالا).

  4. امنیت — برای ارتباط با سرور از HTTPS استفاده کنید. اگر سرور شما دارای گواهی خودامضا است، باید fingerprint یا certificate را مدیریت کنید. برای درخواست‌های حساس از توکن/Authorization استفاده کنید.

  5. مدیریت خطا — سناریوهای مختلف خطا (عدم اتصال WiFi، خطای سرور، پرداخت ناقص، گیر کردن کالا) باید در نسخه نهایی پوشش داده شود.

  6. لاگ‌ها و پایگاه داده — پیشنهاد می‌شود لاگ تراکنش‌ها را هم روی سرور و هم به‌صورت محلی (در EEPROM یا SD) ذخیره کنید تا در صورت بروز مشکل قابل پیگیری باشد.

  7. ریفاند خودکار — اگر دستگاه نتواند کالا را تحویل دهد بعد از پرداخت، باید مسیر ریفاند (از طریق سرور و درگاه) پیاده شود.

سیستم پرداخت آنلاین (مهم‌ترین بخش)

مراحل کامل پردازش پرداخت

انتخاب کالا

کاربر از روی صفحه کلید، دکمه، تاچ یا NFC کالای مورد نظر را انتخاب می‌کند.

ایجاد شناسه سفارش

آردوینو یک ID یکتا تولید می‌کند و به سرور ارسال می‌کند.

ساخت لینک پرداخت

سرور شما با استفاده از API درگاه:

  • زرین‌پال

  • آیدی‌پی

  • نکست‌پی

  • پی‌پینگ

لینک پرداخت می‌سازد.

نمایش QR پرداخت

آردوینو QR Code رابط پرداخت را روی نمایشگر نشان می‌دهد.

بررسی تراکنش

پس از پرداخت:

  • درگاه به سرور شما Webhook ارسال می‌کند

  • سرور نتیجه را به آردوینو می‌دهد

تحویل کالا

در صورت پرداخت موفق:

  • موتور فعال می‌شود

  • کالا آزاد می‌شود

  • سنسور خروج کالا را تأیید می‌کند

اگر کالا خارج نشود:

  • وجه قابل برگشت است

  • دستگاه پیام خطا نمایش می‌دهد

مدیریت پیشرفته موجودی

روش‌های تشخیص موجودی

شمارش دیجیتالی

بعد از هر تحویل، یک واحد کم می‌شود
(ساده اما ممکن است خطا داشته باشد)

سنسور وزن

برای کالاهای بسته‌بندی‌شده مناسب است

  • دقت بالا

  • نیازمند Load Cell و ماژول HX711

سنسور نوری

تشخیص وجود یا نبود کالا در هر قفسه

امنیت و حفاظت

امنیت پرداخت

  • استفاده از HTTPS

  • تأیید امضای پاسخ‌های درگاه

  • جلوگیری از Replay Attack

امنیت سخت‌افزاری

  • جلوگیری از تکان شدید

  • درب قفل‌شونده با آهنربای الکترومغناطیسی

  • استفاده از پوشش فلزی ضد ضربه

امنیت شبکه

  • جلوگیری از ارسال درخواست تقلبی

  • Rate Limit برای API

  • استفاده از Token

تست نهایی

چک‌لیست کامل:

  • تست هر موتور جداگانه

  • تست اتصال WiFi در چند محیط مختلف

  • تست پرداخت واقعی

  • تست خروج کالا تحت بار ۳۰ تا ۵۰%

  • تست قطع اینترنت در زمان خرید

  • تست سنسورهای ایمنی

توسعه‌های پیشنهادی آینده

امکانات حرفه‌ای برای نسخه تجاری:

  • اپلیکیشن موبایل برای مدیریت

  • پرداخت NFC و RFID

  • پشتیبانی از کیف پول

  • فروش با تخفیف هوشمند

  • تشخیص چهره برای احراز هویت

  • نمایشگر صنعتی ۱۰ اینچ

  • دوربین داخلی

  • ارسال گزارش فروش به تلگرام یا واتساپ

  • سیستم هوشمند پیش‌بینی فروش (AI)

جمع‌بندی نهایی

ساخت یک وندینگ‌ماشین هوشمند با آردوینو پروژه‌ای کاملاً عملی و قابل تجاری‌سازی است.
با افزودن WiFi + درگاه پرداخت آنلاین دستگاه شما به سطح کاملاً حرفه‌ای می‌رسد و می‌تواند در هر مکانی مثل:

  • مدارس

  • باشگاه‌ها

  • فروشگاه‌ها

  • دانشگاه

  • پارک‌ها

مورد استفاده قرار گیرد.

سخن نهایی

امیدوارم این مطلب شروع یک ایده جذاب براتون بوده باشه ایده فروش خودکار و هوشمند با استفاده از آردوینو اگر تمایل دارید برای مرحله دوم این پروژه می‌توانیم موارد زیر رو نیز تکمیل کنیم، برای ما در قسمت نظرات بنویسید حتما که چه موردی رو مد نظر دارید :
✅ دیاگرام سیم‌کشی
✅ کد کامل آردوینو
✅ طراحی بدنه سه‌بعدی (STL)

✅ سایر ...

منتظر قسمت دوم باشید. با تشکر از همراهی شما

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *