Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
This project uses a Particle Photon and a JY-LKM1638 display which has eight 7-Seg digits eight dual-color LEDs and eight push buttons.
The Photon connects to the internet and gets the time and displays it on the JY-LKM1638. There's a 1 second timer running which increments the seconds every time it expires and sets a flag. In the main loop() whenever this flag is set the time is incremented by a second and updated on the display. The code also checks for any deviations in the time due to inaccuracy of oscillator and it cross-checks the internally managed time with the time provided by the Particle server and if it's off by more than 2 seconds then the internal time is once again synced with the online time.
Since this project doesn't have an RTC therefor a WiFi connection is a must for it to get the time.
This project exposes two functions which can be called from console.particle.io or from the Particle application on Android or iPhone.
1. Watch Mode - Helps to choose between 12 Hr or 24 Hr mode.
2. Display Intensity - Helps to turn off the display or choose between eight intensity levels.
Any changes made using these functions are stored in the emulated EEPROM of Photon and hence the values are retained even if there's on power to the board.
This project also exposes seven variables, which can be used for debugging and testing.
#define ACTIVE 0x01 //Display Active
#define INTENSITY 0x00 //Intensity of the display
#define OFF_TM1638 0x00 //Off Led
#define GREEN_TM1638 0x01 //Green Led
#define RED_TM1638 0x02 //Red Led
#define YELLOW_TM1638 0x03 //Yellow Led
#define DATA_WRITE_INCR_ADDR 0x40 //Command to switch TM1638 for automatic increment address mode
#define DATA_WRITE_FIX_ADDR 0x44 //Command to switch TM1638 for fix address mode
#define DATA_READ_KEY_SCAN_MODE 0x42 //Command for read key code from TM1638
#define ADDRSET 0xC0 //Command to set address 0x00
#define DISP_ON 0x8F //Command to Display ON and set (max) brightness
#define DISP_OFF 0x80 //Command to Display OFF
#define AM_PM_FORMAT 0x00
#define HR_FORMAT 0x01
#define HYPHEN 0x40
#define EEPROM_ADDR 0x00
#define MY_SCL D0
#define MY_SDA D1
const uint8_t Num[]=
{ //Code table of numbers
0x3F, //0
0x06, //1
0x5B, //2
0x4F, //3
0x66, //4
0x6D, //5
0x7D, //6
0x07, //7
0x7F, //8
0x6F, //9
0x77, //A
0x7C, //B
0x39, //C
0x5E, //D
0x79, //E
0x71 //F
};
const uint8_t ERROR_DATA[] =
{
0x79, // E
0x50, // r
0x50, // r
0x5C, // o
0x50, // r
0,
0,
0
};
uint8_t MY_SSEL = D2; //for some reason MY_SSEL has to be an int
uint8_t time_hour, time_min, time_sec, temp_number = 0, counter = 0;
bool flag = FALSE;
struct values //these must be of int type if we want to use Particle.variable function
{
int intensity;
int timeFormat;
}backupVariables;
void SPI_SendData(uint8_t dat);
void TM1638_Init(void);
void TM1638_DisplayClean(void);
void TM1638_SetupDisplay(uint8_t active, uint8_t intensity);
void TM1638_SendCommand(uint8_t cmd);
void TM1638_SendData(uint8_t address, uint8_t dat);
void TM1638_ShowDig(uint8_t position, uint8_t data, uint8_t Dot);
void TM1638_ClearDig(uint8_t position, uint8_t dot);
void TM1638_ShowError(void);
void TM1638_ShowDecNumber(unsigned long number, uint8_t dots, uint8_t startingPos);
void TM1638_ShowSignedDecNumber(signed long number, uint8_t dots);
void TM1638_ShowHexNumber(unsigned long number, uint8_t dots);
void TM1638_ShowLed(uint8_t number, uint8_t color);
void TM1638_ShowLeds(uint8_t color);
void TM1638_LedBinaryCounter(uint8_t number, uint8_t color);
void Get_Time(uint8_t time_format);
void Set_TimeZone_India(void);
void Set_TimeZone_Global(uint8_t value);
void Show_Time(void);
void Update_Time(void);
void do_every_second(void);
int watchMode(String cmd);
int displayCtrl(String cmd);
uint8_t TM1638_GetKey(void);
Timer timer(1000, do_every_second);
void setup()
{
Particle.variable("Hour", time_hour);
Particle.variable("Minute", time_min);
Particle.variable("Second", time_sec);
Particle.variable("Intensity", backupVariables.intensity);
Particle.variable("Time Format", backupVariables.timeFormat);
Particle.variable("Time Mismatch", temp_number);
Particle.function("Watch Mode", watchMode);
Particle.function("Display Intensity", displayCtrl);
timer.start();
pinMode(MY_SCL, OUTPUT);
pinMode(MY_SDA, OUTPUT);
pinMode(MY_SSEL, OUTPUT);
EEPROM.get(EEPROM_ADDR, backupVariables);
if(backupVariables.timeFormat > 1)
{
backupVariables.intensity = 0;
backupVariables.timeFormat = 0;
EEPROM.put(EEPROM_ADDR, backupVariables);
}
TM1638_Init();
TM1638_SetupDisplay(ACTIVE, backupVariables.intensity);
Set_TimeZone_India();
Get_Time(backupVariables.timeFormat);
Show_Time();
}
void loop()
{
if(flag == TRUE)
{
flag = FALSE;
if(WiFi.ready() == TRUE)
{
Update_Time();
}
}
}
void SPI_SendData(uint8_t dat)
{
uint8_t index;
for(index = 0; index < 8; index++)
{
digitalWrite(SDA, (dat & 0x01));
dat >>= 1;
delayMicroseconds(1);
pinResetFast(SCL);
delayMicroseconds(1);
pinSetFast(SCL);
}
pinResetFast(SDA);
}
void TM1638_Init(void)
{
TM1638_DisplayClean(); //Clean display
TM1638_SendCommand(DISP_OFF); //Display off
TM1638_SendCommand(DATA_WRITE_FIX_ADDR); //Set address mode
TM1638_SendCommand(ADDRSET);
TM1638_SetupDisplay(ACTIVE, INTENSITY);
}
void TM1638_DisplayClean(void)
{
uint8_t index;
TM1638_SendCommand(DATA_WRITE_INCR_ADDR); //Set address mode
for (index = 0; index < 16; index++)
{
TM1638_SendData(index, 0x00);
}
}
void TM1638_SetupDisplay(uint8_t active, uint8_t intensity)
{
TM1638_SendCommand (0x80 | (active ? 8 : 0) | intensity);
}
void TM1638_SendCommand(uint8_t cmd)
{
pinResetFast(MY_SSEL); //Set STROBE = "0"
SPI_SendData(cmd);
pinSetFast(MY_SSEL); //Set STROBE = "1"
}
void TM1638_SendData(uint8_t address, uint8_t dat)
{
TM1638_SendCommand(DATA_WRITE_FIX_ADDR);
pinResetFast(MY_SSEL); //Set STROBE = "0"
SPI_SendData(0xC0 | address); //Set first address
SPI_SendData(dat); //Send data
pinSetFast(MY_SSEL); //Set STROBE = "1"
}
void TM1638_ShowDig(uint8_t position, uint8_t data, uint8_t Dot)
{
TM1638_SendData(position << 1, Num [data] | (Dot ? 0x80 : 0));
}
void TM1638_ClearDig(uint8_t position, uint8_t dot)
{
TM1638_SendData(position << 1, 0x00 | (dot ? 0x80 : 0));
}
void TM1638_ShowError(void)
{
uint8_t index;
for(index = 0; index < 8; index++)
{
TM1638_SendData(index << 1, ERROR_DATA[index]);
}
}
void TM1638_ShowDecNumber(unsigned long number, uint8_t dots, uint8_t startingPos)
{
uint8_t index;
if(number > 99999999)
{
TM1638_ShowError();
}
else
{
for(index = 0; index < 8 - startingPos; index++)
{
TM1638_ShowDig(7 - index, number % 10, (dots & (1 << index)) != 0);
number /= 10;
}
}
}
void TM1638_ShowSignedDecNumber(signed long number, uint8_t dots)
{
if(number >= 0)
{
TM1638_ShowDecNumber(number, dots, 0);
}
else
{
if(-number > 9999999)
{
TM1638_ShowError();
}
else
{
TM1638_ShowDecNumber(-number, dots, 1);
TM1638_SendData(0, HYPHEN);
}
}
}
void TM1638_ShowHexNumber(unsigned long number, uint8_t dots)
{
int index;
for (index = 0; index < 8; index++)
{
TM1638_ShowDig(7 - index, number & 0xF, (dots & (1 << index)) != 0);
number >>= 4;
}
}
void TM1638_ShowLed(uint8_t number, uint8_t color)
{
if(number)
{
TM1638_SendData((number << 1) - 1, color);
}
}
void TM1638_ShowLeds(uint8_t color)
{
uint8_t index;
for(index = 1; index < 9; index++)
{
TM1638_SendData((index << 1)-1, color);
}
}
void TM1638_LedBinaryCounter(uint8_t number, uint8_t color)
{
if(number & 1)
{
TM1638_ShowLed(8, color);
}
else
{
TM1638_ShowLed(8, OFF_TM1638);
}
if(number & 2)
{
TM1638_ShowLed(7, color);
}
else
{
TM1638_ShowLed(7, OFF_TM1638);
}
if(number & 4)
{
TM1638_ShowLed(6, color);
}
else
{
TM1638_ShowLed(6, OFF_TM1638);
}
if(number & 8)
{
TM1638_ShowLed(5, color);
}
else
{
TM1638_ShowLed(5, OFF_TM1638);
}
if(number & 16)
{
TM1638_ShowLed(4, color);
}
else
{
TM1638_ShowLed(4, OFF_TM1638);
}
if(number & 32)
{
TM1638_ShowLed(3, color);
}
else
{
TM1638_ShowLed(3, OFF_TM1638);
}
if(number & 64)
{
TM1638_ShowLed(2, color);
}
else
{
TM1638_ShowLed(2, OFF_TM1638);
}
if(number & 128)
{
TM1638_ShowLed(1, color);
}
else
{
TM1638_ShowLed(1, OFF_TM1638);
}
}
void Get_Time(uint8_t time_format)
{
if(time_format)
{
time_hour = Time.hour();
}
else
{
time_hour = Time.hourFormat12();
}
time_min = Time.minute();
time_sec = Time.second();
}
void Set_TimeZone_India(void)
{
Time.zone(5.5); //India is +5.5Hrs from the UTC
}
void Set_TimeZone_Global(uint8_t value)
{
Time.zone(value);
}
void Show_Time(void)
{
//Print Time
TM1638_ShowDig(0, (time_hour / 10), 0);
TM1638_ShowDig(1, (time_hour % 10), 0);
TM1638_ShowDig(3, (time_min / 10), 0);
TM1638_ShowDig(4, (time_min % 10), 0);
TM1638_ShowDig(6, (time_sec / 10), 0);
TM1638_ShowDig(7, (time_sec % 10), 0);
//Print hyphens (-)
TM1638_SendData(0x04, HYPHEN);
TM1638_SendData(0x0A, HYPHEN);
}
void Update_Time(void)
{
bool update_min = FALSE;
bool update_hour = FALSE;
uint8_t temp_time_sec = 0;
if(time_sec > 59)
{
time_sec = 0;
time_min++;
update_min = TRUE;
}
if(time_min == 60)
{
time_min = 0;
time_hour++;
update_hour = TRUE;
}
if(backupVariables.timeFormat == HR_FORMAT)
{
if(time_hour == 24)
{
time_hour = 0;
}
}
else
{
if(time_hour == 13)
{
time_hour = 1;
}
}
TM1638_ShowDig(6, (time_sec / 10), 0);
TM1638_ShowDig(7, (time_sec % 10), 0);
if(update_min == TRUE)
{
update_min = FALSE;
TM1638_ShowDig(3, (time_min / 10), 0);
TM1638_ShowDig(4, (time_min % 10), 0);
}
if(update_hour == TRUE)
{
update_hour = FALSE;
TM1638_ShowDig(0, (time_hour / 10), 0);
TM1638_ShowDig(1, (time_hour % 10), 0);
}
if(time_sec == 30)
{
temp_time_sec = Time.second();
if((temp_time_sec > 32) || (temp_time_sec < 28))
{
TM1638_LedBinaryCounter(++temp_number, GREEN_TM1638);
Get_Time(backupVariables.timeFormat);
Show_Time();
}
}
}
void do_every_second(void)
{
flag = TRUE;
time_sec++;
}
int watchMode(String cmd)
{
if((cmd == "12H") || (cmd == "12h"))
{
backupVariables.timeFormat = AM_PM_FORMAT;
EEPROM.put(EEPROM_ADDR, backupVariables);
Get_Time(AM_PM_FORMAT);
Show_Time();
return 1;
}
else if((cmd == "24H") || (cmd == "24h"))
{
backupVariables.timeFormat = HR_FORMAT;
EEPROM.put(EEPROM_ADDR, backupVariables);
Get_Time(HR_FORMAT);
Show_Time();
return 0;
}
else
{
return -1;
}
}
int displayCtrl(String cmd)
{
if(cmd == "off")
{
TM1638_SetupDisplay(0, 0);
return 9;
}
else if(cmd == "0")
{
TM1638_SetupDisplay(1, 0);
backupVariables.intensity = 0;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 0;
}
else if(cmd == "1")
{
TM1638_SetupDisplay(1, 1);
backupVariables.intensity = 1;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 1;
}
else if(cmd == "2")
{
TM1638_SetupDisplay(1, 2);
backupVariables.intensity = 2;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 2;
}
else if(cmd == "3")
{
TM1638_SetupDisplay(1, 2);
backupVariables.intensity = 3;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 2;
}
else if(cmd == "4")
{
TM1638_SetupDisplay(1, 4);
backupVariables.intensity = 4;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 4;
}
else if(cmd == "5")
{
TM1638_SetupDisplay(1, 5);
backupVariables.intensity = 5;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 5;
}
else if(cmd == "6")
{
TM1638_SetupDisplay(1, 6);
backupVariables.intensity = 6;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 6;
}
else if(cmd == "7")
{
TM1638_SetupDisplay(1, 7);
backupVariables.intensity = 7;
EEPROM.put(EEPROM_ADDR, backupVariables);
return 7;
}
else
{
return -1;
}
}
uint8_t TM1638_GetKey(void)
{
uint8_t KeyData = 0;
uint8_t Status[4] = {0, 0, 0, 0};
uint8_t index, key_bit;
pinMode(SDA, INPUT);
pinResetFast(MY_SSEL); //digitalWrite(MY_SSEL, 0); // Set STROBE = "0"
SPI_SendData(DATA_READ_KEY_SCAN_MODE);
delayMicroseconds(5);
pinSetFast(MY_SDA); //digitalWrite(SDA, 0x01); //SDA_Write(0x01);
delayMicroseconds(5); // wait to scan keys ready
for(index = 0; index < 4; index++)
{
for(key_bit = 0; key_bit < 8; key_bit++)
{
pinResetFast(MY_SCL);
delayMicroseconds(2);
Status[index] |= (digitalRead(SDA) << key_bit);
pinSetFast(MY_SCL);
delayMicroseconds(2);
}
delayMicroseconds(5);
}
pinSetFast(MY_SSEL);
delayMicroseconds(10);
pinMode(SDA, OUTPUT);
if(Status[0] == 0x01)
{
KeyData |= 0x01;
}
if(Status[0] == 0x10)
{
KeyData |= 0x05;
}
if(Status[1] == 0x01)
{
KeyData |= 0x02;
}
if(Status[1] == 0x10)
{
KeyData |= 0x06;
}
if(Status[2] == 0x01)
{
KeyData |= 0x03;
}
if(Status[2] == 0x10)
{
KeyData |= 0x07;
}
if(Status[3] == 0x01)
{
KeyData |= 0x04;
}
if(Status[3] == 0x10)
{
KeyData |= 0x08;
}
return KeyData;
}
Comments