Friday, July 31, 2020

HDSP clock fully through-hole revision 3

The only SMD component in the HDSP clock was the USB miniB connector. To make the kit completely beginner-friendly, this connector was replaced by either of its two (right angle or straight) through hole equivalents. Other changes (from revision 2) are:
  • fixed the two smaller pads for DS1307
  • some re-routing (which reduced the number of vias to 5)

The TH right angle USB connector is to be mounted on the top, as shown below.


The straight USB connector can only be mounted on the bottom side of the PCB, as shown below.


This latter configuration allows the clock to be used without any additional enclosure, with just two standoffs holding it in a vertical position, like this:


Assembly instructions for the HDSP clock kit can be found here, with the only difference being the last step, where the miniB USB connector is soldered.



Monday, July 20, 2020

Arduino OLED display shield

The 2.4" I2C OLED display I had sitting idle is too big for the "Promini OLED Clock shield", yet a perfect candidate for a regular Arduino shield. This is how it looks soldered on a prototype shield with two buttons on top, attached to wsduino running the OLED Clock sketch (each of the 5 faces shown):






The current sketch uses U8glib library and takes about 27k of ATmega328's 31k program memory. It could be enhanced by adding alarm (buzzer, relay etc.), since most of the digital pins are available (only D3 and D9 are used for the 2 buttons). Adding NTP time sync (with an ESP8266 module) could also be done, but one/some of the faces will need to be dropped because of memory constraints. All these are exercises/homework for the inquiring minds :)

Note: My nice beveled 2.42" OLED does not seem to be so ubiquitous.


 A quick search on ebay for similar 2.42" OLED displays returns a different style, already mounted on a larger PCB:


This may fit on the Arduino protoshield, but it may look bulky.

Friday, June 19, 2020

HDSP clock with SCD5583 display

Here is another interesting "intelligent" LED matrix display, whose name actually makes some sense (unlike HDSP-2354, among many others): SCD5583A. I reckon it means: "Serial Character Display with 5x5x8 dot matrix". 3 is the code for green (0 for red, 1 for yellow).
This display is now obsolete (replaced by touch screens in avionics, the industry for which it was designed), but it is still available on ebay for a reasonable price (except for incredibly high shipping cost).

Compared to other intelligent displays like the above-mentioned HDSP-2534 employed by the HDSP clock (hence the bland unimaginative name), SCD558X does not have an internally-defined font and requires the user to provide one. This makes it a little more complicated to use, but also allows for more flexibility, making it truly international (non-English character set). Even more, each pixel can be addressed individually, like a graphical 5x40 LED matrix.
The fact that is serial means fewer pins are required for interfacing (3 control pins in this case) and less driving circuitry (no need for the external shift register).

The photo below shows the display connected to an Arduino and running the HDSP sketch adapted for SCD5833.


Practically, the HDSP sketch uses only one function to write to display, aptly called "writeDisplay()", that needs to be re-written. The support for SCD5583 is provided by the class SCD5583, shown below (header + cpp files, compiled with Arduino IDE 1.8.13).

#ifndef _SCD5583_H_
#define _SCD5583_H_

#include  "Arduino.h"

class SCD5583
{
  public:
    SCD5583(byte loadPin, byte dataPin, byte clkPin);
    void clearMatrix();
    void setBrightness(byte b);
    void writeLine(char text[9]);

  private:
    void _sendData(byte data);
    void _getCharDefinition(byte* rows, char c);
    void _selectIndex(byte position);

    byte _loadPin;
    byte _dataPin;
    byte _clkPin;
};

#endif // _SCD5583_H_
-----------------------------------------------
#include "SCD5583.h"
#include "font5x5.h"

#define MAX_CHARS 8   // set to 10 for SCD5510X (10 digits) or to 4 for SCD554X (4 digits);
#define NUM_ROWS  5

SCD5583::SCD5583(byte loadPin, byte dataPin, byte clkPin)
{
  pinMode(loadPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clkPin, OUTPUT);
  _loadPin = loadPin;
  _dataPin = dataPin;
  _clkPin  = clkPin;
  clearMatrix();
//  setBrightness(0);
}

void SCD5583::clearMatrix()
{
  _sendData(B11000000);
}

void SCD5583::setBrightness(byte b)
{
  if (b > 0 && b < 7)
  {
     _sendData(B11110000 + b);
  }
}

void SCD5583::_selectIndex(byte position)
{
   _sendData(B10100000 + position);
}

void SCD5583::_sendData(byte data)
{
  byte mask = 1;
  digitalWrite(_loadPin, LOW);
  digitalWrite(_clkPin,  LOW);
  for (byte i = 0; i < 8; i++)
  {
    digitalWrite(_dataPin, mask & data? HIGH : LOW);
    digitalWrite(_clkPin, HIGH);
//  delay(10);
    digitalWrite(_clkPin, LOW);
    mask = mask*2;
  }
  digitalWrite(_loadPin, HIGH);
}

