Friday, November 23, 2012

Battery Charging - Part 5 (Harbor Freight item 42292)

Yes, I finally assembled and tested the various ideas into a single box for my float charger for lead acid batteries.  This project has been going on for over a year now and may be getting close to having a nice working prototype.  I incorporated a voltage display and an XBee set up to transmit analog readings into the mix.  So, what happens is you plug the charger into the wall and the XBee comes on, samples the voltage before the output diode and sends it to whatever device you set it up to talk to.  This way I can monitor the battery state of charge remotely without even having to go into the garage.  Here's an interior view of the charger:


On the lower left is the charging circuit that I came up with based (roughly) on the Harbor Freight 42292; at the top, stuck to the side is a series 2 XBee plugged into a breakout board; and on the right is the voltmeter display.  This is all enclosed in a plastic rectangular enclosure.  When assembled it looks like this:


Of course, since the XBee is a 3.3V device, I had to add a power supply for it.  I took the power directly from the float voltage and used an AP1117T33 regulator.  When I first started testing the combination, it got pretty hot pretty quickly, even when it wasn't hooked to a battery.  A little checking (I unplugged the XBee) told me that the XBee and its two LEDs were sucking enough power to heat it up, so I enabled sleep on the XBee.  This way, it would be off most of the time and only turn on to take a couple of measurements and transmit them.  This totally solved the problem.Here's the schematic I eventually settled on.  

If you're following this, you'll see that it's a simple variable supply that I set to 13.4V with resistor R2 which feeds a 3.3V supply to power the XBee.  The voltage divider R4, R5 sets the level that is in the 1.2V analog input range of the XBee.  Since these regulators sometimes tend to oscillate, I added capacitors C1 and C2 just to make sure I didn't drive the XBee nuts.  That's also why I added the resistor R6, to keep it away from reset.

To set it up, I put a 1K resistor across the output and set the voltage using R2 to 13.4 volts.  Then I centered the pot R5 and checked what was being transmitted.  By adjusting the pot, I was able to get a nice reading at the receiving end which I translated back into voltage and can display with an XBee connected Arduino.

There's a little jitter in the XBee readings caused by transients.  I can solve that two ways, add a capacitor on pin 20 of the XBee or do a rolling average on the receiving device.  I haven't decided which to do yet. I'm leaning toward a rolling average, but experience will tell me if it even needs to be fixed.  Here's a short sample of the Arduino output I set up.  Obviously, this isn't what I'll eventually use, but it's nice to be able to see the charger out in the garage:

A fun item I noticed is that the circuitry for monitoring the battery and transmitting the value can be separated from the charger.  That way I could hook a device to the battery and it would transmit the voltage to something that could alert me to go attend to the darn thing.  I may pursue that for something in the future.

No, I'm still not done.  This thing was a real pain to build.  It came up from several prototypes and kept getting more complex and looks like a mess because of this.  I think I may journey into getting a custom PC board made to put the thing together with since I want at least five of them.  Also, since I now have a box that will hold it, I'm going to mount the components on the lid with a wire to the display on the box part.  That way I can actually screw stuff down so it doesn't bounce around over time.

Monday, November 19, 2012

Apple Device: "Charging is not supported with this accessory"

I don't have a single Apple device in my house, no iPad, iPhone, iPod, iAnything.  I don't even have any apples.  However, the little wall warts I bought <link> have a bad reputation because they don't work on some iDevices.  While it's true that they don't, it really isn't their fault, it's Apple's.  This is the message:
Notice that it doesn't say that the device is the wrong voltage, wrong polarity, or wrong anything else?  That's because the device just doesn't meet the Apple standards of being made by Apple specifically for a particular Apple device.  They really, really want to lock you into buying your stuff from them and them alone.

My little chargers are fine and will work with Apple stuff with a simple modification that I won't do since I don't need to charge Apple devices.  What Apple did was use the D- and D+ (the two middle pins on a standard type A usb plug) to supply specific voltages that Apple could look at to tell if the charger was one of theirs.

