Sunday, April 19, 2015

Reducing Arduino’s Power Consumption Part 4 (Turning Off the BOD)

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:
}

Thursday, April 9, 2015

nRF24L01 / Accelerometer RC Car

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;
}

Monday, February 16, 2015

Getting Started with the nRF24L01 Transceiver

In this video we look at how to get up and running with the low cost nRF24L01+ transceiver module from Nordic.



nRF24L01 connected to an Arduino Pro Mini


***********Arduino code for transmit module*********************************
//This sketch is from a tutorial video for getting started with the nRF24L01 tranciever module on the ForceTronics YouTube Channel
//the code was leverage from Ping pair example at http://tmrh20.github.io/RF24/pingpair_ack_8ino-example.html
//This sketch is free to the public to use and 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 "printf.h" //This is used to print the details of the nRF24 board. if you don't want to use it just comment out "printf_begin()"

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
byte counter = 1; //used to count the packets sent
bool done = false; //used to know when to stop sending packets
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.

void setup()  
{
  Serial.begin(57600);   //start serial to communicate process
  printf_begin();        //This is only used to print details of nRF24 module, needs Printf.h file. It is optional and can be deleted
  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
  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
  wirelessSPI.stopListening();
  wirelessSPI.printDetails();                   // Dump the configuration of the rf unit for debugging
}


void loop()  
{

 if(!done) { //if we are not done yet
    Serial.print("Now send packet: "); 
    Serial.println(counter); //serial print the packet number that is being sent
    unsigned long time1 = micros();  //start timer to measure round trip
    //send or write the packet to the rec nRF24 module. Arguments are the payload / variable address and size
   if (!wirelessSPI.write( &counter, 1 )){  //if the send fails let the user know over serial monitor
       Serial.println("packet delivery failed");      
   }
   else { //if the send was successful 
      unsigned long time2 = micros(); //get time new time
      time2 = time2 - time1; //calculate round trip time to send and get ack packet from rec module
      Serial.print("Time from message sent to recieve Ack packet: ");
      Serial.print(time2); //print the time to the serial monitor
      Serial.println(" microseconds");
       counter++; //up the packet count
   }
   
   //if the reciever sends payload in ack packet this while loop will get the payload data
   while(wirelessSPI.available() ){ 
       char gotChars[5]; //create array to hold payload
       wirelessSPI.read( gotChars, 5); //read payload from ack packet
       Serial.print(gotChars[0]); //print each char from payload
       Serial.print(gotChars[1]);
       Serial.print(gotChars[2]);
       Serial.println(gotChars[3]);
       done = true; //the ack payload signals we are done
     }
  }

    delay(1000);
}

***********Arduino code for receiver module*********************************
//This sketch is from a tutorial video for getting started with the nRF24L01 tranciever module on the ForceTronics YouTube Channel
//the code was leverage from Ping pair example at http://tmrh20.github.io/RF24/pingpair_ack_8ino-example.html
//This sketch is free to the public to use and 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 "printf.h" //This is used to print the details of the nRF24 board. if you don't want to use it just comment out "printf_begin()"

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
byte gotByte = 0; //used to store payload from transmit module
bool done = false;
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, the "LL" is for LongLong type

void setup()   
{
  Serial.begin(57600);  //start serial to communicate process
  printf_begin();  //This is only used to print details of nRF24 module, needs Printf.h file. It is optional and can be deleted
  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
  wirelessSPI.setRetries(5,15);                 // 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
  wirelessSPI.printDetails();                   //print details of nRF24 module to serial, must have printf for it to print to serial
}

void loop()  
{   
    if(gotByte >= 9 & !done) { //once we get 10 packets send ack packet with payload telling the transmit module we are done
       char cArray[5] = "done"; //create char array to store "done," note that the fifth char is for the null character
       wirelessSPI.writeAckPayload(1, cArray, sizeof(cArray));  //send ack payload. First argument is pipe number, then pointer to variable, then variable size
     }
   //loop until all of the payload data is recieved, for this example loop should only run once
    while(wirelessSPI.available() & !done){ 
     wirelessSPI.read( &gotByte, 1 ); //read one byte of data and store it in gotByte variable
     Serial.print("Recieved packet number: "); //payload counts packet number
     Serial.println(gotByte); //print payload / packet number
    }
    
    if(gotByte > 9) done = true; //we are finished so set "done" to true
   
  delay(200);    
}

***********Arduino code for Printf.h file*********************************
/*
 Copyright (C) 2011 J. Coliz <maniacbug@ymail.com>

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 version 2 as published by the Free Software Foundation.
 */

/**
 * @file printf.h
 *
 * Setup necessary to direct stdout to the Arduino Serial library, which
 * enables 'printf'
 */

#ifndef __PRINTF_H__
#define __PRINTF_H__

#ifdef ARDUINO

