Introduction: Remote Controlled Car

Voor dit project heb ik een afstand bestuurbare auto gemaakt. Ik ben altijd gefascineerd geweest door draadloze verbindingen, hoe twee losse systemen zonder fysieke verbinding alsnog met elkaar kunnen communiceren.

Vandaar dat ik voor dit project gefocust heb op deze draadloze verbinding. Hierbij hoort het maken van een controller en een auto die naar commando’s luistert.

Verder bevat de auto zelf een geautomatiseerd licht systeem. Als het te donker wordt gaan automatisch de lampen aan. Bij het opstarten van de auto wordt de lichtsensor gekalibreerd om extra accuraat te zijn. Deze lamp kan ook met de hand aan/uit gezet worden via de controller. Als laatste bevat de auto ook nog een toeter, die ook via de controller bedient kan worden.


Reflectie:

Tijdens dit project heb ik geleerd hoe ik moet 3D printen. Ik heb zelf mijn eigen printer in elkaar gezet en ingesteld. Deze heb ik vervolgens gebruikt om alle onderdelen te printen. Dit was ook de eerste keer dat ik mijn eigen printmodellen gemaakt heb! Persoonlijk vond ik het wel eerst heel lastig om met precieze afmetingen te werken, elke centimeter moet kloppen willen je onderdelen op het frame passen.

Verder kan ik ook afraden om cyanoacrylaatlijm te gebruiken. De dampen die van de lijm af kwamen hebben ervoor gezorgd dat de ramen vol met witte vlekken zitten, deze vlekken gaan er helaas niet meer af.

Verder was solderen ook best lastig. Ik had thuis een soldeerbout, maar de punt was redelijk groot en grof. Hierdoor was het heel lastig om precies te zijn, maar met vaak genoeg proberen is het gelukkig toch prima gelukt.

Ook kan ik aanraden om zo vroeg mogelijk te beginnen. Ik had de auto voor een groot deel afgemaakt en ben daarna weken gestopt. Hierdoor moest ik op het laatste moment snel nog alles afmaken en in elkaar gooien om tot een mooi eindproduct te komen.

Supplies

Hieronder is een lijst te vinden met alle onderdelen die gebruikt zijn voor het samenstellen van de auto en de controller:


Controller:

  • 1x Arduino Uno
  • 1x Joystick shield
  • 1x nRF24L01 (kan zowel met als zonder antenne)
  • 1x Power bank
  • 1x Arduino voedingskabel

Auto:

  • 1x Arduino Uno
  • 1x Motor Driver Module - L298n Dual H-bridge
  • 2x 3-6V DC motor met ingebouwde versnellingsbak
  • 2x Gepaste wielen voor de bestelde DC motors
  • 1x Klein zwenkwieltje
  • 1x nRF24L01 (liefste met antenne voor extra bereik)
  • 1x 9v batterijconnector
  • 1x 9v batterij
  • 1x Arduino prototyping shield
  • 1x Photoresistor
  • 1x LED lampje
  • 2x 220 Ohm weerstanden
  • Stuk karton (optioneel)
  • Ducttape (optioneel)
  • 2x elastiek

Overig gereedschap & onderdelen:

  • Stevige lijm (heb persoonlijk cyanoacrylaatlijm gebruikt, niet aan te raden)
  • 3D printer
  • Rol PLA
  • Soldeerbout
  • Soldeertin
  • Een voorraad male-to-female en male-to-male draden
  • Schroevendraaier
  • Doorzichtig plastic

Step 1: De Controller

De controller bestaat uit 3 hoofdonderdelen. De Arduino, de joystick shield en de tranceiver. Deze 3 onderdelen kunnen direct op elkaar vastgeklikt worden. De joystick shield bevat namelijk een aansluiting voor de transceiver (deze is te vinden naast de joystick).


Aangezien de controller uit 3 kant-en-klare onderdelen bestaat, betekent het ook dat er al code voor bestaat. Deze code zorgt ervoor dat alle knoppen en de joystick functioneren, en dat alles via de transceiver gestuurd wordt. De code is te vinden op de volgende website:

