The ForceTronics blog provides tutorials on creating fun and unique electronic projects. The goal of each project will be to create a foundation or jumping off point for amateur, hobbyist, and professional engineers to build on and innovate. Please use the comments section for questions and go to forcetronics.com for information on forcetronics consulting services.
This is the fourth, and most likely the last, in a series where we look at how to reduce your Arduino's power consumption. This series is great for anybody working on a project that is battery powered and you want to ensure the longest battery life possible. In this part we will look at how to turn off the Brown Out Detector (BOD) to save power.
************************Arduino Code*******************************************
/* This Arduino Sketch is part of a tutorial on the ForceTronics YouTube Channel and demonstrates how to use the Sleep cabilities on Arduino as well as turn off the ADC to get low power consumption. In this tutorial the Extended Fuse on the Atmega was configured to turn off the Brown Out Detection (BOD) for even further power savings. It is free and open for anybody to use at their own risk. */ /* To turn off the BOD avrdude was used via the command prompt, the following command was used: avrdude -c usbtiny -p atmega328p -U efuse:w:0x07:m */ #include <avr/sleep.h> void setup() { delay(6000); //Delay to see normal power level first sleep_enable(); //enable the sleep capability set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set the type of sleep mode. Default is Idle ADCSRA &= ~(1<<ADEN); //Turn off ADC before going to sleep (set ADEN bit to 0) sleep_cpu(); //enter sleep mode. Next code that will be executed is the ISR when interrupt wakes Arduino from sleep } void loop() { // put your main code here, to run repeatedly: }
In this video we build a remote control car using Arduino and the nRF24L01+ transceiver for wireless communication / control of the car. To control this car we won't be using the classic joystick, but instead a glove! The glove will use an accelerometer (MPU-6050) to control the car's direction and speed based on the position of your hand.
//********************Arduino Code for the RC Car******************************** //This code is for a remote control car using the nRF24L01 for wireless communication. The tutorial on this project can be found on the ForceTronics Youtube channel //This code is free and open for anybody to use or modify at your own risk #include <Wire.h> //This library is needed for I2C communication (motor shield uses this) #include <Adafruit_MotorShield.h> //Library for the adafruit motor shield #include "utility/Adafruit_PWMServoDriver.h" //needed for motor shield, file is found in the library folder of Adafruit_MotorShield.h #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ // Create the motor shield object with the default I2C address Adafruit_MotorShield AFMS = Adafruit_MotorShield(); // create an object for each motor and assign it to a port on the shield Adafruit_DCMotor *M1 = AFMS.getMotor(1); Adafruit_DCMotor *M2 = AFMS.getMotor(2); Adafruit_DCMotor *M3 = AFMS.getMotor(3); Adafruit_DCMotor *M4 = AFMS.getMotor(4); const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message //The controller sends a "packet" to control the speed and direction of the car. The first and last byte just signal the start and end of the packet //The second and third bytes represent the speed and direction of the car. The second byte is for forward and backwards //The third byte is for right and left directions byte bArray[] = {255, 125, 125, 254}; //This array holds the speed and direction packet RF24 wirelessSPI(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI) const uint64_t pAddress = 0xB00B1E5000LL; //Create a pipe addresses for the 2 nodes to communicate over, my address spells boobies :-) int sCount = 0; //variable to track timer to stop motor, if communication is lost this will shut off motor void setup() { AFMS.begin(); //Start motor shield object, create with the default frequency 1.6KHz wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled, this means rec send acknowledge packet to tell xmit that it got the packet with no problems wirelessSPI.enableAckPayload(); // Allow optional payload or message on ack packet, even though we are not using this wirelessSPI.setRetries(5,10); // Defines packet retry behavior: first arg is delay between retries at 250us x 5 and max no. of retries wirelessSPI.openReadingPipe(1,pAddress); //open pipe o for recieving meassages with pipe address wirelessSPI.startListening(); // Start listening for messages, because we are the reciever motorStop(); //ensure motor is at stop to start } void loop() { if(wirelessSPI.available()){ //check to see if a data packet is available from transmitter wirelessSPI.read( bArray, 4 ); //read 4 bytes of data and store it in array if(verifyPacket) { //verify it is a valid packet to control car setMotorSpeed(bArray[1], bArray[2]); //get the forward / backward and right / left speeds sCount = 0; //reset count } else { //do something here if a bad packet was recieved } } delay(1); //short delay before looping again sCount++; //increment the loop count if(sCount > 60) { motorStop(); } //if we do not get a packet from the controller } //This function makes sure a packet is valid by checking it has the correct start and end byte //it also checks to see if the forward / backward and right / left speeds are valid bool verifyPacket() { if(bArray[0] == 255 & bArray[3] == 254 & bArray[1] < 251 & bArray[2] < 251) return true; else return false; } //This function is used to set the direction and speed based on the two bytes from the controller //125 means stop, above 125 means right or forward, below 125 means left or backwards void setMotorSpeed(int upDown, int leftRight) { int lR = 0; //left and right direction variable, zero is stop int bF = 0; //forward and backward direction variable, zero is stop if(leftRight == 125) { //if true no left or right turn (stop) lR = 0; } else if(leftRight > 125) { //if this is true right turn lR = 1; leftRight = leftRight - 125; //scale variable from 0 to 125 } else { //else this is a left lR = 2; } if(upDown == 125) { //if true no forward or back (stop) bF = 0; } else if(upDown > 125) { //if this is true go forward bF = 1; upDown = upDown - 125; //scale variable from 0 to 125 } else { //this is go backwards bF = 2; } //We have direction now set speed //scale turn and back / forward if(lR == 0 && bF == 0) { //stop all motors if no forward / backward and right / left direction motorStop(); } else if (bF==1) { //Go forward if(lR == 0) { //go straight forward goForward(scaleSpeed(upDown)); //Send forward speed } else if(lR == 1) { //go forward and right goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 1); //send forward and right turn speeds } else { goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 1); //send forward and left turn speeds } } else if (bF==2) { //same thing but this is backwards if(lR == 0) { //go straight backwards goBackward(scaleSpeed(upDown)); } else if(lR == 1) { //go forward and right goTurn(scaleSpeed(scaleTurn(upDown,leftRight)), scaleSpeed(upDown), 0); } else { goTurn(scaleSpeed(upDown),scaleSpeed(scaleTurn(upDown,leftRight)), 0); } } else { //No forward or backwards direction so just do a turn if(lR==1) { //Right turn goRight(scaleSpeed(leftRight)); } else { //left turn goLeft(scaleSpeed(leftRight)); } } } //This function scales the speed value from controller to a value for the motor //max motor speed is 250 and max value from controller is 125 int scaleSpeed(int scale) { float r = ((float)scale/125)*250; //scale to value between 1 and 250 return int(r); //covert to int value and return } //Used to scale turn value, based on forward or backward speed as well as turn speed int scaleTurn(int fBSp, int lRSp) { float r =(float)fBSp*(1 - (float)lRSp/125); return int(r); } //function to stop the motors void motorStop() { M2->run(RELEASE); M4->run(RELEASE); M1->run(RELEASE); M3->run(RELEASE); } //function to tell motors to go forward, input is speed void goForward(int mSpeed) { M1->setSpeed(mSpeed); M2->setSpeed(mSpeed); M3->setSpeed(mSpeed); M4->setSpeed(mSpeed); M2->run(FORWARD); M4->run(FORWARD); M1->run(FORWARD); M3->run(FORWARD); } //function to tell motors to go backward, input is speed void goBackward(int mSpeed) { M1->setSpeed(mSpeed); M2->setSpeed(mSpeed); M3->setSpeed(mSpeed); M4->setSpeed(mSpeed); M2->run(BACKWARD); M4->run(BACKWARD); M1->run(BACKWARD); M3->run(BACKWARD); } //function for left or right turn. inputs are speed for left tires and speed for right tires //and whether we are going forward or backwards void goTurn(int rTire, int lTire, int forward) { M1->setSpeed(rTire); M2->setSpeed(lTire); M3->setSpeed(rTire); M4->setSpeed(lTire); //code to turn Right if(forward) { M2->run(FORWARD); //M2 and M4 are left tires M4->run(FORWARD); M1->run(FORWARD); //M1 and M3 are right tires M3->run(FORWARD); } else { M2->run(BACKWARD); M4->run(BACKWARD); M1->run(BACKWARD); M3->run(BACKWARD); } } //right turn function, no forward or backwards motion void goRight(int tSpeed) { M1->setSpeed(tSpeed); M2->setSpeed(tSpeed); M3->setSpeed(tSpeed); M4->setSpeed(tSpeed); //code to turn Right M2->run(FORWARD); //left tires M4->run(FORWARD); M1->run(BACKWARD); //right tires M3->run(BACKWARD); } //left turn function, no forward or backwards motion void goLeft(int tSpeed) { M1->setSpeed(tSpeed); M2->setSpeed(tSpeed); M3->setSpeed(tSpeed); M4->setSpeed(tSpeed); //code to turn Right M2->run(BACKWARD); //left tires M4->run(BACKWARD); M1->run(FORWARD); //right tires M3->run(FORWARD); }
//********************Arduino Code for the Glove Controller**************************** //This code is for a remote control car using the nRF24L01 for wireless communication //and accel to dictate the cars direction and speed based of hand postion. //The tutorial on this project can be found on the ForceTronics Youtube channel //This code is free and open for anybody to use or modify at your own risk #include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+ #include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/ #include "I2Cdev.h" //the MPU6050 Accel uses I2C communication #include "MPU6050.h" // Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation // is used in I2Cdev.h #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif const int pinCE = 9; //This pin is used to set the nRF24 to standby (0) or active mode (1) const int pinCSN = 10; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out MPU6050 accelgyro; //declare the object to access and cotrol the accel (we don't use the gyro) RF24 wirelessSPI(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection const uint64_t pAddress = 0xB00B1E5000LL; // Radio pipe addresses for the 2 nodes to communicate. The address spells boobies :-) //The controller sends a "packet" to control the speed and direction of the car. The first and last byte just signal the start and end of the packet //The second and third bytes represent the speed and direction of the car. The second byte is for forward and backwards //The third byte is for right and left directions byte bArray[] = {255, 125, 125, 254}; //This array holds the speed and direction packet void setup() { // join I2C bus (I2Cdev library doesn't do this automatically) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif accelgyro.initialize(); //initialize the accel object wirelessSPI.begin(); //Start the nRF24 module wirelessSPI.setAutoAck(1); // Ensure autoACK is enabled so rec sends ack packet to let you know it got the transmit packet payload wirelessSPI.enableAckPayload(); // Allow optional ack payloads (we don't use this) wirelessSPI.setRetries(5,15); // Sets up retries and timing for packets that were not ack'd, current settings: smallest time between retries, max no. of retries wirelessSPI.openWritingPipe(pAddress);// pipe address that we will communicate over, must be the same for each nRF24 module (this is a transmitter) wirelessSPI.stopListening(); //we are the transmitter so don't need to listen } void loop() { int x, y, z; //create variables to hold accel values (we don't use z direction) accelgyro.getAcceleration(&x, &y, &z); //get accel values, note variables are sent by reference buildArray(buildXValue(x), buildYValue(y)); //build speed and direction array or packet, this is what we send to the car to control if (!wirelessSPI.write( bArray, 4 )){ //if the send fails let the user know over serial monitor //put code here to do something if sending the packet fails } delay(5); //delay a before sending the next packet } //This function is used to build the forward / backwards direction and speed value //The X direction of the accel is used for the forward / backwards direction and speed //Note that the accel value has to be scaled to fit in a byte of data byte buildXValue(int xV) { if(xV <= 1000 & xV >= -1000) { //This creates a cushion for the stop value so the car is not constantly moving return 125; //this is the stop value } else if (xV > 1000) { //if positive value then car is being directed forward xV = xV - 1000; if(xV > 15000) { xV = 15000; } //ceiling value for forward speed return (scaleSpeed(xV,15000) + 125); //scale speed to send, add 125 since this is forward } else { //Negative x value is to tell the car to go backwards xV = xV * -1; //conver negative value to positive xV = xV - 1000; if(xV > 15000) { xV = 15000; } //set ceiling on accel value return scaleSpeed(xV,15000); //scale to 1 to 125 } } //This function is used to build the right and left direction and speed value //The Y direction of the accel is used for the right and left direction and speed //Note that the accel value has to be scaled to fit in a byte of data byte buildYValue(int yV) { if(yV <= 1000 & yV >= -1000) { //This creates a cushion for the stop value so the car is not constantly moving return 125; //this is the stop value } else if (yV > 1000) { //if positive value then car is being directed right yV = yV - 1000; if(yV > 11000) { yV = 11000; } //ceiling value for right speed return scaleSpeed(yV,11000); } else { //Negative x value is to tell the car to go backwards yV = yV * -1; yV = yV - 1000; if(yV > 11000) { yV = 11000; } //ceiling value for left speed return (scaleSpeed(yV,11000)+125); //scale speed to send, add 125 since this is left } } //This function scales the accel speed value to a value that can fit in a byte byte scaleSpeed(int scale, int sVal) { float r = ((float)scale/sVal)*125; //speed is between 0 to 125 return (byte)r; } //This function builds the packet that sends the speed and direction //The first and last byte is used to represent the start and end of the packet void buildArray(byte xV, byte yV) { bArray[0] = 255; bArray[1] = xV; bArray[2] = yV; bArray[3] = 254; }