Feb 072015
 

EC1204B Led Rotating Clock: Schematic, upgrade, firmware

0. Foreword

Please, do not post elsewhere the code found here.

– the code isn’t ready yet:

          – I wish to develop more with several DS1621 sensors on a cable, in order to collect more than one temperature;

– The code should reside in one place and people should use links to my site instead. I have a hard time browsing the Internet for pieces of code sometimes, and finding old or obsolete code instead of links to the original developer is always a pain in the ass.

– I expect people to comment on the code and make optimizations/new developments, especially for student usage. Having this in mind, the code itself is an ongoing process which can be followed by students, from its first state (intelligible, but unoptimized) to its latest state (optimized, but unintelligible). Many people asked me how the code becomes awful and almost impossible to understand. It is my chance to show that I always start from a human approach and I am ending to a computer approach, and that this is always an endeavour that takes time and patience and endless computations and verifications, it doesn’t just come from thin air.

– the last reason is that I collect stats on page visits and downloads, which show me where to insist with open source development and where not to. I cannot collect stats from foreign sites, nor do I wish or have the time to do so.

 

1. Introduction

I have bought 3 pieces of EC1204B clock kits from Banggood.com. At ~ US$10, it seemed a good deal and most interesting.

I have built the first one in ~3 hours and spent another ~6 hours to tutor a pupil of mine into soldering the second one.

The 3rd one had a badly burned firmware and didn’t want to start. Banggood will replace it, but I thought to spend some time and rewrite the firmware, since there were some things in the original firmware I didn’t like and I wished to improve.

Figure 1. Original EC1204B Led Rotating Clock

Moreover, what is the point in having a soldering kit without having the firmware and playing for hours with it afterwards, making modifications to the firmware and learning both soldering and programming?

So, I have decided to make this project and to turn it over to the community. Teachers will have a starting point to use this interesting and cheap kit to tutor their pupils.

More advanced hobbyists will have a full working example, using the excellent PCB coming with the kit.

The whole programming stuff took me 8 full days, including the time to write and post this article.

Later edit:

It took me 8 days to finish the first version, 16 days to the first working one, including errors, modifications and improvements suggested by Philipp Klaus and Sergey, with their kind comments.

Much later edit: After almost two years I still have found improvements to the code.

Disclaimer:

The software/firmware presented here is a complete rewrite from scratch, just by observing the schematic and by understanding the way it works. It did not involve any kind of disassembly of the original firmware, reverse engineering or other similar stuff.

I do not condone copying the work of others in any way. However, progress is difficult to achieve these days from zero and it is easier to build up on something that already exists, while respecting the original work and effort.

I advice my readers to buy Banggood kits, the PCB has an excellent quality and all the parts delivered will be used, even with the modifications I suggest in this post. The idea is to start learning soldering and coding using affordable ressources.

 Video: Modified EC1204B clock, latest version.

2. Modifications to the original schematic

First of all, the kit contains a few spare parts, that we will use to make the actual modifications to the clock.

Why modify the kit?

The clock is powered by an AT89S52 processor, which is an enhanced version of the classic Intel 8051. Pin to pin compatible, this processor is capable of running much faster than the original i8051. However, finding a nice development interface (IDE) and a good programmer for it is quite difficult. So, why not use more a more modern processor?

I found the AT90S8515 as being pin to pin compatible with the AT89S52, with minor differences:

– pins 29, 30 and 31 which have different functions. These pins are not used anyway, so no modifications required here.

– the reset pin (pin 9) is active high on the AT89S52 and low on the AT90S8515. So, a first modification would be required here.

– Pins 12 and 13, tied to the micro switches, should be pulled up with resistors, in order to minimize noise on these pins.

– AT90S8515 runs at 8MHz maximum while the original AT89S52 works at 12 MHz.

Figure 2.Modifications to EC1204B Led Rotating Clock

The  modifications are written on the picture, but I will enumerate them here one more time:

1. Cut wiring on PCB, removing the link from the reset switch to VCC.

2. Solder a piece of wire as shown, linking the reset switch to GND.

3. Desolder C5 and R21. Solder R21 at the place of C5, building a pull-up resistor for the reset pin.

4. Do not solder anything at the place of R21. A capacitor on the reset pin will make the processor unprogrammable through ISP (in-system programming), since the RST signal is handled by the programmer.

5. Solder two resistors between 4.7Ko and 10Ko as shown, in order to pull up the SET and SELECT signals. The kit provides for 3 extra 10Ko resistors. I have used 5.6Ko resistors just because the other ones were not at hand at the time I have make these modifications.

6. Desolder the 12MHz quartz and replace it with an 8 MHz one. AT90S8515 will not run consistently (or will not run at all) at 12 MHz.

Later edit: Don’t do it at home! I wondered for quite some time how an overclocked Atmel processor would perform. So I reverted to the original 12MHz crystal, closed my eyes and powered the circuit after defining the new frequency in the software and recompiling it. Surprise! It really worked!

After a few days, though, I saw some glitches:

– mode 9 of displaying seconds doesn’t work as expected – the leds are completely crazy;

– the connection with “some” DS18B20 stop working after a while: the display shows always 127 (communication error). They start working again after a reset, so…

Still, the processor is as cold as before and, if you’re not too bothered by these two issues, you can test it for several months and post your experience here.

Figure 3. EC1204B Led Rotating Clock with AT90S8515 micro controller

At this time, the clock is ready to be connected to an external programmer. I have used an STK500:

Figure 4. Modified EC1204B Led Rotating Clock connected to a STK500 development board

 

Whichever programmer you wish to use, you need wires to link the programmer’s signals to the clock’s programming pins. The clock’s PCB is clearly marked with all the signals: GND, RST, SCK, MISO, MOSI and VCC.

Grab Atmel Studio 4 from Atmel’s website , WinAVR from SourceForge , configure Atmel Studio to use WinAVR toolchain, load the project below, burn it into your processor and, voilà!

 

3. Modus Operandi (How It Works)

 

Video 1: EC1204 Modus Operandi

This operation manual was updated on 26.02.2017, following the many additions to the firmware during the last two years. All timigs were also recomputed during this rewrite of the operation manual.

Definitions:

SET key: the left key

SELECT key: the middle key

RESET key : the right key

A. Special Startup Mode: Seconds Led Design

Keep pressing both SELECT and SET keys then press shortly RESET. Depress the SELECT and SET keys. You are now into Seconds Led Design menu.

Pressing SELECT, you will see numbers from 1 to 9 corresponding to the 9 ways seconds can be displayed by the circular leds. The leds show a live preview for every display mode.

Pressing SET , the selected mode will be written to memory and you will hear a confirmation beep. The selected mode will be in effect after a power off of the clock

Not pressing any key will revert to Clock Mode after about 10 seconds but the selected mode will not be written to memory, preserving it just until the next reset or power toggle.

B. Special Startup Mode: EU or US mode

Keep pressing the SELECT key then press shortly RESET. Depress the SELECT key. This will toggle between European (EU) and United States (US) modes and will affect the way the clock is displaying information:

EU mode: the clock displays 24h time and temperature in Celsius;

US mode: the clock displays 12h time and temperature in Fahrenheit;

C. Special Startup Mode: temperature toggle every 10 seconds

Keep pressing the SET key then press shortly RESET. Depress the SET key. This will toggle between regular time display mode and a mix of time and temperature. In the latter case, the clock displays the time, except for 0,10,20,30,40 and 50 seconds when it will display the measured temperature for one second. This mode was requested and it can be used in rooms or locations where a visual display of the temperature is important: saunas, datacenters etc.

Only the temperature of the first sensor is displayed in this mode.

 D. To Show The Temperature

While in clock mode, short press SELECT. This will trigger the temperature mode, displaying it for all connected temperature sensors in a loop.

First, the sensor number will be displayed: “tE: 1”. A second press on SELECT will display the corresponding temperature for the first sensor.

If there are multiple DS18B20 sensors connected to the clock (maximum is 5 sensors), pressing SELECT again will display in order “tE: 2” then pressing select again the temperature of the second sensor and so on, in a loop.

EC. To Show The Day, Month and Year

While in clock mode, press SET and depress it as long as “dAtE” is displayed on the screen (<5 seconds). The month and day will be displayed for about 2  seconds.

Alternatively, pressing SET in this mode, you don’t have to wait for “dAtE” to be displayed. A short press will suffice.

Press SET again and the year will be displayed for about two seconds.

Press SET again and the clock will show the time.

 D. To Set The Alarm

While in clock mode, press SET and depress it as long as “S :AL” is displayed on the screen (6-10 seconds).

