RC Benchy 2.0 - Thuyền RC Mạnh Mẽ Để Kéo Dinghy hoặc SUP
Mô hình RC Benchy 2.0 mạnh mẽ này được thiết kế để kéo theo xuồng hoặc ván SUP. Phiên bản có động cơ dưới nước được đề xuất vì hiệu suất vượt trội và các cải tiến. Hướng dẫn chi tiết về các bộ phận cần thiết và mã code cho bộ điều khiển và bộ thu được cung cấp.
Mô tả
Mình khuyên mọi người nên in phiên bản có động cơ dưới nước, vì nó hoạt động tốt hơn hẳn và đã được tối ưu hóa thêm một chút.





Các bộ phận bạn cần để tự làm đều có ở đây:
Băng keo chống thấm: https://amzn.to/4jjP1x7
ESP 8266: https://amzn.to/43rXUhZ
Nút bấm chống nước: https://amzn.to/3H9MR5W
Bộ đôi pin: https://amzn.to/459B2GT
Pin kèm sạc: https://amzn.to/45kAqhu
2x ESC làm mát bằng nước: https://amzn.to/44Qbn5H
2x ESC không làm mát: https://amzn.to/4ms6nKU
2x Động cơ dưới nước: https://amzn.to/43aXXQB
Cánh quạt thuyền: https://amzn.to/4moC7R3
Sơn chống thấm Dichtol: https://amzn.to/43HND2z
Bảng mạch đục lỗ: https://amzn.to/43uxwnU
Chèn ren M4: https://cnckitchen.store/de/products/heat-set-insert-m4-x-8-1-50-pieces
Mã code cho điều khiển từ xa:
BẠN PHẢI THAY ĐỔI ĐỊA CHỈ IP THÀNH CỦA ESP NHẬN TRONG MẠNG CỦA BẠN!
#include <ESP8266WiFi.h>
#include <espnow.h>
// THAY THẾ BẰNG ĐỊA CHỈ MAC của bộ thu của bạn
uint8_t broadcastAddress[] = {0xEC, 0x64, 0xC9, 0xDE, 0x34, 0x2F};
// Cấu trúc để gửi dữ liệu
// Phải khớp với cấu trúc của bộ thu
typedef struct struct_message {
int l;
int r;
int f;
} struct_message;
// Tạo một struct_message có tên myData
struct_message myData;
// Hàm gọi lại khi dữ liệu được gửi
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
Serial.print("Trạng thái gói tin cuối cùng được gửi: ");
if (sendStatus == 0){
Serial.println("Giao hàng thành công");
}
else{
Serial.println("Giao hàng thất bại");
}
}
void setup() {
// Khởi tạo Serial Monitor
Serial.begin(115200);
pinMode(D6, INPUT); // Đặt chân liên kết làm đầu vào
pinMode(D5, INPUT); // Đặt chân bên phải làm đầu vào
pinMode(D7, INPUT); // Đặt chân tiến làm đầu vào
// Đặt thiết bị làm Wi-Fi Station
WiFi.mode(WIFI_STA);
// Khởi tạo ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Lỗi khi khởi tạo ESP-NOW");
return;
}
// Sau khi ESPNow được khởi tạo thành công, chúng ta sẽ đăng ký CB Gửi để
// nhận trạng thái của gói tin đã truyền
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
// Đăng ký peer
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
void loop() {
// Đặt các giá trị để gửi
myData.l = digitalRead(D6); // Đọc nút bên trái và lưu trạng thái vào Message
myData.r = digitalRead(D5); // Đọc nút bên phải và lưu trạng thái vào Message
myData.f = digitalRead(D7); // Đọc nút tiến và lưu trạng thái vào Message
Serial.print("Nút Trái: ");
Serial.println(digitalRead(D6));
Serial.print("Nút Phải: ");
Serial.println(digitalRead(D5));
Serial.print("Nút Tiến: ");
Serial.println(digitalRead(D7));
// Gửi tin nhắn qua ESP-NOW
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
delay(1);
}
Mã code cho bộ thu:
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <Servo.h>
Servo MotorL;
Servo MotorR;
int s = 40;
int g = 0;
int incomingl = 0;
int incomingr = 0;
int incomingf = 0;
const int rampdauer = 2000; // 1 giây tính bằng mili giây
const int resetZeit = 100;
// Ví dụ cấu trúc để gửi dữ liệu
// Phải khớp với cấu trúc của bộ thu
typedef struct struct_message {
int l;
int r;
int f;
} struct_message;
// Tạo một struct_message có tên myData
struct_message myData;
// Hàm gọi lại sẽ được thực thi khi dữ liệu được nhận
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&myData, incomingData, sizeof(myData));
incomingl = myData.l;
incomingr = myData.r;
incomingf = myData.f;
}
void setup() {
// Khởi tạo Serial Monitor
Serial.begin(115200);
MotorL.attach(5); // GPIO 5 là D1
MotorR.attach(4); // GPIO 4 là D2
MotorL.write(0);
MotorR.write(0);
// Đặt thiết bị làm Wi-Fi Station
WiFi.mode(WIFI_STA);
// Khởi tạo ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Lỗi khi khởi tạo ESP-NOW");
return;
}
// Sau khi ESPNow được khởi tạo thành công, chúng ta sẽ đăng ký CB nhận để
// lấy thông tin gói tin nhận được
esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));
}
void loop() {
if (incomingl != 0 || incomingr != 0 || incomingf != 0) {
// kiểm tra lệnh điều khiển tiến
if (incomingf == 1){
if (incomingr == 1 || incomingl == 1){
if (incomingl == 1){
GasRampL(s);
GasRampR(s/2);
g = 0;
Serial.println("Tiến và Trái");
delay(1);
}
if (incomingr == 1){
GasRampL(s/2);
GasRampR(s);
g = 0;
Serial.println("Tiến và Phải");
delay(1);
}
} else {
GasRampL(s);
GasRampR(s);
g = 0;
Serial.println("Tiến tới");
delay(1);
}
} else {
if (incomingl == 1){
GasRampL(s);
g = 0;
Serial.println("Chỉ Trái");
delay(10);
} else if (incomingr == 1){
GasRampR(s);
g = 0;
Serial.println("Chỉ Phải");
delay(1);
}
}
} else {
// Dừng động cơ khi không nhận được tín hiệu
MotorL.write(0);
MotorR.write(0);
Serial.println("Động cơ đã dừng");
delay(5);
}
}
void GasRampL(int zielWert) {
// Biến tĩnh cục bộ giữ nguyên giá trị giữa các lần gọi hàm
static unsigned long startZeit = 0; // Thời điểm bắt đầu điều khiển ramp
static unsigned long letzterAufruf = 0; // Thời điểm gọi hàm lần cuối
static int gasWert = 10; // Giá trị gas hiện tại đang tăng dần
// Tính toán thời gian hiện tại
unsigned long aktuelleZeit = millis();
unsigned long vergangeneZeit = aktuelleZeit - startZeit;
// Nếu hàm chưa được gọi trong hơn 50 ms, đặt lại giá trị gas
if (aktuelleZeit - letzterAufruf > resetZeit) {
gasWert = 10; // Đặt lại giá trị gas
startZeit = aktuelleZeit; // Bắt đầu thời gian ramp mới
}
// Kiểm tra xem thời gian ramp đã đạt đến chưa
if (vergangeneZeit < rampdauer) {
// Tính toán giá trị gas hiện tại tùy thuộc vào thời gian
gasWert = map(vergangeneZeit, 0, rampdauer, 10, zielWert);
} else {
// Nếu hết thời gian, giá trị gas sẽ giữ nguyên ở giá trị mục tiêu
gasWert = zielWert;
}
// In giá trị gas hiện tại
Serial.print("Giá trị Gas: ");
Serial.println(gasWert);
// Điều khiển động cơ (MotorL.write, nếu là servo hoặc PWM)
MotorL.write(gasWert);
// Cập nhật thời gian gọi lần cuối
letzterAufruf = aktuelleZeit;
}
void GasRampR(int zielWert) {
// Biến tĩnh cục bộ giữ nguyên giá trị giữa các lần gọi hàm
static unsigned long startZeit = 0; // Thời điểm bắt đầu điều khiển ramp
static unsigned long letzterAufruf = 0; // Thời điểm gọi hàm lần cuối
static int gasWert = 10; // Giá trị gas hiện tại đang tăng dần
// Tính toán thời gian hiện tại
unsigned long aktuelleZeit = millis();
unsigned long vergangeneZeit = aktuelleZeit - startZeit;
// Nếu hàm chưa được gọi trong hơn 50 ms, đặt lại giá trị gas
if (aktuelleZeit - letzterAufruf > resetZeit) {
gasWert = 10; // Đặt lại giá trị gas
startZeit = aktuelleZeit; // Bắt đầu thời gian ramp mới
}
// Kiểm tra xem thời gian ramp đã đạt đến chưa
if (vergangeneZeit < rampdauer) {
// Tính toán giá trị gas hiện tại tùy thuộc vào thời gian
gasWert = map(vergangeneZeit, 0, rampdauer, 10, zielWert);
} else {
// Nếu hết thời gian, giá trị gas sẽ giữ nguyên ở giá trị mục tiêu
gasWert = zielWert;
}
// In giá trị gas hiện tại
Serial.print("Giá trị Gas: ");
Serial.println(gasWert);
// Điều khiển động cơ (MotorR.write, nếu là servo hoặc PWM)
MotorR.write(gasWert);
// Cập nhật thời gian gọi lần cuối
letzterAufruf = aktuelleZeit;
}
Giấy phép
File mô hình
Chưa có bản in nào được khoe. Hãy là người đầu tiên!
Chưa có bình luận nào. Hãy là người đầu tiên!