https://www.viralsciencecreativity.com/post/arduino-wireless-joystick-car-nrf24l01-transceiver


Probeer de controller te testen met het gegeven script. Dit script print automatisch welke knoppen er zijn ingedrukt. Kijk of alle knoppen functioneren en of de joystick X en Y waardes werken.


Als de joystick shield volledig werkt kan je beginnen met het printen van het omhulsel. Ik raadt het aan om het omhulsel met 100% infill te printen, aangezien de wanden niet heel dik zijn. Net zoals de code, kan het omhulsel online gevonden worden:

https://www.instructables.com/Universal-Arduino-Controller/

Step 2: Auto Onderdelen Testen (iteratie 1)

Nu is het tijd om de onderdelen van de auto te testen. Als eerste moeten alle onderdelen aangesloten worden zoals te zien op de fritzing diagram. De wielen, antenne, licht systeem en buzzer kunnen allemaal los van elkaar getest worden.


Hieronder is de code te vinden voor de auto en alle bijbehorende onderdelen (comments heb ik in het engels gedaan, omdat de code zelf ook engels is):

Hoofd script:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <LinkedList.h>
RF24 radio(9, 10); // CE, CSN
const byte address[6] = "11101";


void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();


setupBuzzer();
lightSetup();
wheelSetup();
}


void loop()
{
  
  if (radio.available())              //Looking for the data.
  {
    char text[32] = "";                 //Saving the incoming data
    radio.read(&text, sizeof(text));    //Reading the data


    //If the data contains a direction, send it to the wheels
    if(text[0] == 'X') {
        driveWheels(text);
    }


    //Check if buttons are pressed
    handleButtons(text);
  } else {
    
  }


  //Do button actions if pressed
  activateButtons();
  //Handle automated light
  handleLights();


  //Slight delay for the radio to work properly
  delay(50);
}


Knoppen afhandelen:

int buzzerPin = A2;
float buzzerValue = 0.0;


float lightPressedValue = 0.0;
bool lightButtonJustPressed = false;


//Buttons use a float value to see if they are pressed or not.
//This is to filter out any missed radio transmissions. Otherwise the buzzer would constantly turn on anf off. Now it waits a bit before turning off.


void setupBuzzer() {
  pinMode(buzzerPin, OUTPUT);
}


void handleButtons(String data) {
  if(data == "up") {
    lightPressedValue = 1.0;
  }


  if(data == "left") buzzerValue = 1.0;
  buzzerValue -= 0.1;


  if(buzzerValue > 0.6) {
    digitalWrite(buzzerPin, HIGH);
  } else {
    digitalWrite(buzzerPin, LOW);
  }
}


void activateButtons() {
  lightPressedValue = lightPressedValue - 0.1;


  if(lightPressedValue >= 0.5) {
    if(!lightButtonJustPressed) {
      lightButtonJustPressed = true;
      lightButtonPressed();
    }
  } else {
    lightButtonJustPressed = false;
  }
}


Motor besturing:

// Motor A connections
int enA = 6;
int in1 = 4;
int in2 = 5;
// Motor B connections
int enB = 3;
int in3 = 7;
int in4 = 2;


//Motor rotation speed is calculated based on the X and Y values of the joystick. Since there are only 2 wheels, we need to control forwards and backwards movement as well as rotational movement simultaneously.


void wheelSetup() {
  // Set all the motor control pins to outputs
  pinMode(enA, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  
  // Turn off motors - Initial state
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);
}




void driveWheels(String data) {
  int ind1 = data.indexOf('Y');


  String xString = data.substring(0,ind1);
  xString.remove(0,1);
  float x = xString.toFloat();


  String yString = data.substring(ind1+1,data.length());
  float y = yString.toFloat();


  if(abs(x-512) < 50 && abs(y-512) < 50) {
    setLeftMotor(0);
    setRightMotor(0);
    return;
  } else {
    driveLeftWheel(x,y);;;;;;
    driveRightWheel(x,y);
  }
}


