Pages

Monday, January 28, 2013

Power with a Li-Ion (or LiPo) 3.7 volt cell

I'm running my project with 6 AA re-chargeable batteries. This turns out to be a bit of a pain because the batteries are not easily accessible, so I started looking for an alternative solution.

I found a lot of batteries like the Li-Ion or LiPo re-chargeablebatteries but I was not finding any way to convert that up to 5V. I look over several sites looking for an inverter, or regulator or something that would increase the voltage. Then I discovered the magic word was "boost". There are several types of voltage boosters, and boosters with chargers built in.

Sparkfun sells a Boost Converter that goes from 3.x to 5 volts and a combo board LiPo Charger/Booster. However, during my brief search, I only found one place that sells a LiPo charger/booster that also allows the UBS connection to be used to program your Arduino board, that was SeeeStudio. It called a LiPo Rider (400ma) and LiPo Rider Pro at 1000 ma output.

Although I plan to do most of my programming in wireless mode, it would be nice to occasionally program while charging the battery.

I'll admit I only looked at a few Robotics/Arduino/Microcontroller sites, so there may be other systems out there. Anyhow, this means I can attach my board to this charger/booster with a very short home made USB connector and power it and program it at the same time.

Sunday, January 27, 2013

CritterBot with a Little Sense of Balance

My goal to this point was to put a bot inside a critter crawl ball. I'm not sure why, but it just seemed like a fun thing to do.

So here we have it. I've attached the high torque gear motor axles to 3/4 inch wooden dowels and attached them to the sides of a 13 inch critter crawl that I bought at the pet store.

I specifically made the assembly heavy at one end so I could use gravity as a drive mechanism. Never mind steering at this point, in fact I'm not sure it can ever be steered, but I'll explore that later.

By adding the accelerometer, I'm able to detect the tilt of the assembly. So using this, I should be able to keep the motor from just flipping the assembly over randomly. In other words, I need some sense of balance as I want to keep the assembly horizontal to the floor to maximize the gravity imbalance, thus causing it to roll.

After mounting the accelerometer I created this graph. What I thought would be a difficult algorithm turned out to be quite simple. If I want to move forward, then I should keep the Z axis at zero, and I can simply use the sign of X to determine if I need to add or subtract power from the motor. So if the angle of the assembly is at 45 degrees in the Z axis, and the X axis is positive, then add more power. Simple. If I want to reverse direction, then just the opposite is true if I ignore the sign of the Z axis. The controller has subscribed to accelerometer events, so the forward code uses those events to adjust the speed:

case ADXL345_STATUS_SIG:
{
  int8_t x = Q_EVT_CAST(ADXL345EventArg)->x;
  int8_t z = Q_EVT_CAST(ADXL345EventArg)->z;

  if( moveState == MOVE_STATE_FORWARD )
  {
    if( z > 5 )
    {
      motorSpeed +=  x < 0 ? -10 : +10;

      if( motorSpeed < 0 )
        motorSpeed = 0;
      else if( motorSpeed > 100 )
        motorSpeed = 100;

      DCMotorControl_Set(DC_SPIN_MOTOR, motorSpeed, DCMD_MODE_FORWARD);
    }
  }
}

I've hard coded the values here but eventually I'll make them constants, or variables if the situation arises. So we add plus 10 or minus 10 percent to the motor speed if the z axis is not within 5 degrees of zero.

Backing up to the accelerometer process, it sends out an average angle update to all subscribers