void SCD5583::_getCharDefinition(byte* rows, char c)
{
  for (byte i = 0; i < NUM_ROWS; i++)
  {
    rows[i] = font5x5[((c- 0x20) * NUM_ROWS) + i];
  }
}

void SCD5583::writeLine(char* text)
{
  byte rows[NUM_ROWS];
  for (int i = 0; i < MAX_CHARS; i++)
  {
    _selectIndex(i);
    _getCharDefinition(rows, text[i]);
    for (int r = 0; r < NUM_ROWS; r++)
    {
      _sendData(rows[r]);
    }
  }
}

As mentioned previously, the font must be provided too (content of file "font5x5.h" shown below):

#ifndef _FONT5X5_H_
#define _FONT5X5_H_

// ascii characters 0x41-0x7a (32-127);
static unsigned char font5x5[] =
{
  0x00, 0x20, 0x40, 0x60, 0x80, // space
  0x04, 0x24, 0x44, 0x60, 0x84, // !
  0x0A, 0x2A, 0x40, 0x60, 0x80, // "
  0x0A, 0x3F, 0x4A, 0x7F, 0x8A, // #
  0x0F, 0x34, 0x4E, 0x65, 0x9E, // $
  0x19, 0x3A, 0x44, 0x6B, 0x93, // %
  0x08, 0x34, 0x4D, 0x72, 0x8D, // &
  0x04, 0x24, 0x40, 0x60, 0x80, // '
  0x02, 0x24, 0x44, 0x64, 0x82, // (
  0x08, 0x24, 0x44, 0x64, 0x88, // )
  0x15, 0x2E, 0x5F, 0x6E, 0x95, // *
  0x04, 0x24, 0x5F, 0x64, 0x84, // +
  0x00, 0x20, 0x40, 0x64, 0x84, // ,
  0x00, 0x20, 0x4E, 0x60, 0x80, // -
  0x00, 0x20, 0x40, 0x60, 0x84, // .
  0x01, 0x22, 0x44, 0x68, 0x90, // /
  0x0E, 0x33, 0x55, 0x79, 0x8E, // 0
  0x04, 0x2C, 0x44, 0x64, 0x8E, // 1
  0x1E, 0x21, 0x46, 0x68, 0x9F, // 2
  0x1E, 0x21, 0x4E, 0x61, 0x9E, // 3
  0x06, 0x2A, 0x5F, 0x62, 0x82, // 4
  0x1F, 0x30, 0x5E, 0x61, 0x9E, // 5
  0x06, 0x28, 0x5E, 0x71, 0x8E, // 6
  0x1F, 0x22, 0x44, 0x68, 0x88, // 7
  0x0E, 0x31, 0x4E, 0x71, 0x8E, // 8
  0x0E, 0x31, 0x4F, 0x62, 0x8C, // 9
  0x00, 0x24, 0x40, 0x64, 0x80, // :
  0x00, 0x24, 0x40, 0x6C, 0x80, // ;
  0x02, 0x24, 0x48, 0x64, 0x82, // <
  0x00, 0x3F, 0x40, 0x7F, 0x80, // =
  0x08, 0x24, 0x42, 0x64, 0x88, // >
  0x0E, 0x31, 0x42, 0x64, 0x84, // ?
  0x0E, 0x35, 0x57, 0x70, 0x8E, // @
  0x04, 0x2A, 0x5F, 0x71, 0x91, // A
  0x1E, 0x29, 0x4E, 0x69, 0x9E, // B
  0x0F, 0x30, 0x50, 0x70, 0x8F, // C
  0x1E, 0x29, 0x49, 0x69, 0x9E, // D
  0x1F, 0x30, 0x5E, 0x70, 0x9F, // E
  0x1F, 0x30, 0x5E, 0x70, 0x90, // F
  0x0F, 0x30, 0x53, 0x71, 0x8F, // G
  0x11, 0x31, 0x5F, 0x71, 0x91, // H
  0x0E, 0x24, 0x44, 0x64, 0x8E, // I
  0x01, 0x21, 0x41, 0x71, 0x8E, // J
  0x13, 0x34, 0x58, 0x74, 0x93, // K
  0x10, 0x30, 0x50, 0x70, 0x9F, // L
  0x11, 0x3B, 0x55, 0x71, 0x91, // M
  0x11, 0x39, 0x55, 0x73, 0x91, // N
  0x0E, 0x31, 0x51, 0x71, 0x8E, // O
  0x1E, 0x31, 0x5E, 0x70, 0x90, // P
  0x0C, 0x32, 0x56, 0x72, 0x8D, // Q
  0x1E, 0x31, 0x5E, 0x74, 0x92, // R
  0x0F, 0x30, 0x4E, 0x61, 0x9E, // S
  0x1F, 0x24, 0x44, 0x64, 0x84, // T
  0x11, 0x31, 0x51, 0x71, 0x8E, // U
  0x11, 0x31, 0x51, 0x6A, 0x84, // V
  0x11, 0x31, 0x55, 0x7B, 0x91, // W
  0x11, 0x2A, 0x44, 0x6A, 0x91, // X
  0x11, 0x2A, 0x44, 0x64, 0x84, // Y
  0x1F, 0x22, 0x44, 0x68, 0x9F, // Z
  0x07, 0x24, 0x44, 0x64, 0x87, // [
  0x10, 0x28, 0x44, 0x62, 0x81, // \
  0x1C, 0x24, 0x44, 0x64, 0x9C, // ]
  0x04, 0x2A, 0x51, 0x60, 0x80, // ^
  0x00, 0x20, 0x40, 0x60, 0x9F, // _
  0x0A, 0x2A, 0x40, 0x60, 0x80, // '
  0x00, 0x2E, 0x52, 0x72, 0x8D, // a
  0x10, 0x30, 0x5E, 0x71, 0x9E, // b
  0x00, 0x2F, 0x50, 0x70, 0x8F, // c
  0x01, 0x21, 0x4F, 0x71, 0x8F, // d
  0x00, 0x2E, 0x5F, 0x70, 0x8E, // e
  0x04, 0x2A, 0x48, 0x7C, 0x88, // f
  0x00, 0x2F, 0x50, 0x73, 0x8F, // g
  0x10, 0x30, 0x56, 0x79, 0x91, // h
  0x04, 0x20, 0x4C, 0x64, 0x8E, // i
  0x00, 0x26, 0x42, 0x72, 0x8C, // j
  0x10, 0x30, 0x56, 0x78, 0x96, // k
  0x0C, 0x24, 0x44, 0x64, 0x8E, // l
  0x00, 0x2A, 0x55, 0x71, 0x91, // m
  0x00, 0x36, 0x59, 0x71, 0x91, // n
  0x00, 0x2E, 0x51, 0x71, 0x8E, // o
  0x00, 0x3E, 0x51, 0x7E, 0x90, // p
  0x00, 0x2F, 0x51, 0x6F, 0x81, // q
  0x00, 0x33, 0x54, 0x78, 0x90, // r
  0x00, 0x23, 0x44, 0x62, 0x8C, // s
  0x08, 0x3C, 0x48, 0x6A, 0x84, // t
  0x00, 0x32, 0x52, 0x72, 0x8D, // u
  0x00, 0x31, 0x51, 0x6A, 0x84, // v
  0x00, 0x31, 0x55, 0x7B, 0x91, // w
  0x00, 0x32, 0x4C, 0x6C, 0x92, // x
  0x00, 0x31, 0x4A, 0x64, 0x98, // y
  0x00, 0x3E, 0x44, 0x68, 0x9E, // z
  0x06, 0x24, 0x48, 0x64, 0x86, // {
  0x04, 0x24, 0x44, 0x64, 0x84, // |
  0x0C, 0x24, 0x42, 0x64, 0x8C, // }
  0x00, 0x27, 0x5C, 0x60, 0x80, // ~
};