Well, this little trick didn't last long before the engineers that make battery extenders and chargers figured it out.  Obviously, they grabbed a real apple charger and ripped it open to see what kind of shenanigans Apple was up to.  One manufacturer Lady Ada wrote up a nice description of what was happening and how she worked around it for one of her products, the MintyBoost.  She has a nice description of how she overcame the problem and made her stuff work <link>.

This also explains over 90% of the complaints on the web about various iDevices not working with a charger.  Basically, these two voltages (D1 and D2) are tiny and can vary a lot based on contact resistance, length of cable, net draw of the device, etc.  Basically any darn thing at all.  For example some of the solutions are to turn off the iPhone, plug the cable and charger into the phone, plug the charger into the wall, turn on the phone, repeat a number of times to allow the gremlins to align correctly.  And, this occasionally works.  Another solution is to lower the screen intensity, put the phone in airplane mode, then hook up the charger. There's always someone that says you need a new cable, better charger, or hold your mouth differently.  In the case of the iPhone, there was an OS update that caused a significant number people to have charging problems.  The fix for many of these non-technical folk was to go to the Apple store and get a new device or have a new part installed that will work.  Of course there are the Apple cultist that insist that only a new Apple charger will solve the problem.

But people, electrons are electrons and it isn't you or your device when a problem like this turns up, it's Apple exercising their control over their cult.  Annoying.

