Tạo một quy trình công nghiệp với các bước bằng Arduino - Phần 2: công việc có điều kiện

I. Giới thiệu

Ở bài viết trước, mình đã đề cập đến vấn đề là "Làm thế nào để xây dựng một quy trình công nghiệp trên Arduino". Ở bài viết đó, mình đã đề cập đến vấn đề quy trình tuần tự không điều kiện, và với sự hưởng ứng từ Cộng đồng qua vấn đề làm một máy công nghiệp dùng để sản xuất sản xuất thành phẩm, mình muốn đóng góp một cái gì đó để dự án này hoàn thiện, đó cũng làm một cách để rèn luyện khả năng xử lý vấn đề thông qua mô tả mà không trực tiếp "chạy" máy devil! Vấn đề mình muốn giải quyết đó là xây dựng một quy trình công nghiệp có điều kiện.

II. Bài toán đặt ra

Bạn cần đọc kĩ và thực hành ở bài Tạo một quy trình công nghiệp với các bước bằng Arduino trước khi đọc tiếp, vì như vậy bạn mới thấy được những khó khăn trong bài viết đó. Từ đó, cảm nhận những thay đổi trong bài viết này.

Ngoài ra, bạn có thể download bộ thư viện tại đây và tạo một sketch mới sau đó paste vào hoặc donwload sketch mẫu tại đây. Mình chưa tạo một thư viện hoàn toàn vì muốn các bạn trải nghiệm trước từ đó đóng góp hơn nữa, mình vẫn thấy nó còn thiếu một điều gì đó cool.

Bài toán thực tế được đưa ra là, làm thế nào để điều khiển nhiều động cơ bước, trong đó, động cơ bước kia phải hoạt động rồi mới đến lượt động cơ bước này. Bài toán này không khó nếu chỉ có 2 hay 3 động cơ bước. Nhưng với 7, 8 động cơ bước thì việc biết chính xác lúc nào động cơ kia được chạy quả không phải là một điều đơn giản cho việc code theo hướng thủ tục. Ở bài viết trước, mình có nói đến quy trình công nghiệp, nhưng rõ ràng là nó không thực tế tí nào, vì nó không quan tâm đến công việc kia đã hoàn thành hay chưa, chỉ cần đến đúng lúc thì nó chạy :D. Có thể nó phù hợp với công việc quét LED hơn chăng crying?

Bài toán đặt ra là: thư viện phải biết khi nào bước đó được hoạt động, nghĩa là thêm điều kiện để kiểm tra bước đó được phép hoạt động hay không? Nhưng chưa cần giải quyết rẻ nhánh (nếu không được trong bao lâu đó thì rẻ nhánh sang bước khác) devil.

III. Giải quyết vấn đề

Ta có thể dễ dàng thấy rằng vấn đề này rất giống với vấn đề trước chỉ khác ở chỗ xác định đươc "thời điểm kết thúc chinh xác". Như vậy, mình sẽ thiết kế một hướng đối tượng tên kIndustryCalendar kế thừa đối tượng kCalendar đã có để tiết kiệm thời gian ngồi gõ code devil.

Về cơ bản đối tượng kIndustryCalendar giống với kCalendar hết, vì vậy, các lưu ý sẽ được giữ nguyên không thay đổi gì, mình chỉ nói những thay đổi mới thôi nhé.

Khác với kCalendar, bạn phải tính toán thời điểm chính xác trong quy trình của bước đó, thì ở lớp kIndustryCalendar bạn chỉ cần xác định khoảng thời gian giữa bước thứ i - 1 và bước thứ i, hoặc là xác định điều kiện để bước thứ i được thực hiện heart.

IV. Lập trình

Để hiểu rõ hơn, bạn xem đoạn code mẫu mình đã viết như sau.

#include "Timer.h"
#include "kIndustryCalendar.h"



//job1
void job1() {
	unsigned long time = millis();
	Serial.print("Job1: ");
	Serial.println(time);
}

