Tuesday, April 8, 2014

Micro clock with 7-segment bubble display

I am not particularly attracted by 7-segment LED displays, but I liked the HP bubble display that Sparkfun started selling recently. Like with the Nixie tubes, there aren't many things one can build with them, and my obvious choice was a clock. Being one of the smallest 7-segment LED displays out there, the clock had to be miniature as well. It took some thinking and planning to come to this final "design":


This micro clock uses the Pro Mini sandwiched between the LiPo battery undershield and the display shield (that also includes the RTC), both DIY on protoboard.


The LiPo undershield uses a cheap (less than $1 on ebay) USB charger board, which also provides the USB connector for power, since Pro Mini does not have one. The small 180mAh LiPo battery sits on top of the charger, but still allowing the 2 LEDs ("charging" and "charged") to be visible. There is also a switch for turning the battery power on or off. The clock is normally powered from a USB power adapter, but it can also work on battery for about 9 hours (the measured average current draw is around 20mA) when the USB cable is not plugged in. In this latter case, it can be turned on from the power switch to show the time only when needed.

The display shield is oversized to make the clock sit at an angle. This solution also provided enough room to include the RTC chip and the backup battery. Actually, and this will probably be the topic of a future post on DIY miniaturization, I did not have a choice for creating a separate RTC shield. The machined headers I used, not the stacking kind (which would have been disproportionately big), only allowed a top and a bottom shield around the Pro Mini board.


As one can imagine, the sketch is trivial (after reading the excellent Sparkfun tutorial :) and based on the SevenSeg arduino library.
But there is always a little glitch somewhere. In this case, I misinterpreted the datasheet. Normally, digit 1 is the left (most significant) one. My wiring assumed digit 1 is the rightmost (least significant) digit.
I fixed it in software: redefined digit1...digit4, then rotated segments (segA to SegF) 180 degrees.


#include "SevSeg.h"
#include
#include "DS1307.h"

SevSeg myDisplay;
unsigned long timer;

void setup()
{
   int displayType = COMMON_CATHODE;

   // arduino pins connected to cathodes;
   int digit1 = 15; //Pin 1
   int digit2 = 13; //Pin 10
   int digit3 =  3; //Pin 4
   int digit4 = 14; //Pin 6

   // arduino pins connected to the segments (anodes);
   int segC =  6; //Pin 3
   int segE =  4; //Pin 2
   int segG =  5; //Pin 7
   int segB = 12; //Pin 11
   int segD =  8; //Pin 8
   int segF = 11; //Pin 9
   int segA =  7; //Pin 12

   // not connected yet;
   int segDP= 10; //Pin 5

   int numberOfDigits = 4; // this display has 4 digits;

   // initialize the 7-segment display object;
   myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP);

   myDisplay.SetBrightness(100); // 100% brightness

   timer = millis();
}

void loop()
{
  int rtc[7];
  RTC_DS1307.get(rtc, true);
  int minute = rtc[DS1307_MIN];
  int hour   = rtc[DS1307_HR];
  char timeString[10];
  sprintf(timeString, "%02d%02d", hour, minute);
  myDisplay.DisplayString(timeString, 0);

  if (millis() - timer >= 1000)
    timer = millis();
}

The next version of the bubble clock should use two displays (8 digits), showing the seconds as well.

Saturday, April 5, 2014

The simplest Arduino LCD Clock

I wrote up this post in response to the many inquiries I get about Arduino school projects.

Here I describe how to build the most basic (and probably the lowest cost as well) Arduino clock, using a real time clock (RTC) chip and a 1602 LCD display (2 lines of 16 characters). Unlike most of the LCD clocks out there (the only notable exception being Kevin Rye's LCD clock), this one is also encased and ready to be used in real life. Actually, the build starts with the enclosure (the very affordable adafruit LCD stand), which drives the requirements for the rest of the components.

Note: This LCD clock does not have any buttons. The time and date are set with the serial monitor (or Hyperterminal or any other serial console), by sending specific commands. I was thinking of adding the Bluetooth module, but then the process would be the same (or maybe even more complex, if you consider the pairing step), and it would only replace the PC with an Android phone or tablet.
Using serial monitor makes sense because the Pro Micro already has on-board USB interface, that is, one doesn't need the extra FTDI cable to communicate with the PC (also requiring attention to the connector's orientation when plugging it in).


