Wemos kết nối socket.io (phần 3)

Xin chào các bạn! Bài trước mình đã giới thiệu sơ qua về Wemos. Hôm nay, chúng ta cùng bắt tay vào việc lập trình phần mềm điều khiển em nó nhé.

I. Chuẩn bị công cụ

Việc đầu tiên chúng ta cần cài đặt công cụ để lập trình. Ở đây mình dùng Arduino IDE. Bạn có thể tải về bản Arduino IDE mới nhất tại đây.

Các bạn sẽ tự hỏi là em Wemos này có liên quan hay có họ hàng gì với arduino không mà lại sử dụng Arduino IDE?!! Thực chất 2 board này không có họ hàng gì đâu. Arduino có phần cứng bao gồm một board mạch nguồn mở được thiết kế trên nền tảng vi xử lý AVR Atmel 8bit, hoặc ARM Atmel 32-bit. Còn Wemos thì lại dùng module wifi esp8266. Ta dùng Arduino IDE vì đã có sẵn 1 thư viện core arduino viết cho esp8266 nói chung và Wemos nói riêng tại đây. Qua đó chúng ta có thể sử dụng được tất cả (không phải tất cả nhưng cũng hầu hết) các thư viện của arduino và cách viết chương trình sẽ theo phong cách của arduino. Phong cách này như thế nào thì ta sẽ cùng tìm hiểu ở phía dưới nhé. Nhìn sơ qua lượt Star trên github thì các bạn đã biết nó có nhiều người sử dụng như thế nào rồi phải không. 😃)

Sau khi cài đặt xong Arduino IDE, ta sẽ tiến hành cài đặt thư viện core arduino cho esp8266.

B1: Add board url

Vào File => Preferences. Add Boards manager link: http://arduino.esp8266.com/stable/package_esp8266com_index.json

B2: Install esp8266 platform

Vào Tools => Board => Boards Manager. Tìm kiếm và cài esp8266 platform

II. Giới thiệu giao diện Arduino IDE

Giao diện của Arduino IDE rất đơn giản và dễ sử dụng. Nó bao gồm các thành phần chính sau.

Thanh menu

  • File: Chứa các mục mở file, thêm mới, lưu file.
  • Edit: Chứa các mục liên quan đến edit source code copy, cut, paste,...
  • Sketch: Chứa các mục compile code, upload code (nạp chương trình cho chíp), manage library
  • Tools: Chứa các mục thực hiện việc chọn board, chọn port,...

Thanh công cụ

Trên thanh công cụ có một số các nút hay được sử dụng trong qúa trình viết và nạp code.

Icon của nó khá trực quan và từ trái sang phải chức năng của từng nút như sau:

  • Compile code
  • Upload code
  • New sketch
  • Save sketch
  • Open sketch

III. Chạy chương trình đầu tiên

Để nạp được code cho Wemos bạn cần phải chọn chọn đúng board wemos trong category esp8266. Ở đây mình dùng Wemos D1 mini. Và cần phải chọn đúng cổng serial mà thiết bị được cắm vào. Cái này thông thường trên Ubuntu sẽ là ttyUSB0 nếu bạn chỉ cắm 1 thiết bị (có trường hợp nó là ttyACM0, ttyUSB1). Để chắc chắn nó được cắm vào cổng nào thì bạn rút thiết bị ra rồi xem mặc định đã có cổng nào rồi lại cắm lại xem nó đã thêm cổng nào. Cổng thêm đó chính là cổng kết nối với Wemos.

Chú ý: Để nạp được chương trình cho board các bạn cần chú ý các điểm sau:

  • Chọn đúng board
  • Chọn đúng cổng COM
  • Phải chắc chắn là cổng nạp code của board không bị sử dụng bởi một thiết bị khác kể cả thiết bị phần cứng bên ngoài lẫn chương trình chạy trên máy tính. Giả sử bạn đang chạy lệnh để xem các log hiện ra trên serial monitor thì cần phải tắt đi thì mới có thể nạp được chương trình mới vào. Đối với serial monitor mặc định của arduino thì nó sẽ tự động tắt khi bạn ấn nút nạp code.

Xong các yêu cầu cần thiết bây giờ sẽ test thử với một chương trình đơn giản: LED BLINK

B1: Mở chương trình LED BLINK cho Wemos Khi ta thay đổi board thì sẽ có các Example riêng cho từng board đó. Ở đây phải chọn đúng Example cho Wemos (nó thuộc họ esp8266). B2: Verify/Compile code. Click chuột vào nút Verify để verify code. Hoặc bạn có thể ấn tổ hợp phím tắt Ctr + R B3: Upload code lên board. Click chuột vào nút Upload hoặc ấn tổ hợp phím Ctr + U

Nếu bạn thấy cái đèn trên Wemos nhấp nháy là đã thành công rồi đấy.

Sau đây chúng ta sẽ tìm hiểu sâu hơn về các chân cắm của Wemos một chút.

IV. Giới thiệu Wemos

1. Giới thiệu chức năng các chân

Chức năng của các chân của Wemos như sau:

