Bỏ qua tới nội dung
LSLab

Viết firmware đồng hồ đơn giản với Arduino

  • Trung cấp
  • Bài viết
  • Từ 16 tuổi
  • 14 phút đọc
Bo vi điều khiển gắn cáp USB đặt cạnh đồng hồ LCD trên bàn phòng thí nghiệm.

Firmware đồng hồ có thể bắt đầu bằng Arduino core, thư viện màn hình và các phần xử lý giờ, thời tiết, cấu hình tách nhau. Thiết kế dưới đây dùng vòng lặp có lịch công việc và trạng thái rõ ràng. Mục tiêu là sửa từng phần mà vẫn giữ mặt giờ hoạt động.

Mục lục
Bắt đầu từ bo và màn đã chạy ví dụ độc lập.
  • Bo chạy firmware
  • Màn được thư viện hỗ trợ

Bạn sẽ học được gì

  • Chia mã theo trách nhiệm thay vì gom mọi việc vào phần vẽ.
  • Mô tả vòng lặp lấy dữ liệu theo lịch có giới hạn chờ.
  • Thiết kế menu nút bấm, báo thức và cập nhật OTA có đường phục hồi.

Cần biết trước

Nên đọc về giờ và thời tiết cùng cấu hình LCD; cần hiểu hàm và cấu trúc dữ liệu cơ bản.

Nên bắt đầu từ bộ công cụ nào?

Với bản mẫu đề xuất, chọn Arduino core cho bo được hỗ trợ và dùng các ví dụ chính thức làm điểm khởi đầu. Arduino có setup() chạy khi khởi động hoặc đặt lại. Ví dụ SimpleTime của Espressif minh họa WiFi.begin(), configTime() và getLocalTime() cho kết nối và giờ mạng.

Trước tiên hãy nạp ví dụ giờ mạng lên đúng bo, xem kết quả qua cổng nối tiếp, rồi thử ví dụ riêng của thư viện màn hình. Khi cả hai hoạt động độc lập mới ghép chúng. Ghi lại phiên bản core, thư viện, mã bo và cấu hình màn trong ghi chú cấu hình để có thể dựng lại bản đang chạy.

Dùng cổng nối tiếp để kiểm đường dữ liệu khi phát triển. Trong bản dùng hằng ngày, nút bấm chỉnh báo thức và đèn; mục “Cài Wi-Fi” mới mở trang cài đặt nội bộ qua điểm truy cập. Espressif có API cho chế độ trạm và điểm truy cập; trang biểu mẫu, việc xác nhận địa danh và cách đóng chế độ cài đặt vẫn là phần ứng dụng cần viết.

Đừng coi ví dụ có sẵn là toàn bộ sản phẩm. Một ví dụ chờ kết nối chỉ minh họa đường đi thành công. Đồng hồ thực tế còn cần hủy chờ, thử lại và cho phép người dùng thao tác khi mạng hỏng.

Ghép từng phần sau khi thử riêng, rồi kiểm lỗi.
  • Thử hiển thị
  • Thêm dữ liệu mạng
  • Khôi phục cấu hình

Chia mã và trạng thái ra sao?

Dùng một bản dữ liệu chung cho phần hiển thị, với trạng thái của từng nguồn. Cấu trúc đề xuất như sau.

Phần mãĐầu vàoKết quả
Cấu hìnhLựa chọn đã xác nhậnĐịa danh, tọa độ, múi giờ, đèn và lịch báo thức
Nút và menuSự kiện nhấn đã lọcChuyển mục, sửa bản tạm, lưu hoặc hủy
Báo thứcGiờ hợp lệ, lịch đã lưuLần hẹn kế tiếp, trạng thái reo hoặc báo lại
OTAYêu cầu cập nhật đã xác nhậnTiến độ, lỗi, phiên bản và trạng thái kiểm tra khởi động
Giữ giờĐồng hồ cục bộ và lần đồng bộGiờ cùng trạng thái hợp lệ
Thời tiếtTọa độ và phiên bản lựa chọnDữ liệu, dấu thời gian, trạng thái
Hiển thịBản dữ liệu và hồ sơ mànMặt đồng hồ hoặc trang cài đặt
NguồnTín hiệu nguồn được phần cứng hỗ trợChế độ đèn và lịch hoạt động

