Xe điều khiển từ xa (DRIFT) sử dụng NRF24L01 - Kết hợp với Hoodloader2 để đảm bảo sự truyền nhận THỜI GIAN THỰC

Thực sự thì với bài viết này, mình đã được truyền cảm hứng về làm một chiếc xe điều khiển từ xa với sóng vô tuyến 2.4GHz. Mình rất thích xe điều khiển từ xa, vì vậy, mình đã cùng với ksp và nhóm của bạn ý hoàn thiện chiếc xe đầu tiên trong cộng đồng sử dụng sóng vô tuyến 2.4GHz này.

Mình rất hi vọng Nguyen Manh Hung và các bạn khác sẽ có những bài viết hướng dẫn về những module như thế này nữa, để từ đó mình có thể được truyền cảm hứng và liên lạc với bạn bè mình để hoàn thiện nên những sản phẩm bá đạo. 

Xe của mình DRIFT được đó nhé!

I. Bản thiết kế xe

Đây là bản thiết kế xe của nhóm khá hay của nhóm ksp và mình. Với việc thiết kế chỉ dựa trên một miếng mica 10li duy nhất, khiến cho xe rất nhẹ nhưng lại cứng, không cần bất cứ vật hỗ trợ gì hết ngoài 4 gá offroad với sự gọt giũa cả của nhóm.

Các bạn có thể download tại https://github.com/ngohuynhngockhanh/NRFcar_Hoodloader (tìm nút Download Zip bằng tổ hợp phím Ctrl + F).

Nhớ là phải có phần mềm Sketchup để xem nhé bạn hiền!

Các bạn phải làm theo thiết kế của mình mới drift được nhé, còn không thì chỉ tới, lui, trái, phải thôi nha heart.

1. Tổng quan

Các bạn xem tại file sketchup/1.skp

Mặt cắt trên

Mặt cắt dưới

Mặt cắt trái

2. Phần gá hộp số

Nhóm 14CNTN1 của ksp đã thiết kế xe theo chiều hướng offroad với độ nghiêng so với đường vuông góc với mặt phẳng ngang là 45 độ. Gồm có 2 file là sketchup/print 3d.skpsketchup/print 3d - flip.skp

Xem nhóm mình gắn như hình này là ok à.

3. In 3D

Các bạn sẽ in file 2 file sketch/print 3d.stl và 2 file sketch/print 3d - flip.stl. Tỉ lệ 1:1, nhựa PLA, màu nào cũng được nha heart. Các bạn gửi cho dịch vụ in và nói như vậy là họ in. Còn bạn nào có sẵn máy in 3D thì dùng file .x3g trong đó nhé.

4. Cắt laser

Các bạn chỉ cần ra tiệm quảng cáo cắt laser để cắt 1 trong các file sketch/1.2004.dxf hoặc sketch/1.2007.dxf hoặc sketch/1.2010.dxf. Mình đã xuất ra các định dạng theo phiên bản autocad 2004, 2007 và 2010. Tất nhiên, corel vẫn dùng được nhé.

Các bạn sẽ cắt trên mica 10li trong suốt cho đẹp, tỉ lệ 1:1.

II. Danh sách thiết bị cần chuẩn bị

  • 2 mạch Arduino UNO hoặc Arduino Mega 2560 đã được lên đời Hoodloader2. Các mạch khác không chơi với bài của mình được nhé!
  • 2 mạch NRF24L01
  • 4 nút bấm tới, lùi, trái, phải.
  • 1 biến trở để chỉnh góc cua và 1 điện trở 1k để hạn dòng cho biến trở không quá nóng.
  • 3 con đèn LED để báo góc cua.
  • 3 con điện trở cho đèn LED.
  • 2 gá print 3d và 2 gá print 3d - flip (xem ở trên)
  • 1 bảng mica khung (xem ở trên)
  • Mỏ hàn
  • Chì hàn
  • 4 hộp số vàng
  • 4 bánh xe vàng
  • Module L298D để điều khiển động cơ.
  • 1 cục pin Lipo cho xe (khuyên dùng loại 11.1V - dung lượng tùy sở thích của bạn)
  • 1 cục pin dự phòng cho mạch phát.
  • Breadboard hoặc hàn mạch như mình.
  • Dây breadboard đực và cái.