//job2
void job2() {
	unsigned long time = millis();
	Serial.print("Job2: ");
	Serial.println(time);
}

void job3() {
	Serial.println("Ban da nhan phim tren Serial");
}

bool waitForSerial() {
  bool res = Serial.available();
  //xóa hết buffer của Serial
  while (Serial.available())
    Serial.read();
  return res;
}

void setup()
{
	
	//Khởi tạo serial ở mức baudrate 115200
	Serial.begin(115200);

	//Khởi gạo class timer (design pattern singleton) - bắt buộc phải có trong hàm setup (trước khi khởi tạo các job)
	Timer::getInstance()->initialize();

	//Khởi tạo lịch, các công việc phải được sắp xếp theo chiều thời gian tăng dần để tránh lỗi
	kIndustryCalendar::getInstance()->initialize();
	//Chạy lệnh job1 ở thời điểm 0 trong chu kỳ
	kIndustryCalendar::getInstance()->addJob(job1, (unsigned long)0);
	//Chạy lệnh job2 ở thời điểm 1000ms trong chu kỳ
	kIndustryCalendar::getInstance()->addJob(job2, (unsigned long)1000);

    //Job3 sẽ chỉ được thực hiện khi bạn gửi một ký tự gì đó qua Serial (bật serial monitor lên và nhấn bất kì rồi ấn Enter :D)
    kIndustryCalendar::getInstance()->addJob(job3, waitForSerial);

	//Bắt đầu tính giờ thời gian của quy trình đầu tiên
	kIndustryCalendar::getInstance()->startFirstJob();
}

//trong hàm loop chỉ nên có những hàm này, bạn muốn viết một chức năng khác? Xin hãy tạo một job và đưa vào thời khóa biểu scheduler như hàm dưới

void loop()
{
	//đầu hàm loop phải có để cập nhập thời điểm diễn ra việc kiểm tra lại các tiến trình
	Timer::getInstance()->update();
	
	kIndustryCalendar::getInstance()->update();

	//cuối hàm loop phải có để cập nhập lại THỜI ĐIỂM (thời điểm chứ ko phải thời gian nha, tuy tiếng Anh chúng đều là time) để cho lần xử lý sau
	Timer::getInstance()->resetTick();
	
}

Giống như thư viện trước, job1 và job2 sẽ lần lượt được thực hiện, nhưng job3 chỉ được thực thi khi job1 đã chạy xong!

Điều khiển động cơ bước như thế nào nhỉ?

Easmple 4 diagram

Mình lắp mạch như trên và thử đọn code này và cảm nhận đoạn code dưới đây nào. Thư viện điều khiển động cơ bước, các bạn download ở đây.

#include "Timer.h"
#include "kIndustryCalendar.h"
#include <AccelStepper.h>

AccelStepper stepper1(1, 9, 8);
AccelStepper stepper2(1, 7, 6);

//job1
void job1() {
    unsigned long time = millis();
    Serial.print("Job1: ");
    Serial.println(time);
    stepper1.moveTo(1000);
}

bool waitJob1Finish() {
    return stepper1.distanceToGo() == 0;
}

//job2
void job2() {
    unsigned long time = millis();
    Serial.print("Job2: ");
    Serial.println(time);
    stepper2.moveTo(-1000);
}

bool waitJob2Finish() {
    return stepper2.distanceToGo() == 0;
}

//job3
void job3() {
    unsigned long time = millis();
    Serial.print("Job3: ");
    Serial.println(time);
    stepper2.moveTo(1000);
    stepper1.moveTo(-1500);
}

bool waitJob3Finish() {
    return (stepper2.distanceToGo() == 0) && (stepper1.distanceToGo() == 0);
}

void jobEnd() {
    //chả làm gì cả, một hàm để đảm bảo được gọi trước khi kết thúc. Hoạt dùng để đếm số lần hoạt động của máy từ đó ra quyết định dừng máy lại :)
}