Preferences giữ dữ liệu qua khởi động lại và mất nguồn, phù hợp cho lựa chọn nhỏ. Trong thiết kế này, chỉ ghi khi người dùng xác nhận thay đổi. Giờ đang chạy và bộ đếm lập lịch được giữ ở trạng thái vận hành; lưu cấu hình không thay thế khối giữ giờ dự phòng.

Tách trạng thái đã lưu khỏi trạng thái đang sửa. Khi người dùng hủy cài đặt, dùng lại bản đã xác nhận. Khi lưu, kiểm địa danh và tọa độ trước khi thay bản đang dùng. Cách tổ chức này cho phép thử biểu mẫu mà chưa cần đổi phần vẽ mặt giờ.

Bộ hiển thị đọc bản dữ liệu, không tự tải mạng.
  • Giờ và độ hợp lệ
  • Thời tiết và độ cũ
  • Bản dữ liệu hiển thị
  • Bộ hiển thị

Vòng lặp nên chạy như thế nào?

Ưu tiên đọc nút và cập nhật mặt giờ trong khi những công việc mạng tiến từng bước theo thời hạn. Mã dưới đây là mã giả mô tả cấu trúc, chưa phải sketch nạp được; các hàm dịch vụ là giao diện cần triển khai cho bo và thư viện đã chọn.

setup:
  settings = loadConfirmedSettings()
  display.begin(screenProfile)
  buttons.begin(buttonProfile)
  drawUnavailableFace()
  alarm.restoreScheduleAndHandledOccurrence(settings)
  ota.runPendingBootSelfTest(settings, display, buttons, alarm)
  network.startIfConfigured(settings)

loop:
  events = buttons.pollDebouncedEvents()
  if alarm.isRinging():
    alarm.handleStopAndSnooze(events)
  else:
    menu.handle(events)
  network.tickUntilDeadline()
  timeService.tickIfDue()
  alarm.tick(timeService, monotonicClock, settings)
  weatherService.tickIfDue(settings.locationVersion)
  acceptOnlyValidCurrentLocationResults()
  ota.tickIfConfirmedAndAllowed(powerState, alarm, menu)
  view = makeSnapshot(timeService, weatherService, powerState, alarm, ota)
  redrawChangedRegions(view)
  lighting.applyPolicy(buttons, powerState)

Thiết kế tick nghĩa là mỗi lần gọi làm phần việc đã sẵn sàng rồi trả quyền điều khiển. Đặt tên hàm tick không tự làm một thư viện đồng bộ thành bất đồng bộ. Nếu HTTP đang chặn, đặt timeout hữu hạn và đo phản hồi nút trong lúc chờ; khi không đạt yêu cầu, dùng tác vụ mạng riêng hoặc API bất đồng bộ có ranh giới dữ liệu rõ ràng.

Yêu cầu HTTPS cần xác minh máy chủ theo khả năng thư viện đã chọn. Tài liệu Espressif mô tả dùng chứng chỉ gốc hoặc gói chứng chỉ để xác minh. Hãy chuẩn bị giờ hợp lệ và cấu hình chứng chỉ trước khi thử đường tải thời tiết; giữ lỗi xác minh là lỗi riêng để chẩn đoán, thay vì bỏ kiểm chứng chỉ.

Sau lỗi, lên lịch lần thử tiếp theo và trả về mặt đồng hồ. Khi thành công, thay bản dữ liệu đã kiểm tra rồi yêu cầu vẽ lại. Thời tiết không cần được tải lại trong từng lượt vẽ. Sau khi các đường lỗi cơ bản ổn định, thêm báo thức và OTA theo các trạng thái dưới đây; chế độ ngủ phải giữ đúng lịch đánh thức.

Công việc mạng phải trả quyền điều khiển để nút còn phản hồi.
  • Đọc thao tác
  • Tiến công việc sẵn sàng
  • Kiểm thời hạn
  • Trả về vòng lặp

Cài đặt và báo thức bằng nút bấm thế nào?

Thiết kế đề xuất dùng ba nút Menu, + và −, với thao tác lưu và hủy hiện ngay trên màn hình. Arduino giải thích hiện tượng dội nút: một lần bấm cơ học có thể bị đọc thành nhiều lần. Ví dụ Debounce dùng millis() theo dõi thời gian để lọc tín hiệu; ứng dụng vẫn phải tự phân biệt nhấn ngắn với giữ nút.

