Arduino Elektronik Örnekler ve Projeler

ESP8266 NodeMCU Web Sunucusu: Step Motor Kontrolü (WebSocket)

Bu kılavuzda, bir step motoru kontrol etmek için bir web sayfası görüntüleyen ESP8266 NodeMCU kartı ile bir web sunucusunun nasıl oluşturulacağını öğreneceksiniz. Web sayfası, adım sayısını girmenize ve saat yönünde veya saat yönünün tersine yön seçmenize olanak tanır. Ek olarak, motorun o anda dönüp dönmediğini veya durup durmadığını da gösterir. İstemci ile sunucu arasındaki iletişim WebSocket protokolü ile sağlanır. Tüm istemciler mevcut motor durumuyla güncellenir.


Gerekli Malzemeler;
  • 28BYJ-48 Step Motor + ULN2003 Motor Sürücü
  • ESP8266
  • Bağlantı Kabloları
  • 5V Adaptör

Dosya Sistemi Yükleme Eklentisi;

Bu projeyi ESP8266 dosya sistemine (LittleFS) oluşturmak için gereken HTML, CSS ve JavaScript dosyalarını yüklemek için Arduino IDE: LittleFS Dosya Sistemi yükleyicisi için bir eklenti kullanacağız  . Henüz yapmadıysanız, dosya sistemi yükleyici eklentisini kurmak için sonraki öğreticiyi izleyin.

Arduino IDE’de ESP8266 NodeMCU LittleFS Dosya Sistemi Yükleyicisini Kurun.


Kütüphaneler;

Bir mikrodenetleyici ile step motorları kontrol etmenin farklı yolları vardır. Step motoru ESP8266 ile kontrol etmek için AccelStepper kitaplığını kullanacağız . Bu kitaplık, motoru tanımlı sayıda adımla kolayca hareket ettirmenize, hızını, ivmesini ve çok daha fazlasını ayarlamanıza olanak tanır. Kütüphane, yöntemlerinin nasıl kullanılacağını açıklayan harika belgelere sahiptir. Buradan kontrol edebilirsiniz.

Kütüphaneyi Arduino IDE’nize kurmak için sonraki adımları izleyin.

  1. Git Sketch > Kütüphane Dahil > Kitaplıkları yönet …
  2. “Accelstepper” için arama yapın.
  3. Mike McCauley’nin AccelStepper kitaplığını kurun. 1.61.0 sürümünü kullanıyoruz.
AccelStepper Eklentisi
AccelStepper Eklentisi

Web sunucusunu oluşturmak için aşağıdaki kitaplıkları yüklemeniz gerekir:

ESPAsyncWebServer ve ESPAsynTCP kitaplıkları, Arduino Kitaplık Yöneticisi aracılığıyla kurulamaz. Kütüphane dosyalarını indirmek için önceki bağlantılara tıklamanız gerekir. Ardından, Arduino IDE’nizde  Sketch >  Include Library  >  Add .zip Library’ye gidin  ve az önce indirdiğiniz kitaplıkları seçin.


Şematik Diyagram;

Aşağıdaki şematik diyagram, step motor ile ESP8266 arasındaki bağlantıları göstermektedir.

ESP8266 * Step Motor Sürücü Bağlantısı
ESP8266 * Step Motor Sürücü Bağlantısı

Not: ULN2003 motor sürücüsüne harici bir 5V güç kaynağı kullanarak güç vermelisiniz.

Motor sürücüsüESP8266
IN1GPIO 5
IN2GPIO 4
IN3GPIO 14
IN4GPIO 12
Projeye Genel Bakış;

Aşağıdaki resim, bu proje için oluşturacağınız web sayfasını göstermektedir.

Web Arayüz -
Web Arayüz –
  • Web sayfası, motorun hareket etmesini istediğiniz adım sayısını girebileceğiniz ve yönü seçebileceğiniz bir form gösterir: saat yönünde veya saat yönünün tersine.
  • Ayrıca motor durumunu da gösterir: motor dönüyor veya motor duruyor . Ek olarak, motor döndüğü sürece dönen bir dişli simgesi vardır. Dişli, seçilen yöne göre saat yönünde veya saat yönünün tersine döner.
Client - server Bağlantısı
Client – server Bağlantısı
  • Sunucu ve istemci, WebSocket protokolünü kullanarak iletişim kurar.
  • GO’ya tıkladığınızda düğmesine bastığınızda, WebSocket protokolü aracılığıyla tüm bilgileri içeren bir mesaj gönderen bir Javascript işlevini çağırır: adımlar ve yön ( 3 ). Mesaj aşağıdaki formattadır:
steps&direction

Yani 2000 adım ve saat yönünü gönderirseniz, aşağıdaki mesajı gönderecektir:

2000&CW
  • Aynı zamanda web sayfasındaki motor durumunu değiştirecek ve dişli doğru yönde dönmeye başlayacaktır ( 2 ).
  • Ardından sunucu mesajı ( 4 ) alır ve motoru buna göre döndürür ( 5 ).
  • Motor dönmeyi bıraktığında ( 6 ), ESP istemci(ler)e yine WebSocket protokolü aracılığıyla motorun durduğunu bildiren bir mesaj gönderir ( 7 ).
  • Müşteri(ler) bu talebi alır ve motor durumunu web sayfasında ( 8 ) günceller.
Dosyalarınızı Düzenleme;

ESP dosya sistemine yüklemek istediğiniz dosyalar adlı bir klasöre yerleştirilmelidir. veriproje klasörü altında. Üç dosyayı bu klasöre taşıyacağız:

  • index.html web sayfasını oluşturmak için;
  • stil.css web sayfasını biçimlendirmek için;
  • script.js websocket iletişimini yönetmek ve dişli animasyonunu başlatmak/durdurmak için.
Dosyalar
Dosyalar

HTML, CSS ve JavaScript dosyalarını adlı bir klasöre kaydetmelisiniz. veriönceki şemada gösterildiği gibi Arduino eskiz klasörünün içinde. Bu dosyaları ESP8266 dosya sistemine (LittleFS) yükleyeceğiz.


HTML Dosyası;
<!DOCTYPE html>
<html>
<head>
  <title>Stepper Motor</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" href="style.css">
  <link rel="icon" type="image/png" href="favicon.png">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
</head>
<body>
  <div class="topnav">
    <h1>Stepper Motor Control <i class="fas fa-cogs"></i></h1>
  </div>
  <div class="content">
        <form>
          <input type="radio" id="CW" name="direction" value="CW" checked>
          <label for="CW">Clockwise</label>
          <input type="radio" id="CCW" name="direction" value="CCW">
          <label for="CW">Counterclockwise</label><br><br><br>
          <label for="steps">Number of steps:</label>
          <input type="number" id="steps" name="steps">
        </form>
        <button onclick="submitForm()">GO!</button>
        <p>Motor state: <span id="motor-state">Stopped</span></p>
        <p><i id="gear" class="fas fa-cog"></i> </p>

  </div>
</body>
<script src="script.js"></script>
</html>

JavaScript kullanarak değiştirmek istediğimiz HTML öğelerine kimlikler ekledik; radyo düğmeleri ve giriş alanı:

  • saat yönünde radyo düğmesi: id=”CW”
  • ters yönde radyo düğmesi: id=”CCW”
  • adımlar giriş alanı: id=”steps”
<input type="radio" id="CW" name="direction" value="CW" checked>
<label for="CW">Clockwise</label>
<input type="radio" id="CCW" name="direction" value="CCW">
<label for="CW">Counterclockwise</label><br><br><br>
<label for="steps">Number of steps:</label>
<input type="number" id="steps" name="steps">

Form sonuçlarını WebSocket protokolü ile sunucuya (ESP8266) göndermek istiyoruz. Bu yüzden, tıklandığında (onclick) çağırır. SubmitForm() JavaScript bölümünde daha sonra göreceğiniz gibi sonuçları sunucuya gönderen kullanıcı tanımlı javascript işlevi.

<button onclick="submitForm()">GO!</button>

Ayrıca, motor durumunu görüntülemek için bir paragraf da ekledik. iki <span> arasındaki Motor-State id sindeki metni değiştirebilirsiniz. Javascript tetiklemektedir.

<p>Motor state: <span id="motor-state">Stopped</span></p>

Son olarak, bir dişliyi gösteren bir paragraf var. id=”gear”. Dişliyi hareket ettirmek için bu kimliğe ihtiyacımız var.

<p><i id="gear" class="fas fa-cog"></i> </p>

JavaScript dosyasına başvurmanız gerektiğini unutmayın (Script.js) aşağıdaki gibi HTML dosyasında:

<script src="script.js"></script>

CSS Dosyası;

Style.css  adlı bir dosya oluşturun aşağıdaki içerikleri ekleyin.

html {
  font-family: Arial, Helvetica, sans-serif;
}

h1 {
  font-size: 1.8rem;
  color: white;
}

p{
  font-size: 20px;
  text-align: center;
}

.topnav {
  overflow: hidden;
  background-color: #0A1128;
  text-align: center;
}

body {
  margin: 0;
}

.content {
  padding: 20px;
  max-width: max-content;
  margin: 0 auto;
}

input[type=number], select {
  width: 100%;
  padding: 12px 20px;
  margin: 8px 0;
  display: inline-block;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}

form{
  border-radius: 5px;
  background-color: #f2f2f2;
  padding: 20px;
}