Don't despair yet.  There are ways to get around this problem that actually make sense.  No, you don't need to wet a Qtip with perfume and clean the contacts, letting it air dry for an hour(this is a real suggestion), there are much more mundane solutions.  For example, this site <link> talks about a little connector that has Apple plugs on both ends and works around the problem.  However, the darn thing is too expensive (I'm not a fan of this solution, but some of you might be).  There are several other similar devices out there that solve the problem.  You can also solve the problem by building something yourself (gasp !); I know this goes against life philosophy of the myriad teenagers that talked their parents into getting this cool new gadget, but life isn't fair.

Just cut a USB charging cable that mates with the device and add a little voltage divider.  The schematic of the changes is:
If this picture doesn't look familiar, take another look at Lady Ada's description, it was taken directly from there.  See, Apple uses the voltage divider to set specific voltages on D+ and D- to identify their devices.  Other manufacturers don't do this kind of thing unless they're trying to meet the USB charging standards, and for a low price device it isn't worth the effort or cost. Just another effort of Apple to wrangle you into their pen.

So, you ask, "What do you care, you don't own an Apple device?"  I don't, but my less fortunate friends, neighbors, and relatives do.  They get really annoyed when their devices won't work on one of my chargers when they come to visit, and they hate it when a charger stops working and gives them this silly, non descriptive message.  Also, I found zero, nada, explanations of what was going on to help people with this problem.  That annoyed me more than anything else, so I decided to post it here and a couple of other places, hoping that poor souls that are eating up Google cpu cycles could, at least, understand the problem better.

Soak a Qtip in perfume and clean the contacts?  Really?

Sunday, November 18, 2012

Tiny Power Supplies for Micro Controller Projects

When you build micro controller projects, eventually you want to do something real that will have to work long term.  For this you need a power supply that is small, maybe small enough to put in a light switch, ceiling fan, or dryer control panel.  These things are hard to find.  I've spent hours on Google trying to find really small 5V supplies with some success, but never fitting my needs.  Nice little supplies that you have to buy in 100+ quantities, nice little supplies that cost US $50, etc are out there, but who wants to invest that kind of money in a technology that changes almost daily?

In a few project postings I describe how I cut the guts out of a wall wart like this:
These little devices work great as microcontroller power supplies.  The one above will put out up to 2A and doesn't seem to get hot doing it.  I've had several in service, out in the weather for two years.  At a cost less than US $5 each (usually with free shipping) they are a fine solution.  I've taken the supply out of the plastic case and installed it into devices like this <link>:
The little board in the upper left is the guts of the wall wart.  I've even used the entire wall wart as a mounting platform for another device:
This device <link> has an XBee mounted on the side of the wall wart transmitting temperature every minute or so.  All-in-all, a pretty useful device repurposed from the iPad craze.  However, technology moves on and I want to try a couple of ideas that would work better if I had an even smaller power supply.  I asked on a couple of forums and was pointed to one of these:
I already had two of them charging mobile devices in the house so I decided to gut one of them and see what was inside:
This little supply is TINY. Yes, I had to cut the case off with a Dremel to get to the guts; the top was seriously glued to the bottom and refused to come off. The two board with connecting cable configuration could create a problem mounting, but nothing I can't overcome, and the wires to the AC pins might give me a problem, but it couldn't be too hard to solve; I like this.  So, looking around the web to see what kind of deal I could get on them I found them for US $1 on Amazon.  Yes, you read that right, a single dollar, with free shipping.  So:
Here's my pile of stock devices.  

Since supply and demand act on everything, the price at Amazon has gone up to US $1.45 as of this writing and could go either up or down based on activity, but they are still a bargain.  

Even with this solution available, I'm still searching for possibilities.  The rough requirements for the supply are: minuscule size, 5V at a least an amp (relays, you know), easy to mount, things like that.  

To you suppliers out there, there's a market here that you aren't exploiting.  SparkFun, Lady Ada, Itead, etc. are you listening?  You folks in China, what are you waiting for?

Saturday, November 10, 2012

Using the XBee Library Part 2

Previously, I talked about using Andrew Rapp's XBee library on a project and posted an example.  This is the second example I've been working on.  The objective is to understand the library well enough to re-implement some of my devices using the library because I'm getting tired of doing my own parsing of the basic packet.  Yes, I have finer control, but I've found I don't really need finer control.  So, here is an example of using the code on an Arduino 2560 with the XBee connected to Serial3.  This leaves me two unused serial ports to play with since I'm using the main serial port as my debugging and status port.

The Arduino Sketch
/*
XBee RX test for a Arduino Mega2560 using Serial3 as the XBee serial
input for a Series 2 XBee.  I took Andrew Rapp's receive example and modified
it heavily.

I wanted to experiment with and better understand how to use his XBee
library for an actual project.  With the inclusion of support for SoftwareSerial
so that the XBee can be put on digital pins leaving the Arduino serial port
available for debugging and status, the library's usefullness shot way up.  These
changes also allow for the use of the additional serial ports on a Mega2560

This is a HEAVILY commented example of how to grab a receive packet off the
air and do something with it using series 2 XBees.  This example also supports
a remote XBee end device sending analog data.  Series 1 XBees are left as an
exercise for the student.
*/

#include <XBee.h>

XBee xbee = XBee();
XBeeResponse response = XBeeResponse();
// create reusable response objects for responses we expect to handle
ZBRxResponse rx = ZBRxResponse();
ZBRxIoSampleResponse ioSample = ZBRxIoSampleResponse();

void setup() {
  // start serial
  Serial.begin(9600);
  // and the third serial port
  Serial3.begin(9600);
  // now that they are started, hook the XBee into
  // the third serial port
  xbee.setSerial(Serial3);
  // I think this is the only line actually left over
  // from Andrew's original example
  Serial.println("starting up yo!");
}

void loop() {
    // doing the read without a timer makes it non-blocking, so
    // you can do other stuff in loop() as well.
    xbee.readPacket();
    // so the read above will set the available up to
    // work when you check it.
    if (xbee.getResponse().isAvailable()) {
      // got something
      // I commented out the printing of the entire frame, but
      // left the code in place in case you want to see it for
      // debugging or something.  The actual code is down below.
      //showFrameData();
      Serial.print("Frame Type is ");
      // Andrew calls the frame type ApiId, it's the first byte
      // of the frame specific data in the packet.
      Serial.println(xbee.getResponse().getApiId(), HEX);
   
      if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) {
        // got a zb rx packet, the kind this code is looking for
     
        // now that you know it's a receive packet
        // fill in the values
        xbee.getResponse().getZBRxResponse(rx);
     
        // this is how you get the 64 bit address out of
        // the incoming packet so you know which device
        // it came from
        Serial.print("Got an rx packet from: ");
        XBeeAddress64 senderLongAddress = rx.getRemoteAddress64();
        print32Bits(senderLongAddress.getMsb());
        Serial.print(" ");
        print32Bits(senderLongAddress.getLsb());
     
        // this is how to get the sender's
        // 16 bit address and show it
        uint16_t senderShortAddress = rx.getRemoteAddress16();
        Serial.print(" (");
        print16Bits(senderShortAddress);
        Serial.println(")");
     
        // The option byte is a bit field
        if (rx.getOption() & ZB_PACKET_ACKNOWLEDGED)
            // the sender got an ACK
          Serial.println("packet acknowledged");
        if (rx.getOption() & ZB_BROADCAST_PACKET)
          // This was a broadcast packet
          Serial.println("broadcast Packet");
       
        Serial.print("checksum is ");
        Serial.println(rx.getChecksum(), HEX);
     
        // this is the packet length
        Serial.print("packet length is ");
        Serial.print(rx.getPacketLength(), DEC);
     
        // this is the payload length, probably
        // what you actually want to use
        Serial.print(", data payload length is ");
        Serial.println(rx.getDataLength(),DEC);
     
        // this is the actual data you sent
        Serial.println("Received Data: ");
        for (int i = 0; i < rx.getDataLength(); i++) {
          print8Bits(rx.getData()[i]);
          Serial.print(' ');
        }
     
        // and an ascii representation for those of us
        // that send text through the XBee
        Serial.println();
        for (int i= 0; i < rx.getDataLength(); i++){
          Serial.write(' ');
          if (iscntrl(rx.getData()[i]))
            Serial.write(' ');
          else
            Serial.write(rx.getData()[i]);
          Serial.write(' ');
        }
        Serial.println();
        // So, for example, you could do something like this:
        handleXbeeRxMessage(rx.getData(), rx.getDataLength());
        Serial.println();
      }
      else if (xbee.getResponse().getApiId() == ZB_IO_SAMPLE_RESPONSE) {
        xbee.getResponse().getZBRxIoSampleResponse(ioSample);
        Serial.print("Received I/O Sample from: ");
        // this is how you get the 64 bit address out of
        // the incoming packet so you know which device
        // it came from
        XBeeAddress64 senderLongAddress = ioSample.getRemoteAddress64();
        print32Bits(senderLongAddress.getMsb());
        Serial.print(" ");
        print32Bits(senderLongAddress.getLsb());
     
        // this is how to get the sender's
        // 16 bit address and show it
        // However, end devices that have sleep enabled
        // will change this value each time they wake up.
        uint16_t senderShortAddress = ioSample.getRemoteAddress16();
        Serial.print(" (");
        print16Bits(senderShortAddress);
        Serial.println(")");
        // Now, we have to deal with the data pins on the
        // remote XBee
        if (ioSample.containsAnalog()) {
          Serial.println("Sample contains analog data");
          // the bitmask shows which XBee pins are returning
          // analog data (see XBee documentation for description)
            uint8_t bitmask = ioSample.getAnalogMask();
            for (uint8_t x = 0; x < 8; x++){
            if ((bitmask & (1 << x)) != 0){
              Serial.print("position ");
              Serial.print(x, DEC);
              Serial.print(" value: ");
              Serial.print(ioSample.getAnalog(x));
              Serial.println();
            }
          }
        }
        // Now, we'll deal with the digital pins
        if (ioSample.containsDigital()) {
          Serial.println("Sample contains digtal data");
          // this bitmask is longer (16 bits) and you have to
          // retrieve it as Msb, Lsb and assemble it to get the
          // relevant pins.
          uint16_t bitmask = ioSample.getDigitalMaskMsb();
          bitmask <<= 8;  //shift the Msb into the proper position
          // and in the Lsb to give a 16 bit mask of pins
          // (once again see the Digi documentation for definition
          bitmask |= ioSample.getDigitalMaskLsb();
          // this loop is just like the one above, but covers all
          // 16 bits of the digital mask word.  Remember though,
          // not all the positions correspond to a pin on the XBee
          for (uint8_t x = 0; x < 16; x++){
            if ((bitmask & (1 << x)) != 0){
              Serial.print("position ");
              Serial.print(x, DEC);
              Serial.print(" value: ");
              // isDigitalOn takes values from 0-15
              // and returns an On-Off (high-low).
              Serial.print(ioSample.isDigitalOn(x), DEC);
              Serial.println();
            }
          }
        }
        Serial.println();
      }

      else {
        Serial.print("Got frame id: ");
        Serial.println(xbee.getResponse().getApiId(), HEX);
      }
    }
    else if (xbee.getResponse().isError()) {
      // some kind of error happened, I put the stars in so
      // it could easily be found
      Serial.print("************************************* error code:");
      Serial.println(xbee.getResponse().getErrorCode(),DEC);
    }
    else {
      // I hate else statements that don't have some kind
      // ending.  This is where you handle other things
    }
}