List of materials
  • LCD stand - from adafruit;
  • 16x2 LCD - from various sources: adafruit, sparkfun, ebay;
  • Pro Micro (sparkfun's original or clones from ebay) - board compatible with Arduino Leonardo, chosen because it is the only one fitting the LCD stand, both in size and the position of the USB connector;
  • prototyping PCB - I used this, but many others can be found on ebay;
  • 10k trimpot (for adjusting the contrast on LCD);
  • DS1307 real time clock IC + 32kHz crystal - from ebay;
  • CR1220 coin battery + battery holder - from ebay;
  • two 10k resistors (pull ups for I2C lines);
  • a bunch of wires;
  • micro USB cable - from ebay;
  • 16-pin male header and 16-pin female header - from ebay;
  • USB power adapter.

Depending on the component sourcing, the cost for the whole project can vary between $20 (ebay) and $45 (adafruit, sparkfun).


Step 1
Solder the 16-pin male header onto the LCD display.

Step 2
Assemble the LCD stand including the LCD as well, selectively following the manufacturer's instructions.


Step 3
Cut a piece of prototyping PCB to match the size of the LCD. This will be used as a "backpack" (plugged into the LCD) holding the Pro Micro board, the RTC and the battery.

Step 4
Solder the female header onto the backpack in the appropriate position (so that it can be plugged into the LCD).

Step 5
Solder the small Pro Micro board on the PCB so that the micro USB connector matches the side opening of the stand, also making sure that the micro USB cable can be plugged in through that opening.
Then wire the Pro Micro to the header of the LCD display, as follows:
  • pin A1 of Pro Micro to pin 4 (RS) of LCD
  • pin A0 of Pro Micro to pin 6 (EN) of LCD
  • pin 16 of Pro Micro to pin 11 (D4) of LCD
  • pin 10 of Pro Micro to pin 12 (D5) of LCD
  • pin 15 of Pro Micro to pin 13 (D6) of LCD
  • pin 14 of Pro Micro to pin 14 (D7) of LCD
  • LCD's pins 1, 5 and 16 are wired to ground
  • LCD's pin 2 is connected to Vcc (5V)
  • LCD's pin 3 is connected to the 10k trimpot slider (middle pin), used for adjusting the contrast
  • LCD's pin 15 (backlight LED) is connected to Pro Micro's pin 9.
A good tutorial on connecting a 16x2 LCD to Arduino can be found here.

Step 6
Solder the RTC, battery holder and crystal, then connect the I2C lines (SDA and SCL) to the Pro Micro, according the the schematic below.


DS1307 talks to any microcontroller on I2C, through 2 wires named SDA and SCL. Unlike Arduino 2009 or UNO (with ATmega328) where SDA and SCL are on pins A4 and A5 respectively, Leonardo and Pro Micro (with ATmega32u4) use pins D2 and D3 as SDA and SCL.

We also need to pullup the I2C lines (SDA and SCL), by connecting D2 and D3 to Vcc through the 10k resistors. The final result is shown in the photo below.


I used the SMD version of DS1307, soldered on the PCB's plated side (see photo below). Also note the trimpot, soldered on the same side for accessibility.


Step 7
Familiarize yourself with the Pro Micro (or the equivalent Arduino Leonardo). Read about how to install the board's driver, if this is the first time you use it.

Step 8
Download the sample sketch (link to github to be provided), compile (in Arduino IDE 1.04 or later, after you made sure that "Arduino Leonardo" is selected as the current board), then upload.

Step 9
Open the serial monitor and set the current time by sending the command TIME=hh:mm:ss, where hh is the actual hour and mm is the actual minute (e.g. TIME=08:25:00 or TIME=15:42:38).
As well, set the date by sending the command DATE=yy/mm/dd, e.g. DATE=14/04/03.

Step 10
Disconnect the USB cable from the computer and power the clock from the USB power adapter.
The time and date are displayed as shown in the photo below.



Troubleshooting
  • If the LCD does not show anything:
    • adjust the trimpot for contrast;
    • check the wiring, by making sure the connections are right, according to the list in step 5;
    • check that there are no shorts between pins.
  • If the date and/or time show 0:
    • check that the DS1307 is connected correctly to pins D2 and D3 and to the rest of components, according to the schematic in step 6;
    • check that the backup battery provides 3V and is inserted properly.

Monday, February 17, 2014

"Method and apparatus" for mass-synchronizing clocks

Most of my clocks (e.g. this Nixie clock, this other Nixie clock, BookClock, and obviously Wise Clock 4) have on-board Bluetooth, intended mainly for setting up the time, without the need for buttons (the lousy holes I would drill may negatively impact the aesthetics).
To set up the time, simply send the command TIME=hh:mm:ss, where "hh", "mm", "ss" are the hours, the minutes and the seconds, respectively.

Setting up multiple clocks is a tedious process: pair your Android tablet with one at a time, then (from BlueTerm) send the command that includes the correct time. Then repeat for each clock.

What if you could broadcast the TIME=... command? And that command to include the most accurate time, acquired from GPS? This is what this post is about. Now you have the "method".

Next, to the "apparatus". It consists essentially of 3 parts: GPS receiver, microcontroller and Bluetooth master module. Putting them together is trivial, since both GPS receiver and Bluetooth module communicate through serial ports.


I used an old (now discontinued at the major online stores, but still available on ebay) Fastrax UP-501 GPS module I already had laying around. But any GPS receiver should work as well, including the Adafruit Ultimate GPS Breakout.

The "Bluetooth master module" is a re-programmed HC-05 (see the datasheet) as master, with CMODE=1 (for broadcasting).

The sketch, presented below, uses SoftwareSerial library to communicate with the GPS module (Rx on D3, Tx on D4) and TinyGPS library to extract the time from the NMEA sentence. The BTBee module is connected to the hardware serial port (D0, D1).


#include "TinyGPS.h"
#include "SoftwareSerial.h"
SoftwareSerial GPSSerial(4, 3);

// GPS Fatrax UP501, connected as follows:
// - power pin to 3.3V
// - ground pin to ground
// - VBAT pin to 3.3V if no battery is used
// - TX pin to D4
// - RX pin to D3 through a voltage divider (2 resistors, 10k + 4k7)

#define _DEBUG_  true

// commands  for GPS module;
#define PMTK_SET_NMEA_UPDATE_1HZ  "$PMTK220,1000*1F"
#define PMTK_SET_NMEA_UPDATE_5HZ  "$PMTK220,200*2C"
#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F"

// turn on only the second sentence (GPRMC)
#define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"
// turn on ALL THE DATA
#define PMTK_SET_NMEA_OUTPUT_ALLDATA "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28"

// MTK command datasheet at http://www.hhhh.org/wiml/proj/nmeaxor.html

TinyGPS gps;

void setup()  
{
  // default baud rate for BTBee (on hardware serial);
  Serial.begin(9600);

  // default baud rate for UT501 (on software serial);
  GPSSerial.begin(9600);
  
  // turn on all the available data (for 9600 baud you'll want 1Hz rate);
  GPSSerial.println(PMTK_SET_NMEA_OUTPUT_ALLDATA);
  
  // set update rate to 1Hz
  GPSSerial.println(PMTK_SET_NMEA_UPDATE_1HZ);

#ifdef _DEBUG_
  Serial.println("ready for reading GPS...");
#endif
}

void loop()
{
  acquireTimeFromGPS();
  delay(5000);
}

void acquireTimeFromGPS()
{
  unsigned long age;
  int Year;
  byte Month, Day, Hour, Minute, Second, Hundredths;

  if (feedgps())
  {
#ifdef _DEBUG_
    Serial.println("GPS feed acquired...");
#endif
    gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, &Hundredths, &age);
    char buf[20] = {0};
    sprintf(buf, "TIME=%02d:%02d:%02d", Hour, Minute, Second+1);
    Serial.println(buf);
  }
}

