Web
Analytics
 

The RF Nano Controller Upgrade! Building your own custom NRF24L01 Arduino based Robot Controller

Hey guys! As I'm sure you know by now, I'm a huge fan of the NRF24L01, it's a great way to get two Arduinos talking. When the RF Nano (Arduino Nano with integrated NRF24L01) came on the scene about a year ago, I was so excited. It had never been so easy for me and my students to build custom controllers for our robotics projects! Since then, I've been using our trusty RF Nano controller PCB. The same one we've had in the shop! It has been a trusty little board, but now it's time for an upgrade! As always, if you'd like to watch the video for this tutorial we have it right here. Enjoy!




For those of you reading along, here's our original RF Nano controller.


And here is our new upgrade!

Aside from the new board shape, we've added two large pushbuttons to the left of the controller, a power switch in the lower left-hand side of the controller, two right-angle shoulder buttons, a surface-mount 18650 battery holder around back, and a black acrylic protection plate!


We've got two main goals with this tutorial here. The first is to share some really fun code examples and share some of the background info for anyone who has purchased our RF Nano controller in our shop. The second is to provide the files, and the guidance you'd need to build your very own custom controller! Whether you bought a controller from us, or are trying to build your own, the first step is taking a closer look at our schematic!


Part One: The Schematic

One of the coolest things about the RF Nano is that because the radio IC is integrated into the Arduino Nano itself, the schematic and project as a whole can remain much more simple, and clean. In this example, I'm using EasyEDA to create this schematic. I love using EasyEDA, it's free, feature-rich, and a perfect way to make custom circuit boards. Let's take a look at our schematic.

Right and Left Bumpers:

These guys are wired up to pins D6 and D7 on our Arduino, with the other side of our buttons tied to GND. We'll be using the Arduino's internal pull-up resistor so that's all we'll need in terms of circuitry to read our button data!


A and B Buttons:

We'll have our larger black push buttons wired up to pins D4 and D5


Right and Left Joysticks:

As with all joysticks and potentiometers, we need 5v headed into our joysticks and GND heading out, with the voltage change pin wired up to pins A0 and A1. The pushbuttons inside of our joysticks are wired up just like our other buttons, with one side wired up to a digital pin, and the other side tied to GND. The pins we use for the pushbuttons are pins D2 and D3!


MPU60-50:

The accelerometer/gyroscope module we're using is the MPU-6050, it's a great little board! And since it's an i2c device, we only need 4 pins total to get it working! 5v goes in, GND goes out, and we have our SDA and SCL pins. On a standard Arduino Uno and Nano, the i2c bus is hardwired into pins A4 and A5, with A4 for SDA and A5 for SCL.


Part Two: The PCB

Now, one of our biggest goals here is to create an awesome new PCB shape that will take our controller project to the next level! There are more than a few ways to pull this next part off, but I've found that one of the easiest is to use your favorite CAD program to create a .DXF file for your circuit board outline. What's cool about using this method is that we'll be able to use that same .DXF to cut out our acrylic backplate! Now for this example, I'm using Fusion 360, but there are all kinds of ways to create your controller PCB shape, if you'd like you can download the .DXF for this project right here.


In Fusion 360, this is simply a matter of creating a new "sketch", and using the arc, line, circle, and curve tool to create your one custom shape! Feel free to watch our video to see this part in action. In this example, I started with an "arc" then used the line tool and the "conic curve" tool to make the first half of my controller PCB outline.



What I usually like to do is create half of my controller first, we'll then duplicate and rotate it around! That way we know that both halves are perfectly symmetrical!


This part seemed a little strange to me, instead of a "copy" or "duplicate" tool, the easiest way to copy this half of the sketch is with the move tool. Highlight your sketch, and press"M" on your keyboard.


Be sure that you have the "Create Copy" option selected. Once that's all set, all you'll need to do is move over your newly created half, rotate, and line it up with the other half of your controller! Once you have that taken care of, all you'll need to do it click on finish sketch, then save your sketch as a .DXF. I would recommend saving the file somewhere simple, like the desktop, we'll use your newly created file in the next step!