button {
  background-color: #034078;
  border: none;
  padding: 14px 20px;
  text-align: center;
  font-size: 20px;
  border-radius: 4px;
  transition-duration: 0.4s;
  width: 100%;
  color: white;
  cursor: pointer;
}

button:hover {
    background-color: #1282A2;
}

input[type="radio"] {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border-radius: 50%;
  width: 16px;
  height: 16px;
  border: 2px solid #999;
  transition: 0.2s all linear;
  margin-right: 5px;
  position: relative;
  top: 4px;
  }

input[type="radio"]:checked{
  border: 6px solid #1282A2;
}

#motor-state{
  font-weight: bold;
  color: red;
}

#gear{
  font-size:100px;
  color:#2d3031cb;
}

.spin {
  -webkit-animation:spin 4s linear infinite;
  -moz-animation:spin 4s linear infinite;
  animation:spin 4s linear infinite;
}

.spin-back {
  -webkit-animation:spin-back 4s linear infinite;
  -moz-animation:spin-back 4s linear infinite;
  animation:spin-back 4s linear infinite;
}

@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }

@-moz-keyframes spin-back { 100% { -moz-transform: rotate(-360deg); } }
@-webkit-keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); } }
@keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); transform:rotate(-360deg); } }

Motor durumu metnini yazı tipi ağırlığını biçimlendiriyoruz (bold) ve renk (red). CSS’de belirli bir kimliğe atıfta bulunmak için şunu kullanın: (ÖRn: #motor durumu).

#motor-state{
  font-weight: bold;
  color: red;
}

Aşağıdaki satırlar gear simgesinin rengini ve boyutunu biçimlendirir; kimliğinin gear, bu yüzden ona atıfta bulunuyoruz #gear:

#gear{
  font-size:100px;
  color:#2d3031cb;
}

Ardından, iki sınıfı biçimlendiriyoruz döndürmek ve geri dönüşhenüz herhangi bir HTML öğesine atfedilmemiş. atfedeceğizdöndürmek ve geri dönüş motor hareket etmeye başladığında JavaScript kullanarak dişliye sınıflar.

Bu sınıflar, animasyondişliyi döndürme özelliği. hakkında daha fazla bilgi edinmek içinanimasyonmülk çalışır, bu hızlı eğiticiye bir göz atmanızı öneririz.

.spin {
  -webkit-animation:spin 4s linear infinite;
  -moz-animation:spin 4s linear infinite;
  animation:spin 4s linear infinite;
}

.spin-back {
  -webkit-animation:spin-back 4s linear infinite;
  -moz-animation:spin-back 4s linear infinite;
  animation:spin-back 4s linear infinite;
}

@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }

@-moz-keyframes spin-back { 100% { -moz-transform: rotate(-360deg); } }
@-webkit-keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); } }
@keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); transform:rotate(-360deg); } }

JavaScript Dosyası;

Script.js adlı bir dosya oluşturun  aşağıdaki içerikleri ekleyiniz.

var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onload);
var direction;

function onload(event) {
    initWebSocket();
}

function initWebSocket() {
    console.log('Trying to open a WebSocket connection…');
    websocket = new WebSocket(gateway);
    websocket.onopen = onOpen;
    websocket.onclose = onClose;
    websocket.onmessage = onMessage;
}

function onOpen(event) {
    console.log('Connection opened');
}

function onClose(event) {
    console.log('Connection closed');
    document.getElementById("motor-state").innerHTML = "motor stopped"
    setTimeout(initWebSocket, 2000);
}

function submitForm(){
    const rbs = document.querySelectorAll('input[name="direction"]');
    direction;
    for (const rb of rbs) {
        if (rb.checked) {
            direction = rb.value;
            break;
        }
    }

    document.getElementById("motor-state").innerHTML = "motor spinning...";
    document.getElementById("motor-state").style.color = "blue";
    if (direction=="CW"){
        document.getElementById("gear").classList.add("spin");
    }
    else{
        document.getElementById("gear").classList.add("spin-back");
    }
    
    var steps = document.getElementById("steps").value;
    websocket.send(steps+"&"+direction);
}

function onMessage(event) {
    console.log(event.data);
    direction = event.data;
    if (direction=="stop"){ 
      document.getElementById("motor-state").innerHTML = "motor stopped"
      document.getElementById("motor-state").style.color = "red";
      document.getElementById("gear").classList.remove("spin", "spin-back");
    }
    else if(direction=="CW" || direction=="CCW"){
        document.getElementById("motor-state").innerHTML = "motor spinning...";
        document.getElementById("motor-state").style.color = "blue";
        if (direction=="CW"){
            document.getElementById("gear").classList.add("spin");
        }
        else{
            document.getElementById("gear").classList.add("spin-back");
        }
    }
}

Bu proje için JavaScript’in nasıl çalıştığını görelim.

