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
11 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ả

Vấn đề của số chấm động và số nguyên trong ngôn ngữ lập trình C++ trên board mạch Arduino

Có bao giờ bạn tự hỏi: "Dự án của mình làm tốt thế này, chạy ngon lành rành rành thế này, chắc không có bugs đâu?". Thực sự, nếu dự án của bạn không có phần xử lý số thực chấm động trong đó thì mình nghĩ phần code của bạn sẽ hoạt động ngon lành theo thời gian. Nhưng mà có số thực thì từ từ, chúng ta cần xét lại code. Trước đây, có một số bạn nhắn tin riêng hỏi mình về code với điểm chung là "code mình chạy ngon lành lúc đầu, sau đó bị lỗi, không rõ nguyên nhân". Loại trừ các phần code logic sai ra, thì hầu hết đều là do lỗi khi xử lý số chấm động mà không quan tâm đến nền tảng lập trình bên dưới! Mà cũng đúng, chúng ta rất dễ bị đánh lừa bởi chính đoạn code chúng ta viết. Vì nó có báo lỗi biên dịch đâu mà, kaka. Qua bài viết này, mình muốn phân tích và cùng các bạn rút kinh nghiệm về số chấm động float, cách hạn chế lỗi sai với số chấm động.

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

Game thử tài trí nhớ - Làm game với Arduino cực kỳ đơn giản

Với mong muốn giúp các newbie có thể dễ dàng hình dung việc lập trình trên board mạch Arduino. Hôm nay, mình sẽ hướng dẫn các bạn làm một game khá thú vị, đó là game: "Thử tài trí nhớ". Chỉ việc sử dụng một ít led, điện trở, nút nhấn và 01 servo, cùng với tay nghề độ mod khéo léo sẵn có của bạn, chúng ta đã có một game khá thú vị rồi!

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