void driveLeftWheel(float x, float y) {
  float yOffset = y-512;
  float rotationSpeed = map(yOffset,-512,512,-100,100);


  float xOffset = x-512;
  rotationSpeed = constrain(rotationSpeed + map(xOffset,-512,512,-100,100),-100,100);


  if(rotationSpeed > 0) {
    setLeftMotor(1);
  } else {
    setLeftMotor(2);
  }


  setLeftMotorSpeed(rotationSpeed);
}


void driveRightWheel(float x, float y) {
  float yOffset = y-512;
  float rotationSpeed = map(yOffset,-512,512,-100,100);


  float xOffset = x-512;
  rotationSpeed = constrain(rotationSpeed - map(xOffset,-512,512,-100,100),-100,100);


  if(rotationSpeed > 0) {
    setRightMotor(1);
  } else {
    setRightMotor(2);
  }


  setRightMotorSpeed(rotationSpeed);
}


void setLeftMotorSpeed(float rotationSpeed) {
  float analogSpeed = map(abs(rotationSpeed),0,100,0,255);
  if (analogSpeed < 100) analogSpeed = 50;
  analogWrite(enA, analogSpeed);
}


void setRightMotorSpeed(float rotationSpeed) {
  float analogSpeed = map(abs(rotationSpeed),0,100,0,255);
  if (analogSpeed < 100) analogSpeed = 50;
  Serial.println(analogSpeed);
  analogWrite(enB, analogSpeed);
}


void setLeftMotor(int state) {
  if (state == 0) {
    //Motor off
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  } else
  if (state == 1) {
    //Motor forward
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  } else
  if (state == 2) {
    //Motor backward
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  }
}


void setRightMotor(int state) {
  if (state == 0) {
    //Motor off
    digitalWrite(in3, LOW);
    digitalWrite(in4, LOW);
  } else
  if (state == 1) {
    //Motor forward
    digitalWrite(in3, HIGH);
    digitalWrite(in4, LOW);
  } else
  if (state == 2) {
    //Motor backward
    digitalWrite(in3, LOW);
    digitalWrite(in4, HIGH);
  }
}


LED besturing:

int LIGHT_PIN = 8;
int LIGHT_SENSOR_PIN = A0;


enum LIGHT_MODE {
  OFF,
  ON,
  AUTOMATIC
};


LIGHT_MODE currentMode = AUTOMATIC;


LinkedList<int> sensorValues = LinkedList<int>();
int MEASUREMENT_COUNT = 10;


bool lightsOn = false;
int lightTriggerValue = 0;


void lightSetup() {
  pinMode(LIGHT_PIN, OUTPUT);
  pinMode(LIGHT_SENSOR_PIN, INPUT) ;
}




void lightButtonPressed() {
  switch(currentMode) {
    case OFF:
    currentMode = ON;
    setLights(true);
    break;
    case ON:
    currentMode = AUTOMATIC;
    blinkLights(3);
    break;
    case AUTOMATIC:
    currentMode = OFF;
    setLights(false);
    break;
  }
}


void setLights(bool on) {
  lightsOn = on;
  
  if(on) {
    digitalWrite(LIGHT_PIN, HIGH);
  } else {
    digitalWrite(LIGHT_PIN, LOW);
  }
}


void handleLights() {
  readSensor();
  setLights();
}


void readSensor() {
  if(sensorValues.size() >= MEASUREMENT_COUNT) {
    sensorValues.pop();
    //Calibrate light on startup for accurate readings
    if(lightTriggerValue == 0) lightTriggerValue = getAverageLightValue();
  }


  sensorValues.add(analogRead(A0));
}


void setLights() {
  if(currentMode != AUTOMATIC) return;


  int averageValue = getAverageLightValue();


  //Light requires a bit more light to turn on, then to turn off. This is due to the internal light turning on. This prevents the light sensor from triggering itself by turning on the LED.
  if(averageValue < lightTriggerValue-3) {
    setLights(true);
  } else if(averageValue > lightTriggerValue-1) {
    setLights(false);
  }
}