Mỗi chân đều có khá nhiều chức năng và các chức năng chính sẽ được ghi trên board rồi nên các bạn sẽ không lo cắm nhầm chân hay gì cả. Các kí hiệu trên chân đó ví dụ D1, D2,.. thì sẽ có sẵn const lưu địa chỉ của chân đó với chân tương ứng. Khi lập trình bạn chỉ cần gọi chỉ số này để set giá trị cho chân đó là được.

Tất cả các chân của Wemos D1 mini đều có ngắt ngoài.

2. Ngắt là gì

Để hiểu được ngắt ngoài thì bạn cần phải hiểu ngắt là gì.

Ngắt là khái niệm chỉ việc mà hệ thống tạm dừng thực hiện chương trình chính để thực hiện một chương trình khác. Sau khi thực hiện xong chương trình ngắt thì chương trình chính sẽ được tiếp tục thực hiện từ đoạn bị ngắt. Kiểu như bạn đang làm một việc gì đó nhưng lại có một việc cần ưu tiên cao hơn xen vào. Bạn tạm dừng việc đang làm đó và đi làm việc xen vào kia. Sau khi làm xong việc xen vào thì bạn lại phải tiếp tục làm công việc chính đang còn dang dở.

Ngắt ngoài là ngắt có nguồn tác động từ bên ngoài.

Ví dụ ta cài đặt chương trình: nếu chân D3 từ mức cao đến mức thấp thì sẽ xảy ra ngắt ngoài chẳng hạn. Khi đó chân D3 mà chuyển từ mức cao xuống mức thấp (có thể là do mình nối trực tiếp chân đó lên 3.3V xong lại ngắt kết nối với 3.3V và nối chân đó xuống GND chẳng hạn) thì lúc này chương trình chính sẽ tạm dừng thực hiện. Hệ thống sẽ thực hiện chương trình phục vụ ngắt.

Ngoài ngắt ngoài thì còn 1 loại nữa là ngắt timer. Có nghĩa là sau một khoảng thời gian nhất định chương trình phục vụ ngắt sẽ được thực hiện. Và tất nhiên chương trình chính bị tạm dừng rồi. Vì mình chỉ có 1 core để thực hiện chương trình thôi.

Ngắt có thể khá khó hiểu cho nhưng bạn mới bắt đầu vì vậy mình sẽ có thể sẽ có bài viết nói riêng về vấn đề này với các ví dụ dễ hiểu hơn.

3. Dung lượng bộ nhớ

Theo như trong datasheet thì wemos có bộ nhớ là 4Mb và ta có thể viết chương trình có kích thước tới 4Mb. Tuy nhiên thì Arduino IDE mặc định chỉ cho giới hạn dung lượng chương trình của bạn là 1Mb mà thôi. Dung lượng này có thể thay đổi được trong file config của board. Nếu bạn quan tâm đến việc này thì hay liên hệ với mình, mình sẽ giúp đỡ tận tình. 😃)) Nhưng bạn yên tâm, 1Mb code thì khá đủ để mình viết code rồi. 3M còn lại bạn có thể dùng nó để lưu data, các file. Nó giống như là bạn phân chia ổ cứng. 1 ổ 1Mb để cài hệ điều hành, 1 ổ 3Mb để lưu dữ liệu đó.

Chú ý:

  • Các chân tín hiệu của wemos chỉ chịu được hiệu điện thế tối đa là 3.3V và tất nhiên các chân đó sẽ cung cấp mức cao là 3.3V. Trên mạch wemos có một chân 5v để cấp cho các thiết bị khác cắm vào. Các bạn không được cắm chân này để set cho chân tín hiệu của wemos lên mức cao.
  • Không được chạm vào dây cáp usb hoặc chân tín hiệu của wemos trong lúc nạp để phòng trường hợp dây lỏng hay dữ liệu nạp vào bị hỏng.

V. Wemos kết nối wifi - socket

Để sử dụng kết nối wifi của Wemos thì ta cần include thư viện Wifi. Thư viện này có sẵn trong gói cài đặt arduino - esp8266

Sử dụng socket thì ta có thư viện socket client. Thư viện này bạn có thể tải về tại đây. Thư viện này mình lấy nguồn của anh washo4evr và có sửa lại một chút tăng hiệu năng và các hàm emit với on giống socket.io hơn. Bạn có thể tải thư viện của mình tại đây.

Các file thư viện dowload về bạn phải giải nén ra và đặt thư mục đó vào trong thư mục /Home/username/Arduino/libraries hoặc path_install_arduino_IDE/libraries.

Khởi động hoặc khởi động lại Arduino IDE để nó nhận thư viện. Khi nào bạn vào example mà thấy examples của thư viện đó là bạn đã cài đặt thành công rồi đấy.

Xong phần chuẩn bị thật nhiều công đoạn. Bây giờ chúng ta bắt đầu code thôi. 😃))

1. Tạo project mới

Thư mục chứa code của Arduino IDE là: Home/Arduino/sketch. Mỗi project phải được chứa trong 1 thư mục riêng cùng tên với file .ino

Bạn vào File => New để tạo một project mới với tên là aquarium-wemos