case ADFTIMER_NOTICE:
{
  if( Q_EVT_CAST(ADFTimerEventArg)->processID == me->m_ProcessID )
   {
      ...
      ADXL345EventArg *ea  = Q_NEW(ADXL345EventArg, ADXL345_STATUS_SIG);
      ea->x = me->xAve;
      ea->y = me->yAve;
      ea->z= me->zAve;
      QF::PUBLISH(ea, ADXL345_STATUS_SIG);

at regular intervals. These intervals are based on the accelerometer's subscription to the timer process for an event every 200 milliseconds:

     ADFTimer_StartTimer(me->m_ProcessID, 0, 200);

So the accelerometer takes readings every 200 milliseconds, averages those readings, and sends out an accelerometer event every 5 events, or about one second. What surprised me was that with only a 1 second sample, the CritterBot appeard to behave fairly well. In the following video, you can see the bot climb to 90 degrees and stop because gravity didn't get the ball rolling. But as soon as it starts rolling the bot tries to stay at zero Z axis so you can see and hear it accelerate across the floor.


Cool.

I'll need to experiment more, which I will be able to do as soon as I can program it remotely. I've received my Adafruit parts and built the wireless XBee receiver part, but need my SeeeStudio order to get the transmitter part up and running.

Tuesday, January 22, 2013

Why are PWM ports not all created equal?

When I posted about my H-Bridge motor control, I indicated that it didn't work on ports 10 and 11, but when I switched to ports 5 and 6 everything started to work correctly. I also mentioned that some code examples have you connect to specific ports, not just any ports. What bothered me was that it wasn't obvious why you "must" connect to a specific port. "Are all PWM ports created equal?" kind of question on my part. Well, while I was trolling the Arduino forum I found out about equality when someone asked about "the best" PWM ports to use.

It so happens that the PWM ports are modulated by timers. Ok I knew that. What I didn't know is that ports are assigned to specific timers, not any available timer. Now that makes a difference. From this link we have the following port/timer assignments:

3    PD 3        PWM T2B, Ext Int 1

5    PD 5        PWM T0B
6    PD 6        PWM T0A

9    PB 1        PWM T1A
10   PB 2        PWM T1B

11   PB 3        PWM T2A, MOSI


So timer zero is assigned PWM ports 5 and 6; timer 1 is assigned to pins 9 and 10; and timer 2 is assigned to 3 and 11.

In my application, timer 2 is used for the Quantum Leap Framework, so I could try to use pin 11 for PWM but it will be taken over by my QF/AD framework. So this is a good reason that I couldn't drive the motor from pin 11 via. PWM.

What this chart tells me is that for my specific application, I can't use pins 3 and 11 for PWM, but they are fine as standard digital I/O.

I'm thinking of adding a PWM driven speaker to my project and now I know what ports I can use.

Monday, January 21, 2013

ADF Timer event

It didn't take long to realize that I didn't need all of my devices to be checking on status updates every 10 milliseconds, and I also needed to have some long timed events, like run the motor for 2 seconds. To this end I created a timer event.

For example, I wanted to read the accelerometer x y and z angles every 250 milliseconds.  The timer event class has an add timer method that allows you to specify a "ProcessID" and a "TimerID". The process ID is the class that is creating the timer. Each class gats it's unique process ID from the QF unique priority. These are all encapsulated in the class variable m_ProcessID. This means any class can get its own unique id by referencing me->m_ProcessID. Because each "process" can have multiple timers, you can specify them with timer IDs. The signature of the add method is:

ADFTimer_AddTimer(uint8_t processID, uint8_t timerID, ADFTIMER_TYPE type)

Creating a timer in the ADXL345 class:

ADFTimer_AddTimer(me->m_ProcessID, 0, ADFTIMER_REPEAT);

creates a repeating timer, as opposed to a ADFTIMER_ONSHOT timer. Starting the timer:

ADFTimer_StartTimer(uint8_t processID, uint8_t timerID,
                        uint16_t milliSeconds)

for this process using timer ID 0 for 250 ms is

ADFTimer_StartTimer(me->m_ProcessID, 0, 250);

All we have to do subscribe to the event during class initialization:

me->subscribe(ADFTIMER_NOTICE);

and we will receive notices from the timer. Once a notice comes in we need to ensure it is our timer:

if( Q_EVT_CAST(ADFTimerEventArg)->processID == me->m_ProcessID );

and then we can react to it. In the above case, the accelerometer is only using one timer so we don't need to further determine which timer it is.

So currently I have the following device startups:

    ADFTimer_Start          ( ++priority );
    DigitalODC_Start        ( ++priority );
    DigitalIDC_Start        ( ++priority );
    AnalogIDC_Start         ( ++priority );
    DCMotorControl_Start    ( ++priority );
    ADXL345Handler_Start    ( ++priority );

Plus my controller:

    MyController_Start      ( ++priority );

In about 17K, the ADF platform can:

  • Write to N number of digital output pins
  • Read N number of digital input pins
  • Write to N number of Analog pins
  • Control N number of H-Bridge DC motors
  • Read 1 accelerometer on the IIC bus.
  • Time N number of events
The last point I want to make it that the timer resolution is somewhat variable depending on the number of interrupts per second that are designated in the bsp.h file. For example, I'm currently running at 200 interrupts per second:

#define BSP_TICKS_PER_SEC  200U
#define TICK_DIVIDER ((F_CPU / BSP_TICKS_PER_SEC / 1024) - 1)
#define TICK_MILLIS  (1000/BSP_TICKS_PER_SEC)

This would provide a 5 millisecond time tick. However, if ticks per second is set to the minimum of 66, That is about 15ms per tick. If the time of the event is not exactly a modulo of the tick millis, then the time will be longer by tick millis. This means in my current scenario, my timer is either exactly correct or off by plus 5 ms.

Sunday, January 20, 2013

Simple Accelerometer Data Conversion to Degrees

What I though would be a very simple search turned into hours of research and reading. My task is simple. Read an accelerometer, in this case an ADXL345, and determine what its orientation is in degrees. Geeez, you would have thought I was the first person to do this, so now I'll be the first person to just give you the simple lines of code to do it.

   double xyz[3];
   double ax, ay, az;

   adxl.get_Gxyz(xyz);
   ax = xyz[0];
   ay = xyz[1];
   az = xyz[2];
   double xAngle = atan( ax / (sqrt(square(ay) + square(az))));
   double yAngle = atan( ay / (sqrt(square(ax) + square(az))));
   double zAngle = atan( sqrt(square(ax) + square(ay)) / az);

   xAngle *= 180.00;   yAngle *= 180.00;   zAngle *= 180.00;
   xAngle /= 3.141592; yAngle /= 3.141592; zAngle /= 3.141592;

Yea I know this could have been better written, but it works. There. Done! My 3 hours of research resulted in 11 or so lines of code. There are lots of blogs and DIY posts that go into the physics of accelerometers and go on at length about vector addition, angles, radians etc. But if you're like me all I want to know is if my toy robot fell down and can't get up.

But credits are due. I did find something I could read and understand without a masters in math at www.arduinoos.com. My code is a distillation of code found on page 5.

Note the get.Gxyz method returns (what I understand) are voltage levels from 0 to about 1.1 volts. My ADXL345 is running on IIC, but I can only assume if you are reading analog voltages through analog input pins you would get something similar.

Now, if you use this code you might notice some slight problems. You see the accelerometer should be calibrated, and/or the readings should be normalized. But this code will get you going if your task is rather simple. If you're building a fleet of quadcopters this might not be enough code.

I'm reading angles from 0 to 89.8 degrees in both the positive and negative direction. Now I don't think I'm finished with my angle calculations because I expect my toy to get bounced around a bit and I'll have to sort that out, but this is a simple first start.

I wish I could have found a post like this last night when I started my research, so hopefully you will find this of use, which is the purpose of this blog. Adruinos for Gumps.

Thursday, January 17, 2013

DC Motors and an ADF motor controller


My first project needs a motor assembly with high torque so I elected to purchase a Tamiya 72004 Worm Gearbox Kit from Pololu. Additionally I bought a T.I. DRV8833 H-Bridge board for about $7.00. I also bought a Radio Shack bread board and a Tamiya 70157 Universal Plate Set to hold everything together.

As you can see in the photos, I put the motor, a 3 cell AA battery pack and Arduino board (with grove extension) on one side, and another AA battery pack on the other side with the H-Bridge chip on the breadboard. (the assembly is being held by a PanaVise in the photographs).

I assembled the motor gear box for a 336:1 gear ratio but it can also be assembled with a 216:1 gear ratio. The axle is slow, maybe 1 RPS when the DC motor is at 100% so it has very high torque. In fact, if I clamp the drive shaft the motor is able to spin the entire assembly around in a circle with very little strain.

The Seeeduino board is powered by combining both battery packs, 6 rechargeable AA's. Each battery produces 1.2 volts so the total is 7.2 volts. This is slightly below the minimum recommended input voltage for the on board voltage regulator. This voltage, through the power connector, makes it's way to the on-board power regulator. The incoming line has a forward biased diode to keep current from back flowing through the regulator chip. If you have multiple power sources like I do, then check to make sure your regulator is protected. I've read this is recommended so as to not fry your board.

I have to admit that I was skeptical about having the batteries plugged into the Seeeduino board and then just plugging and unplugging the USB connector, but the board switches over with no problems. In fact while it's running with the usb connected it is running on the usb power, not my batteries, but pulling the usb connector causes the board to switch to the batteries with no glitch. Very cool.

Note that I soldered 3 noise suppression diodes to the motor. Pololu sells a noise suppression pack of three 0.1 uF capacitors. You can read more about DC motor noise suppression at the HydraRaptor blog. Note that I did not use ferrite beads in my suppression scheme as specified in the HydraRaptor blog.

Additionally I used a power supply decoupling capacitor based on this tutorial.

here is a diagram of my current project created with Fritzing.


In order to run the motor as I am, you need two PWM outputs. I originally used pins 10 and 11 which are marked on the board as PWM, but one of the pins would not modulate (I can't remember which one). Anyhow, after some fiddling around I move the motor to PWM pins 5 and 6 and it started to work. I did go back and verify that pins 10 and 11 were functioning correctly as digital outputs. So this is a caveat. I've also seen documentation that recommends you connect something like a servo to a specific I/O pin. I don't know why a specific GPIO in a group of similar GPIO's (like the PWM's) is chosen over the others.

I decided to split the power supply, one set to drive the motors and another set added to that to drive the Arduino board. If I decide I need more battery power, I'll add another 3 cell set in parallel to the "lower" set. I did this mainly because I can't drive the motors at 7.2 volts, I don't want to add a voltage regulator to drop 7.2 down to say 6 volts, and I didn't want to stress the onboard 5v regulator. So the motors will be driven at 3*1.2v or 3.6v. (note that a rechargeable 1.5 volt battery produces a constant 1.2 volts until it is fully discharged).

To control the motor I added a new Motor class to my Asynchronous Device Framework. The class is designed to control more DC H-Bridge motors than you could possibly put on an Arduino board, but if I get to 4 motors I'll not have to add any motor class controller code. Once again I use a linked list, in this case virtual motors, that get added at run-time.

There are only three public motor control members, one to start the controller in the ino setup, one to add a motor in the MyController start-up, and one to control it during run-time:

DCMotorControl_Start(uint8_t priority);

DCMotorControl_Add(uint8_t motorID, uint8_t pin1, uint8_t pin2);

DCMotorControl_Set(uint8_t motorID, uint8_t speedPercent,
                                 DCMD_MODE motorMode);

Where DCMD_MODE is defined as

 enum DCMD_MODE
{   
    DCMD_STOP,
    DCMD_COAST,
    DCMD_FORWARD,
    DCMD_REVERSE
};

In my code I define the motor pins, and an arbitrary but unique motor id as:

const uint8_t    DCMOTORin1           =    5;
const uint8_t    DCMOTORin2           =    6;
const uint8_t    DC_SPIN_MOTOR        =    56;

and then create it during runtime startup with:

DCMotorControl_Add(DC_SPIN_MOTOR, DCMOTORin1, DCMOTORin2);

and then control it in the MyController process with a 'make the spin motor go in reverse at 25%' :

DCMotorControl_Set(DC_SPIN_MOTOR, 25, DCMD_REVERSE);

The asynchronous device platform's capabilities far exceed the hardware capabilities of my Seeeduino board. The controller is intended to control a specific type of DC motor controller, the H-Bridge. I could envision a motor type DC_HBRIDGE or DC_STEPPER that would adapt to the controller in a generic way, or you could even create a specific type for a custom hardware controller.

The current ADF motor controller uses the following control logic based on the DRV8833 operation:

// recurse through the list of motor output devices
// and change the state as described in the event args
void DCMCListWalker(VMODevice * link, DCMDEventArg const * device)
{
    if(link != NULL)
    {
        if( link->m_MotorID == device->motorID )
        {
            uint8_t speed = map(device->motorSpeedPercent,0,100,0,255);
            if(device->motorMode == DCMD_COAST )
            {
                analogWrite(link->m_Pin1, 0);
                analogWrite(link->m_Pin2, 0);
            }
            else if(device->motorMode == DCMD_FORWARD )
            {
                analogWrite(link->m_Pin1, speed);
                analogWrite(link->m_Pin2, 0);
            }
            else if(device->motorMode == DCMD_REVERSE )
            {
                analogWrite(link->m_Pin1, 0);
                analogWrite(link->m_Pin2, speed);
            }
            else // STOP
            {
                analogWrite(link->m_Pin1, 255);
                analogWrite(link->m_Pin2, 255);
            }
        }
        else if(link->m_NextVMODevice != NULL )  // not it, go deeper
            DCMCListWalker(link->m_NextVMODevice, device);
    }
}

Also note that although I use forward and reverse, it would probably be good to add something like clockwise and counter-clockwise, or left and right to the enumerations for readability.

Tuesday, January 1, 2013

First Circuit Design for Prototype Model 1


I plan to drive a 3-4.5V rated brush DC motor with a Pololu DRV3388; generate sounds on a small 16 ohm speaker, and motion control with a Pololu 3001HB servo. I’ve elected to run on 2 packs of 3 cell 1.5V rechargeable batteries, both providing about 3.6v each. My Seeeduino Duemilanove board will be driven from the battery packs in series through the onboard power jack, while taping off one 3.6V pack for the DC motor. I’m isolating the speaker with a 2N2222 (or equivalent) so I can drive the 500 mW rated speaker to about 400 mw.

The Seeeduino board has a forward bias diode driving a 78M05 5 volt regulator (making the 7.2V input marginal). What is not shown in this diagram are 3 noise suppression caps on the DC motor. I will also have an I2C 3 axis accelerometer and several other analog and digital I/O devices running off the 5V onboard regulator. I’ll be doing wireless development using an Adafruit XBee Adapter kit so that is not shown either. Eventually I’ll probably use a DPST power switch so I can switch both battery packs.

I’m very new to Fritzing so the drawing is not to my satisfaction (I had to retouch connections for clarity). If I create a PCB I might change GPIO pin assignments.