Once you have your circuit board outline made it's time to head back over to EasyEDA to convert our schematic into a PCB! Open up your schematic from before, and make sure you have all of the connections you'd like all figured out. If you'd like to customize our design, be sure you make all of the changes you'd like before this next step. Once you're satisfied and ready to move on, you'll want to save your schematic and then click the "convert to PCB" button at the top of your screen.

In a new tab, you should see all of your components, just as you'd seem them with a regular square-shaped PCB. At this point, you'll want to highlight the purple square (your PCB outline) and delete it!


Next, we need to import our newly created .DXF and assign it as our new PCB outline! You'll want to head to over to the file tab up top, and select "import", then ".DXF" just as we see here in the picture below.


Once you've selected .DXF, then you'll see the following menu, be sure that you're applying the newly imported .DXF as the board outline. Your menu items should look just like mine. If everything looks good, go ahead and select "import"!


Your last step is to place all of your components on your PCB! You may want to do things like move the battery to the back of the PCB, add text, holes, etc. Here's what my final draft looks like. If you'd like to see our finished board files, we have the schematic file and the circuit board design file you can import into easyEDA, as well as the Gerber on our gitHub, feel free to check them out right here.


Once you have your design finalized you can generate the Gerber file, and ship it off to your favorite PCB manufacturer! I use JLCPCB for all of our circuit boards, but I've heard great things about a number of PCB manufacturers out there. If you'd like you can also download the Gerber file for our controller here.


Part Three: Testing out our inputs

Alright, so you've got your PCB manufactured and soldered up, it's time to test it out! Let's head over to the Arduino IDE, and we'll write a quick test sketch to get the data from our Joysticks, and pushbuttons. This first example is to simply print out our data and test that everything is working. We'll work on sending that data out with our radio a little later.


Testing Controller Inputs

/* Hey guys! This is a simple sketch to make sure that all of the 
 * digital and analog inputs on your RF Nano controller are working 
 * properly. We have other examples on our tutorials page about how to 
 * send and recive this data.  Have fun!
 *
 * -ACBR 2020
 */
 
int joyRB = 2;         //Each digital input needs one variable for
int joyRBstatus;       //the pin being used, as well as one variable
int joyLB = 3;         //to store the status of each button
int joyLBstatus;
int btnA = 4;
int btnAstatus;
int btnB = 5;
int btnBstatus;
int btnR = 6;
int btnRstatus;
int btnL = 7;
int btnLstatus;
int joyRX;
int joyRY;
int joyLX;
int joyLY;


void setup() {
Serial.begin(9600);
pinMode(joyRB, INPUT_PULLUP);   //Because we're using the internal
pinMode(joyLB, INPUT_PULLUP);   //pullup resistor on the Arduino we
pinMode(btnA, INPUT_PULLUP);    //need to declare each button as an
pinMode(btnB, INPUT_PULLUP);    //input_pullup
pinMode(btnR, INPUT_PULLUP);
pinMode(btnL, INPUT_PULLUP);
}


void loop() {
joyRBstatus = digitalRead(joyRB);  //Assigning our status variables
joyLBstatus = digitalRead(joyLB);  //to the pins they are connected to.
btnAstatus = digitalRead(btnA);    //the digitalRead command will show
btnBstatus = digitalRead(btnB);    //0 or 1 when the button is pressed
btnRstatus = digitalRead(btnR);
btnLstatus = digitalRead(btnL);
joyRX = analogRead(A0);            //Using the analogRead command will
joyRY = analogRead(A1);            //let us see a really useful string
joyLX = analogRead(A2);            //of numbers representing joystick
joyLY = analogRead(A3);            //position. 


Serial.print(joyRBstatus);      //Printing out our controller data!
Serial.print(" ");
Serial.print(joyLBstatus);
Serial.print(" ");
Serial.print(btnAstatus);
Serial.print(" ");
Serial.print(btnBstatus);
Serial.print(" ");
Serial.print(btnRstatus);
Serial.print(" ");
Serial.print(btnLstatus);
Serial.print(" ");
Serial.print(joyRX);
Serial.print(" ");
Serial.print(joyRY);
Serial.print(" ");
Serial.print(joyLX);
Serial.print(" ");
Serial.println(joyLY);
delay(10);
}