Trạng tháiMenu+−
Mặt giờNhấn mở menuBật đènXem lịch báo thức
Chọn mụcNhấn để mở; giữ để thoátMục tiếp theoMục trước
Sửa một trườngNhấn sang trường kế; giữ để hủyTăng giá trịGiảm giá trị
Trang xác nhậnNhấn để chọn Lưu hoặc HủyChuyển lựa chọnChuyển lựa chọn
Đang reoNhấn để dừng lần nàyBáo lạiBật đèn, giữ nguyên chuông

Menu đề xuất gồm Báo thức, Đèn nền, Giờ và múi giờ, Cài Wi-Fi, Cập nhật, Thông tin. Mỗi trang chỉ sửa một giá trị; tên trường và lựa chọn đang chọn luôn hiện rõ. Khi giữ Menu đã phát sinh lệnh hủy, lần thả nút không được tạo thêm lệnh nhấn ngắn. Chống dội và nhận dạng giữ nút dùng bộ đếm thời gian, tránh vòng chờ khiến mặt giờ ngừng cập nhật.

Để đặt báo thức, mở Báo thức, chọn Bật, chỉnh giờ, phút, ngày lặp và khoảng báo lại, rồi xác nhận Lưu. Bản đầu chỉ có một lịch, cho phép lặp hằng ngày hoặc chọn ngày trong tuần. Hủy hoặc hết thời gian thao tác sẽ bỏ bản sửa tạm; mặt giờ hiện lịch đã lưu cùng trạng thái bật/tắt. Dừng chuông chỉ kết thúc lần đang reo, còn Tắt báo thức là thay đổi lịch phải lưu.

Trong mô hình đề xuất, cấu hình gồm mã lịch, giờ địa phương, phút, ngày lặp, bật/tắt và khoảng báo lại. Preferences lưu cấu hình đã xác nhận. Trạng thái vận hành gồm chờ giờ hợp lệ, đã hẹn, đang reo và báo lại. Khi chưa có giờ hợp lệ, hiện “Chờ đặt giờ” và chưa kích chuông. Người dùng có thể đặt ngày, giờ và múi giờ bằng nút khi mất mạng; thời tiết vẫn báo chưa có dữ liệu.

Mỗi lần hẹn có khóa gồm mã lịch, ngày địa phương và giờ hẹn. Trước khi reo, ghi khóa đã xử lý rồi mới bật đầu ra âm thanh. Lựa chọn này ưu tiên tránh reo lặp sau khởi động lại, nhưng mất nguồn giữa hai bước có thể làm bỏ lỡ chuông; cần ghi rõ và thử giới hạn ấy.

Lần đồng bộ lùi giờ không xóa khóa. Với giờ bị bỏ qua khi đổi múi giờ hoặc chỉnh tiến đồng hồ, chính sách bản đầu là bỏ lần đã qua và hẹn lần kế tiếp, không reo bù. Giờ địa phương lặp lại khi đổi quy tắc giờ chỉ reo một lần theo khóa đó.

Báo lại dùng bộ đếm đơn điệu, tức bộ đếm đo thời gian trôi qua độc lập với việc chỉnh giờ lịch. Trong bản đề xuất, khởi động lại hủy lần báo lại và hiện thông báo, nhưng vẫn giữ lịch gốc. Bản đầu hoãn ngủ sâu trong lúc chờ báo lại. Chuông tự dừng khi đạt thời hạn reo đã cấu hình. Mất mạng khi giờ cục bộ còn hợp lệ không ngăn kiểm lịch; mất toàn bộ nguồn vẫn cần giải pháp giữ giờ riêng.

Wi-Fi và địa danh dài nhập ở trang cài đặt trên điện thoại. Chọn Cài Wi-Fi bằng nút mới mở chế độ này; màn hình hiện cách truy cập và cách thoát. Nút vẫn chỉnh được báo thức, giờ và đèn khi không có điện thoại. Đây là đặc tả thao tác cần triển khai, chưa phải thư viện menu có sẵn.

Thao tác nút sửa bản tạm; chỉ xác nhận mới thay lịch báo thức.
  • Cụm nút Menu, + và −
  • Trường đang sửa và xác nhận
  • Lịch báo thức đã lưu
  • Hủy về bản đã lưu

