Hướng dẫn toàn tập là LED ma trận giá tiền ảo và đồng bộ dữ liệu Internet - Học STEM IoT

28/06/2022
huong-dan-toan-tap-la-led-ma-tran-gia-tien-ao-va-dong-bo-du-lieu-internet-hoc-st

Mô tả dự án: 
Bất cứ chúng ta đều cần cập nhập thông tin, nào là tin tức đời sống, lượt xem youtube hay là tỉ giá ngoại tệ, tiền ảo,... Doanh nghiệp cũng cần cập nhật giá cho các sản phẩm tại các shop của họ. Các bạn không cần bỏ ra một núi tiền để nghiên cứu nữa. Hãy cùng làm theo bài viết này, và nó sẽ truyền cảm hứng cho bạn. Bài viết này, khác ở chỗ, nó không hoạt động độc lập một mỉnh lẻ loi mà nó có thể scale-out ra hàng ngàn thiết bị ngay tức khắc mà bạn không cần lo lắng gì về server cả!

1.Tại sao bạn lại cần đọc bài này?
Đây là một bài viết toàn văn, đầy đủ các bước thực hiện, giải thích source code. Bạn không biết gì về lập trình mạng, IoT, cũng đừng lo lắng, bạn chỉ việc kéo thả các khối lệnh rất đơn giản mà thôi! Nói đơn giản là thế nhưng thực sự nó rất thú vị, bất cứ ai dù không biết gì về lập trình thì vẫn có thể làm được và làm tốt. Quan trọng hơn hết, chỉ cần bạn làm một thiết bị thì bạn có thể scale-out ra 10000 thiết bị ngay lập tức mà không cần tính toán gì ngoài việc tính toán tiền phần cứng! Điều mà một doanh nghiệp, công ty thực sự cần trong mọi dự án.

Nào, chúng ta cùng bắt đầu thôi

2.Bạn cần chuẩn bị những gì để bắt đầu
Phần mềm
Trên điện thoại di động:
iNut - Công tắc wifi (các bạn search trong apple store hoặc google play từ khóa inut là ra). Tải về trên Apple Store, tải về ở Google Play.
Trên máy tính:Git
NodeJS (mirror)
Phần cứng
iNut - Cảm biến
Arduino Uno hoặc Arduino Mega hoặc Arduino Nano
Module hiển thị 4 led ma trận MAX7219
Dây cắm breadboard
Dây USB
Các bạn có thể in 3d bộ khung của led ma trận này tại đây
Nếu các bạn muốn mua trọn bộ có thể liên lạc với mình Mr.Khánh 097 276 8491.

3.Kết nối IoT cho thiết bị iNut Cảm biến
Bạn sử dụng dây sạch Micro USB của Android hoặc xuống bước dưới nối dây như hình :

Cài đặt cho mạng (iNut_<con số>) (Password: inut12345)

4.Cách nối dây
Đối với board mạch Arduino Nano và Mega, các bạn cứ tìm cái ụ SPI tương tự như hình nhé! Cách nối chân tương tự. Đối với Arduino Nano, các bạn nối D1 của iNut cảm biến vào A4, D2 nối A5.

5.Chương trình
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
#include <Wire.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
 
#define N_SENSOR  6
float sensors[N_SENSOR]; //biến lưu trữ danh sách cảm biến
 
int pinCS = 10; // Chọn chân SPI, các bạn có thể thay đổi số này trừ 11, 12, 13 (đối với Arduino Uno và Nano
 
 
byte numberOfHorizontalDisplays = 1; //số trong Hiển thị theo chiều ngang
byte numberOfVerticalDisplays = 4; // Số trong hiển thị theo chiều dọc. Bạn mua nhiều module hơn chỉ việc ghép nối tiếp lại và sửa số ở đây :D
 
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays); // cấu hình matrix
 
String tape = "<3 iNut xLED <3";
String newTape = "";
 
 
byte wait = 30; // thời gian chạy chữ.
 