If everything went as planned and your code compiles, then you'll want to go ahead and plug in your controller and upload your code! Once your code is uploaded, open up the Serial Monitor, and you should see all of your joystick data! It will more than likely look something like this.

As you press each digital button, you'll see the series of 1's turn to 0's. You'll also see your last 4 values change from 0 to 1023 as you move around your joysticks!


Once we know that the digital buttons and the joysticks are working, it's time to test out our MPU-6050 accelerometer gyro combo board! The MPU-6050 is an i2c device, so getting it connected is pretty easy. The following example code will allow us to grab the "pitch" and "roll" value representing the position of the IC chip in space. Let's check it out!


/*   
*   Hey Guys! 
*  The following example code, is designed to test the MPU-6050 on our 
*   RF Nano Controller, for more examples about sending that data out 
*  feel free to check out the tutorials page on our website!
*
*  -ACBR 2020
 */

#include <Wire.h>
#define MPU 0x68  // I2C address of the MPU-6050
double AcX,AcY,AcZ;
int Pitch, Roll; 

void setup() {
  Serial.begin(9600); 
  init_MPU();
}

void loop() {
  FunctionsMPU(); 
  Roll = FunctionsPitchRoll(AcX, AcY, AcZ);   
  Pitch = FunctionsPitchRoll(AcY, AcX, AcZ);  
  Serial.print("Pitch: "); Serial.print(Pitch);
  Serial.print("\t");
  Serial.print("Roll: "); Serial.print(Roll);
  Serial.print("\n");
}
 
void init_MPU(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  delay(1000);
}
 
double FunctionsPitchRoll(double A, double B, double C){
  double DatoA, DatoB, Value;
  DatoA = A;
  DatoB = (B*B) + (C*C);
  DatoB = sqrt(DatoB);
  
  Value = atan2(DatoA, DatoB);
  Value = Value * 180/3.14;
  
  return (int)Value;
}


void FunctionsMPU(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,6,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
}

Once you have that next example code compiled and uploaded you'll want to open up the serial monitor once again and see what we've got! If everything is working properly, you'll see something just like this.

If your MPU-6050 was perfectly flat it would register 0 and 0 but that's pretty tough to get set perfectly. If the numbers change as you rotate your controller around then you're ready to move on! Let's get that data out!


Part Four: Sending and Receiving our data

Alright, we know that all of our controller data is working, it's time to use the onboard NRF24L01 to send that data out to our project! We'll be using the ACBR Rover Robotics Kit as our example, but there are all kinds of awesome things you can do with this example code. First, we'll look at just what it takes to send out our joystick and pushbutton data! Take a look at the example code below.

Sending our digital and analog data

/* Hey guys!
*  This example code take in the digital data from the pushbuttons
*  as well as the joystick data for the ACBR RF Nano controller, and
*  can be sent to any NRF24L01 being used with an Arduino, as well as 
*  all Arduino based ACBR Robotics Kits.  Have fun!
*  -ACBR 2020
*/
 
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>
#define CE_PIN   10
#define CSN_PIN 9
const uint64_t pipe = 0x1; //communication pipeline
int controllerData[10];  // 10 element array
RF24 radio(CE_PIN, CSN_PIN); // Activate  the Radio
int joyRX;
int joyRY;
int joyLX;
int joyLY;
int btnJoyR = 2; 
int btnJoyL = 3;  
int btnStatusJoyR; 
int btnStatusJoyL; 
int btnA = 4;
int btnB = 5;
int btnStatusA;
int btnStatusB;
int btnR = 6;
int btnL = 7; 
int btnStatusR;
int btnStatusL;