int serial_putc( char c, FILE * ) 
{
  Serial.write( c );

  return c;


void printf_begin(void)
{
  fdevopen( &serial_putc, 0 );
}

#else
#error This example is only for use on Arduino.
#endif // ARDUINO

#endif // __PRINTF_H__

Monday, February 2, 2015

Getting Started with Digital Potentiomenters

In this video we will look at what Digital Potentiometers are, how to understand their specs, and we will go over an example setup with two digital potentiometers and Arduino.



********************Arduino Code*************************************************
//Example Arduino sketch used to control two digital potential in a video tutorial on the ForceTronics YouTube Channel
//This code is in the public domain and free for anybody to use at their own risk

#include "SPI.h" //SPI communication library

int nVolatile = 0b00100000; //status register value to store resistance value in non-volatile memory
int vWrite = 0b00000000; //status register to write resistance value (volatile)
int countPot1 = 0; //variable for MCP4161 dig pot value
int countPot2 = 0; //variable for MCP4131 dig pot value
int countDown1 = 0; //variable to control counting up and down for dig pot 1
int countDown2 = 0; //variable to control counting up and down for dig pot 2

void setup() {
  pinMode(6, OUTPUT);   //Set PWM pin to output 
  pinMode(2, OUTPUT);   //This pin controls SPI comm with dig pot 1
  pinMode(3, OUTPUT);   //This pin controls SPI comm with dig pot 2
  SPI.begin(); //Starts SPI communication
  analogWrite(6, 100); //Set pin 6 to PWM at 39 percent duty cycle
}

void loop() {
  writeToDigPot1(countPot1); //write resistance setting to dig pot 1
  writeToDigPot2(countPot2); //write resistance setting to dig pot 2

 if(!countDown1 & countPot1 < 255) { //If counting up and below 256 
   countPot1++; //up the resistance setting by 1
 }
 else { //time to count down now
  if(countPot1 > 0) { //if resistor setting is above 0
    countPot1--; //reduce resistor value
    countDown1 = 1; //remain in countdown mode
  } 
  else { //switch back to count up mode
    countPot1++; //up the resistance setting by 1
    countDown1 = 0; //set to count up mode
  }
 }

 if(!countDown2 & countPot2 < 70) { //If counting up and below 70
  countPot2++; //up the resistance setting by 1
 }
 else { //time to count down now
  if(countPot2 > 0) { //if resistor setting is above 0
    countPot2--; //reduce resistor value
    countDown2 = 1; //remain in countdown mode
  } 
  else { //switch back to count up mode
    countPot2++; //up the resistance setting by 1
    countDown2 = 0; //set to count up mode
  }
 }
  
  delay(8);
}

//This function handles the SPI communication to change resistor setting to dig pot 1
//input argument is resistance setting
void writeToDigPot1(int val) {
  digitalWrite(2, LOW);   //enable SPI comm to dig pot one
  digitalWrite(3, HIGH);  //disable SPI comm to dig pot 2
  delay(1); //short delay to ensure dig pins changed state
  SPI.transfer(vWrite); //status register set to write
  SPI.transfer(val); //write resistance value to dig pot
}

//This function handles the SPI communication to change resistor setting to dig pot 2
//input argument is resistance setting
void writeToDigPot2(int val) {
  digitalWrite(3, LOW);   //enable SPI comm to dig pot 2
  digitalWrite(2, HIGH);  //disable SPI comm to dig pot 1
  delay(1); //short delay to ensure dig pins changed state
  SPI.transfer(vWrite); //status register set to write
  SPI.transfer(val); //write resistance value to dig pot
}

//This function handles the SPI communication to set dig pot 1 value in non-volatile memory
//input argument is resistance setting
void writeDigPot1NV(int val) {
  digitalWrite(2, LOW);   //enable SPI comm to dig pot one
  digitalWrite(3, HIGH);  //disable SPI comm to dig pot 2
  delay(1); //short delay to ensure dig pins changed state
  SPI.transfer(nVolatile); //status register set to non-volatile write
  SPI.transfer(val); //write resistance value to dig pot
}

Sunday, January 25, 2015

How to Use a MOSFET as a Switch

In this video we will cover:
  • What is a MOSFET
  • MOSFET switch vs mechanical switch
  • How to use MOSFET as a switch
  • Go over example using a MOSFET as a switch with Arduino



Arduino Code********************************************************************
//This example code was used on the Forcetronics YouTube Channel to demonstrate how to use 
//A MOSFET as a switch. The code is open for anybody to use or modify

const int nMOS = 2; //create variable for n channel MOSFET pin
const int pMOS = 3; //create variable for p channel MOSFET pin

void setup() {
  pinMode(nMOS, OUTPUT);   // set pin to output
  pinMode(pMOS, OUTPUT);   // set pin to output
}

void loop() {
  digitalWrite(nMOS, HIGH);   // set n MOSFET gate to high, this will turn it on or close switch
  digitalWrite(pMOS, HIGH);   // set p MOSFET gate to high, this will turn it off or open switch
  delay(750);
  digitalWrite(nMOS, LOW);    // set n MOSFET gate to low, this will turn it off or open switch
  digitalWrite(pMOS, LOW);    // set p MOSFET gate to low, this will turn it on or close switch
  delay(750);
}

Wednesday, January 21, 2015

Maximizing Arduino’s ADC Resolution and Accuracy Part 3

In part three we look at the three main sources of error in an ADC measurement. We will discuss how to reduce total ADC error and how to quantify the total error of an ADC measurement.


Sunday, January 11, 2015

Maximizing Arduino’s ADC Resolution and Accuracy Part 2

In part 2 we will look at how to increase the accuracy of our ADC measurements using the built-in Noise Reduction Mode



Arduino Code********************************************************************
/*This example sketch shows how to make ADC measurements via registers and how to use the low noise or noise cancellation ADC measurement capability. 
This was shown in an ADC tutorial on the ForceTronics YouTube Channel.
This code is free and open for anybody to use at their own risk. 1/9/15
*/
#include <avr/sleep.h>

int normADC[10]; //Create an arrray to hold the "normal" or non-low noise ADC measurements
int lowNoiseADC[10]; //Create an array to hold the low noise ADC measurements

int const aVCC = 0b01000000; //variable to set ADC to use VCC as reference
int const iREF = 0b11000000; //variable to set ADC to use internal 1.1V as reference
int const aREF = 0b00000000; //variable to set ADC to use VCC as reference
//bits 7 and 6 select the ADC reference source
//The four zeros (bits 3 thru 0) at the end of the above binary register values sets the analog channel to A0
//Bit 5 selects right adjust for the result and bit 4 is not used

//Setup code only run once
void setup() {
  ADMUX = aVCC; //Configure the ADC via the ADMUX register for VCC as reference, result right adjust, and source A0 pin
  ADCSRA |= 1<<ADEN; //Turn the ADC on by setting the ADEN bit to 1 in the ADCSRA register
  ADCSRA |= 1<<ADIE; //Setting the ADIE bit to 1 means when the ADC is done a measurement it generates an interrupt, the interrupt will wake the chip up from low noise sleep
  ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));    //Prescaler at 128 so we have an 125Khz clock source
  //The ADC clock must be between 50kHz and 200kHz, since the Uno clock is 16MHz we want to divide it by 128 to get 125kHz. This may need to be adjusted for other Arduino boards
  //We do not have to use registers to set the sleep mode since we are using the avr/sleep.h library
  sleep_enable(); //enable the sleep capability
  set_sleep_mode(SLEEP_MODE_ADC); //set the type of sleep mode. We are using the ADC noise reduction sleep mode
  //Note that in this sleep mode the chip automatically starts an ADC measurement once the chip enters sleep mode
  //Note that the reason for making the ADC measurements and storing in arrays was to avoid using the arduino serial functions with sleep mode since serial uses interrupts
  for(int i=0; i<10; i++) { //Loop ten times and make ten ADC measurements in low noise mode
    sei(); //enable interrupts
    sleep_cpu(); //enter low ADC noise sleep mode, this action turns off certain clocks and other modules in the chip so they do not generate noise that affects the accuracy of the ADC measurement
    //The chip remains in sleep mode until the ADC measurement is complete and executes an interrupt which wakes the chip from sleep
    lowNoiseADC[i] = ADC; //get ADC result and store it in low noise measurement array
  }
  
  for(int k=0; k<10; k++) { //loop executes 10 times and makes 10 normal ADC measurements
    ADCSRA |= 1<<ADSC; //this bit starts conversion. It will remain high until conversion is complete
    while(ADCSRA & (1<<ADSC)) { delay(1);} //When ADSC bit goes low conversion is done
    normADC[k] = ADC; //get ADC result and store it in normal ADC measurement array
  }
  
  Serial.begin(9600); //Start serial comm to print out results to serial monitor
  while(!Serial.available()) { delay(1); } //wait to print out results until serial monitor is open and any character is sent
  Serial.println("Noise reduction:"); //labels the readings being printed
  for(int j=0; j<10;j++) { //loop ten times to print out all the low noise measurement in array
    Serial.println(convertToVolt(lowNoiseADC[j]),3); //Convert each low noise measurement to a voltage level and send it over serial with three decimal places precision
  }
  Serial.println(); //print a line feed
  Serial.println("No noise reduction:"); //print heading for normal ADC measurements
  for(int l=0; l<10;l++) { //loop ten times and print each ADC reading
    Serial.println(convertToVolt(normADC[l]),3); //Convert each normal ADC measurement to a voltage level and send it over serial with three decimal places precision
  }
}

void loop() {
 //do nothing in the loop
}

//This function takes the ADC integer value and turns it into a voltage level. The input is the measured ADC value.
float convertToVolt(int aVAL) {
  float refVal = 5.01;  //hard coded in AVCC value, measured the real value for higher accuracy
  return (((float)aVAL/1023)*refVal); //formula to convert ADC value to voltage reading
}

//this is the interrupt service routine for the ADC interrupt. This must be in the code for the interrupt to work correctly
ISR(ADC_vect) {
}