byte spacer = 1; // khoảng cách cách chữ
byte width = 5 + spacer; // độ rộng của font là 5 fixel
byte intensity = 15;
 
 
//Tạo vùng nhớ lưu trữ các biến tiền tệ để dễ dàng truy xuất.
//Ví dụ: list["BTC=>USD"] = 6000 list["BTC=>EUR"] = 4999 
// Khi bạn muốn sửa giá trị của BTC=>USD chỉ cần làm phép gán list["BTC=>USD"] = 6666 (giá trị mới chẳng hạn...)
//Đây chỉ là một cách dùng, các bạn có thể thay thế BTC=>USD = giá tiền bằng một cặp key => value mà bạn thích.
// Ví dụ: Temp = 33.3 (nhiệt độ nhà nấm là 33.3 độ C), Humi = 69.6% (Độ ẩm hiện tại là 69.6%,...)
StaticJsonBuffer<234> jsonBuffer; //Đối với các bạn muốn lưu và hiển thị nhiều số hơn thì sửa số 234 này cho lớn hơn là được. Các bạn dùng Arduino Mega thì vô tư còn các bạn dùng Arduino thì như vầy là đủ rồi!
static JsonObject& list = jsonBuffer.createObject();
void setup()
{
    //Bật Serial ở baudrate 9600 cho thoải mái
    Serial.begin(9600);
    
    
    //Khởi tạo EEPROM để dùng lưu trữ các biến khi mất điện
    EEPROM.begin();
    
    //Để sử dụng iNut Cảm biến - bạn cần chạy lệnh này
    //... để Arduino của bạn ở thành một thiết bị I2C Slave với địa chỉ là 10 :D (hơi học thuật một xíu bạn có thể bỏ qua)
    Wire.begin(10);
    
    
    ////Đọc các giá trị lưu ở "ổ cứng epprom". Bản chất epprom không phải là ổ cứng, nó là một vùng nhớ sẽ không bị mất đi khi cúp điện (ROM)
    wait = EEPROM.read(0); // thời gian chạy chữ.
    Serial.print(F("Wait: "));
    Serial.println(wait);
    if (wait > 200)
        wait = 50;
    Serial.println(wait);
    spacer = EEPROM.read(1); // khoảng cách cách chữ
    Serial.print(F("spacer: "));
    Serial.println(spacer);
    if (spacer > 10)
        spacer = 1;
    Serial.println(spacer);
    width = EEPROM.read(2); // độ rộng của font là 5 fixel
    Serial.print(F("width: "));
    Serial.println(width);
    if (width > 20)
        width = spacer + 5;
    Serial.println(width);
    intensity = EEPROM.read(3);
    Serial.print(F("intensity: "));
    Serial.println(intensity);
    if (intensity == 0 || intensity > 15)
        intensity = 15;
    Serial.println(intensity);
    Wire.onReceive(receiveEvent);
    Wire.onRequest(i2cRequestEvent);
    matrix.setIntensity(intensity); // cài đặt giá trị độ tương phản từ 0 đến 15.
 
    //Cứ để nguyên các giá trị mặc định, và chạy :D, sau đó bạn thử thay thế thuộc tính để xem hiệu ứng nhé!
    // điều chỉnh hiển thị theo nhu câu của người dùng.
    //  matrix.setPosition(0, 1, 3);  // The first display is at <0, 0>
    //   matrix.setPosition(1, 1, 0);  // The second display is at <1, 0>
    //   matrix.setPosition(2, 2, 0);  // The third display is at <2, 0>
    //  matrix.setPosition(3, 3, 0);  // And the last display is at <3, 0>
    //  ...
    // matrix.setRotation(0, 2);    // Màn hình hiển thị đầu tiên là vị trí đảo ngược
    matrix.setRotation(3); // The same hold for the last display
}
 
 
//Gửi dữ liệu lên Internet - Các bạn chỉ cần thêm đoạn này vào chương trình và không cần hiểu sâu về nó
void i2cRequestEvent()
{
    //Buộc phải có nếu bạn muốn gửi dữ liệu
    char *data = (byte*)&sensors;
    Wire.write(data, sizeof(sensors));
    Serial.println(F("."));
}
//Bắt dữ liệu từ Internet trả về - Các bạn chỉ cần thêm đoạn này vào chương trình và không cần hiểu sâu về nó
volatile char buffer[33];
volatile boolean receiveFlag = false;
bool firstTimeInit = false;
void receiveEvent(int howMany)
{
    Wire.readBytes((byte *)buffer, howMany);
    buffer[howMany] = 0;
    receiveFlag = true;
    Serial.println(F("!"));
}
 