Cấu trúc thư mục Arduino trong thư mục Home\UserName nó sẽ như thế này:

Nội dung chương trình mới tạo sẽ như sau:

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

Mình sẽ giải thích một chút chức năng của 2 hàm trên:

  • Hàm setup là hàm chỉ chạy 1 lần khi khởi động chương trình. Và sẽ là hàm đầu chạy đầu tiên.
  • Hàm loop sẽ chạy lặp đi lặp lại nhiều lần. Cho đến khi hết điện thì hàm này mới dừng. Bạn hãy tưởng tượng. Chương trình C sẽ chạy từ trên xuống dưới. Khi nào hết chương trình thì sẽ dừng. Như vậy nếu không có hàm loop này thì chương trình của mình chỉ chạy 1 lần duy nhất là sẽ dừng ngay. Bản chất hàm loop sẽ được đặt trong vòng lặp while(1) khi chạy chương trình chính (main).

Bạn hãy xem sơ đồ dưới đây để biết quá trình chạy của chương trình:

2. Viết chương trình bật tắt đèn từ xa

Đây có lẽ là phần dân lập trình chúng ta thích nhất.

Sơ đồ:

Đầu tiên sẽ là chương trình điều khiển tắt bật cái đèn trên wemos. Đèn này sẽ gắn liền với chân số 2 (chân D4, hằng số LED_BUILTIN hoặc BUILTIN_LED). Các bạn chú ý là khi cung cấp điện áp thấp (LOW) vào chân này thì đèn sẽ sáng, cấp điện áp cao (HIGH) vào là đèn sẽ tắt.

Bạn hãy đọc kĩ các comment để hiểu rõ chương trình nha. 😉

/*==================================*\
|        Client nạp vào wemos        |
\*==================================*/
#include <SocketIOClient.h>

const char* ssid = "wifi_name";
const char* password = wifi_pasword";

// Server Ip
String host = "192.168.2.109";
// Server port
int port = 3000;

// Khởi tạo socket
SocketIOClient socket;

// Kết nối wifi
void setupNetwork() {
    WiFi.begin(ssid, password);
    uint8_t i = 0;
    while (WiFi.status() != WL_CONNECTED && i++ < 20) delay(500);
    if (i == 21) {
        while (1) delay(500);
    }
    
    // Hàm này là hàm in log ra serial
    Serial.println("Wifi connected!");
}

// Thay đổi trạng thái đèn theo dữ liệu nhận được
void changeLedState(String data) {
    if (data == "[\"led-change\",\"off\"]") {
        digitalWrite(LED_BUILTIN, HIGH);
        Serial.println("Led off!");
    } else {
        digitalWrite(LED_BUILTIN, LOW);
        Serial.println("Led on!");
    }
}

void setup() {

    // Cài đặt chân LED_BUILTIN là chân đầu ra tín hiệu
    pinMode(LED_BUILTIN, OUTPUT);
    
    // Cài đặt giá trị mặc định là đèn tắt
    digitalWrite(LED_BUILTIN, HIGH);
    
    // Bắt đầu kết nối serial với tốc độ baud là 115200.
    // Khi bạn bật serial monitor lên để xem log thì phải set đúng tốc độ baud này.
    Serial.begin(115200);
    setupNetwork();
    
    // Lắng nghe sự kiện led-change thì sẽ thực hiện hàm changeLedState
    socket.on("led-change", changeLedState);
    
    // Kết nối đến server
    socket.connect(host, port);
}

void loop() {
     // Luôn luôn giữ kết nối với server.
    socket.monitor();
}

Code server socket.io:

/*==================================*\
|         Server Socket.io           |
\*==================================*/
require('dotenv').config();
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var colors = require('colors');
var port = process.env.PORT || 3000;

server.listen(port, function () {
    console.log('Server listening at port %d', port);
});

app.use(express.static(__dirname + '/public'));

io.on('connection', function (socket) {
    console.log('New client connect'.gray);

    socket.on('led-change', function(data) {
        console.log(data);
        socket.broadcast.emit('led-change', data);
    });
    socket.on('disconnect', function () {
        console.log('Client disconnect'.gray);
    });
});

Code client điều khiển:

/*==================================*\
|         Client Socket.io           |
\*==================================*/
import './sass/app.scss';
require('./bootstrap');

let socket = io();

$('#button').change(function (event) {
    var data;
    if ($(this).prop('checked')) {
        data = 'on';
    } else {
        data = 'off';
    }

    socket.emit('led-change', data);
});

Code đầy đủ tại đây: https://github.com/HoangHoi/viblo/tree/led_socket_io_basic

Sau khi nạp code cho wemos xong, bạn hãy vào tool => serial monitor hoặc ấn phím Ctrl+Shift+M để mở hộp thoại serial monitor xem các log mình đã log ra. Nhớ chọn tốc độ baud giống với tốc độ baud đã cài đặt trong hàm setup nhé.

Và đây sẽ là thành quả của chúng ta

Chúc các bạn thành công!

Có bất kì thắc mắc hay góp ý nào, các bạn hãy comment bên dưới nhé.