void setup()   {
  Serial.begin(9600); /* Opening the Serial Communication */
  pinMode(btnR, INPUT_PULLUP);
  pinMode(btnL, INPUT_PULLUP);
  pinMode(btnA, INPUT_PULLUP);
  pinMode(btnB, INPUT_PULLUP);
  pinMode(btnJoyR, INPUT_PULLUP);
  pinMode(btnJoyL, INPUT_PULLUP);
  radio.begin();
  radio.openWritingPipe(pipe);
}

void loop()   {
  radio.write(controllerData, sizeof(controllerData) );
  joyRX = analogRead(A0);
  joyRY = analogRead(A1);
  joyLX = analogRead(A2);
  joyLY = analogRead(A3);
  btnStatusR = digitalRead(btnR);
  btnStatusL = digitalRead(btnL);
  btnStatusA = digitalRead(btnA);
  btnStatusB = digitalRead(btnB);
  btnStatusJoyR = digitalRead(btnJoyR);
  btnStatusJoyL = digitalRead(btnJoyL);
  controllerData[0] = joyRX;
  controllerData[1] = joyRY;
  controllerData[2] = joyLX;
  controllerData[3] = joyLY;
  controllerData[4] = btnStatusR;
  controllerData[5] = btnStatusL;
  controllerData[6] = btnStatusA;
  controllerData[7] = btnStatusB;
  controllerData[8] = btnStatusJoyR;
  controllerData[9] = btnStatusJoyL;

This next example receives the data we just sent out, and uses that data to move our ACBR Rover Robotics kit! This example uses only the joystick data, but there are all kinds of awesome things you can do with the button data we have to work with. Let's take a look!


Receiving our digital and analog data

/*   
*  Hey Guys! 
*  The following example code, is designed to receive the data from two 
*  joysticks and push buttons using the NRF24L01 2.4 GHz radio 
*  transiever. Then use that data to control an Arduino based robot. 
*  While this code was designed for the ACBR Rover Robotics kit, it can 
*  be adapted to be used with any arduino based robot. This code is 
*  also completely compatible with the RF Nano (Arduino Nano with built 
*  in NRF24L01).
*
*  -ACBR 2020
*/
 
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define CE_PIN 10
#define CSN_PIN 9
const uint64_t pipe = 0x1;  
RF24 radio(CE_PIN, CSN_PIN);
int controllerData[10];
int enOne = 3;
int inOne = 4;
int inTwo = 5;
int enTwo = 6;
int inThree = 7;
int inFour = 8;

void setup()  {
  Serial.begin(9600);
  delay(1000);
  Serial.println("Nrf24L01 Receiver Starting");
  radio.begin();
  radio.openReadingPipe(1,pipe);
  radio.startListening();
  pinMode(enOne, OUTPUT);
  pinMode(enTwo, OUTPUT);
  pinMode(inOne, OUTPUT);
  pinMode(inTwo, OUTPUT);
  pinMode(inThree, OUTPUT);
  pinMode(inFour, OUTPUT);
}

void loop() {
  if ( radio.available() )
  {
    bool done = false;
    while (!done)
    {
      done = radio.read(controllerData, sizeof(controllerData));
      Serial.print(controllerData[0]);
      Serial.print(" ");
      Serial.print(controllerData[1]);
      Serial.print(" ");
      Serial.print(controllerData[2]);
      Serial.print(" ");
      Serial.print(controllerData[3]);
      Serial.print(" ");
      Serial.print(controllerData[4]);
      Serial.print(" ");
      Serial.print(controllerData[5]);
      Serial.print(" ");      
      Serial.print(controllerData[6]);
      Serial.print(" ");
      Serial.print(controllerData[7]);
      Serial.print(" ");
      Serial.print(controllerData[8]);
      Serial.print(" ");
      Serial.println(controllerData[9]);
      delay(50);
      
      if(controllerData[0] > 700)
      {
        Serial.println("forward!!!");
        analogWrite(enOne, 255);
        digitalWrite(inOne, LOW);
        digitalWrite(inTwo, HIGH);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, LOW);
        digitalWrite(inFour, HIGH); 
      }
      else if(controllerData[0] < 300)
      {
        Serial.println("backwards!!");
        analogWrite(enOne, 255);
        digitalWrite(inOne, HIGH);
        digitalWrite(inTwo, LOW);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, HIGH);
        digitalWrite(inFour, LOW); 
      }


      else if(controllerData[1] > 800)
      {
        Serial.println("left!");
        analogWrite(enOne, 255);
        digitalWrite(inOne, LOW);
        digitalWrite(inTwo, HIGH);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, HIGH);
        digitalWrite(inFour, LOW);
      }
      else if(controllerData[1] < 300)
      {
        Serial.println("right!");
        analogWrite(enOne, 255);
        digitalWrite(inOne, HIGH);
        digitalWrite(inTwo, LOW);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, LOW);
        digitalWrite(inFour, HIGH);
      }
      else
      {
       Serial.println("Stopped!");
       analogWrite(enOne, 0);
       digitalWrite(inOne, LOW);
       digitalWrite(inTwo, LOW);
       analogWrite(enTwo, 0);
       digitalWrite(inThree, LOW);
       digitalWrite(inFour, LOW);
      }
    }
  }
  else
  {    
      Serial.println("Darn, not working yet!");
       analogWrite(enOne, 0);
       digitalWrite(inOne, LOW);
       digitalWrite(inTwo, LOW);
       analogWrite(enTwo, 0);
       digitalWrite(inThree, LOW);
       digitalWrite(inFour, LOW);
  }
}