bool feedgps()
{
  while (GPSSerial.available())
  {
    char c = GPSSerial.read();
#ifdef _DEBUG_
    Serial.print(c);
#endif

    if (gps.encode(c))
      return true;
  }
  return false;
}

As protoboard I used the XBee Shield from seeedstudio because it had a socket for my BTBee (plus 3V3 regulator) and also ample space for processor and GPS. (It could even fit in a Altoids tin if  the GPS receiver is soldered directly to the board, without headers.)

Future improvements should include a couple of status LEDs, one to show that the data was being acquired from GPS, another to indicate that the "TIME" command was successfully built and broadcast. Similarly but more expensively, a small OLED screen could be used to display the activity.
On the software side, the time, which comes as UTC in the NMEA sentence, should be adjusted to the current time zone, probably based on the longitude.

Saturday, February 8, 2014

Altoids Geiger counter

My "Remixed Geiger counter" board fits almost perfectly, by chance, in the "classic" Altoids box, with room left for the SI-29 GM tube, the 1100mAh LiPo battery and the 0.96" OLED display.


The ATmega328 processor runs at 8MHz with the internal oscillator, a better choice (than the 16MHz of Arduino 2009) for the LiPo voltage of approx 3.7V.  The display I used is compatible with the monochrome 128x64 OLED display from Adafruit.
It is powered at 3V3, requiring a voltage regulator (78L33, TO-92), placed where the the trim-pot (for adjusting LCD contract) was supposed to be (top-right corner of the board).