void setup()
{
    
    //Khởi tạo serial ở mức baudrate 115200
    Serial.begin(115200);
    
    //gán các giá trị tốc độ tối đa và gia tốc khác nhau cho động cơ bước!
    stepper1.setMaxSpeed(300); // tốc độ tối đa
    stepper1.setAcceleration(1000); // gia tốc
    stepper2.setMaxSpeed(200);
    stepper2.setAcceleration(800);
    
    //Khởi gạo class timer (design pattern singleton) - bắt buộc phải có trong hàm setup (trước khi khởi tạo các job)
    Timer::getInstance()->initialize();
    
    //Khởi tạo lịch, các công việc phải được sắp xếp theo chiều thời gian tăng dần để tránh lỗi
    kIndustryCalendar::getInstance()->initialize();
    //Chạy lệnh job1 ở thời điểm 0 trong chu kỳ
    kIndustryCalendar::getInstance()->addJob(job1, (unsigned long)0);
    //Chạy lệnh job2 ở thời điểm 1000ms trong chu kỳ
    kIndustryCalendar::getInstance()->addJob(job2, waitJob1Finish);
    
    kIndustryCalendar::getInstance()->addJob(job3, waitJob2Finish);
    
    kIndustryCalendar::getInstance()->addJob(jobEnd, waitJob3Finish);
    
    //Bắt đầu tính giờ thời gian của quy trình đầu tiên
    kIndustryCalendar::getInstance()->startFirstJob();
}

//trong hàm loop chỉ nên có những hàm này, bạn muốn viết một chức năng khác? Xin hãy tạo một job và đưa vào thời khóa biểu scheduler như hàm dưới

void loop()
{
    //đầu hàm loop phải có để cập nhập thời điểm diễn ra việc kiểm tra lại các tiến trình
    Timer::getInstance()->update();
    
    kIndustryCalendar::getInstance()->update();
    
    //cuối hàm loop phải có để cập nhập lại THỜI ĐIỂM (thời điểm chứ ko phải thời gian nha, tuy tiếng Anh chúng đều là time) để cho lần xử lý sau
    Timer::getInstance()->resetTick();
    stepper1.run();stepper2.run(); //cho 2 động cơ bước chạy
}

V. Kết luận

Thử và cảm nhận và đóng góp ý kiến cho mình dưới mục bình luận nhé. Và đừng quên chụp hình sản phẩm để mình cập nhập vào bài viết nha haha.

lên
8 thành viên đã đánh giá bài viết này hữu ích.
Các dự án được truyền cảm hứng

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

Tiết kiệm RAM trong Arduino?

Như đã nói ở bài trước Cách lưu trữ các biến số, mảng, chuỗi trong Arduino, chúng ta đã biết rằng các loại biến trong Arduino được lưu ở những vùng nhớ khác nhau trong RAM, và khi hết RAM thì chương trình của bạn sẽ die một cách bất ngờ - vì lỗi không nằm trong code.

Vì vậy, hôm nay, chúng ta sẽ tìm cách giải quyết vấn đề "làm thế nào để giảm thiểu việc sử dụng RAM trong một sketch Arduino?".

lên
7 thành viên đã đánh giá bài viết này hữu ích.

ESP8266 kết nối Internet - Phần 1.1: ESP8266 đi thuê phòng ở khách sạn Socket Server

Ở bài viết Phần 1: Cài đặt ESP8266 làm một socket client kết nối tới socket server trong mạng LAN. Trong bài này, chúng ta đã làm mô hình một thiết bị ESP8266 kết nối vào Socket Server. Nhưng trong thực tế, Socket là một mô hình mạng có thể kết nối nhiều thiết bị với nhau. Và qua bài viết này, mình làm một ví dụ cho ESP8266 kết nối với một ESP8266 khác. Cùng khám phá nhé.

lên
4 thành viên đã đánh giá bài viết này hữu ích.