Cập nhật OTA để thêm chức năng và sửa lỗi ra sao?

OTA, viết đầy đủ là Over-the-Air update, cho phép thay firmware qua kết nối mạng mà không cắm cáp nạp mỗi lần. Espressif mô tả cập nhật ứng dụng vào vùng chưa được chọn khởi động, xác minh ảnh rồi mới đổi lựa chọn cho lần khởi động sau. Cấu hình cần ít nhất hai vùng ứng dụng OTA và vùng dữ liệu OTA.

Bản đề xuất chỉ cập nhật ứng dụng; giữ nguyên bootloader, bảng phân vùng và vùng cấu hình. Khi chuẩn bị bản nạp có dây đầu tiên, phải chọn bố cục flash đủ chứa bản hiện tại lẫn bản tiếp theo. Ghi đúng loại bo, LCD và phiên bản cấu trúc cấu hình vào hồ sơ phát hành. Một tệp dành cho màn khác có thể sai phần cứng dù cùng họ vi điều khiển, nên ứng dụng phải kiểm hồ sơ tương thích trước khi cài.

Một luồng cập nhật minh họa có thể đi qua các bước sau.

  1. Người dùng mở Cập nhật, xem phiên bản hiện tại, bản mới và ghi chú chức năng hoặc lỗi đã sửa. Kiểm tra phiên bản chỉ hiện thông báo; cài đặt cần xác nhận riêng.
  2. Chỉ cho cài khi dùng USB ổn định, không sửa cấu hình, không đang reo hoặc báo lại, và còn đủ khoảng trống trước lịch chuông kế tiếp. Nếu chưa đạt, hiện lý do hoãn và vẫn giữ mặt giờ.
  3. Tải từ địa chỉ phát hành đã cấu hình qua HTTPS có xác minh máy chủ. Espressif cũng hỗ trợ kiểm chữ ký ảnh firmware khi được cấu hình; bản sản phẩm đề xuất yêu cầu ảnh ký bằng khóa phát hành tin cậy. Phần khóa, chứng chỉ và kiểm chữ ký phải được triển khai trước khi mở tính năng.
  4. Kiểm bo đích, hồ sơ phần cứng, kích thước và tính hợp lệ của ảnh trước khi chấp nhận. Tải dở, lỗi xác minh hoặc sai hồ sơ phải báo lỗi; giữ bản hiện tại và cho thử lại, thay vì khởi động bản chưa đủ điều kiện.
  5. Khi ảnh đã được chấp nhận, kiểm lại điều kiện nguồn và báo thức trước khi khởi động lại. Sau khởi động, kiểm đọc cấu hình, vẽ màn, xử lý nút và khởi tạo báo thức; chỉ sau đó mới xác nhận bản mới hoạt động.

Rollback là quay về ứng dụng trước nếu bản mới không đạt kiểm tra. Espressif yêu cầu bật CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE cho cơ chế này; ứng dụng gọi esp_ota_mark_app_valid_cancel_rollback() khi kiểm tra đạt. Không suy ra Arduino core đang dùng đã bật rollback chỉ vì có chức năng tải OTA. Cần dựng và kiểm bootloader phù hợp, hoặc đánh dấu chưa hỗ trợ phục hồi tự động. Giữ cổng nạp có dây để phục hồi khi cơ chế OTA không dùng được.

Kiểm khởi động chỉ kiểm chức năng cục bộ, không đòi Internet hoặc thời tiết mới. Nếu cấu trúc cấu hình thay đổi, bản đề xuất đọc bản cũ và tạo bản chuyển đổi riêng; chỉ xác nhận sau khi kiểm tra đạt. Bản firmware trước vẫn phải đọc được cấu hình còn giữ lại khi rollback. Phục hồi ứng dụng cần được thử cùng dữ liệu, thay vì mặc định lưu Preferences sẽ giải quyết mọi thay đổi.

OTA có khoảng gián đoạn khi khởi động lại. Bản mẫu phải công bố giới hạn này và hoãn cập nhật quanh giờ báo thức theo cửa sổ đã cấu hình. Nếu cần bảo đảm chuông trong mọi lần mất nguồn hay cập nhật, phải thiết kế cơ chế báo thức độc lập; bài này chưa đưa ra bảo đảm đó.