The sketch uses Adafruit_SSD1306 library, with the wiring to the display as defined below:

#define OLED_DC 8
#define OLED_CS 7
#define OLED_CLK 4
#define OLED_MOSI 3
#define OLED_RESET 5

Note that the same D3-D8 are used for connecting to the LCD 1602 display in the "regular" DIYGeigerKit.
Also note that I did not install the "click" LED nor the buzzer, relying instead on the OLED display to indicate the radiation level.

Since the lid needs to be open anyway in order to see the screen, I thought it does not make sense to drill 2 holes in the box for the USB miniB connector (used for charging the battery) and the on/off switch. (As well, this sounds like a believable excuse for being lazy.)


If I were to improve on it, I would replace the right-angle toggle switch with a straight-up one (easier to operate), then add a button or two for user inputs (configuration parameters, menu navigation etc). The "click" buzzer and the LED could be made "digital", wired to controller's outputs. Adding a Bluetooth module would be also useful (if it works at all from inside the closed box, I need to try it). Ideally, I would also add RTC, GPS and SD card, for logging purposes. And then it would become a smaller and cheaper version of Safecast bGeigieNano :)


Friday, January 31, 2014

Interrupt-based sketch for Adafruit coin acceptor

Some time ago I bought this coin acceptor. Programming it to accept 4 different types of coins is convoluted, but quite ingenious, and works exactly as the spec sheet indicates, without glitches. Surprising for such a complex task, considering that the user interface consists of 3 buttons and two 7-segment LED displays.
After setting it up, the mechanism identifies each coin type reliably, even providing user-friendly feedback (showing on the LED display the number of impulses sent over the COIN/white wire), very helpful for debugging.

Now onto actually using it. Adafruit's graciously provided the "Piggy bank" sample project,  including the Arduino sketch as well. The sketch however, counts the impulses simply in the loop() function. That may work fine in that particular setup, with just the coin acceptor (1 coin type nonetheless) and the LCD display. If you want to add more hardware (printer, buttons, bluetooth, SD card, LEDs etc), you need a smarter, hardware-independent, way to count the impulses from the coin acceptor. Naturally, that involves interrupts.

I present below a snapshot of my interrupt-based sketch, tested in a more complex hardware setup. The COIN wire is connected to D2, attached to interrupt INT0. Function coinISR() gets called for each impulse from the coin acceptor. A coin is recognized when a train of expected number of impulses is received in a sequence.


#define COIN_PIN 2

void setup()
{
  // Debugging output
  Serial.begin(9600);

  // set up the LCD's number of rows and columns:
  lcd.begin(16, 2);

  Serial.println("Ready...");
  
  pinMode(COIN_PIN, INPUT);
  attachInterrupt(0, coinISR, RISING);  // COIN wire connected to D2;
}


// total amount of money collected;
float money = 0.0;

// gets incremented by the ISR;
// gets reset when coin was recognized (after train of pulses ends);
volatile int pulses = 0;
volatile long timeLastPulse = 0;


// executed for every pulse;
void coinISR()
{
  pulses++;
  timeLastPulse = millis();
}


void loop()
{
  lcd.setCursor(0,0);
  lcd.print("Please put coins");
  lcd.setCursor(0,1);
  lcd.print("PAID $");
  lcd.print(money);

  long timeFromLastPulse = millis() - timeLastPulse;
  if (pulses > 0 && timeFromLastPulse > 200)
  {
    // sequence of pulses stopped; determine the coin type;
    if (pulses == 2)
    {
      Serial.println("Received dime (2 pulses)");
      money += .1;
    }
    else if (pulses == 5)
    {
      Serial.println("Received quarter (5 pulses)");
      money += .25;
    }
    else if (pulses == 10)
    {
      Serial.println("Received looney (10 pulses)");
      money += 1.0;
    }
    else if (pulses == 15)
    {
      Serial.println("Received tooney (15 pulses)");
      money += 2.0;
    }
    else
    {
      Serial.print("Unknown coin: ");
      Serial.print(pulses);
      Serial.println(" pulses");
    }

    pulses = 0;
  }
}