First you may change the minutes of the alarm by pressing SELECT.

Pressing SET again will display the alarm hour.

You may change the hour of the alarm by pressing SELECT.

Pressing SET again will display “on” or “oFF” depending on the value read at start time from the EEPROM.

You may change if the alarm is on or off by pressing SELECT.

Pressing SET again will write the alarm values into EEPROM and the you will hear a confirmation beep.

Not pressing the final SET will change the alarm time, but it won’t be memorized in EEPROM. Thus, a power toggle or a RESET will revert alarm time to whatever was stored in EEPROM.

E. To Stop The Alarm

Just press either SET, SELECT or RESET to stop the alarm until the next time match will occur. If no button is pressed, the alarm will keep beeping for about 10 minutes.

F. To Set The Time

While in clock mode, press SET and depress it as long as “S :CL” is displayed on the screen (more than 10 seconds).

First you may change the year by pressing SELECT.

Pressing SET again will display the month.

You may change the month by pressing SELECT.

Pressing SET again will display the day.

You may change the day by pressing SELECT.

Pressing SET again will display the minutes.

You may change the minutes by pressing SELECT.

Pressing SET again will display the hour.

You may change the hour by pressing SELECT.

Pressing SET again will write the set time into DS1302 and the you will hear a confirmation beep.

Not pressing the final SET will discard all modifications made and the clock will revert to the previous settings.

Every step has a 10 seconds timeout. Not pressing any key during this interval will revert the clock to time display mode. All the settings will be lost.

 4. Firmware

The code is (almost) fully commented,, because I wanted to make it easy to read, easy to understand, and a possible start for a programming course example.

What can a pupil learn from this nice clock?

Hardware:

– How to solder both through-hole and SMD components, by hand;

– How to arrange for parts on both sides of a PCB and solder them, taking into account the height of each part.

– How to find the best spots to make modifications on a given PCB.

Software:

– How to deal with the special registers of a micro controller;

– How to enable/disable interrupts;

– How to multiplex data on a large bus (12 bits) using interrupts;

– How to use variables, constants, #define and EEPROM variables and to grasp the difference between them;

– How to build custom menus for a given task;

– How to make variables prisoners between two given values;

– How to use 1-wire and 3-wire communications;

– How to dynamically program ports for input or output;

– How to write nice and clean code, easy to debug; The RTC library is a bad example of how to write a library.

– How to program a basic alarm which can be shut off;

– How to deal with bitwise operations. I wrote the program specially using |,||,~,^,&,&&,_BV(), binary and hex values, to show the many ways an operation could be done.

-How to use local and global variables;

I am sure a good tutor will find several other stuff to dissect and learn to willing pupils. The main code is almost fully commented for a better understanding.

 

A. DS18B20 stuff.

You can download the DS18B20 datasheet from here.

The AVR GCC library I have used was written by Davide Gironi and I have made minor modifications on it. Please consult his work at http://davidegironi.blogspot.com

B. DS1302 stuff.

You can download the DS1302 datasheet from here.

5. Final considerations and Downloads

 

The code takes almost all the AT90S8515 flash memory (7.51K out of 8K available), so there is place for small modifications.

There is also place for improvements, if one is interested enough.

Another candidate to replacing AT89S52 would be an ATMega8515, which can run at 16 MHz instead of 8MHz when driven with a crystal. This processor is also able to run at 8MHz with an RC internal oscillator. If programmed as such, we don’t need the crystal and the two capacitors anymore!

I know there is also an other candidate from the ATMega family, compatible pin to pin with AT89S52, but I let to you the burden to browse through datasheets and discover it 😉

 

All the project for Atmel Studio 4 can be downloaded here. It contains an error, see 7. Errata

The schematic of the EC1204B clock can be downloaded here.

AT90S8515 datasheet can be downloaded here.

AT89S52 datasheet can be downloaded here.

 

 6. Improvements to the Code

 

Improvement #1

 

Taking all the lines similar to

 d[10]=0b00010000 | dectobin(seconds-in1*8)*((seconds>= 0) && (seconds<= 7));

, adding a function named bb:

uint8_t bb(uint8_t in11, uint8_t sec1, uint8_t sec2) //function to reduce the code
{
    return dectobin(seconds-in11*8)*((seconds>= sec1) && (seconds<= sec2));
}

and transforming the line into

 d[10]=0b00010000 | bb(in1,0,7);

makes the code drop from 7.6Kbit to 6.5 Kbit.

While the original code would not fit into an ATMega8515, because of the loader which takes some place in the flash, the optimized code fits entirely and there is some space left for further coding.

 

 Improvement #2

Going further ,taking all the block of  lines similar to

d[10]=0b00100001 | dectobin(seconds-in1*8)*((seconds>= 0) && (seconds<= 7));
d[11]=0b10000100 | dectobin(seconds-in1*8)*((seconds>= 8) && (seconds<= 15));
d[12]=0b00010000 | dectobin(seconds-in1*8)*((seconds>= 16) && (seconds<= 23));
d[13]=0b01000010 | dectobin(seconds-in1*8)*((seconds>= 24) && (seconds<= 21));
d[14]=0b00001000 | dectobin(seconds-in1*8)*((seconds>= 32) && (seconds<= 39));
d[15]=0b00100001 | dectobin(seconds-in1*8)*((seconds>= 40) && (seconds<= 47));
d[16]=0b10000100 | dectobin(seconds-in1*8)*((seconds>= 48) && (seconds<= 55));
d[17]=0b00010000 | dectobin(seconds-in1*8)*((seconds>= 56) && (seconds<= 59));

, adding a function named cc:

void cc(uint8_t d0,uint8_t d1,uint8_t d2,uint8_t d3,uint8_t d4,uint8_t d5,uint8_t d6,uint8_t d7, uint8_t in111)
{

            d[10]=d0 | bb(in111,0,7);
            d[11]=d1 | bb(in111,8,15);
            d[12]=d2 | bb(in111,16,23);
            d[13]=d3 | bb(in111,24,31);
            d[14]=d4 | bb(in111,32,39);
            d[15]=d5 | bb(in111,40,47);
            d[16]=d6 | bb(in111,48,55);
            d[17]=d7 | bb(in111,56,59);
}

and transforming all the block of lines into one single line,

cc(0b00100001,0b10000100,0b00010000,0b01000010,0b00001000,0b00100001,0b10000100,0b00010000,in1);

repeating the whole process in all the program, makes the code drop again from 6.5 Kbit to 5.86 Kbit.

 

With just two functions related to the way we display seconds, the code dropped with an astounding 1.74K (21%).

Unreadable code, but no less much smaller than the original!

I dare you to try reducing the code to 4K, while keeping all its features!

 

7. ERRATA

 

The code above (clock.c) contained an error related to DS1302: instead of using dt.date, I have used dt.day. Thus, reading and writing the date showed and wrote in fact the day of the week.

This error has been addressed below.

 

8. More Modifications And More Learning

While looking at the kit, there is something missing on the PCB: a 4-pin connector marked “bluetooth”.

Pin 1: connected to pin 10 of the processor

Pin 2: connected to pin 11 of the processor

Pin 3: GND

Pin 4: VDD

According to the datasheet, Pin10 is RxD and Pin11 is TxD of the embedded UART (AT90S8515) or USART (ATmega8515).

I remembered those HC-05 or HC-06 bluetooth modules available on the market and I realized the producer already thought to a further development of the kit, probably the possibility to set its parameters through bluetooth, from a phone.

While this is an interesting development, it doesn’t imho add too much to a learning curve. It just augments the addiction young people have today to smart phones.

 

Why not put his connector to a better use?

I had at home some DS1621 digital temperature chips, working on I2C.

So, I have created the following schematic:

fig.5

 Figure 5. Schematic for a DS1621 addon card.

 

I fitted the board with the help of  two 4-pin male and female connectors to the clock’s board and started to write code.

What is so interesting in this DS1621 chip? Well, nothing special. It is just another thermometer, using I2C communication.

 

What is special is the fact that ATmega8515, which I have used for this modification:

a) does not have a hardware Two Wire Interface (or TWI) that could be used for I2C communication. Thus, a bit banged library had to be used (a library which recreates by software a whatever protocol, instead of relying on hardware).

b) it can be programmed to run out of its own internal 8MHz oscillator, meaning that the 8MHz crystal I had to changed may be removed permanently.

c) may be clocked by crystals of up to 16MHz, so we could resolder the original 12 MHz crystal (while I don’t see the point of doing this).

In order to maximize speed, an assembler code had to be used. Thanks to Peter Fleury, who has already wrote this library years ago, the implementation became suddenly much easier.

 

The new project for Atmel Studio 4, which includes the I2C  (orTWI)  and the DS1621 AVR GCC libraries can be downloaded here.

ATmega8515 datasheet can be downloaded here.

DS1621 datasheet can be downloaded here.

 

9. Ooops! I Did It Again!

 9.1 First attempt

What an interesting chapter number. 9. Identical to the number of the case switch function I have left you for homework.

Well, I did it again. Couldn’t help myself.

I have already embedded this small video, but here it is again:

 

The first attempt of writing this function is as follows:

     case 9:
            timestamp1=seconds;
            if (timestamp1!=timestamp2)
            {
                if (odd(dt.minute))
                {
                    for(j=0;j<=seconds;j++)
                    {
                        in1=j/8;
                        fillbefore(in1,0xFF);
                        _delay_us(700);
                        d[in1+10]=p2(j-in1*8);
                        fillafter(in1,0x00);
                        _delay_us(700);
                    }
                }
                else
                {
                    for(j=0;j<=seconds;j++)
                    {
                        in1=j/8;
                        fillbefore(in1,0x00);
                        _delay_us(700);
                        d[in1+10]=~p2(j-in1*8);
                        fillafter(in1,0xFF);
                        _delay_us(700);
                    }
                }
            //timestamp1=dt.second;
            timestamp2=dt.second;
            }
            break;

 

The computation is the following:

– if the minutes are odd, we light every led until the current lit led equals the current second, then we turn off all other leds, one by one

– we wait for 700 microseconds

– if the minutes are even, we turn off every led until the current lit led equals the current second, then we lit all other leds, one by one

– we wait for 700 microseconds

The total computing time reaches ~90 milliseconds, there is a rest of 10 milliseconds to have a delay between seconds.

These delays over delays have a negative impact on the way the clock works, especially if another execution thread comes into action:

– the alarm sounds like crap;

– setting the clock becomes a pain in the a** because short key presses are almost never felt, since the clock is “waiting for time to pass”.

 

This led me to reconsider the approach and to try to eliminate the delays, without having a strong visual impact.

  9.2 Second and final attempt

 

Since the display and the leds are multiplexed, and the timer overflow code runs again and again (in fact it runs 1024 times a second), why not use a counter to see how many refreshes were made, divide this counter by 13 and use the result? Why 13? Well, 1024/24=78. We could use the first 60 numbers (0-59) to light up the corresponding led, and wait during the numbers 60 to 78.

This approach would solve more than one issue:

– the delay between two leds is determined by the setting of the timer itself, i.e. by hardware, instead of software (delays);

– the delay between two leds is very precise, since we don’t do for cycles anymore.

– the delay between the end of the last led and the following second is very precise;

– we don’t use delays anymore, so reading keys and sounding an alarm will perform as good as in the other modes of seconds  display (1 to 8).

– the processor is processing slightly more than in the first case, when it had to process A LOT.

 

The second form of the same function is the following:

 

     case 9:     
            j=refresh/13;
            if (odd(dt.minute))
            {
                if (j<=seconds)
                {
                    in1=j/8;
                    fillbefore(in1,0xFF);
                    d[in1+10]=p2(j-in1*8);
                    fillafter(in1,0x00);
                }
            }
            else
            {
                if (j<=seconds)
                {
                    in1=j/8;
                    fillbefore(in1,0x00);
                    d[in1+10]=~p2(j-in1*8);
                    fillafter(in1,0xFF);
                }
            }
            break;

 

There is an issue though: the refresh/13 and the start of a new seconds are almost certainly not in phase. Resetting the refresh counter at 1024 does not mean it will be reset at the same moment a new second is coming. This leads to a shift between showing the seconds dots and displaying the seconds leds. When the seconds reach 59 and before changing to zero again, part of the leds don’t lit anymore.

The solution was to reset the refresh counter at the same time the read second from the DS1302 changed. This was accomplished in the timer overflow routine with a simple code, as follows:

 

ISR(TIMER1_OVF_vect)
{
    uint8_t PV;
//comment up to the next sign in order to use first version of function for seconds #9
//    |
//    |
//    v

    refresh++;
    timestamp1=seconds;
    if (timestamp1==timestamp2)
    {
        if (!refresh_resetted)
        {
            refresh=0;
            refresh_resetted=1;
        }
    }
    else
    {
        refresh_resetted=0;
    }

//    ^
//    |
//    |
//comment from the first sign in order to use first version of function for seconds #9

    switch (digit_addressed)

... blah blah the same thing as in previous versions

    if(digit_addressed>=12) digit_addressed=0;
//comment the following line in order to use first version of function for seconds #9
//    |
//    |
//    v

    timestamp2=dt.second;

//    ^
//    |
//    |
}

 

I have discovered a few errors in the firmware (delays not working) and also typos in this very article.

I wrote also a couple of supplementary functions to lower the code size.

Enjoy downloading the latest firmware, datasheets etc, here.

Comment with your ideas, findings etc.

10. Pulsing display

While setting the alarm or the clock, the display should pulse.

With the current firmware, this doesn’t happen.

How to solve this issue without a too big overhead?

 

Because we are counting refresh cycles, we could display the useful digits on the first half (up to 512) and display the NULL symbol on the second half.

We define two new variables:

uint8_t pulsing=0, showdigit=0;

then we alter the Timer Overflow routine, the part pertaining to digits display:

 

    if (pulsing) showdigit=(refresh<512); else showdigit=1;
    switch (digit_addressed)
    {
        case 0:
        PV=PORTD;PV &= ~_BV(4);PV |= _BV(5);PV |= _BV(6);PV |= _BV(7);PORTD=PV;
        if (showdigit) PORTA = ~(d[0]); else PORTA= ~(SEG_NULL);
        PORTC=0xFF;
        break;
        case 1:
        PV=PORTD;PV |= _BV(4);PV &= ~_BV(5);PV |= _BV(6);PV |= _BV(7);PORTD=PV;
        if (showdigit) PORTA = ~(d[1]); else PORTA= ~(SEG_NULL);
        PORTC=0xFF;
        break;
        case 2:
        PV=PORTD;PV |= _BV(4);PV |= _BV(5);PV &= ~_BV(6);PV |= _BV(7);PORTD=PV;
        if (showdigit) PORTA = ~(d[2]); else PORTA= ~(SEG_NULL);
        PORTC=0xFF;
        break;
        case 3:
        PV=PORTD;PV |= _BV(4);PV |= _BV(5);PV |= _BV(6);PV &= ~_BV(7);PORTD=PV;
        if (showdigit) PORTA = ~(d[3]); else PORTA= ~(SEG_NULL);
        PORTC=0xFF;
        break;
        ...the rest of the function is the same as before

 

The main routine has also to be altered, in order to set the modes where we wish the display to be pulsing or not:

 

        //we are focusing on this part of the main function, deciding what to display when there is no key pressed (free running clock)
        //and we add for each display mode a line stating whether we wish a pulsing display or not
        if (key==0)
        {
            //void all long key presses
            switch (ClockMode)
            {
            case SHOWCLOCK:
                if (t2==0)
                {
                    //show clock
                    pulsing=0;
                    dt=get_date_time();
                    digit=dt.hour*100+dt.minute;
                    seconds=dt.second;
                }
                else
                if (t2<=5)
                {
                    //show the date
                    pulsing=0;
                    t1=6000;
                    t2=0;
                      dt=get_date_time();
                    seconds=dt.second;
                    ClockMode=SHOWDATE;
                }
                else
                if (t2<=10)
                {
                    //set the alarm
                    pulsing=1;
                    t1=900;
                    t2=0;
                      dt=get_date_time();
                    seconds=dt.second;
                    ClockMode=SETALMINUTES;
                }
                else
                {
                    //set the date
                    pulsing=1;
                    t1=900;
                    t2=0;
                    dt1=dt;
                    ClockMode=SETYEAR;
                }
                break;    
            case SHOWTEMP:    
                //show temperature until timer expires
                //digit = ds18b20_gettemp();
                pulsing=0;
                digit = get_ds1621_temperature();
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SHOWDATE:
            //show the date
                pulsing=0;
                dt=get_date_time();
                digit=dt.month*100+dt.date;
                seconds=dt.second;
                _delay_ms(1);
                break;    
            case SHOWYEAR:
            //show the year
                pulsing=0;
                dt=get_date_time();
                digit=2000+dt.year;
                seconds=dt.second;
                _delay_ms(1);
                break;    
            case SETHOURS:
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETMINUTES:    
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETDATE:
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETMONTH:
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETYEAR:
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETALHOURS:
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETALMINUTES:
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETAL:
                pulsing=1;
                  dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            case SETSECMODE:
                //read seconds to be displayed
                pulsing=1;
                dt=get_date_time();
                seconds=dt.second;
                _delay_ms(10);
                break;    
            default:
                break;    
            }
        }
        else
        //set key was pressed
        if (key==1)
        {
            if (AlarmOn)
            {
                AlarmOn=0;
            }
            switch (ClockMode)
            {
            case SHOWCLOCK:
                //set to SHOWTEMP and set the timer to ~10 seconds
                t1=900;
                ClockMode=SHOWTEMP;
                break;    
            case SHOWTEMP:    
                //nothing to do    
                break;    
            case SHOWDATE:
                break;    
            case SHOWYEAR:
                break;    
            case SETHOURS:
                pulsing=0;
                t1=900;
                if (++dt1.hour>MaxHours) dt1.hour=MinHours;
                break;    
            case SETMINUTES:    
                pulsing=0;
                t1=900;
                if (++dt1.minute>MaxMinutes) dt1.minute=MinMinutes;
                break;    
            case SETDATE:
                pulsing=0;
                t1=900;
                if (++dt1.date>MaxDate) dt1.date=MinDate;
                break;    
            case SETMONTH:
                pulsing=0;
                t1=900;
                if (++dt1.month>MaxMonth) dt1.month=MinMonth;
                break;    
            case SETYEAR:
                pulsing=0;
                t1=900;
                if (++dt1.year>MaxYears) dt1.year=MinYears;
                break;    
            case SETALHOURS:
                pulsing=0;
                t1=900;
                if (++ALHours>MaxALHours) ALHours=MinALHours;
                break;    
            case SETALMINUTES:
                pulsing=0;
                t1=900;
                if (++ALMinutes>MaxALMinutes) ALMinutes=MinALMinutes;
                break;    
            case SETAL:
                pulsing=0;
                t1=900;
                if (++ALSet>MaxALSet) ALSet=MinALSet;
                break;    
            case SETSECMODE:
                pulsing=0;
                dt=get_date_time();
                seconds=dt.second;
                t1=900;
                if (++SecMode>MaxSecMode) SecMode=MinSecMode;
                break;    
            default:
                break;    
            }
        }
        else
        //select key pressed
        if (key==2)
        {
            if (AlarmOn)
            {
                AlarmOn=0;
            }
            switch (ClockMode)
            {
            case SHOWCLOCK:
                //short key press 1-5 seconds: show date/time for 10 seconds
                //long key press 6-10 seconds: enter alarm set
                //more than 10 seconds pressed: enter date set
                t2++;
                digit=t2;
            case SHOWTEMP:
                break;    
            case SHOWDATE:
                t1=6000;
                ClockMode=SHOWYEAR;
                break;    
            case SHOWYEAR:
                ClockMode=SHOWCLOCK;
                break;    
            case SETHOURS:
                //write the time/date/year into the DS1302
                pulsing=0;
                set_date_time(dt1);
                t1=0;
                beep();
                _delay_ms(200);
                break;    
            case SETMINUTES:    
                ClockMode=SETHOURS;
                break;    
            case SETDATE:
                ClockMode=SETMINUTES;
                break;    
            case SETMONTH:
                ClockMode=SETDATE;
                break;    
            case SETYEAR:
                ClockMode=SETMONTH;
                break;    
            case SETALHOURS:
                ClockMode=SETAL;
                break;    
            case SETALMINUTES:
                ClockMode=SETALHOURS;
                break;    
            case SETAL:
                //write Alarm Values into EEPROM
                pulsing=0;
                eeprom_write_byte(&EALHours,ALHours);
                eeprom_write_byte(&EALMinutes,ALMinutes);
                eeprom_write_byte(&EALSet,ALSet);
                t1=0;
                beep();
                _delay_ms(200);
                break;    
            case SETSECMODE:
                //write SecMode into EEPROM
                pulsing=0;
                eeprom_write_byte(&ESecMode,SecMode);
                t1=0;
                beep();
                _delay_ms(200);
                break;    
            default:
                break;    
            }
        }
        t1--;
        if (t1==0xFFFF) t1=0;
        if (t1==0) ClockMode=SHOWCLOCK;
        display();
        CheckAlarm();
        }

        ...the rest is the same

Enjoy downloading the latest firmware, datasheets etc, here.

Comment with your ideas, findings etc.

11. Multiple DS18B20 on a wire

 

This development took some time because I was waiting for my DS18B20 stack to arrive.

Thanks, Sergey, for commenting on my blog and giving me the necessary push to write the code.

Thanks, Philipp Klaus, for showing me the error in defining the CPU frequency. It helped a lot, especially in the 1-wire protocol, where delays are of the essence and strongly tied to the CPU frequency.

 

The nice little clock is able now to show up to 5 (modifiable variable) DS18B20 sensors, connected in parallel on the same wire.

The program does an automatic search for the sensors and is able to read and display the temperature of each of the sensors it finds at start-up.

Sensors can be plugged in live, while the clock is running, but to detect new sensors the clock must be reset from the reset button.

If the temperature shows at times “85C” it is because this is the initial value of the sensor.

If the temperature shows “127C” it means there is a miscommunication with that particular sensor. Check the cables, solderings etc. It might also mean the sensor is dead, in which case it needs to be replaced.

 

The order the sensors are discovered seems random to the end user. This order is in fact determined by the serial number of each sensor. The good thing is that the sensors are always detected in the same order if they are not changed. Their position on the wire doesn’t matter.

While adding or removing sensors, the order modifies again, but remains the same if no more sensors are added/removed after a reset or power failure. This way, in order to match the software order with the physical order of the sensors, it is enough to rearrange the position of the sensors on the wire so it matches the software detection order.

 

Connecting the sensors must be done by using shielded microphone cables especially for longer distances (more than 3 meters or 9 feet). An alternative would be the use of a 3-wire twisted cable. Check and see what works.

 

What is NOT implemented in software:

– negative temperatures (with a “-” in front of the temperature). I tried putting an ice cube onto one sensor and it got down to…1C after 5 minutes. It didn’t want to get to a lower temperature. The code is however opened to modifications.

– parasitic supply and all the other exhaustive features DS18B20 know about. I’ll leave this for whoever wishes to better the DS18B20 library. It’s not hard. It’s just that I don’t have the time to write a full library for this sensor.

– CRC8. First, it would be a software burden, bearing in mind the flash is almost full with this version of the firmware. Second, you don’t need CRC8 is your sensors are connected with shielded microphone cables of up to 3 meters. Cautin, however, in very noisy environments. Reading could be wrong.

The seconds don’t show accurately on mode 9 while performing temperature readings, mainly because the low level of the one-wire protocol are disabling and re-enabling interrupts. All other functions are working just fine.

This firmware release is not implementing the I2C protocol anymore, neither is it capable to read from the DS1621 anymore. The libraries were removed from the project in order to make space for the larger code of the main program and the new DS18B library. The files remained however in the project’s zip file.

Enjoy downloading the latest firmware, datasheets etc, here.

Comment with your ideas, findings etc.

 

12. Displaying Celsius or Fahrenheit units

I had a request from a US citizen to make the clock display the temperature in Fahnrenheit. I’d like to say to him a public Thank You for the donation he has made.

Since there is not much space left into the mega8515 chip with the latest additions, I wondered a few days on how to implement this but while keeping the Celsius display functionality too.

Writing a code to another menu level would have resulted in a code bigger than the available 8K of flash, so something else had to be done.

In clock.c, a supplementary definition came up:

 

//ECFUnit defines Celsius or Fahrenheit display
//ECFUnit=0 means Celsius Display
//ECFUnit=1 defines Fahnrenheit Display
EEMEM uint8_t ECFUnit=1;
uint8_t CFUnit;

The ECFUnit value is stored in EEPROM and it is read at init time then it is then stored in the CFUnit variable.

When the temperature is read from the sensor (in Celsius, because this is how the sensor is manufactured), the CFUnit is checked and, if it is equal to 1, it will compute the following equation:

digit=digit*9/5+32

basically transforming the Celsius value to a Fahnrenheit value.

So, switching from C to F requires a rewrite of just the EEPROM values. I guess that, depending on where one lives, one will select the display unit just once in a lifetime.

 

I have checked how it displays up to 255F and I have actually discovered a small hidden bug that I have also addressed.

At temperatures over 100F, the second digit disappeared if it was zero. So I checked that part of the code again handling the temperature and it looked like this:

            temp=digit;
            d[4]= temp/100;
            temp-=d[4]*100;
            d[5]= temp/10;
            temp-=d[5]*10;
            d[6]= temp;
            if (d[4]==0) d[0]=SEG_NULL; else d[0]= seg[d[4]];
            if (d[5]==0) d[1]=SEG_NULL; else d[1]= seg[d[5]];
            d[2]= seg[d[6]];
            d[3]= seg[12];

The code made sense: if the second digit (d[5]) was zero, indeed the second displayed digit (d[1]) was set to NULL, even if the first digit was different than zero.

An interesting bug, I thought I have addressed all the small bugs until now but practice shows that tiny errors can always survive in a code.

So, the code was changed to:

            temp=digit;
            d[4]= temp/100;
            temp-=d[4]*100;
            d[5]= temp/10;
            temp-=d[5]*10;
            d[6]= temp;
            if (d[4]==0) d[0]=SEG_NULL; else d[0]= seg[d[4]];
            if ((d[5]==0) && (d[4]==0)) d[1]=SEG_NULL; else d[1]= seg[d[5]];
            d[2]= seg[d[6]];
            d[3]= seg[12+CFUnit*2];

Now, the second digit is NULL if it is equal to zero AND if the first digit is also equal to zero.

Changing the unit letter from C to F was simple. The “C” letter has an index of 12 and the “F” letter an index of 14. We should display “C” if CFUnit is equal to zero and “F” if CFUnit is equal to 1.

The index of the letter to be displayed becomes then 12+CFUnit*2.

Enjoy downloading the latest firmware, datasheets etc, here.

Comment with your ideas, findings etc.

13. Displaying the temperature every x seconds while displaying the time

Peter Casper had a request to alternatively show the clock and the temperature, for he wishes to use the clock in an infrared-heated room.

I thought using the same idea as above, so a new EEMEM variable was defined and if at programming time it is equal to 1, it fires a condition in the main loop that shows the temperature for 1 second every 10 seconds. It displays just with one (or the first) temperature sensor.

Some optimizations to the display routine were also made.

Because this firmware is about to hit the available flash memory in the mega8515, I had to look closely of the size of the binary data that was about to be burned into the MCU. How to do this, since the HEX file size has nothing to do with the size of the binary code? I have written this morning a Windows tool that converts Intel HEX files to binary and viceversa + it allows for automation…well, silent repetition of a load and save operations. I know, I am lazy, that’s why I wished for automation. Find what it is all about in this post.

Enjoy downloading the latest firmware, datasheets etc, here.

Comment with your ideas, findings etc.

 14. Combining all latest developments in one and allowing for setup without the need to reprogram the flash

While working on the latest requests for US mode (12H display and Fahnrenheit instead of Celsius), I used EEPROM variables to shorten the code and allow for its implementation. Unfortunately, to change these modes, the clock needed to be reprogrammed.

After many optimizations, I have succeeded to implement a way to set these modes without the need for a programmer.

Resetting the clock with the SELECT key pressed will toggle between US and EU mode:

  • EU mode displays 24H and Celsius. Setting the clock and alarm shows 24H.
  • US mode displays 12H and Fahnrenheit. Setting the clock and alarm shows 12H with A or P indicator for AM/PM.

Resetting the clock while pressing the SET key will toggle between regular clock display and clock/temperature display (temperature is displayes for 1 second every 10 seconds).

Resetting the clock while pressing both SELECT and SET keys will enter Seconds Display Mode, where you can choose between the 9 ways to display rotating seconds.

Enjoy downloading the latest firmware, datasheets etc, here.

At this moment, the firmware has EXACTLY 8192 bytes, occupying all the flash memory available on the processor:

 

15. Latest optimizations and additions

Having lots of people testing the software is certainly an asset, as other may discover hidden bugs otherwise impossible to see.

It just happened to one clock that, when the power came down and up again repeatedly, the eeprom section of the clock just erased by itself.

A more in-depth look at the mega8515 datasheet states in fact eeprom corruption in special circumstances, when the voltage is slowly rising, on heavy filtered power sources, or, maybe, when power goes down and up again, leading to the same effect. The MCU executes erratic commands and the voltage is too low for proper eeprom operation, so anything can happen.

Since the code was 100% full, I had to look and identify which are the similar sections of code and write a  procedure to be called insted of repeating the same lines of code. This code was identified in the menu section of the main loop. It was optimized and I got some other ~300 bytes of flash that could be used to implement an eeprom corruption prevention system.

Basically, all constants in the eeprom were defined with separate default values and a crc was implemented. Every eeprom read was checking the crc and every time an eeprom value changed, like the alarm etc, the crc was recomputed and written into the eeprom. If the crc is not the expected one while reading any eeprom value, the whole bunch of eeprom variables are reinitialized with their default values.

The important part of the code:

EEMEM uint8_t ECFUnit=0;
uint8_t CFUnit;
EEMEM uint8_t ESwing=1;
uint8_t Swing;
EEMEM uint8_t EUSMode=0;
uint8_t USMode;
EEMEM uint8_t ESecMode=9;
EEMEM uint8_t EALMinutes=0;
EEMEM uint8_t EALHours=0;
EEMEM uint8_t EALSet=0;
//ECRC and DefCRC have to be recomputed manually after a modification to any of the following eeprom variables:
//ECFUnit, ESwing, EUSMode, ESecMode, EALMinutes, EALHours, EALSet
//ECRC=ECFUnit+ESwing+EUSMode+ESecMode+EALMinutes+EALHours+EALSet
EEMEM uint8_t ECRC=10;
uint8_t DefCRC=10;

 

An example of modifying the default values, like if we wish for and US Mode display by default (AM/PM display, Fahrenheit for temperature) would need the modification of the following variables:

EEMEM uint8_t EUSMode=1;
EEMEM uint8_t ECFUnit=1;

The CRC has to be recomputed and modified:

ECFUnit+ESwing+EUSMode+ESecMode+EALMinutes+EALHours+EALSet=1+1+1+9+0+0+0=12

So these two lines have also to be modified:

EEMEM uint8_t ECRC=12;
uint8_t DefCRC=12;

Enjoy downloading the latest firmware, datasheets etc, here.

 

16. Negative temperatures

This firmware does not use the floating point library, it would become huge. Thus it relies solely on integer operations, most of it unsigned.

Temperatures, however, are signed. Using several sensors and having one outside at winter should display negative temperatures, but at this point it doesn’t.

Since at the moment that I am writing these lines there are negative temperatures outside, I thought to address this final issue.

I have observed that, when the temperature was dropping under zero C, the clock was displaying temperatures counting down from 127.

This latest firmware takes negative temperatures into account and displays them correctly, both with Celsius and Fahnrenheit degrees.

Enjoy downloading the latest firmware, datasheets etc, here.

 

17. Timings

All the latest additions to the firmware made that some timings related to the duration some information was displayed became erroneous. This latest development addresses these errors and corrects them.

Enjoy downloading the latest firmware, datasheets etc, here.

  72 Responses to “EC1204B Led Rotating Clock: Schematic, upgrade, firmware”

  1. Great work!!. Really appreciated. What about to use an 89C450? you can program it via a serial port, it has 64k rom and can run up to 33MHz.

    • I appreciate the nice words, but, to answer your question, I think using 89C450 is not a good idea.

      Reasons:

      1: the price:
      89C450 is ~$10 on ebay.
      AT89S52 is less than $1.
      AT90S8515 is ~$2
      ATMega8515 is ~$2

      2: the development IDE. With the advent of Arduino, almost everybody uses Atmel. Some are using PIC, but I guess there are less PIC users.
      Atmel and Arduino have both very strong and easy development platforms. I don’t know Maxim.

      3: my main idea is to transform the kit into a learning kit with (almost) instant results.

      4: I guess that a processor at 4MHz would do the job nicely. I’ll try and post in a few days my findings. 33MHz is too much processing power for what it takes with this small project.
      One thing one must learn is to adapt processing power to the project needs. Less heat, less energy consumed, more trees on our lonely planet.

  2. Comrades, and nobody asked the purpose of connecting multiple sensors DS18B20? they can also “hang” on one line a lot, but need to be processed in the firmware.
    it would be very interesting example: Displays the number of the sensor and the temperature at this point, on the street, in the next room, etc …
    upsss – for the number of the sensor is not enough characters: (… although … 6 sensors can indicate:
    http://images.mysku.ru/uploads/images/02/19/97/2015/02/05/32389b.png
    for example, to “negative” reserve g
    and sensors: 1st – a, 2nd – f, 3 – e, 4 – d etc.
    as well lacks precision temperature display. Desirable: ##. # C

    • Nice idea. Will give it a try.

      However:

      – The longer the cabled with DS18B20 attached on it, the higher the noise on the cable.
      – Reading more than one DS18B20 is a pain. It doesn’t work all the time. I have tried and I am tired of reading 85C and 127C.
      – Having the temperature sensor so close to the circuit gives wrong reading because the device itself heats just enough to shift the local temperature by 2-3 degrees higher.
      – linking a second DS1621 with a shielded 3-wire cable of 1-2 meters, changing the I2C address of this second device and modifying a little bit the UI routine to reflect both temperatures, is doable:

      click:
      0001
      delay 1 second
      29oC
      second click
      0002
      delay 1 second
      02oC

      Displaying tenths of degrees is also doable, but functions with parameters transmitted through pointers must be used, in order to avoid the usage of the floating point library, which is huge (and slow) and won’t fit in the flash memory. I personally don’t see the point of doing this. We’re not casting iron. And even if we did…
      What was not addressed in the whole software was the display of negative values, but this was on purpose. Others should try to solve this (easy) matter. At least that is the point of the article, to make this clock a learning tool for beginners.

      • to avoid errors with a DS18B20 IMHO need to apply “pull-up” resistor (4.3k)

        • Yes for the pullup. The library also should be adapted for parasitic supply.
          I am expecting a shipment of DS18B20 these days and I will rewrite the library to include more than one device.

          I will try also to link multiple DS1621 to a longer cable to see how it performs with I2C.

          I will post my findings as soon as I’ll finish dealing with them.

    • Sergey, it is done.
      It took some time because I was waiting for my DS18B20 stack to arrive.

      What is NOT tested:
      – negative temperatures (with a “-” in front of the temperature). I tried putting an ice cube onto one sensor and it got down to…1C after 5 minutes. It didn’t want to get to a lower temperature. The code is however opened to modifications. If you live in Siberia, it wouldn’t be too hard 🙂
      – parasitic supply. I’ll leave this for whoever wishes to better the DS18B20 library. It’s not hard. It’s just that I don’t have time.
      What is NOT implemented: CRC8. First, it was a software burden, bearing in mind the flash is almost full. Second, you don’t need CRC8 is your sensors are connected with shielded microphone cables of up to 3 meters.

      Take a look at the article for more info.

  3. Hi! Thanks for sharing the code. Very useful!

    May I point you to an error in your code for the ATMega8515? You are incorrectly defining the chip frequency as 80MHz with this line: #define F_CPU 80000000. Instead, set it to #define F_CPU 8000000L to specify 8MHz. As a result, you will find that all your delays have to be changed in many places of the code as the delay.h was calculating them incorrectly until now.

    Thanks again for sharing your work!

    • Thanks for pointing this out.
      I have made the necessary changes to the code and uploaded again the last archive with the modified delays.

    • Thanks again, Philipp, for showing that, in my enthousiasm, the post became unreadable because of posting all that code while doubling it with software zips.
      I should say that defining correctly the CPU frequency solved the issues I had with the strict timing of DS18B20.
      Your feedback for the project was so important that I find necessary to mention you here.

  4. It is really appreciate to be having good knowledgeable person with diy ds 1302 clock

    there is a huge problem in blue tooth module of this clock is not connecting with most of blue tooth of android mobiles (they say this is because of difference of blue tooth version, ie clock blue tooth module is in version 1.2 and most of now a days mobile phones has blue tooth version 4.0 or higher)
    Some other say problem with old type android app is not support to modern smart phones

    Please do me a favour please update clock /Its blue tooth module or to wŕite a andròid app to connecting android mobile and clock via blue tooth
    so ineed a help from you any suitable modification to operate this clock by android mobile via blue tooth , please do needful
    sorry for my poor english

    • I understand your request, but in my opinion there is no need to develop on bluetooth programming of the clock.
      The issue with bt connection is hardware related, so I am noing to insist on this.

      • Bluetooth is a necessary feature because when you hang the clock on top of your wall and there is no easy access to change time, you can easily change the clock with your phone.

  5. Please post the “Fuse” setting, used for programming, for both the AT90S8515 and ATMega8515 processors.

    Thank You
    Jack

    PS. Thanks for making your source code available

    • Hi Jack,

      I am sorry I don’t have the devices at hand to post a print screen here. However the MCUs are quite easy to set up and understand:

      AT90S8515 has only one fuse, SPIEN. Keep it on because you are programming in-system.

      ATmega8515 fuses should be as follows:

      AT90S8515 compatibility: S8515C should be off
      Watchdog is not used: WDTON should be off
      SPIEN should be on
      EESAVE should be off
      BootSZ should be selected to be as low as possible, we don’t use the bootloader
      BOOTRST should be off
      BODLEVEL should be off, we don’t use it
      BODEN should be off

      CKOPT, SUT1, SUT0, CKSEL3, CKSEL2, CKSEL1 and CKSEL0 should be selected to match the quartz oscillator you are using. Start-up time is not important.

  6. I have your firmware loaded and the clock is up and running.
    Thanks for your help with the fuse settings.

    I have a question, How do you sync the seconds to a time reference.
    (Why not, Zero the seconds, at the time the clock is set and you hear the beep.)

    The best that, I have been able to do, is a hit and miss, when trying to sync the clock.
    By removing the battery and then applying the 5 volt power, so that, the clock starts running,
    at the time, the reference, is at zero seconds.

    Am I missing something, as to how one sets the clocks time?

    Thanks Again

    Jack

    • Hello again Jack,

      honestly, I’ve never thought to sync the seconds, neither to even show them.
      This is because the DS1621 is not a circuit meant to keep a very accurate track of time.
      Any circuit which uses a 32kHz oscillator will have a significant error eventually, so why bother setting the seconds?

      A much better version of the DS1621 would be the DS3231 which is “a low-cost, extremely accurate I2C real-time clock (RTC) with an integrated temperature-compensated crystal oscillator”.

      Just for the exercise, setting the seconds to zero while setting the clock is an easy task and requires just one more line of code.
      If you look in rtc.h, the structure of dateTime is:

      typedef struct
      {
      uint8_t second;
      uint8_t minute;
      uint8_t hour;
      uint8_t date;
      uint8_t month;
      uint8_t day;
      uint8_t year;
      } dateTime;

      In fact, this structure already provides a place for seconds.
      To zero the seconds, it is enough to add one line of code in clock.c, as follows:

      case SETHOURS:
      //write the time/date/year into the DS1302
      pulsing=0;
      dt1.second=0;
      set_date_time(dt1);
      t1=0;
      beep();
      _delay_ms(200);
      break;

      Recompile the code, burn and voila! Every time you set the time, the seconds become zero.

  7. I was recently looking at building an Arduino based RDF, although that may change, and was looking at a display for it. My previous RDF unit had a LED “compass” type display but it is a lot of work to make, Then I noticed these LED clocks, I have done some 8051 programming in the past but was wondering how difficult it may be to implement a digital compass , only the RX at TTL level would be required.. The value sent by the RDF unit and displayed on the clock.

    • Hi Paul,

      no need to use the old 8051, an atmega8515 could do the trick and it would also be easier to program with a commercially available programmer.

      You may also integrate the RDF on the same board if it would fit.

      If you wish to use the clock as a display for RDF data, I’d need an RDF module to bring all this alive.

  8. I have programmers for 8051s so that is no obstacle. I’m not sure if I willbuild the RDF unit with an Arduino or RPi, the Arduino uses a lot less power and I may even be able to use a TI MPS processor to reduce the power. I even have a stock of 11.052MHz crystals so I will probably reuse the original micro in the clock and just use it as the display. The data oassed to the clock from the RDF would be a heading and a quality rating. I envisage the 4 digit display will give the heading while the LED circle will give an indication. As the quality rating goes down more LEDs adjacent to the prime LED will light. I would send the quality in a range of 1 to 4, and have 1 to 4 LEDs light. For a 1 there would be 3LEDs , for a 4 there would be 9. Your work will help speed things along greatly.

    • Paul,

      I am glad part of my work serves your project. As all display programming is handled by the timer itself, all you have to do is to rewrite the main loop along with the serial communication. Of course, the display programming has also to be trimmed of everything related to the clock itself, but this is the easiest part to do. If you have the time, please drop a video link here with the real stuff in action.

      Have fun chasing kangaroos 🙂

  9. I just finished building this clock and I get an ero2 on the display screen. Anybody have a clue what I did wrong

    • Can you add a picture with the error?
      In fact, can you add pictures with both sides, as clear as possible you can take them pictures?

  10. Hello, Can you send the original firmware of EC1204B (using AT89S52), please?
    my e-mail : devin9a@gmail.com
    Thank You

  11. DS18B20 clock circuit is not working properly:
    when Power on display error 2 : Eo-2

    do you have solution for this problem with out DS18B20?
    I was waiting for my DS18B20 to arrive.

    • Hi fadi,

      does this happen with the original firmware of the clock? This error is not implemented in my firmware…

      • yes , with the original firmware

        • With the original firmware, there cannot be a software issue. it is most certainly a hardware one.
          Check the power on the DS18B20 and also check if there are signals at powerup on the communication pin.
          If everything is OK, most definitely the sensor itself is burned out.

          • yes, the sensor is burned out.
            do you have way to power on without sensor change or original firmware change ?

          • Actually no. Probably the error message is in the sensor library. This IS a programming flaw, since you have bought a clock and not a thermometer. It should show your sensor is burned and then it should show the time 🙁

  12. Greetings, i have reviewed your blog, I’m very interested in modifying the functions.
    I’m very good at hardware but not very good with software. i was hoping if you can change the firmware for farenheit temp (i’m from USA) and to stop the scrolling. id like to have where i can manually switch between functions. i dont care for multiple temp sensors, one is ok with me. I have paypal, credit card and western union. please let me know the cost for software rewrite. recap:
    1 – stop the scrolling between time / date / temp – main display the time
    2 – would like to press a button to switch between time / date / temp then few seconds go back to main display – the time.
    3 – only one temp sensor is ok with me.
    4 – like to keep the seconds choices.
    Again, i’m willing to pay for you to rewrite the software. have paypal, credit card and western union.
    I also have access to unlimited hardware. i already use the STK500 and have the ATmega8515 ready to go.

    Please let me know either way. Thank you.

    • Hello Richard,

      the temperature being displayed in Fahnrenheit is already done on my other clock project (the big digit display one), at the request of another guy living in Miami. So, easy to do it on this firmware version too since the other is in fact a modified version of this one.

      However, my firmware version does not scroll at all. You might have misidentified it with the second version of the original firmware, the one showing date/time, hours/seconds and temperature in a loop and yes, this version scrolls.

      Also, my version uses one button to switch between time and date and the second button to switch between time and temperature. If you really wish to cycle between time/date/temp, it can be done with a few modifications.

      Thirdly, the temperature sensor library detects automatically how many sensors are installed in parallel on the clock. So if you use only one, it will show only that one. If you did not know, Romania is one of the countries with the most important temperature difference between summer (40-47C in August) and winter (goes down to even -25 – -35C in February) so, having at least two sensors, one for indoors and one for outdoors, becomes somehow indispensable. The fact of using one or more sensors does not have an impact on the firmware size nor on the ram the software uses at runtime, since there is an array defined in the software that uses the same amount of ram no matter if you use 1 or 5 sensors. Soldering an additional sensor and rebooting the clock will show the temperature the additional sensor is measuring.

      I did not understand what you wish to say about the seconds. My version doesn’t show the seconds now. Would you care to develop the subject please?

      Please email me after reading this, a private conversation does not really belong on the website. Thanks!

  13. Hello,
    I’n not a programmer. Can someone programming 2 pcs. EC1204, that the rotating clock shows alternate only time and temperature? Contact me please, I am interested.
    Thank’s and best regards

    • Guten tag Peter,

      what you wish can be done, but this requires changing the micro controller AND a modification of the board according to my article. Are you able to do at least the hardware modification? More by email.

      Cheers

  14. Hello Cristian,
    The changing is made. I’m waiting for the programming. I thank you very much for your courtesy and professional support. I’ll tell you, as soon as it works.

    Best regards!

    • Hello Jarek,

      I have heard of this EOR2 issue but never encountered it. My house is already filled with these clocks and I am already working on a bigger version of it, using 4 inch display elements.
      While looking at the error you have found, with the reset pulse being too short, it happened more than one to have my clocks unresponsive after burning the firmware, but a second burn was OK.
      I wonder what is happening…is it the Atmel MCUs themselves or the writing procedure…who knows.

      As I said in a previous post, I am not really a fan of github, for too many reasons to write here.

      Thanks for the nice words.

  15. Hello, Christian. Thank you for your contribution. I bought a couple of these boards and have just modified one for ATmega162 AVR and fired it up for the first time tonight. Using your -1 code, it is multiplexing the 7-segment display (I haven’t installed the ring LEDs yet, I didn’t like the standard colors and have ordered others), but the display comes up in a random mode and won’t listen to the buttons. Very encouraging that the display multiplexing works, but I have some work to do to track down what incompatibilities I’ve run into. I’m using Atmel Studio 6.2 for now.

    Eric in Richmond, CA

  16. Hi, Christian. Thanks for your contribution. I’ve got a couple of these boards and I’m working on an adaptation using the ATmega162, starting with your code version -1. Display mux is working fine, but I still haven’t gotten the menus to work right.
    Regards,
    Eric in Richmond, CA

  17. Hi Cristian,
    thank you very much for your work and the wide documentation!
    I’d also like to work with EC1204B clock. I think about using the ATmega8515 µC instead of AT90S8515.
    I think there are a couple of folks interessted using EC1204B for there own projects. Have you thought about upload your code to github ?
    Best regards
    Bernd

    • Hello Bernd,
      yes, the idea to upload the code to Github has crossed my mind.
      I would like to keep it here though, for statistics purposes. It really matters to see how many people actually use it. No matter the place where the code resides, it is and will stay free.

      • Ok, I understand.
        My plan is to convert your code to use in AStudio 7.0 and with ATmega8515.
        (and some other changes for my privat project)
        Do you mind if I upload some day?
        PS: I can’t find any email, pls let me know!

    • –> ‘Solder two resistors between 4.7Ko and 10Ko as shown, in order to pull up the SET and SELECT signals.’

      Hi Christian,
      another question pls: Is it really necessary to solder hardware pull-up’s?
      Cause you already pullup PD2 and PD3 via software in your Init().
      Kind regards,
      Bernd

      • Yes. According to the datasheet, the internal pull-ups are somewhere between 50-60kohms.
        Without the resistors, I have experienced an erratic behavior at times, like if the buttons were pushed by an invisible hand 🙂
        It may well be because of the fact that I keep one of the clocks next to my computer, soldering gun and lots of other stuff emitting electromagnetic radiation.
        I did not really go into details, just soldered the resistors and voilà!

  18. Dear Christian,
    thanks for your email adress.
    I have currently finished to convert your code to the ATmega8515.
    All works fine and I Iike your idea to switch between the setting modes
    by analysing the time the user pressed the MODE button.
    I like to add some features like:
    – the nerf gun detector to stop the alarm clock
    (see example: https://www.youtube.com/watch?v=-2WAyDod0W4)

    – add the possibility to show the number of days to a particular date
    (to show my nephew the number of days until he is 18 🙂

    I will send you a version if all togehter works fine!

    Best regards
    Bernd

    • Hello Bernd,

      Am I missing something? There is code for the ATmega8515, more evolved than the one for AT90S8515 which I have discontinued after a certain step.
      The discontinuation was because the 90S was behaving more erratic at times and it was slower, even with the same crystal. Weird but true.

      You might wish to consider the ATMega162 instead, which has 16k of eeprom, if you wish to enhance the code. The latest version almost did not fit in the mcu, so I had to rewrite small bits of code to make place. AND the ATMega162 is the only mcu pin to pin compatible.

      Good luck coding!
      Cristian

      • Oh sorry that’s my mistake!
        I download the code from “5. Final considerations and Downloads” and start working O:-)

        • My bad. There’s always something after “final”. A newer idea, a code enhancement etc 🙂
          The arrow thing never crossed my mind though 🙂 However, the arrow thingy is implemented in hardware lol 🙂

        • Hello again Bernd,

          I have just reread the lines I was writing over the almost 3 years I have developed and documented the code.
          You might find interesting to follow the explanations I have written to every step of the development phases after step 5, since all of them were much more challenging than the original development and they can contribute to a deeper understanding of development practices.
          Since you seem to be the first of my readers interested more in the development phase than in the final product, you might find useful techniques to:
          – lower the code size while maintaining its features;
          – tricks to use to enhance an already working code.

          I would like very much to hear of you personal experiences, so that others can better themselves.

          Cheers,

          Cristian

        • Hello again Bernd,

          I have just reread the lines I was writing over the almost 3 years I have developed and documented the code.
          You might find interesting to follow the explanations I have written to every step of the development phases after step 5, since all of them were much more challenging than the original development and they can contribute to a deeper understanding of development practices.
          Since you seem to be the first of my readers interested more in the development phase than in the final product, you might find useful techniques to:
          – lower the code size while maintaining its features;
          – tricks to use to enhance an already working code.

          I would like very much to hear of you personal experiences, so that others can better themselves.

          Cheers,

          Cristian

  19. Hello Cristian,

    Recently i have bought a EC1204B clock. now i want to display only time without temperature and date. please help me with code.

    tnx.

  20. Hello Cristian,

    I have bought this clock. I want to display only time without temperature & date.
    please help me.
    tnx

    • Shafiq,

      the code I have written is self explainable. My intention is to make people learn, not to write software on demand.

  21. hola que tal como se encuentra sabes estoy tratando de construir este reloj de hace tiempo y aun no lo logro y por favor no se si me podria compartir su codigo hex pata at 89s52 le agradeceria bastante , no puedo encontrar en internet el codigo ayudeme por favor gracias y saludos
    mi correo es alfreperez0887@gmail.com

    • Saludos,

      Lamento no trabajo con at89S52, así que no tengo ningún código hexadecimal para este mcu.

  22. patch for EC1204B-5.zip to remove ghost digits and annoying decimal point flickering:

    — “Kello_original/Software AT90S8515/clock.c” 2016-04-11 19:54:26.000000000 +0300
    +++ “Kello/Software AT90S8515/clock.c” 2018-03-25 22:53:37.330341872 +0300
    @@ -7,7 +7,6 @@
    #include
    #include
    #include
    -#define F_CPU 12000000L
    #include

    @@ -180,65 +179,56 @@
    //comment from the first sign in order to use first version of function for seconds #9

    if (pulsing) showdigit=(refresh<512); else showdigit=1;
    + PORTC=0xFF;
    + PORTA=0xFF;
    + PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    switch (digit_addressed)
    {
    case 0:
    PV=PORTD;PV &= ~_BV(4);PV |= _BV(5);PV |= _BV(6);PV |= _BV(7);PORTD=PV;
    if (showdigit) PORTA = ~(d[0]); else PORTA= ~(SEG_NULL);
    – PORTC=0xFF;
    break;
    case 1:
    PV=PORTD;PV |= _BV(4);PV &= ~_BV(5);PV |= _BV(6);PV |= _BV(7);PORTD=PV;
    if (showdigit) PORTA = ~(d[1]); else PORTA= ~(SEG_NULL);
    – PORTC=0xFF;
    break;
    case 2:
    PV=PORTD;PV |= _BV(4);PV |= _BV(5);PV &= ~_BV(6);PV |= _BV(7);PORTD=PV;
    if (showdigit) PORTA = ~(d[2]); else PORTA= ~(SEG_NULL);
    – PORTC=0xFF;
    break;
    case 3:
    PV=PORTD;PV |= _BV(4);PV |= _BV(5);PV |= _BV(6);PV &= ~_BV(7);PORTD=PV;
    if (showdigit) PORTA = ~(d[3]); else PORTA= ~(SEG_NULL);
    – PORTC=0xFF;
    break;
    case 4:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (0<<DDC0) | (1<<DDC1) | (1<<DDC2) | (1<<DDC3) | (1<<DDC4) | (1<<DDC5) | (1<<DDC6) | (1<<DDC7);
    PORTA = ~(d[10]);
    break;
    case 5:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (1<<DDC0) | (0<<DDC1) | (1<<DDC2) | (1<<DDC3) | (1<<DDC4) | (1<<DDC5) | (1<<DDC6) | (1<<DDC7);
    PORTA = ~(d[11]);
    break;
    case 6:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (1<<DDC0) | (1<<DDC1) | (0<<DDC2) | (1<<DDC3) | (1<<DDC4) | (1<<DDC5) | (1<<DDC6) | (1<<DDC7);
    PORTA = ~(d[12]);
    break;
    case 7:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (1<<DDC0) | (1<<DDC1) | (1<<DDC2) | (0<<DDC3) | (1<<DDC4) | (1<<DDC5) | (1<<DDC6) | (1<<DDC7);
    PORTA = ~(d[13]);
    break;
    case 8:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (1<<DDC0) | (1<<DDC1) | (1<<DDC2) | (1<<DDC3) | (0<<DDC4) | (1<<DDC5) | (1<<DDC6) | (1<<DDC7);
    PORTA = ~(d[14]);
    break;
    case 9:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (1<<DDC0) | (1<<DDC1) | (1<<DDC2) | (1<<DDC3) | (1<<DDC4) | (0<<DDC5) | (1<<DDC6) | (1<<DDC7);
    PORTA = ~(d[15]);
    break;
    case 10:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (1<<DDC0) | (1<<DDC1) | (1<<DDC2) | (1<<DDC3) | (1<<DDC4) | (1<<DDC5) | (0<<DDC6) | (1<<DDC7);
    PORTA = ~(d[16]);
    break;
    case 11:
    – PORTD= (1<<DDD4) | (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
    PORTC= (1<<DDC0) | (1<<DDC1) | (1<<DDC2) | (1<<DDC3) | (1<<DDC4) | (1<<DDC5) | (1<<DDC6) | (0<9) d[0]= seg[d[4]]; else d[0]= SEG_NULL;
    – d[1]= seg[d[5]];
    //add dot point on odd seconds
    – if (odd(seconds)) d[1]+=SEG_dot;
    + if (odd(seconds)) d[1]=seg[d[5]]+SEG_dot;
    + else d[1]= seg[d[5]];
    d[2]= seg[d[6]];
    d[3]= seg[d[7]];
    }

  23. If you would like to skip the soldered pullup’s , and use the internal pullups.

    void SetPortACD(uint8_t i, uint8_t j)
    {
    //PORTD= 0xF0; //set nil to digits (7-Seg select lines on PD4..PD7) //Would disable pullups on PD2 & PD3
    PORTD |= 0xF0; //set nil to digits (7-Seg select lines on PD4..PD7) // Or the high-bits high , and preserve PD2 & PD3 Pullup’s (Key inputs)
    ..
    ..

    • Same reply:

      You could do this. It is theoretically valid. I did this. I had erratic key press events. I decided to solder external resistors instead.
      Practice beats theory.

  24. Ohh and enable pullups

    PORTD=0xff;
    DDRD =_BV(0) | _BV(1) |_BV(4)|_BV(5)|_BV(6)|_BV(7);
    /*
    If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is
    activated. To switch the pull-up resistor off, PORTxn has to be written logic zero or the pin has to
    be configured as an output pin. The port pins are tri-stated when a reset condition becomes
    active, even if no clocks are running.
    */
    PORTD = ((1 << KEYSELECT) | (1 << KEYSET )); // Enable pullup on Key inputs

    • You could do this. It is theoretically valid. I did this. I had erratic key press events. I decided to solder external resistors instead.
      Practice beats theory.

      • Hi Cristian,

        thats why you reset the first 4 bits of PORTD in line 205 !

        Try to edit the line to this:
        PORTD = (0xFC & (~(1< set PORTD but not TX, RX, PLUS, MODE

        ..so practice and theory are in love together 😉

  25. Hi Cristian,
    I like clocks….
    Got one of these recently from MPJA. Got a couple NOS AT90S8515 and made the PCB
    mods. I am programming with a Needham EMP30 parallel programmer. Loaded the FLASH & EEPROM
    to the device but no operation. Does mot light any LEDs or draw any power <10mA for sure.

    Got any suggestions?

    • When you change the original MCU with an AT90S8515, the RST pin operates differently than the original MCU. If you did not perform the hardware modification in the article, your CPU will stay reset indefinitely, thus it will not operate.
      Please perform the hardware modifications explained in the article and it should start working.

  26. Cristian, I wanted to repeat your project, but it doesn’t work))) I used Atmega8515-16pu. On the indicator shows tE:0 – C801, at the same time, the LEDs often flash, after the first start after 5 seconds it beeps.
    The board was modified, the quartz was removed and left, the firmware was installed last, used the AVRDUDEProg program.
    If you return the AT89s52, everything works, but does not want to with ATMEGA8515

    • Hi,

      the project works, as you could see in my videos.
      Take a look at the ATmega8515 fuses, maybe the issue is there.
      I remember someone else had this problem and it was fixed with the correct fuses.

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

Prove you are human: *

Switch to mobile version
Advertisment ad adsense adlogger