III. Lắp mạch

Mega làm tương tự nhé heart.

1. Mạch thu - Atmega16u2

Thật ra là frizting không có vẽ được mấy cái hình nào liên quan đến con Atmega16u2 này nên mình sẽ viết ra thành bảng cho các bạn dễ nối nhé.

Atmega16U2 NRF24L01
PB1 SCK (5)
PB2 MOSI (6)
PB3 MISO (7)
PB4 CE (3)
PB5 CS (4)
3.3V VCC (2)
GND GND (1)

HOT: Một đèn LED sẽ được nối ở chân PB6 để báo là nhận được tín hiệu là đang nhận dữ liệu! Nếu nó sáng liên tục => tín hiệu đều. Nếu nó nháy chớp chớp => bạn bị nhiễu hãy gắn thêm tụ 104 và một tụ gốm có điện dung khác giữa VCC và GND.

2. Mạch thu - Atmega328p

Bạn sẽ nối 2 cầu (hộp số song song như hình nhé).

Cầu trái = Cầu A = Cầu 1; Cầu phải = Cầu B = Cầu 2

3. Mạch phát - Atmega16u2

Tương tự mạch thu. Bạn kéo lên xem nhé heart.

4. Mạch phát - Atmega328p

5. Thành quả sau khi lắp đặt các mạch

IV. Lập trình

1. Nạp cho mạch phát - ATmega16U2

#include <SPI.h>
#include "RF24.h"


const byte LED = 6;
const uint64_t pipe = 0xE8E8F0F0E1LL; // địa chỉ để phát
RF24 radio(4, 5);
byte msg[1];
int value = 0;

 
void setup(){ 
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
  Serial1.begin(115200);
  //============================================================Module NRF24
  radio.begin();                     
  radio.setAutoAck(1);               
  radio.setRetries(1,1);       
  radio.setPALevel(RF24_PA_MAX);      // Dung lượng tối đa
  radio.setChannel(10);               // Đặt kênh
  radio.openWritingPipe(pipe);        // mở kênh

  
}
 
void loop(){
  while (!Serial1.available())
    digitalWrite(LED, LOW);
  msg[0] = Serial1.read();
  radio.write(&msg, sizeof(msg));
  digitalWrite(LED, HIGH);
}

2. Nạp cho mạch phát - ATmega328p

const byte BUTTON_PIN[] = {2, 3, 4, 5};
const byte LED_PIN[] = {6, 7, 8};
const int SENSOR_PIN = A5;
const int MIN_SENSOR = 769;
const int MAX_SENSOR = 1023;
const int RANGE_SENSOR = MAX_SENSOR - MIN_SENSOR;
byte msg;
void setup() {
  Serial.begin(115200);
  for (byte i = 0; i < sizeof(BUTTON_PIN); i++)
    pinMode(BUTTON_PIN[i], INPUT_PULLUP);
  for (byte i = 0; i < sizeof(LED_PIN); i++)
    pinMode(LED_PIN[i], OUTPUT);
  
}

void loop() {
  static int oldSensorValue = 0;
  msg = 0;
  for (byte i = 0; i < sizeof(BUTTON_PIN); i++)
    msg |= (1 && !digitalRead(BUTTON_PIN[i])) << i;
  Serial.write(msg);

  int value = analogRead(SENSOR_PIN);
  if (abs(value - oldSensorValue) > 2) {
    oldSensorValue = value;
    value -= MIN_SENSOR;
    msg = 1 << 7;
    //tắt hết đèn
    for (int i = 0; i < sizeof(LED_PIN); i++)
      digitalWrite(LED_PIN[i], LOW);
    for (int i = 0; i < sizeof(LED_PIN); i++) {
      if (value <= (i + 1) * RANGE_SENSOR / 3) {
        digitalWrite(LED_PIN[i], HIGH);
        msg |= 1 << i;
        Serial.write(msg);
        break;
      }
    }
  }
}

Các bạn nhớ thay đổi giá trị chỗ MIN_SENSOR với MAX_SENSOR cho phù hợp với biến trở của bạn nhé heart. Biến trở này sẽ giúp bạn điều chỉnh góc cua của mình, thử vặn biến trở và nhấn tổ hợp phím tới - trái; lùi - trái; tới - phải; lùi phải là bạn sẽ hiểu ý mình ngay mà.