void handleXbeeRxMessage(uint8_t *data, uint8_t length){
  // this is just a stub to show how to get the data,
  // and is where you put your code to do something with
  // it.
  for (int i = 0; i < length; i++){
//    Serial.print(data[i]);
  }
//  Serial.println();
}

void showFrameData(){
  Serial.println("Incoming frame data:");
  for (int i = 0; i < xbee.getResponse().getFrameDataLength(); i++) {
    print8Bits(xbee.getResponse().getFrameData()[i]);
    Serial.print(' ');
  }
  Serial.println();
  for (int i= 0; i < xbee.getResponse().getFrameDataLength(); i++){
    Serial.write(' ');
    if (iscntrl(xbee.getResponse().getFrameData()[i]))
      Serial.write(' ');
    else
      Serial.write(xbee.getResponse().getFrameData()[i]);
    Serial.write(' ');
  }
  Serial.println();
}

// these routines are just to print the data with
// leading zeros and allow formatting such that it
// will be easy to read.
void print32Bits(uint32_t dw){
  print16Bits(dw >> 16);
  print16Bits(dw & 0xFFFF);
}

void print16Bits(uint16_t w){
  print8Bits(w >> 8);
  print8Bits(w & 0x00FF);
}

void print8Bits(byte c){
  uint8_t nibble = (c >> 4);
  if (nibble <= 9)
    Serial.write(nibble + 0x30);
  else
    Serial.write(nibble + 0x37);
     
  nibble = (uint8_t) (c & 0x0F);
  if (nibble <= 9)
    Serial.write(nibble + 0x30);
  else
    Serial.write(nibble + 0x37);
}

It was actually pretty easy to convert my previous example to the 2560; I just changed the port in setSerial() to Serial3 instead of newsoftserial and it worked.  This left me a little time to play with capturing an I/O packet and taking it apart.  The code to handle an I/O packet is in this example.

I wanted the I/O packet because I have an XBee outside on a pole measuring the temperature that sends I/O packets periodically and I want to set up a battery monitor for my lead acid batteries to keep track of their state of charge.  You don't need an Arduino hooked to an XBee for these purposes, but you do need to be able to receive and decode the packets.

Once again, feel free to grab the code and modify it for your purposes.