Now there is a ton of other things you could do with this example code, so feel free to hack it up! At this point, You've got your joystick and button data being sent between your controller, and a second RF Nano. Now it's time to do the same with the MPU-6050. The following examples will follow the same format, with the first example sending the data, and the second example receiving the data. We'll be using the Rover as our example for this as well, but just like before, there are all kinds of awesome applications for a wireless controller with gesture control. Let's take a look at our MPU-6050 send code!


Sending our MPU-6050 Data

/*
*  Hey Guys! 
*  The following example code, is designed to send the data from an 
*  MPU-6050 using the NRF24L01 2.4 GHz radio transiever or RF Nano. 
*  Then use that data to control an Arduino based project.
*/
 
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Wire.h>

#define MPU 0x68  // I2C address of the MPU-6050
#define CE_PIN   10
#define CSN_PIN 9

const uint64_t pipe = 0x1; 
double AcX,AcY,AcZ;
int Pitch, Roll;
int GYRO[2];  
RF24 radio(CE_PIN, CSN_PIN); 

void setup()   {
  Serial.begin(9600); 
  radio.begin();
  radio.openWritingPipe(pipe);
  init_MPU();
}

void loop() {
  radio.write( GYRO, sizeof(GYRO) );
  
  FunctionsMPU(); 
    
  Roll = FunctionsPitchRoll(AcX, AcY, AcZ);   
  Pitch = FunctionsPitchRoll(AcY, AcX, AcZ);  
  
  GYRO[0] = Pitch;
  GYRO[1] = Roll;

  Serial.print("Pitch: "); Serial.print(Pitch);
  Serial.print("\t");
  Serial.print("Roll: "); Serial.print(Roll);
  Serial.print("\n"); 
}
 
void init_MPU(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  delay(1000);
}
 
double FunctionsPitchRoll(double A, double B, double C){
  double DatoA, DatoB, Value;
  DatoA = A;
  DatoB = (B*B) + (C*C);
  DatoB = sqrt(DatoB);
  
  Value = atan2(DatoA, DatoB);
  Value = Value * 180/3.14;
  
  return (int)Value;
}