3. Nạp cho mạch thu (xe) - ATmega16U2

#include <SPI.h>
#include "RF24.h"

const unsigned long DELAY_TIME = 50;
const byte LED = 6;

unsigned long timer = 0;
 
const uint64_t pipe = 0xE8E8F0F0E1LL; // địa chỉ phát
RF24 radio(4, 5);

byte msg[1];


 
void setup(){
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
  Serial1.begin(115200); //serial for atmega328p side
  radio.begin();                     
  radio.setAutoAck(1);              
  radio.setChannel(10);               // Đặt kênh
  radio.openReadingPipe(1,pipe);     
  radio.startListening();        
}
 
void loop(){
  if (radio.available()){
    while (radio.available()){
      radio.read(&msg, sizeof(msg));
      Serial1.write(msg[0]);   
      digitalWrite(LED, HIGH);
    }
    timer = millis();
  }
  
  if (millis() - timer > DELAY_TIME) {
    timer = millis();
    Serial1.write(char(0));
    digitalWrite(LED, LOW);
  }
}

4. Nạp cho mạch thu (xe) - ATmega328p

Nếu xe nó không đi tới, lùi, trái, phải như mình đã code dưới đây, các bạn hãy chỉnh lại dây nối ở chỗ module cầu H L298 nhé.

byte forward = 0;
byte backward= 0;
byte left = 0;
byte right = 0;
byte threshold = 30;


const int motorA1 = 3;
const int motorA2 = 5;
const int motorB1 = 6;
const int motorB2 = 9;
const int MAX_SPEED = 255;

void setup() {
  Serial.begin(115200);
  pinMode(motorA1, OUTPUT);
  pinMode(motorA2, OUTPUT);
  pinMode(motorB1, OUTPUT);
  pinMode(motorB2, OUTPUT);
}

void moveForward() {
  digitalWrite(motorA1, HIGH);
  digitalWrite(motorA2, LOW);
  digitalWrite(motorB1, HIGH);
  digitalWrite(motorB2, LOW);
}

void moveBackward() {
  digitalWrite(motorA2, HIGH);
  digitalWrite(motorA1, LOW);
  digitalWrite(motorB2, HIGH);
  digitalWrite(motorB1, LOW);
}

void rotateRight() {
  digitalWrite(motorA2, HIGH);
  digitalWrite(motorA1, LOW);
  digitalWrite(motorB2, LOW);
  digitalWrite(motorB1, HIGH);
}

void rotateLeft() {
  digitalWrite(motorA1, HIGH);
  digitalWrite(motorA2, LOW);
  digitalWrite(motorB1, LOW);
  digitalWrite(motorB2, HIGH);
}

void turnRight() {
  digitalWrite(motorB1, HIGH);
  digitalWrite(motorB2, LOW);
  analogWrite(motorA1, threshold);
  digitalWrite(motorA2, LOW);
  
}

void turnLeft() {
  digitalWrite(motorA1, HIGH);
  digitalWrite(motorA2, LOW);
  analogWrite(motorB1, threshold);
  digitalWrite(motorB2, LOW);
}

void backRight() {
  digitalWrite(motorB2, HIGH);
  digitalWrite(motorB1, LOW);
  analogWrite(motorA2, threshold);
  digitalWrite(motorA1, LOW);
  
}

void backLeft() {
  digitalWrite(motorA2, HIGH);
  digitalWrite(motorA1, LOW);
  analogWrite(motorB2, threshold);
  digitalWrite(motorB1, LOW);
}

void stop() {
  digitalWrite(motorA1, LOW);
  digitalWrite(motorA2, LOW);
  digitalWrite(motorB1, LOW);
  digitalWrite(motorB2, LOW);
}

void controlMotor() {
  if (forward == 1 && left == 1) {
    turnLeft();
  } else if (forward == 1 && right == 1) {
    turnRight();
  } else if (backward == 1 && left == 1) {
    backLeft();
  } else if (backward == 1 && right == 1) {
    backRight();
  } else if (forward == 1) {
    moveForward();
  } else if (backward == 1) {
    moveBackward();
  } else if (left == 1) {
    rotateLeft();
  } else if (right == 1) {
    rotateRight();
  } else 
    stop();
}