Gateway WebSocket arayüzüne giriş noktasıdır. window.location.hostname bilgisayar adı geçerli sayfa adresini alır (web sunucusu IP adresi)

var gateway = `ws://${window.location.hostname}/ws`;

 Websocket adlı yeni bir global değişken oluşturun.

var websocket;

Direction adlı başka bir global değişken oluşturun yön bu motorun mevcut yönünü tutacaktır: saat yönünde, saat yönünün tersine veya durdurulmuş.

var direction;

Onload ekliyoruz.

window.addEventListener('load',  onload);

Arduino Kroki

Aşağıdaki kodu Arduino IDE’ye kopyalayın. Ağ kimlik bilgilerinizi girin ve hemen çalışacaktır.

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <AccelStepper.h>

#define IN1 5
#define IN2 4
#define IN3 14
#define IN4 12
AccelStepper stepper(AccelStepper::HALF4WIRE, IN1, IN3, IN2, IN4);

String message = "";

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Create a WebSocket object
AsyncWebSocket ws("/ws");

//Variables to save values from HTML form
String direction ="STOP";
String steps;

bool notifyStop = false;

// Initialize LittleFS
void initFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
    Serial.println("LittleFS mounted successfully");
  }
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

void notifyClients(String state) {
  ws.textAll(state);
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    message = (char*)data;
    steps = message.substring(0, message.indexOf("&"));
    direction = message.substring(message.indexOf("&")+1, message.length());
    Serial.print("steps");
    Serial.println(steps);
    Serial.print("direction");
    Serial.println(direction);
    notifyClients(direction);
    notifyStop = true;
    if (direction == "CW"){
      Serial.print("CW");
      stepper.move(steps.toInt());
    }
    else{
      Serial.print("CCW");
      stepper.move(-steps.toInt());
    }
  }
}

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
  switch (type) {
    case WS_EVT_CONNECT:
      Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
      //Notify client of motor current state when it first connects
      notifyClients(direction);
      break;
    case WS_EVT_DISCONNECT:
      Serial.printf("WebSocket client #%u disconnected\n", client->id());
      break;
    case WS_EVT_DATA:
        handleWebSocketMessage(arg, data, len);
        break;
    case WS_EVT_PONG:
    case WS_EVT_ERROR:
     break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

void setup() {
  // Serial port for debugging purposes

  Serial.begin(115200);
  initWiFi();
  initWebSocket();
  initFS();
  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(100);

  // Web Server Root URL
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/index.html", "text/html");
  });
  
  server.serveStatic("/", LittleFS, "/");

  server.begin();
}

void loop() {
  if (stepper.distanceToGo() == 0 && notifyStop == true){  
    direction = "stop";
    notifyClients(direction);
    notifyStop = false;
  }
  ws.cleanupClients();
  stepper.run();
}

Kodu ve Dosyaları Yükle;

Ağ kimlik bilgilerinizi girdikten sonra kodu kaydedin. Çizim  >  Çizim Klasörünü Göster’e gidin  ve data adlı bir klasör oluşturun.

Şablonu Yükle - WebSocket
Şablonu Yükle – WebSocket

Bu klasörün içine HTML, CSS ve JavaScript dosyalarını kaydetmelisiniz.

Ardından, kodu ESP8266 kartınıza yükleyin. Doğru kartın ve COM bağlantı noktasının seçili olduğundan emin olun. Ayrıca, ağ kimlik bilgilerinizi eklediğinizden emin olun.

Kodu yükledikten sonra dosyaları yüklemeniz gerekir. Gidin  Araçlar  >  ESP8266 LittleFS Veri Yükleme  ve dosyalar için beklemek yüklenmesine.

ESP8266 LittleFS Data Upload
ESP8266 LittleFS Data Upload

Her şey başarıyla yüklendiğinde, Seri Monitörü 115200 baud hızında açın. ESP8266 RST düğmesine basın, ESP8266 IP adresini yazdırmalıdır.

Yerel ağınızda bir web tarayıcısı veya birden fazla web tarayıcı penceresi açın ve motoru kontrol etmek için web sayfasına erişin. Motoru kontrol etmek için formu gönderin.

Step Motor Kontrölcü
Step Motor Kontrölcü

Web sayfasındaki dişli doğru yönde dönmeye başlar ve fiziksel motor çalışmaya başlar.

Step Motor Sürücüsü ve Motor
Step Motor Sürücüsü ve Motor

Durduğunda web sayfasındaki vites ve motor durumu buna göre değişir.

4000 Adım
4000 Adım

Birden fazla istemcinin bağlı olması durumunda, tüm istemcilerin motor durumunu neredeyse anında güncellediklerine dikkat edin.

Related posts

IC LM324

Ömer Ersin

Arduino Kullanarak Çalar Saatli Radyo

Ömer Ersin

Robot için Kendi Joystick ‘inizi Yapın

Ömer Ersin