void FunctionsMPU(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,6,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
}

The next example receives that pitch and roll data from our controller, and uses that data to control our Rover! Check it out!


Receiving our MPU-6050 data

/* Hey Guys! 
*  The following example code, is designed to receive the MPU60-50
*  using the NRF24L01 2.4 GHz radio transiever as well as an RF Nano.
*  Then use that data to control an Arduino based robot, like the ACBR 
*  Rover Robotics kit.
*/

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>
#define CE_PIN   10
#define CSN_PIN 9
const uint64_t pipe = 0x1;   
RF24 radio(CE_PIN, CSN_PIN);
int gyro[2]; 
int enOne = 3;
int inOne = 4;
int inTwo = 5;
int enTwo = 6;
int inThree = 7;
int inFour = 8;

void setup() {
  Serial.begin(9600); 
  delay(1000);
  Serial.println("Nrf24L01 Receiver Starting");
  pinMode(enOne, OUTPUT);
  pinMode(inOne, OUTPUT);
  pinMode(inTwo, OUTPUT);
  pinMode(enTwo, OUTPUT);
  pinMode(inThree, OUTPUT);
  pinMode(inFour, OUTPUT);
  radio.begin();
  radio.openReadingPipe(1,pipe);
  radio.startListening();
}

void loop() {
  if ( radio.available() )
  {
    bool done = false;
    while (!done)
    {
      done = radio.read(gyro, sizeof(gyro));
      Serial.print("GYRO X: ");
      Serial.print(gyro[0]);
      Serial.print(" GYRO Y: ");
      Serial.println(gyro[1]);
      
      if(gyro[0] < -30)
      {
        Serial.println("right");
        analogWrite(enOne, 255);
        digitalWrite(inOne, HIGH);
        digitalWrite(inTwo, LOW);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, LOW);
        digitalWrite(inFour, HIGH);
      }
      else if(gyro[0] > 30)
      {
        Serial.println("left");
        analogWrite(enOne, 255);
        digitalWrite(inOne, LOW);
        digitalWrite(inTwo, HIGH);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, HIGH);
        digitalWrite(inFour, LOW);
      }
      else if(gyro[1] < -30)
      {
        Serial.println("backward");
        analogWrite(enOne, 255);
        digitalWrite(inOne, HIGH);
        digitalWrite(inTwo, LOW);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, HIGH);
        digitalWrite(inFour, LOW);
      }
      else if(gyro[1] > 30)
      {
        Serial.println("forward");
        analogWrite(enOne, 255);
        digitalWrite(inOne, LOW);
        digitalWrite(inTwo, HIGH);
        analogWrite(enTwo, 255);
        digitalWrite(inThree, LOW);
        digitalWrite(inFour, HIGH); 
      }
      else
      {
        Serial.println("stopped");
        analogWrite(enOne, 0);
        digitalWrite(inOne, LOW);
        digitalWrite(inTwo, LOW);
        analogWrite(enTwo, 0);
        digitalWrite(inThree, LOW);
        digitalWrite(inFour, LOW); 
      }
      delay(50);
    }
  }
  else
  {    
      Serial.println("Darn, not working yet!");
  }
}

That's it! If you were to open up the serial monitor after uploading the receiver sketch, as you twist and roll your controller, you should see the words "forward", "backward", "left", and "right" appear.


Part Five: Hack it!

There is so much crazy stuff you can do with this! Adding in variable speed control, adding in other crazy sensors to our PCB, the sky's the limit! As always we have these controllers available in our shop here, and as always we take a large portion of our funds and use them to donate robotics kits to young engineers, students, and mad scientists all around the world. We also have all of the files for this project posted open-source, so anyone can take our projects and make them their own. Take care and happy hacking!

429 views0 comments

Recent Posts

See All

Anyone Can Build Robots!

1+ (858) 434 5830

©2020 by Anyone Can Build Robots!