#endif  // _FONT5X5_H_

And finally, the changes in HDSP.ino are:

1. add the following line at the top:
#include "SCD5583.h"

// SCD5583 pins: LOAD to D4, DATA to D5, SDCLK to D3;
SCD5583 scd(4,5,3);
2. replace the function writeDisplay() written for HDSP-2534, with the following:
void writeDisplay()
{
  scd.writeLine(displayBuffer);
}

Hardware-wise, some re-wiring is required on the HDSP board, starting with the removal of the 595 shift register. Then, D3, D4 and D5 (ATmega328 pins 5, 6, 11) must be connected respectively to SDCLK, LOAD and DATA pins (1, 2, 27) of the SCD5583 display.

And there you have it, a working HDSP clock with SCD5583 display.

It is worth mentioning that the measured current drawn was between 10mA (lowest brightness) and 20mA (highest), and that it works similarly well when powered by 3V3.


Wednesday, June 3, 2020

Sure 32x16 bicolor display (HT1632) driven by ESP32

I was recently awoken from the Netflix-infused lethargy by an email from LukeW, who needed help with running the Wise Clock 4 on ESP32. What a great idea that is!

I had an epiphany when I realized what a powerful platform ESP32 is: unbelievable price (about $10 on amazon), serious hardware (memory, speed, WiFi, BT, BLE, multiple USARTs etc.), amazing software support (Arduino IDE, libraries, examples etc.).