void loop()
{
    
    
    //Để đồng bộ các giá trị, ta chỉ việc gán các giá trị sensors[0] => sensors[5] là các giá trị thuộc tính của đèn led
    //iNut Cảm biến cho ta 8 thanh ghi để đồng bộ, tuy nhiên trong dự án này, ta chỉ cần 6 mà thôi.
    //Việc gán các giá trị rất đơn giản như sau 
    sensors[0] = wait;
    sensors[1] = spacer;
    sensors[2] = width;
    sensors[3] = intensity;
    sensors[4] = numberOfHorizontalDisplays;
    sensors[5] = numberOfVerticalDisplays;
 
    static unsigned long timer = 0;
    static unsigned long i = 0;
    
    //đoạn chương trình hiển thị led ma trận với hiệu ứng chữ chạy ngang
    //Thú thực với các bạn là mình cũng không quan tâm nó code gì trong này, chỉ cần biết là tốc độ nó hiển thị ra rất ổn
    //  và việc của chúng ta là thay đổi giá trị của biến chuỗi tape là xong!
    if (millis() - timer > (unsigned long)wait) {
        if (i < width * tape.length() + matrix.width() - 1 - spacer) {
 
            matrix.fillScreen(LOW);
 
            int letter = i / width;
            int x = (matrix.width() - 1) - i % width;
            int y = (matrix.height() - 8) / 2; // center the text vertically
 
            while (x + width - spacer >= 0 && letter >= 0) {
                if (letter < tape.length()) {
                    matrix.drawChar(x, y, tape[letter], HIGH, LOW, 1);
                }
 
                letter--;
                x -= width;
            }
 
            matrix.write(); // Send bitmap to display
            i++;
            timer = millis();
        } else {
            i = 0;
            if (newTape != "")
                tape = newTape;
        }
 
    }
 
    
    if (receiveFlag) { //khi có tín hiệu là đã nhận được lệnh và cần phải xử lý lệnh từ Internet
 
        String command = buffer; //chép lệnh vào biến String cho dễ xử lý
 
 
        Serial.print(command);// in ra lệnh
        Serial.print(' ');
        Serial.println(millis());//in ra thời gian theo millis tính từ lúc arduino chạy để debug
    
    
    //Các bạn xem quy ước lệnh ở phần tiếp theo trong bài viết
        StaticJsonBuffer<100> jsonBuffer;
 
        JsonObject& root = jsonBuffer.parseObject(command.c_str());
 
        if (root.containsKey("c")) {
            byte command_code = root.get<byte>(F("c"));
            byte value = root.get<byte>(F("v"));
            Serial.print(F("COMMAND CODE: "));
            Serial.println(command_code);
            switch (command_code) {
                case 0: //setup wait
                    Serial.println(F("Cap nhap wait"));
                    wait = value;
                    EEPROM.write(0, wait);
                    break;
                case 1: //setup spacer
                    Serial.println(F("Cap nhap spacer"));
 
                    spacer = value;
                    EEPROM.update(1, spacer);
                    break;
                case 2: //setup width
                    Serial.println(F("Cap nhap width"));
 
                    width = value;
                    EEPROM.update(2, width);
                    break;
                case 3: //setup intensity
                    Serial.println(F("Cap nhap intensity"));
 
                    intensity = value;
                    EEPROM.update(3, intensity);
                    matrix.setIntensity(intensity);
                    break;
            }
        } else if (root.containsKey("k")) {
            Serial.print(F("add key "));
            Serial.println(getMemoryFree());
            const char * key = root.get<const char *>(F("k"));
 
            const char * value = root.get<const char *>(F("v"));
            Serial.println(key);
            Serial.println(value);
            if (strlen(key) > 1 && strlen(value) > 1) {
                
                list.set(String(key),  String(value));
                newTape = "";
                // using C++11 syntax (preferred):
                int idx = 0;
                for (auto kv : list) {
                    idx++;
 
 
                    //Serial.println(kv.key);
                    //Serial.println(kv.value.as<char*>());
                    newTape += idx;
                    newTape += ".";
                    newTape += String(kv.key);
                    newTape += " ";
                    newTape += String(kv.value.as<const char*>());
                    newTape += ", ";
                    
                }
 
                if (!firstTimeInit) {
                    tape = newTape;
                    firstTimeInit = true;
                    i = 0;
                }
                tape = newTape;
 
 
                Serial.println(newTape);
            }
        }
 
        receiveFlag = false; //đánh dấu đã xử lý xong lệnh, không cần đọc nữa
    }
    
    //Cứ mỗi 1s là in ra số RAM còn dư của Arduino :D
    static unsigned long timer3 = 0;
    if (millis() - timer3 > 1000UL) {
        timer3 = millis();
        Serial.print(F("RAM: "));
        Serial.println(getMemoryFree());
    }
}
 
