#include "TM1637.h"
#include "I2C.h"
#include "DS1307.h"
#define VERSION "1.00a"
const uint8_t* d_alarmTypeArray[3] = {
(const uint8_t[]){0x3f,0x71},
(const uint8_t[]){0x7C,0x79},
(const uint8_t[]){0x50,0x5E}
};
const uint8_t* d_uuArray[8] = {
(const uint8_t[]){0,0},//0
(const uint8_t[]){0x79,0x5c},//Mo
(const uint8_t[]){0x78,0x1C},//Tu
(const uint8_t[]){0x4F,0x5E},//Wd
(const uint8_t[]){0x78,0x74},//Th
(const uint8_t[]){0x71,0x50},//Fr
(const uint8_t[]){0x6D,0x77},//Sa
(const uint8_t[]){0x6D,0x1C} //Su
};
const uint8_t* stepsizesArray[4] = {
(const uint8_t[]){0x5B,0x3F},//200
(const uint8_t[]){0x06,0x3f},//100
(const uint8_t[]){0x0,0x6D},//50
(const uint8_t[]){0x0,0x5B},//25
};
const uint8_t* deemphasissArray[2] = {
(const uint8_t[]){0x07,0x6d},//75
(const uint8_t[]){0x6D,0x3f},//50
};
const uint8_t* onoffArray[2] = {
(const uint8_t[]){0x3f,0x54},//75
(const uint8_t[]){0x3f,0x71},//50
};
int8_t a_hh = 6;
int8_t a_mm = 0;
volatile int8_t hh = 0;
volatile int8_t mm = 0;
volatile int8_t ss = 0;
volatile int8_t uu = 0;
volatile int8_t date = 1;
volatile int8_t month = 1;
volatile int8_t year = 20;
volatile int8_t brightness = 2;
volatile int8_t alarmType = 0;
volatile int8_t batteryVoltage = 0;
volatile bool radioToggle = false;
volatile uint16_t minfreq = 7600;
volatile uint16_t maxfreq = 10800;
volatile uint16_t freq = 10300;
volatile uint8_t volume = 2;
int32_t stepsizes[4] = { 200, 100, 50, 25 };
volatile int8_t stepsize = 1;
int32_t deemphasiss[2] = { 75, 50 };
volatile uint8_t deemphasis = 0;
volatile bool bass = false;
volatile uint8_t lna = 0;
volatile bool change = true;
volatile bool fetch = true;
static uint32_t save_event_time = 0;
volatile int8_t button_val = 0;
volatile int8_t menu = 0;
volatile int8_t menumode = 0;// 0=Menu|1=Alarm|2=Radio
void alarm(int8_t val){
menumode = 1;
a_mm += val;
// mm = max(0, min(59, mm));
if(a_mm > 59) { a_mm = 0; a_hh += 1;}
if(a_mm < 0) { a_mm = 59; a_hh -= 1;}
if(a_hh > 23) a_hh = 0;
if(a_hh < 0) a_hh = 23;
hh = a_hh;
mm = a_mm;
save_event_time = millis() + 10000;
change = true;
//tm1637.displayTime(a_hh,a_mm,true);
//(constrain(val,0,59)
//delay(50);
}
void toggleRadio(bool radio){
Serial.print("Radio : ");Serial.println(radio);
if(radio){
tm1637.displayNum(freq/10);
save_event_time = millis() + 10000;
}else{
change = true;
}
};
void tuneRadio(int8_t val){
int32_t stepval = stepsizes[stepsize] / 10;
freq += val * stepval;
//minfreq
if(freq > maxfreq) freq = minfreq;
if(freq < minfreq) freq = maxfreq;
//freq = max(7600, min(10800, freq));
//rx.setFrequency(freq);
tm1637.displayNum(freq/10);
button_val = 0;
save_event_time = millis() + 10000;
delay(200);
}
void saveLable(){
tm1637.reset(brightness);
tm1637.writeLoc(0,0x6D);
tm1637.writeLoc(1,0x77);
tm1637.writeLoc(2,0x3E);
tm1637.writeLoc(3,0x79);
}
#define N_PARAMS 18 //14 number of (visible) parameters
enum action_t { UPDATE, UPDATE_MENU, LOAD, SAVE, SKIP };
enum paramsA_t { _NULL,BATTERY,BRIGHTNESS,
ALARM,HH,MM,UU,DATE,MONTH,YEAR,
VOLUME,STEPS,DE,BASS,LNA,
FREQA, ALRM_HH, ALRM_MM,
VERS,
ALL=0xff };
#define get_version_id() ((VERSION[0]-'1') * 2048 + ((VERSION[2]-'0')*10 + (VERSION[3]-'0')) * 32 + ((VERSION[4]) ? (VERSION[4] - 'a' + 1) : 0) * 1) // converts VERSION string with (fixed) format "9.99z" into uint16_t (max. values shown here, z may be removed)
uint8_t eeprom_version;
int eeprom_addr;
#define EEPROM_OFFSET 0x150 // avoid collision with QCX settings, overwrites text settings though
void actionCommon(uint8_t action, uint8_t *ptr, uint8_t size){
switch(action){
case LOAD: eeprom_read_block((void *)ptr, (const void *)eeprom_addr, size); break;
case SAVE: eeprom_write_block((const void *)ptr, (void *)eeprom_addr, size); break;
case SKIP:
//eeprom_addr += size;
break;
}
eeprom_addr += size;
}
template<typename T> void paramAction(uint8_t action, T& value, const uint8_t* menuid, const uint8_t* enumArray[], int32_t _min, int32_t _max){
switch(action){
case UPDATE:
case UPDATE_MENU:
if(((int32_t)value + button_val) < _min) value = _max;
else if(((int32_t)value + button_val) > _max) value = _min;
else value += button_val;
button_val = 0;
if(action == UPDATE_MENU){
tm1637.writeLoc(0,menuid[0]);
tm1637.writeLoc(1,menuid[1]);
}
if(enumArray == NULL){
tm1637.displaySS(2,value);
}else{
tm1637.writeLoc(2,enumArray[value][0]);
tm1637.writeLoc(3,enumArray[value][1]);
}
delay(200);
break;
default:
actionCommon(action, (uint8_t *)&value, sizeof(value));
break;
}
}
int8_t paramAction(uint8_t action, uint8_t id = ALL){
if((action == SAVE) || (action == LOAD)){
eeprom_addr = EEPROM_OFFSET;
for(uint8_t _id = 1; _id < id; _id++) paramAction(SKIP, _id);
}
if(id == ALL) for(id = 1; id != N_PARAMS + 1; id++) paramAction(action, id); // for all parameters
switch(id){
case BATTERY : paramAction(action, batteryVoltage, (const uint8_t[]){0x7c,0x0}, NULL, 1, 99); break;
case BRIGHTNESS: paramAction(action, brightness, (const uint8_t[]){0x7c,0x50}, NULL, 1, 7); break;
case ALARM : paramAction(action, alarmType, (const uint8_t[]){0x77,0x0},d_alarmTypeArray, 0, 2); break;
case HH : paramAction(action, hh, (const uint8_t[]){0x76,0x0},NULL, 0, 23); break;
case MM : paramAction(action, mm, (const uint8_t[]){0x54,0x0},NULL, 0, 59); break;
case UU : paramAction(action, uu, (const uint8_t[]){0x5E,0x6E},d_uuArray, 1, 7); break;
case DATE : paramAction(action, date,(const uint8_t[]){0x5E,0x0},NULL, 1, 31); break;
case MONTH : paramAction(action, month,(const uint8_t[]){0x54,0x54},NULL, 1, 12); break;
case YEAR : paramAction(action, year, (const uint8_t[]){0x6E,0x0},NULL, 0, 99); break;
case VOLUME : paramAction(action, volume, (const uint8_t[]){0x3E,0x5C}, NULL, 0, 15); break;
case STEPS : paramAction(action, stepsize, (const uint8_t[]){0x6D,0x0}, stepsizesArray, 0, 3); break;
case DE : paramAction(action, deemphasis, (const uint8_t[]){0x5e,0x79}, deemphasissArray, 0, 1); break;
case BASS : paramAction(action, bass, (const uint8_t[]){0x7c,0x77}, onoffArray, 0, 1); break;
case LNA : paramAction(action, lna, (const uint8_t[]){0x38,0x54}, NULL, 0, 3); break;
// Invisible parameters
case FREQA : paramAction(action, freq, 0, NULL, 0, 0); break;
case ALRM_HH : paramAction(action, a_hh, 0, NULL, 0, 0); break;
case ALRM_MM : paramAction(action, a_mm, 0, NULL, 0, 0); break;
// Non-parameters
case _NULL: menumode = 0; change = true; break;
default:
// if((action == NEXT_MENU) && (id != N_PARAMS)){
// id = paramAction(action, max(1 /*0*/, min(N_PARAMS, id + ((button_val > 0) ? 1 : -1))) ); break;
// }
break;
//default: if((action == NEXT_MENU) && (id != N_PARAMS)) id = paramAction(action, max(1 /*0*/, min(N_PARAMS, id + ((button_val > 0) ? 1 : -1))) ); break; // keep iterating util menu item found
}
return id;
}
void save(uint8_t mode){
saveLable();
menu = -1;
menumode = 0;
delay(1000);
tm1637.reset(brightness);
switch(mode){
case 1: //alarm
paramAction(SAVE, ALRM_HH);
paramAction(SAVE, ALRM_MM);
change = true;
break;
case 2: //clock
rtc.adjust(hh,mm,ss,uu,date,month,year);
paramAction(SAVE, BRIGHTNESS);
paramAction(SAVE, ALARM);
change = true;
break;
case 3: //radio
paramAction(SAVE, VOLUME);
// paramAction(SAVE, STEPS);
// paramAction(SAVE, DE);
// paramAction(SAVE, BASS);
// paramAction(SAVE, LNA);
tm1637.displayNum(freq/10);
save_event_time = millis() + 10000;
break;
}
}
void handle_buttons(){
uint8_t key = !digitalRead(14) ? 236 :
!digitalRead(15) ? 237 :
!digitalRead(16) ? 238 :
!digitalRead(17) ? 239 :
255;
switch(key){
case 237: //radio
if(menumode == 2){//clock
save(menumode);
menumode = 0; change = true;
}else if(menumode == 3){//radio
save(menumode);
menumode = 0;
tm1637.displayNum(freq/10);
}else if(menumode == 1){//alarm
save(menumode);
menumode = 0; change = true;
}else{
radioToggle = !radioToggle;
toggleRadio(radioToggle);
}
delay(200);
break;
case 236: //menu
menu += 1; // Navigate through menu
if(radioToggle || menumode == 3){
if(menu < 10) menu = 10;
if(menu > 14) menu = 10;
menumode = 3;//radio
save_event_time = millis() + 10000;
}else{
if(menu > 9) menu = 1;
menumode = 2;//clock
}
menu = paramAction(UPDATE_MENU, menu);
button_val = 0;
delay(200);
break;
case 238:
button_val++;
if(menumode > 1) paramAction(UPDATE, menu);
else if(radioToggle) tuneRadio(+1);
else alarm(+1);
break;//up
case 239:
button_val--;
if(menumode > 1) paramAction(UPDATE, menu);
else if(radioToggle) tuneRadio(-1);
else alarm(-1);
break;//down
}
//delay(50);
}
ISR(WDT_vect) {
fetch = true;
}
void setup() {
Serial.begin(115200);
pinMode(14, INPUT_PULLUP);
pinMode(15, INPUT_PULLUP);
pinMode(16, INPUT_PULLUP);
pinMode(17, INPUT_PULLUP);
// paramAction(LOAD, BRIGHTNESS);
// paramAction(LOAD, ALARM);
// paramAction(LOAD, VOLUME);
// paramAction(LOAD, STEPS);
// paramAction(LOAD, DE);
// paramAction(LOAD, BASS);
// paramAction(LOAD, LNA);
// paramAction(LOAD, a_hh);
// paramAction(LOAD, a_mm);
MCUSR = 0;
//wdt_enable(WDTO_4S); // Enable watchdog
WDTCSR = 1<<WDCE | 1<<WDE;
WDTCSR = 0<<WDE | 1<<WDIE | 1<<WDP3 | 0<<WDP2 | 0<<WDP1 | 0<<WDP0;
//paramAction(LOAD);
delay(50);
tm1637.reset(brightness);
// Load parameters from EEPROM, reset to factory defaults when stored values are from a different version
paramAction(LOAD, VERS);
if((eeprom_version != get_version_id())){ // EEPROM clean: if rotary-key pressed or version signature in EEPROM does NOT corresponds with this firmware
eeprom_version = get_version_id();
paramAction(SAVE); // save default parameter values
//lcd.print(F("Reset settings.."));
Serial.println(F("Reset settings.."));
delay(500); //wdt_reset();
}else {
paramAction(LOAD); // load all parameters
}
delay(500);
//rtc.adjust(12,34,0,5,2,8,24);
}
void loop() {
handle_buttons();
if(menu == BATTERY) {
batteryVoltage = 42;
Serial.println("Batt!");
}
if(fetch){
fetch = false;
rtc.now();
if(!(menumode)){
year = rtc.year;
month = rtc.month;
date = rtc.day;
uu = rtc.uu;
mm = rtc.mm;
hh = rtc.hh;
ss = rtc.ss;
if(radioToggle){
if(save_event_time == 0){
change = true;
}
}else{
change = true;
}
//if(!radioToggle) change = true; // TODO not update when save
}
}
if(change){
change = false;
tm1637.displayTime(hh,mm,true);
delay(300);
}
if((save_event_time) && (millis() > save_event_time)){
Serial.println(save_event_time);
save_event_time = 0;
//paramAction(SAVE, FREQA);
if(menumode) {
save(menumode);
}else {
change = true;
}
}
}