void loop() {
  while (Serial.available()) {
    byte msg = Serial.read();
    if ((msg >> 7) == 0) { //di chuyển
      forward = 1 & (msg >> 3);
      backward = 1 & (msg >> 2);
      left = 1 & (msg >> 1);
      right = 1 & msg;
      controlMotor();
    } else { //tốc độ
      threshold = 20;
      for (int i = 2; i >= 0; i--, threshold += 10)
        if (((msg >> i) & 1) == 1)
          break;
    }
  }
}

V. Vì sao xe drift được?

Xe drift được là vì khi không gửi tín hiệu nữa thì ở atmega16u2 (xe) vẫn gửi tín hiệu là chạy trong 50ms. Như vậy, sẽ tạo trớn cho xe, nên khi xe bẻ góc cua thì vẫn còn trớn và đổi chiều đột ngột ở một bên cầu khiến xe bị trượt bánh (drift). Tuy nhiên, bởi vì bánh xe không có cơ cấu bẻ góc bằng servo nên chỉ có thể drift trên các sàn có độ bám đường không quá cao như sàn gạch men, sàn gạch cũ, sàn gỗ nói chung là không phải xi măng và nhựa đường, còn các vật liêu khác, mỏng, trơn đều chơi được hết.

VI. Bật mí nhỏ

Xe này phần lớn là do nhóm ksp làm, mình chỉ đề xuất để thêm vào phần hoodloader nhằm làm xe drift được. Vừa qua nhóm bạn ý đã giành được giải nhất cuộc thi đua xe điều khiển bằng sóng 2.4GHz với module NRF24L01 - có tên gọi là Racer Hero. Chúc mừng nhóm bạn ý và mình nào. Yay!

Youtube: 
Test xe lần đầu tiên của cả nhóm
Drift test
Bài viết truyền cảm hứng: 
lên
17 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ả

Hoodloader2 - Sức mạnh của 2 vi điều khiển trên một board mạch Arduino - USB Host với Arduino UNO / Mega2560

Trước đây, bạn đã từng đặt câu hỏi, cái con Atmega16U2 trên mạch Arduino Uno / Mega2560 của mình làm nhiệm vụ gì chưa? Nếu bạn đã từng đọc bài giới thiệu về Arduino Uno hay Arduino Mega 2560 thì có thể sẽ biết con Atmega16U2 đó sẽ làm nhiệm vụ USB-to-Serial, hay nói cách khác là tạo cổng COM ảo từ đó lập trình cho con vi điều khiển Atmega328p (UNO) hoặc Atmega1280 (Mega 2560). Nhưng khi mình tra datasheet con Atmega16U2 thì thấy rằng, chúng ta đang có một sự lãng phí lớn (12KB flash, 512byte ram) nhưng chỉ nạp bootloader DFU để biến nó thành một mạch USB-to-Serial. Đem vấn đề này đi hỏi ksp, thì mình đã được khai sáng bằng một bootloader với cho con Atmega16U2 này, nó có tên là Hoodloader2 và nó sẽ giúp ta biến con Atmega16U2 này thành một mạch Arduino hoàn chỉnh! Nói cách khác, với Hoodloader2, ta có thể làm việc với 2 con vi điều khiển trên mạch Arduino Uno / Mega2560. HACK NÃO chưa nào?

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

Sử dụng WinSCP, Notepad++ để chỉnh sửa file cho Orange Pi, Raspberry Pi, Intel Galileo, Intel Edison hay bất kỳ máy tính nào

WinSCP là một tiện ích SFTP, FTP client miễn phí, mã nguồn mở dành cho hệ điều hành Windows. Nó sẽ giúp bạn truyền tải file một cách an toàn giữa máy tính nhúng của bạn và máy tính từ xa. Dựa vào đó, WinSCP cũng cung cấp các chức năng quản lí file. Chương trình có hỗ trợ cả SSH để bảo mật, cũng như hỗ trợ phương thức SCP.

lên
3 thành viên đã đánh giá bài viết này hữu ích.
Từ khóa: