CE644 - The ES9018 I2C controller

After many email exchanges with Gwikse from Norway about the Buffalo DAC the idea got born to build a controller to get rid of the Arduino. The Arduino is perfect for prototyping, but for some projects it’s not very suitable. Especially when a lot of wires are involved it quickly becomes a rats nest. I had already designed the Buffalo shield to deal with the rats nest issues, but there was still the desire to have a dedicated controller. 

There where already lot’s of ideas about which features the controller should have, but to get some more input and comments on my ideas I started thread about the controller at DIY audio. Some great ideas where brought up there and some useful advises too. A big “Thank You!” to all that have contributed to it! Without the DIY society I could not have achieved this all.

The controller 

This controller is based on the ATmega644 microprocessor. I use it with a highly modified version of the HiFiDUINO code. However, it can be used for all kinds of projects that required an microprocessor and a LCD, VFD or OLED. Even when not using a display it is still a very versatile board with some handy connectors for an IR receiver, rotary encode and push buttons. A very intuitive user interface can be created by using these. 

CE644- Bottom View 

Now lets talk about it’s features: Below there's a summary of its features. Some of the features will be discussed after the summary. 



The software is intended to be used with the Buffalo DAC's, but will work for other ES9018 DAC too. It is not released yet. 

All these functions can be controlled by using the remote and rotary encoder. There are plans to release another version of the software that allows the use of push buttons instead of the rotary encoder. 

  • Volume control *1
  • Input selection by remote, rotary encoder or push buttons *2
  • Input settings menu (adjusting settings per input) *1
  • Change names of all inputs by using the remote
  • Enable/Disable inputs
  • Backlight adjustment
  • Display auto off timer
  • Real time clock and date
  • Temperature monitoring (Celcius and Fahrenheit)
  • DAC configuration (output, phase and clock settings) *1
  • Controller configuration (IR teach, enable/disable volume control)
  • Standby / Auto standby timer (sleep)

*1 These features are also possible with the original HiFiDUINO software, but some work a bit differently.

*2 There are two different versions of the code. One for using the rotary encoder and the other for using the push buttons.  

ATmega644 Up

According to the datasheet the ATmega644 is a high-performance, low-power Atmel® AVR® 8-bit microcontroller. I would not call the micro-controller a real high performance AVR anymore as there are lots of AVR’s on the market that are much more powerful, but it's more than powerful enough for controlling an DAC. The ATmega644 is equally fast as the widely used ATmega328 (Arduino UNO), but has 64k bytes in-System programmable Flash memory instead of 32kB. It also has more EEPROM memory (2kB) and SRAM (4kB). It has 32 programmable I/O lines. All of them can be set as digital I/O’s and 8 of them also as analog IO’s. Some of these pins have specialized functions like PWM and interrupt. There is an Master/Slave SPI Serial Interface, Byte-oriented Two-wire Serial Interface (I2C) and USART (serial communication) and more. For all other features please consult the datasheet. 

ATmega644 pinout

Programming the ATmega644

There is no USB-to-serial converter on the CE644 controller, therefore the code can only be uploaded to the ATmega644 by using the ISP header.

CE644 -ISP Header 

Any in-system-programmer can be used, but the Arduino is also very suitable for the job. Follow these directions to use a Arduino Uno as 'In Sytem Programmer' to program the ATmega644: 

  1. Install the Arduino IDE (version 1.01).
  2. Click here to download Arduino software extension for the ATmega644.
  3. Unzip and copy all folders and files to the Arduino 'hardware' folder.
  4. Start the Arduino IDE.
  5. Upload the 'ArduinoISP' sketch to your Arduino.
  6. Select the ATmega644 under ToolsBoardATmega644.
  7. Now set the 'Arduino as ISP' programmer under ToolsProgrammerArduino as ISP.
  8. Install a 10uF capacitor between the reset pin and GND with the positive side facing the reset pin as in the image below.
  9. Connect the Arduino to the ISP header according to the table belowMake sure that the controller is not powered by any source other than the Arduino!!!
  10. Open and upload a sketch of your choice. Now the sketch is being uploaded to the controller's AVR. 
Pin name: not-mega pins: mega pins (1280 & 2560): CE644 -ISP pins: ATmega644 pins:
Slave reset 10 53 5 9
MOSI 11 51 4 6
MISO 12 50 1 7
SCK 13 52 3 8
VCC 5V 5V 2 10

Programming a Stand-alone ATmega644 

Note that these instructions will also work for a stand-alone ATmega644, but only when a suitable bootloader is burned to the AVR. To burn the bootloader follow the previous steps 1-8, connect the Arduino pins directly to pins of the Atmega644 according to the table above. Then use the Arduino IDE to burn the bootloader by selecting 'Burn bootloader' under ToolsBurn bootloader.

These are the parts needed to run and program a stand-alone ATmega644 by using a Arduino:

  • ATmega644-20PU (1x)
  • 16 mHz Crystal  (1x)
  • 22 pF Capacitor (2x)
  • Arduino (1x)

Optionally bypass VCC (pin10), AVCC (pin 30) and AREF (pin 32)  to GND (pin 11 & 31) with 100 nF capacitors. 

Arduino as ISP programming a ATmega644

DS1307 - Real Time Clock Up

The DS1307 Serial Real-Time Clock (RTC) is a low-power, full binary-coded decimal (BCD) clock/calendar. Address and data are transferred serially via a 2-wire, bi-directional bus (I2C). The clock/calendar provides seconds, minutes, hours, day, date, month, and year information. The end of the month date is automatically adjusted for months with fewer than 31 days, including corrections for leap year. The clock operates in either the 24-hour or 12-hour format with AM/PM indicator. The DS1307 has a built-in power sense circuit that detects power failures and automatically switches to the battery supply. 

CE644 -DS1307 

The RTC operates in the 24-hour format. A CR2030 3V, 220 mAh lithium button cell battery is powering to the clock when the power to the controller is cut off. Such an battery should allow the clock to run for over 10 years. When the controller enters stand-by mode it automatically dims the backlight and shows the time and date. There is also a menu option to show the time and date while the controller is in active mode. Time and date can both be adjusted in the menu. 

Date & Time clock 

To show the time and date I used the RTC library from Jeelabs. Is is an excellent library that makes it very easy to use the DS1307. A nice function of the library is the time and date can be set when the code is uploaded to the AVR. It uses the time and date from the computer used to upload the code. When the controller is mounted inside the DAC or other device you don’t want to upload the code to set the time and date, therefore I added the functionally to set the time and date. To set the time and date one needs to write the corresponding values to the RTC registers of the DS1307. The RTC registers are located in address locations 00h to 07h.

 DS1307 RTC registers

Every time a value is written to a RTC register the address pointer is shifter automatically to the next address location. This means the address pointer only needs to be set once before you start writing or reading the values to or from the RTC registers. The DS1307 only accepts binary-coded decimal (BCD). The Arduino does not support these. That’s why a function needs to be added to convert the RTC values to BCD.

Here is some Arduino example code that sets the time and date at startup and reads and shows them both in the serial monitor every second.

#define DS1307_ADDRESS 0x68 // Hhard coded I2C address of the DS1307
byte bcdToDec(byte val) {
// Convert binary coded decimal to normal decimal numbers
return ( (val/16*10) + (val%16) );
byte decToBcd(byte val){
// Convert normal decimal numbers to binary coded decimal
return ( (val/10*16) + (val%10) );
void readDateTime(){
// Reset the register pointer
Wire.requestFrom(DS1307_ADDRESS, 7);
int second = bcdToDec(Wire.read()); // 0-59
int minute = bcdToDec(Wire.read()); // 0-50
int hour = bcdToDec(Wire.read() & 0b111111); // 0-23 (24 hour time)
int weekDay = bcdToDec(Wire.read()); // 0-6 -> Sunday - Saturday
int monthDay = bcdToDec(Wire.read()); // 1-31
int month = bcdToDec(Wire.read()); // 1-12 -> January - December
int year = bcdToDec(Wire.read()); // 0-99
//print the date EG 25-11-12 23:59:59
Serial.print(" ");
void setDateTime(){
Wire.write(0x00); // set address pointer 
Wire.write(decToBcd(59)); // Second 0-59
Wire.write(decToBcd(59)); // Minute 0-59
Wire.write(decToBcd(23)); // Hour 0-23
Wire.write(decToBcd(1)); // Day of week 1-7
Wire.write(decToBcd(25)); // Day of month 1-31
Wire.write(decToBcd(11)); // Month 1-12
Wire.write(decToBcd(12)); // Year 0-99 
Wire.write(0x00); //start clock 
void setup(){
Wire.begin(); // join the I2C bus 
setDateTime(); // Set date & time at startup
void loop(){
// Read the time and date every second and
// show these in the serial monitor

MPC42010 Up

This is a 10K two channel digital pot which is controlled by its SPI interface. Since I did already wrote an small article about this pot I will just provide the link to it.

MCP42010 – How to control it using Arduino  

Here's a picture of it. Look for 'IC2'.

CE644 - MCP42010 

PCF8574AT - Remote 8-bit I/O expander for I2C-bus Up

The PCF8574AT provides general purpose remote I/O expansion for most microcontroller families via the two-line bidirectional bus (I2C-bus). The ATmega has already quite some I/O's, but connecting an HD44780-based character display will take seven of its I/O's. This is where the PCF8574A comes in. It provides 8 extra I/O's to the controller and it is used to control the display, thus saving some free pins on the controller's board. It can also be used for other purposes too when using the pads where the LCD should connect, but when doing so the LCD can obviously not be used at the same time.

PCF8574 Functional Diagram

The device's slave address of the is set to 0x38 with the three jumpers (J5 - J7). The address can be changed by setting the jumpers J5 – J7 which connect to the address inputs A0  – A2 . There are eight possible slave addresses. 

PCF8474AT slave address.

|0|1|1|1|0|0|0| 0x38
|0|1|1|1|0|0|1| 0x39
|0|1|1|1|0|1|0| 0x3A
|0|1|1|1|0|1|1| 0x3B
|0|1|1|1|1|0|0| 0x3C
|0|1|1|1|1|0|1| 0x3D
|0|1|1|1|1|1|0| 0x3E
|0|1|1|1|1|1|1| 0x3F

CE644 - PCF8574AT 

The LCD library from fmalpartida is used to control the LCD. Here's a little Arduino example that shows how to use the PCF847AT for controlling an LCD. 

// Constructor without backlight control
@discussion Initializes class variables and defines the I2C address of the
LCD. The constructor does not initialize the LCD.
@param lcd_Addr[in] I2C address of the PCF8474AT.
(The address can be configured using the on board jumpers (J5-J7))
@param En[in] LCD En (Enable) pin connected to the PCF8474AT
@param Rw[in] LCD Rw (Read/write) pin connected to the PCF8474AT
@param Rs[in] LCD Rs (Reset) pin connected to the PCF8474AT
@param d4[in] LCD data 0 pin map on the PCF8474AT
@param d5[in] LCD data 1 pin map on the PCF8474AT
@param d6[in] LCD data 2 pin map on the PCF8474AT
@param d7[in] LCD data 3 pin map on the PCF8474AT */ 
// LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs,
// uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
LiquidCrystal_I2C lcd ( 0x38, 4, 5, 6,
0, 1, 2, 3 );
// Create a set of new characters
const uint8_t charBitmap[][8] = {
{ 0xc, 0x12, 0x12, 0xc, 0, 0, 0, 0 },
{ 0x6, 0x9, 0x9, 0x6, 0, 0, 0, 0 },
{ 0x0, 0x6, 0x9, 0x9, 0x6, 0, 0, 0x0 },
{ 0x0, 0xc, 0x12, 0x12, 0xc, 0, 0, 0x0 },
{ 0x0, 0x0, 0xc, 0x12, 0x12, 0xc, 0, 0x0 },
{ 0x0, 0x0, 0x6, 0x9, 0x9, 0x6, 0, 0x0 },
{ 0x0, 0x0, 0x0, 0x6, 0x9, 0x9, 0x6, 0x0 },
{ 0x0, 0x0, 0x0, 0xc, 0x12, 0x12, 0xc, 0x0 } 
void setup(){
int charBitmapSize = (sizeof(charBitmap) / sizeof(charBitmap[0])); 
lcd.begin(20,4); // initialize the lcd 
for (int i = 0; i < charBitmapSize; i++){
lcd.createChar (i, (uint8_t *)charBitmap[i]);
lcd.home (); // go home
lcd.print("Hello, ARDUINO");
delay ( 1000 );
void loop(){
lcd.home ();
// Do a little animation by writing to the same location
for (int i = 0; i < 2; i++){
for (int j = 0; j < 16; j++){
lcd.print (char(random(7)));
lcd.setCursor (0, 1);


The ADuM1250 is a hot swappable digital isolator with nonlatching, bidirectional communication channels compatible with I2C interfaces. It provides two bidirectional channels, supporting a complete isolated I2C interface.

This device was suggested to me on my DIY Audio thread. It fully isolates my DAC from all possible noise coming from the controller's microprocessor. It is the small chip in the previous picture, look for IC4.

 ADuM1250 I2C Isolator

LD1086 & LD1117

Here's an picture of the LD1086 5 V low drop positive voltage regulator. It's capable of delivering up to 1.5 A, but the current to the controller is limited to 0.75 A by the resettable fuse 'F1'. The regulator's dropout is guaranteed at a maximum of 1.2 V at the maximum output current, decreasing at lower loads. The maximum input voltage is 30 V DC. This means that a wide range of power supplies (7 - 30 V DC) can be used to power the controller, but I would personally not power it with a supply voltage higher than 12 V DC.

CE644 - LD1086 5V positive regulator

This is the LD1117 3.3V low drop positive voltage regulator. It is able to provide up to 800 mA of output current. The dropout voltage is typically 1 V.

CE644 - LD1117 3.3V positive regulator 

Connecting the display

The controller is designed to be used with any HD44780 based 20x4 display. It is the exact same size as such an display and can therefor be mounted piggyback on the display. On the top right side of the controller is a row of trough hole connector pads. These will line up perfectly with the one on any HD44780 based 20x4 display. Note that the display can not be controlled directly from the ATmega644 because it connects to the AT8574AT port expander. This means that the display needs to be controlled via the AT8574AT by means of I2C. I choose to use the AT8574AT to save some I/O's of the ATmega644. It actually saves seven I/O's that can now be used for other applications. 

CE644 - Back With OLED

CE644 - Front With OLED
















0 #1 gwikse 2013-02-02 00:57
Great work Corpius. I am looking forward to fitting it into my system. I am glad that you desided to build a piggyback solution as well as the controller. I remember the model I had started on prior to talking to you about the shield. It`s fun to watch that simple model with its simple approach next to a model of your exelent solution. Cant wait to fit this streamlined product into my DAC. :)

You have no rights to post comments

You are here: Home My Projects Other builds CE644 - The ES9018 I2C controller