Giữ bản trước trong lúc tải; xác nhận bản mới sau kiểm tra khởi động.
  • Tải từ nơi phát hành đã xác minh
  • Vùng nhận bản ứng dụng mới
  • Bản ứng dụng trước để phục hồi
  • Kiểm tra trước khi chấp nhận

Ví dụ trong đời sống

Bắt đầu bằng mặt giờ dùng dữ liệu giả. Thêm giờ mạng, thử rút Wi-Fi và xác nhận mặt giờ vẫn cập nhật. Tiếp theo thêm phản hồi thời tiết lưu sẵn để kiểm dữ liệu thiếu, rồi thay nguồn giả bằng HTTP thật. Cuối cùng lưu địa danh và khởi động lại để kiểm khôi phục.

Nếu nút bật đèn chỉ phản hồi sau khi HTTP hết thời hạn, đã tìm được chỗ chặn. Rút ngắn giới hạn chờ hoặc chuyển công việc mạng khỏi đường xử lý nút, rồi thử lại cùng tình huống. Đây là trình tự phát triển đề xuất, chưa phải firmware đã nạp trên phần cứng.

Tiếp tục ví dụ với một lịch báo thức đã lưu. Tắt Wi-Fi trước giờ hẹn, dùng + để báo lại và Menu để dừng lần reo tiếp theo. Sau đó thử bản OTA có lỗi đọc cấu hình trong môi trường kiểm thử: bản mới phải không được xác nhận; kiểm bản trước cùng lịch đã lưu sau phục hồi. Đây là các ca thử đề xuất, chưa phải kết quả trên bo.

Hiểu lầm thường gặp

Một tệp duy nhất luôn dễ phát triển nhất. Chia mã theo trách nhiệm giúp khoanh vùng phần cần sửa khi đổi màn hoặc API, như trong ví dụ trên.

Lưu mọi giá trị là sẽ khôi phục được giờ. Cấu hình đã lưu và đồng hồ tiếp tục chạy là hai cơ chế khác nhau.

Câu hỏi thường gặp

Có cần viết trình điều khiển màn từ đầu không?

Ưu tiên module có thư viện hỗ trợ đúng cấu hình. Chỉ tự viết driver khi yêu cầu phần cứng thực tế bắt buộc.

Có phải cắm cáp để thêm chức năng mới không?

Sau bản nạp có dây đầu tiên đã chuẩn bị OTA, người dùng có thể xác nhận cập nhật qua menu. Vẫn cần cáp khi phục hồi lỗi ngoài khả năng OTA hoặc đổi bố cục flash.

Mã giả có nạp trực tiếp lên bo được không?

Chưa. Nó xác định cấu trúc công việc; sketch thực tế cần mã bo, driver, kết nối và chứng chỉ phù hợp.

Thuật ngữ trong bài

Tên tiếng Việt, tên tiếng Anh và nghĩa dùng trong bài. Di chuột, chạm hoặc dùng phím Tab tới từ có gạch chân chấm để xem giải thích ngay khi đọc.

Preferences
Thư viện lưu cấu hình nhỏ qua khởi động lại.
setup()
Hàm chuẩn bị chạy một lần lúc bắt đầu sketch.
HTTPS
HTTP dùng kết nối TLS; xác minh máy chủ cần cấu hình chứng chỉ phù hợp.
OTAOver-the-Air update
Cập nhật firmware qua kết nối mạng thay cho cáp nạp ở mỗi lần phát hành.
RollbackApplication rollback
Cơ chế quay về ứng dụng trước khi bản mới không được xác nhận hoạt động, nếu đã bật và còn bản hợp lệ.
Chống dội nútDebounce
Lọc các chuyển trạng thái do tiếp điểm cơ học để nhận thao tác nhấn ổn định.

Bài này chưa được đánh dấu là đã xem

Thấy nội dung sai hoặc chưa an toàn?

Báo cho quản trị viên qua email. Thư có sẵn tên bài và đường dẫn.

Báo lỗi bài này
Xem tất cả bài của chủ đề

Nguồn

Các nguồn này viết cho người đọc lớn hơn. Nếu thấy khó, hãy đọc cùng người lớn.