The latest Arduino IDE I had was version 1.6.7 (2017). Trying to install the support package for ESP32, I got a security error along the lines:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target.

It works fine when downloading it through the browser (https://dl.espressif.com/dl/esptool-2.6.1-windows.zip), meaning that the server certificate that esspressif is using is not recognized by the JVM run by Arduino (folder "java" in the arduino directory). Identifying and adding the certificate to the JVM's keystore is time consuming, so I decided that it is just easier to upgrade to the latest Arduino IDE (currently 1.8.12), installed this time (a first for me) from Microsoft store. Even though the process is seamless, I have no idea where this software was placed (definitely not where I wanted it). I also learned the new way (where have I been for so long?) to include and manage libraries (through the "Add .ZIP library" menu item).

Using the ESP32 devkit is as easy as the first Arduino (Duemilanove). The only thing one needs is a solderless breadboard, which I was lucky to have one around, since I don't remember ever using one before. An interesting fact is that the ESP32 board, inserted in the breadboard I have, leaves room for connecting wires only on one side.


Next step was to connect the Sure 32x16 displays (two, daisy chained). I used these pins:
// pins used to connect to ESP32;
#define HT1632_DATA   12 // Data pin (pin 7 of display connector)
#define HT1632_CS     14 // Chip Select (pin 1 of display connector)
#define HT1632_WRCLK  13 // Write clock pin (pin 5 of display connector)
#define HT1632_CLK    27 // clock pin (pin 2 of display connector)

With only the pin changes, the HT1632 files used in Wise Clock 4 software work perfectly fine with ESP32. They can be found here, called from a test sketch that uses 2 displays (#define NUM_DISPLAYS 2 in file MyHT1632.h).

SD card should be next, using the ESP32 support libraries.
With ESP32, the sky is the limit: hopefully no more program memory limitations, no need for third party libraries (e.g. Sanguino), better support for sound, easier access to WiFi, support for extra peripherals etc. Wow!

It did not take long to test the SD card, using this wiring:

which looks like this on the breadboard:


Running SD_Test.ino sketch example coming with ESP32 libraries on a Wise Clock 4 SD card, this is what serial monitor shows:


Incredible, with no sweat at all, just out of the box. Wow again!

Another small step: modified the SD_Test.ino to read the content of the file, line by line:
void setup()
{
    Serial.begin(115200);
    if(!SD.begin()){
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD card attached");
        return;
    }

  openFile(SD, "/quot1.txt");
}

void openFile(fs::FS &fs, const char * path)
{
    Serial.printf("Reading file: %s\n", path);

    file = fs.open(path);
    if(!file){
        Serial.println("Failed to open file for reading");
        return;
    }
}

char lineBuf[500] = {0};

void readLine()
{
  int i = 0;
  lineBuf[0] = 0;

  while (file.available())
  {
    char c = (char) file.read();
    lineBuf[i++] = c;
    if (c == '\n')  break;
  }
  lineBuf[i] = 0;
}

void loop()
{
  readLine();
  Serial.write(lineBuf);
  delay(1000);
}

And the output, in serial monitor:



Saturday, May 9, 2020

wsduino revision 2

Honestly, I think the hacking required to add buttons for hours and minutes to the Rothko clock is messy. But if  you want an easy, quick and intuitive way to set the time, there is no alternative for buttons; remote control (IR, XBee, BT) would require extra devices, GPS or NTP would need settings. Since the Nixie shield board is already overcrowded, the logical place for the buttons would be on wsduino. And this is the main reason I had to redesign it.
I opted for two right-angle tactile switches placed on the right side of the board, above and below the XBee module. These switches, connected to A0 and A1, are intended mostly for clock functionality, optional for other applications. They could also be replaced by wired external (panel-mounted, not on-board) buttons, when an enclosure is used.

This is how the new wsduino board looks.



Other improvements are:
  • moved the 3V3 regulator for better heat dissipation (it was on the opposite side of 7805);
  • moved the electrolytic capacitors around, to allow for easier removal of the processor from the socket;
  • moved the 1Hz output to a pin between the banks of analog and power pins;
  • removed the SDA and SCL from the top bank of digital pins (they were redundant anyway, plus makes more room for the screw and nut);

The switch that fits wsduino is number 24 in the chart below (used by sellers on ebay):

To recap, wsduino is the arduino-compatible board with DS3231 RTC and XBee support. To build a clock, just add a display shield like the 6-character LED display, the bicolor LED matrix shield ("Mondrian clock") or the 6-tube Nixie shield ("Rothko clock").