Here’s a desk/nightstand clock using Avago HCMS-296x series 4-digit 5*7 LED Displays. And here’s the GitHub repo, in case you’d like to play along at home. https://github.com/Veticus/hcms_296x_clock]
So… what do i do?
Stuff needed for basic breadboard tinkering
- Broadcom/Avago HCMS-296x series display
- Breadboard
- Particle Photon
- A handfull of dupont-type wires
- A microUSB cable for the Photon
The hardware steps
(I’m assuming you know how to use a breadboard)
- Plug the Photon and the display into the breadboard
- Connect pin 4 of the display to the D0 pin on the Photon
- Connect pin 5 to D1
- Connect pin 6 to D2
- Connect pin 7 to D3
- Connect pin 12 to D4
- Connect pins 8 and 9 to GND
- Connect pins 3, 10 and 11 to VCC/Vin
- Take a sip of water/tea/coffee/Club Mate
- Plug the Photon into a USB port
The software steps
- Launch the [Particle Web IDE] or use the [Particle Desktop IDE (Dev)]
- Give your App a title
- Download or clone this repository
- Copy the files into your Particle project
- Chose the right device to upload to
- Upload and flash
- Enjoy! And happy tinkering!
Note: Lines 33 and 34 disable the built-in LED. This helps with nightstand use, but may throw you off. Just comment them out, or use RGB.brightness()
to dim the LED
Now what?
Check out the LedDisplay.h
file, to see what else you can do with these magnificent little displays. I suggest connecting a bunch of them. Just connect everything in parallel, except for the dataPin (Pin 4).
From right to left connect the displays from Pin 4 to Pin 1.
(Pin 1 being “dataOut” while Pin 4 is “dataIn”). Check the datasheet linked below.
You might also have some fun with editing the font file or making animations.
Links to more information
Datasheet for the HCMS-29xxseries
Some great examples can be found in PaulStoffregen’s Arduino Library for the displays: GitHub - PaulStoffregen/LedDisplay
Which files?
// ---- HCMS_TIME_DISPLAY.ino -----
// By Tim Engel
// https://github.com/Veticus
//
//
// Setup of this demo:
// Pin purpose DevPin Pin on Display
//
// dataPin - D0 - HCMS-2963 PIN4
// registerSelect -D1 - HCMS-2963 PIN5
// clockPin -D2 - HCMS-2963 PIN6
// enable -D3 - HCMS-2963 PIN7
// reset -D4 - HCMS-2963 PIN12
// Connect pins 8 and 9 to GND
// Connect pins 3, 10 and 11 to VCC
// LedDisplay method takes 5 pins and an integer.
// The last integer is the number of chars.
// The other pins are as indicated here - in the order indicated.
//
// The font is defined in the font5x7.h file
#include "LedDisplay.h"
LedDisplay myDisplay = LedDisplay(D0, D1, D2, D3, D4, 8);
void setup() {
myDisplay.begin();
myDisplay.setBrightness(15); // Brightness of the display(s) from 0 to 15
RGB.control(true); // Allows us to controll the Particle RGB status LED.
RGB.brightness(0); // Turns the Particle RBG status LED off
}
void loop() {
time_t time = Time.now(); // Gets the current time as a time_t type object.
Time.zone(+1); // Sets the timezone (relative to GMT)
Time.format(time, TIME_FORMAT_DEFAULT); // Allows the time display format to be defined.
myDisplay.home(); // Sets the displays cursor to the first position.
myDisplay.print(Time.format(Time.now(), "%H:%M:%S")); // Format adheres to strftime
}
/*
LedDisplay -- controller library for Avago HCMS-297x displays -- version 0.2
Copyright (c) 2009 Tom Igoe. Some right reserved.
Revisions on version 0.2 and 0.3 by Mark Liebman, 27 Jan 2010
* extended a bit to support up to four (4) 8 character displays.
vim: set ts=4:
Controls an Avago HCMS29xx display. This display has 8 characters, each 5x7 LEDs
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "LedDisplay.h"
// Pascal Stang's 5x7 font library:
#include "font5x7.h"
// The font library is stored in program memory:
#include <avr/pgmspace.h>
#include <string.h>
/*
* Constructor. Initializes the pins and the instance variables.
*/
LedDisplay::LedDisplay(uint8_t _dataPin,
uint8_t _registerSelect,
uint8_t _clockPin,
uint8_t _chipEnable,
uint8_t _resetPin,
uint8_t _displayLength)
{
// Define pins for the LED display:
this->dataPin = _dataPin; // connects to the display's data in
this->registerSelect = _registerSelect; // the display's register select pin
this->clockPin = _clockPin; // the display's clock pin
this->chipEnable = _chipEnable; // the display's chip enable pin
this->resetPin = _resetPin; // the display's reset pin
this->displayLength = _displayLength; // number of bytes needed to pad the string
this->cursorPos = 0; // position of the cursor in the display
// do not allow a long multiple display to use more than LEDDISPLAY_MAXCHARS
if (_displayLength > LEDDISPLAY_MAXCHARS) {
_displayLength = LEDDISPLAY_MAXCHARS;
}
// fill stringBuffer with spaces, and a trailing 0:
for (unsigned int i = 0; i < sizeof(stringBuffer); i++) {
stringBuffer[i] = ' ';
}
this->setString(stringBuffer); // give displayString a default buffer
}
/*
* Initialize the display.
*/
void LedDisplay::begin() {
// set pin modes for connections:
pinMode(dataPin, OUTPUT);
pinMode(registerSelect, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(chipEnable, OUTPUT);
pinMode(resetPin, OUTPUT);
// reset the display:
digitalWrite(resetPin, LOW);
delay(10);
digitalWrite(resetPin, HIGH);
// load dot register with lows
loadDotRegister();
loadControlRegister(B10000001); // set serial mode. see table 1, footnote 1
// set control register 0 for max brightness, and no sleep:
// added: ML send multiple inits to 2nd, 3rd, 4th display, etc.
// set control register 0 for max brightness, and no sleep:
loadControlRegister(B01111111);
loadControlRegister(B01111111);
loadControlRegister(B01111111);
loadControlRegister(B01111111);
loadControlRegister(B01111111);
loadControlRegister(B01111111);
loadControlRegister(B01111111);
// set control register 1 so all 8 characters display:
// loadControlRegister(B10000001);
}
/*
* Clear the display
*/
void LedDisplay::clear() {
this->setString(stringBuffer);
for (int displayPos = 0; displayPos < displayLength; displayPos++) {
// put the character in the dot register:
writeCharacter(' ', displayPos);
}
// send the dot register array out to the display:
loadDotRegister();
}
/*
* set the cursor to the home position (0)
*/
void LedDisplay::home() {
// set the cursor to the upper left corner:
this->cursorPos = 0;
}
/*
* set the cursor anywhere
*/
void LedDisplay::setCursor(int whichPosition){
this->cursorPos = whichPosition;
}
/*
* return the cursor position
*/
int LedDisplay::getCursor() {
return this->cursorPos;
}
/*
* write a byte out to the display at the cursor position,
* and advance the cursor position.
*/
#if ARDUINO >= 100
size_t LedDisplay::write(uint8_t b) {
#else
void LedDisplay::write(uint8_t b) {
#endif
// make sure cursorPos is on the display:
if (cursorPos >= 0 && cursorPos < displayLength) {
// put the character into the dot register:
writeCharacter(b, cursorPos);
// put the character into the displayBuffer
// but do not write the string constants pass
// to us from the user by setString()
if (this->displayString == stringBuffer && cursorPos < LEDDISPLAY_MAXCHARS) {
stringBuffer[cursorPos] = b;
}
cursorPos++;
// send the dot register array out to the display:
loadDotRegister();
}
#if ARDUINO >= 100
return 1;
#endif
}
/*
* Scroll the displayString across the display. left = -1, right = +1
*/
void LedDisplay::scroll(int direction) {
cursorPos += direction;
// length of the string to display:
int stringEnd = strlen(displayString);
// Loop over the string and take displayLength characters to write to the display:
for (int displayPos = 0; displayPos < displayLength; displayPos++) {
// which character in the strings you want:
int whichCharacter = displayPos - cursorPos;
// which character you want to show from the string:
char charToShow;
// display the characters until you have no more:
if ((whichCharacter >= 0) && (whichCharacter < stringEnd)) {
charToShow = displayString[whichCharacter];
} else {
// if none of the above, show a space:
charToShow = ' ';
}
// put the character in the dot register:
writeCharacter(charToShow, displayPos);
}
// send the dot register array out to the display:
loadDotRegister();
}
/*
* set displayString
*/
void LedDisplay::setString(const char * _displayString) {
this->displayString = _displayString;
}
/*
* return displayString
*/
const char * LedDisplay::getString() {
return displayString;
}
/*
* return displayString length
*/
int LedDisplay::stringLength() {
return strlen(displayString);
}
/*
* set brightness (0 - 15)
*/
void LedDisplay::setBrightness(uint8_t bright)
{
// set the brightness:
loadControlRegister(B01110000 + bright);
}
/* this method loads bits into the dot register array. It doesn't
* actually communicate with the display at all,
* it just prepares the data:
*/
void LedDisplay::writeCharacter(char whatCharacter, byte whatPosition) {
// calculate the starting position in the array.
// every character has 5 columns made of 8 bits:
byte thisPosition = whatPosition * 5;
// copy the appropriate bits into the dot register array:
for (int i = 0; i < 5; i++) {
dotRegister[thisPosition+i] = (pgm_read_byte(&Font5x7[((whatCharacter - 0x20) * 5) + i]));
}
}
// This method sends 8 bits to one of the control registers:
void LedDisplay::loadControlRegister(int dataByte) {
// select the control registers:
digitalWrite(registerSelect, HIGH);
// enable writing to the display:
digitalWrite(chipEnable, LOW);
// shift the data out:
shiftOut(dataPin, clockPin, MSBFIRST, dataByte);
// disable writing:
digitalWrite(chipEnable, HIGH);
}
// this method originally sent 320 bits to the dot register: 12_30_09 ML
void LedDisplay::loadDotRegister() {
// define max data to send, patch for 4 length displays by KaR]V[aN
int maxData = displayLength * 5;
// select the dot register:
digitalWrite(registerSelect, LOW);
// enable writing to the display:
digitalWrite(chipEnable, LOW);
// shift the data out:
for (int i = 0; i < maxData; i++) {
shiftOut(dataPin, clockPin, MSBFIRST, dotRegister[i]);
}
// disable writing:
digitalWrite(chipEnable, HIGH);
}
/*
version() returns the version of the library:
*/
int LedDisplay::version(void)
{
return 4;
}
/*
LedDisplay -- controller library for Avago HCMS-297x displays -- version 0.3
27 Jan 2010 ML: extended a bit to support up to four (4) 8 character displays.
Copyright (c) 2009 Tom Igoe. Some right reserved.
Revisions on version 0.2 and 0.3 by Mark Liebman, 27 Jan 2010
* extended a bit to support up to four (4) 8 character displays.
vim: set ts=4:
Controls an Avago HCMS29xx display. This display has 8 characters, each 5x7 LEDs
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Version changes:
* Version 3 adds support for multiple displays. Just change the displayLength to
the total number of characters (for example, 3 8-character displays, displayLength = 24).
*/
// ensure this library description is only included once
#ifndef LedDisplay_h
#define LedDisplay_h
// supports up to four 8 character displays, connected as documented here,
// under "Multiple Displays" http://playground.arduino.cc/Main/LedDisplay
#define LEDDISPLAY_MAXCHARS 32
// include types & constants of Wiring core API
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
// Arduino Print library provides print() and println() functionality
#include "Print.h"
// library interface description
class LedDisplay : public Print {
public:
// constructor:
LedDisplay(uint8_t _dataPin,
uint8_t _registerSelect,
uint8_t _clockPin,
uint8_t _chipEnable,
uint8_t _resetPin,
uint8_t _displayLength);
// initializer method:
void begin();
void clear(); // clear the display
void home(); // set cursor to far left hand position
void setCursor(int whichPosition); // set cursor to any position
int getCursor(); // get the cursor position
#if ARDUINO >= 100
virtual size_t write(uint8_t b); // write a character to the display and advance cursor
#else
virtual void write(uint8_t b); // write a character to the display and advance cursor
#endif
using Print::write;
void setString(const char* _stringToDisplay); // set the displayString variable
const char * getString(); // get the displayString
int stringLength(); // get the length of displayString
void scroll(int direction); // scroll whatever string is stored in library's displayString variable
void setBrightness(uint8_t bright); // set display brightness, 0 - 15
// Control register setters. for addressing the display directly:
void loadControlRegister(int dataByte);
void loadDotRegister();
int version(void); // return library version
private:
// Character display setters:
void writeCharacter(char whatCharacter, byte whatPosition); // write a character to a buffer which will
// be sent to the display by loadDotRegister()
int cursorPos; // position of the cursor
uint8_t dotRegister[LEDDISPLAY_MAXCHARS * 5]; // 5 bytes per character * maxchars
// Define pins for the LED display:
uint8_t dataPin; // connects to the display's data in
uint8_t registerSelect; // the display's register select pin
uint8_t clockPin; // the display's clock pin
uint8_t chipEnable; // the display's chip enable pin
uint8_t resetPin; // the display's reset pin
uint8_t displayLength; // number of bytes needed to pad the string
char stringBuffer[LEDDISPLAY_MAXCHARS+1]; // buffer to hold initial display string
const char * displayString; // string for scrolling
};
#endif
/*! \file font5x7.h \brief Graphic LCD Font (Ascii Characters). */
//*****************************************************************************
//
// File Name : 'font5x7.h'
// Title : Graphic LCD Font (Ascii Charaters)
// Author : Pascal Stang
// Date : 10/19/2001
// Revised : 10/19/2001
// Version : 0.1
// Target MCU : Atmel AVR
// Editor Tabs : 4
//
//*****************************************************************************
#ifndef FONT5X7_H
#define FONT5X7_H
// standard ascii 5x7 font
// defines ascii characters 0x20-0x7F (32-127)
static const unsigned char PROGMEM Font5x7[] = {
0x00, 0x00, 0x00, 0x00, 0x00,// (space)
0x00, 0x00, 0x5F, 0x00, 0x00,// !
0x00, 0x07, 0x00, 0x07, 0x00,// "
0x14, 0x7F, 0x14, 0x7F, 0x14,// #
0x24, 0x2A, 0x7F, 0x2A, 0x12,// $
0x23, 0x13, 0x08, 0x64, 0x62,// %
0x36, 0x49, 0x55, 0x22, 0x50,// &
0x00, 0x05, 0x03, 0x00, 0x00,// '
0x00, 0x1C, 0x22, 0x41, 0x00,// (
0x00, 0x41, 0x22, 0x1C, 0x00,// )
0x08, 0x2A, 0x1C, 0x2A, 0x08,// *
0x08, 0x08, 0x3E, 0x08, 0x08,// +
0x00, 0x50, 0x30, 0x00, 0x00,// ,
0x08, 0x08, 0x08, 0x08, 0x08,// -
0x00, 0x60, 0x60, 0x00, 0x00,// .
0x20, 0x10, 0x08, 0x04, 0x02,// /
0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
0x00, 0x42, 0x7F, 0x40, 0x00,// 1
0x42, 0x61, 0x51, 0x49, 0x46,// 2
0x21, 0x41, 0x45, 0x4B, 0x31,// 3
0x18, 0x14, 0x12, 0x7F, 0x10,// 4
0x27, 0x45, 0x45, 0x45, 0x39,// 5
0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
0x01, 0x71, 0x09, 0x05, 0x03,// 7
0x36, 0x49, 0x49, 0x49, 0x36,// 8
0x06, 0x49, 0x49, 0x29, 0x1E,// 9
0x00, 0x36, 0x36, 0x00, 0x00,// :
0x00, 0x56, 0x36, 0x00, 0x00,// ;
0x00, 0x08, 0x14, 0x22, 0x41,// <
0x14, 0x14, 0x14, 0x14, 0x14,// =
0x41, 0x22, 0x14, 0x08, 0x00,// >
0x02, 0x01, 0x51, 0x09, 0x06,// ?
0x32, 0x49, 0x79, 0x41, 0x3E,// @
0x7E, 0x11, 0x11, 0x11, 0x7E,// A
0x7F, 0x49, 0x49, 0x49, 0x36,// B
0x3E, 0x41, 0x41, 0x41, 0x22,// C
0x7F, 0x41, 0x41, 0x22, 0x1C,// D
0x7F, 0x49, 0x49, 0x49, 0x41,// E
0x7F, 0x09, 0x09, 0x01, 0x01,// F
0x3E, 0x41, 0x41, 0x51, 0x32,// G
0x7F, 0x08, 0x08, 0x08, 0x7F,// H
0x00, 0x41, 0x7F, 0x41, 0x00,// I
0x20, 0x40, 0x41, 0x3F, 0x01,// J
0x7F, 0x08, 0x14, 0x22, 0x41,// K
0x7F, 0x40, 0x40, 0x40, 0x40,// L
0x7F, 0x02, 0x04, 0x02, 0x7F,// M
0x7F, 0x04, 0x08, 0x10, 0x7F,// N
0x3E, 0x41, 0x41, 0x41, 0x3E,// O
0x7F, 0x09, 0x09, 0x09, 0x06,// P
0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
0x7F, 0x09, 0x19, 0x29, 0x46,// R
0x46, 0x49, 0x49, 0x49, 0x31,// S
0x01, 0x01, 0x7F, 0x01, 0x01,// T
0x3F, 0x40, 0x40, 0x40, 0x3F,// U
0x1F, 0x20, 0x40, 0x20, 0x1F,// V
0x7F, 0x20, 0x18, 0x20, 0x7F,// W
0x63, 0x14, 0x08, 0x14, 0x63,// X
0x03, 0x04, 0x78, 0x04, 0x03,// Y
0x61, 0x51, 0x49, 0x45, 0x43,// Z
0x00, 0x00, 0x7F, 0x41, 0x41,// [
0x02, 0x04, 0x08, 0x10, 0x20,// "\"
0x41, 0x41, 0x7F, 0x00, 0x00,// ]
0x04, 0x02, 0x01, 0x02, 0x04,// ^
0x40, 0x40, 0x40, 0x40, 0x40,// _
0x00, 0x01, 0x02, 0x04, 0x00,// `
0x20, 0x54, 0x54, 0x54, 0x78,// a
0x7F, 0x48, 0x44, 0x44, 0x38,// b
0x38, 0x44, 0x44, 0x44, 0x20,// c
0x38, 0x44, 0x44, 0x48, 0x7F,// d
0x38, 0x54, 0x54, 0x54, 0x18,// e
0x08, 0x7E, 0x09, 0x01, 0x02,// f
0x08, 0x14, 0x54, 0x54, 0x3C,// g
0x7F, 0x08, 0x04, 0x04, 0x78,// h
0x00, 0x44, 0x7D, 0x40, 0x00,// i
0x20, 0x40, 0x44, 0x3D, 0x00,// j
0x00, 0x7F, 0x10, 0x28, 0x44,// k
0x00, 0x41, 0x7F, 0x40, 0x00,// l
0x7C, 0x04, 0x18, 0x04, 0x78,// m
0x7C, 0x08, 0x04, 0x04, 0x78,// n
0x38, 0x44, 0x44, 0x44, 0x38,// o
0x7C, 0x14, 0x14, 0x14, 0x08,// p
0x08, 0x14, 0x14, 0x18, 0x7C,// q
0x7C, 0x08, 0x04, 0x04, 0x08,// r
0x48, 0x54, 0x54, 0x54, 0x20,// s
0x04, 0x3F, 0x44, 0x40, 0x20,// t
0x3C, 0x40, 0x40, 0x20, 0x7C,// u
0x1C, 0x20, 0x40, 0x20, 0x1C,// v
0x3C, 0x40, 0x30, 0x40, 0x3C,// w
0x44, 0x28, 0x10, 0x28, 0x44,// x
0x0C, 0x50, 0x50, 0x50, 0x3C,// y
0x44, 0x64, 0x54, 0x4C, 0x44,// z
0x00, 0x08, 0x36, 0x41, 0x00,// {
0x00, 0x00, 0x7F, 0x00, 0x00,// |
0x00, 0x41, 0x36, 0x08, 0x00,// }
0x08, 0x08, 0x2A, 0x1C, 0x08,// ->
0x08, 0x1C, 0x2A, 0x08, 0x08 // <-
};
#endif
Acknowledgments
- Tom Idoe, who wrote the control library in 2009
- Pascal Stang, who wrote the 5x7 font in 2001
- Mark ‘Robotto’ Moore, who tested this for me
- Paul Stoffregen, for the Ardunio library and examples