//Light sensor uses the 10 most recent values to filter out any extreme values.
int getAverageLightValue() {
  int averageValue = 0;
  for(int i = 0; i < sensorValues.size(); i++) {
    averageValue += sensorValues.get(i);
  }
  return averageValue/sensorValues.size();
}


void blinkLights(int amount) {
  for(int i = 0; i < amount; i++) {
    setLights(true);
    delay(100);
    setLights(false);
    delay(100);
  }
}


LET OP:

  • Het adres van de transceiver moet zowel in de controller code als in de code van de auto EXACT hetzelfde zijn. De twee transceivers kunnen alleen met elkaar communiceren als dit adres hetzelfde is.
  • De antenne heeft 3.3V nodig heeft, geen 5V.
  • Installeer de nRF24 library. Download de ZIP, open de editor, schets -> bibliotheek gebruiken -> importeer .zip. https://github.com/nRF24/RF24

Step 3: Solderen (tussenstap)

Als alle onderdelen getest zijn kan de photoresistor en de LED gesoldeerd worden aan het prototyping shield. Als je het prototyping shield loshaalt van de Arduino kan je makkelijk via de onderkant alles vastmaken.

Let op dat de onderdelen aan de juiste pinnen zijn aangesloten! (controleer de fritzing diagram)

Step 4: Eerste Prototype (iteratie 2)

Nu alle onderdelen getest zijn, kunnen we het eerste prototype in elkaar zetten. Probeer een auto te maken met een stuk karton en ducttape.

Maak de motoren vast aan de onderkant van de auto en klik de wielen erop. Tape de Arduino en motor driver ook vast. Sluit alle kabels aan. Sluit de batterij aan en kijk of alle onderdelen nog werken.

Als alle kabels goed zijn aangesloten, zou je nu een werkende auto moeten hebben!

Step 5: Plastic Frame (iteratie 3)

Nu de kartonnen auto werkt, is het tijd om een echt frame te maken. Het frame dat ik voor dit project ontworpen heb bestaat uit 3 delen. 1 deel is het frame zelf. Deze bevat ruimte voor de Arduino, de motor driver en een houder voor de 9V batterij.

De overige 2 delen zijn stukken waar de DC motoren aan verbonden worden (zie afbeelding). Deze 2 verbindstukken kunnen met lijm verbonden worden aan het frame. De verbindstukken hebben 2 uitsteeksels, deze passen perfect in de 4 gaten die zich bevinden op het frame.

De Arduino kan vastgemaakt worden op de achterkant. De motor driver wordt vastgehouden door de kabels die via onder naar de motoren gaan. Ook kan de 9V connector in de batterijhouder gelegd worden. De kabels kunnen via een gat aan de zijkant naar de motor driver.


Print het frame met 100% infill, het is belangrijk dat deze erg stevig is. De motor verbindingstukken kunnen op 50% infill geprint worden.

Step 6: Overkoepeling (Iteratie 4)

Nu alle onderdelen op de auto geïnstalleerd zijn, is het tijd om een overkoepelend deel te printen. Deze moet je ondersteboven printen om het minste plastic te gebruiken voor supports. Het frame moet met 100% infill geprint worden.

Nadat het frame klaar is, kan je het doorzichtige plastic erbij pakken. Knip dit in stukken die netjes passen in alle ramen. Alle ramen die dicht kunnen hebben een klein randje aan de binnenkant, op dit randje kan het plastic neergelegd worden. Gebruik lijm om de ramen te bevestigen.

Vervolgens kan ook de antenne aan de bovenkant verbonden worden. Hiervoor moet je wel eerst alle kabels uit de antenne halen. Nadat je de antenne door de bovenkant gehaald hebt, kan je de kabels weer vast maken.


Om de overkoepeling vast te houden kan je elastiekjes om de haakjes in het frame vastmaken. Deze kan je vervolgens aan de voorkant om de overkoepeling halen om deze vast te maken.

Step 7: Klaar!

Hieronder is een video met het eindresultaat! Veel rijplezier!