//Bạn xem bài viết này nếu muốn hiểu rõ hơn: http://arduino.vn/bai-viet/356-tiet-kiem-ram-trong-arduino
int getMemoryFree() {
    // Trong trường hợp này, ta có thể hiểu extern sẽ khai báo một biến toàn cục trong chương trình (nếu chưa có) hoặc include một biến toàn cục đã được extern trước đó
    extern int __heap_start;
    extern int *__brkval; 
    
    //Dấu & phía trước tên biến / tên con trỏ sẽ cho ta biết vị trí ô nhớ mà nó đang đứng
    //Lưu ý: bài viết này không dành cho beginner và bạn cần tưởng tượng một chút để có thể mườn tượng vấn đề
    return (int) SP - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}
Các bạn chỉ việc nạp chương trình vào Arduino là xong! Nếu bạn chưa biết cách nạp chương trình, thì xem hướng dẫn nạp chương trình cho Arduino nhé.

Để biên dịch chương trình trên thành công, bạn cần 3 thư viện sau: Adafruit_GFX, ArduinoJson, Max72xxPanel. Các bạn có thể tải ngay 3 thư viện này tại đây.

6.Chương trình đồng bộ quản lý dữ liệu trên máy tính
Để cài đặt chương trình điều khiển bảng LED:

Các bạn mở Windows command line lên bằng cách nhấn tổ hợp phím Windows + R (phím Windows là phím giữa Alt và Ctrl á). Sau đó gõ lệnh cmd và nhấn OK.

Trong bản lệnh hiện ra, bạn lần lượt chạy các lệnh sau (chép và dán vào từng lệnh một cho chắc nhé)

Clone code về
git clone https://github.com/ngohuynhngockhanh/iNut-Node-RED-Kickstarter

cd iNut-Node-RED-Kickstarter

git checkout ledmatrix

Cài đặt
npm install

Chạy chương trình
npm start

khi cài đặt xong, bạn sẽ có một thông báo như thế này:

Và các bạn truy cập vào http://127.0.0.1:1880/... nhé! Đây là giao diện của chúng ta.

Các bạn truy cập vào địa chỉ http://localhost:1880/ui/... để xem giao diện đồ họa nhé.

Okay, cài đặt xong rồi, bước tiếp theo là cập nhập các mã thông tin trong ví dụ mẫu cho phù hợp với các thiết bị iNut của bạn. 

Cách chép mã Node-Red topic từ phần mềm iNut

Cách chép mã REST API từ phần mềm iNut

Bạn sửa URL trong khối lệnh trên bằng mã iNut cảm biến của bạn nhé. 

Bình luận
Nội dung này chưa có bình luận, hãy gửi bình luận đầu tiên của bạn.
VIẾT BÌNH LUẬN CỦA BẠN