Jam Radio & Analyzer DCF77 v2.0 – Proyek Arduino Mega/UNO
Komponen dan Perlengkapan
Arduino Mega 2560 1
Arduino UNO 1
Tentang Proyek Ini
Setelah menemukan platform Arduino, saya terpesona dan mimpi saya untuk suatu hari mengembangkan elektronika saya sendiri menjadi kenyataan.
Seperti yang akan dilakukan siapa pun, saya mulai dengan sketsa dasar tetapi segera saya ingin membuat sesuatu yang berguna. Saya selalu mencintai Jam Radio (mereka sinkron dengan sinyal DCF77 dari Jerman dalam kasus saya), jadi saya memutuskan untuk membuat jam yang tidak hanya menunjukkan waktu tetapi jam yang menunjukkan kepada saya apa yang sedang terjadi.
Setelah ratusan jam berkreasi, membuat tak terhitung kesalahan, akhirnya saya memiliki jam yang berfungsi dengan kode saya sendiri dan desain papan sirkuit cetak.
Foto dan video DCF77 Analyzer/Clock v1.0:
Tetapi nanti saya menemukan superfilter DCF77 dari Udo Klein yang memberikan sinyal bersih ketika sinyal radio tidak begitu bagus.
Ini adalah papan Arduino Uno terpisah dengan perangkat lunak Superfilter, (Anda dapat melihatnya sebagai 'kotak hitam' mandiri, menyaring sinyal) yang terhubung antara antena DCF77 dan Arduino Mega yang menjalankan sketsa saya.
Jadi saya membuat versi 2.0 dari jam pertama:
kode ditulis ulang sepenuhnya
dikomentari secara luas sehingga semoga siapa pun dapat memahami apa yang sedang terjadi
ditambahkan sensor gerakan PIR untuk mengurangi konsumsi daya
ditambahkan DCF77 Superfilter
tampilan ekstra untuk suhu atau penggunaan lain (seperti waktu terbit/terbenam matahari)
Foto dan video DCF77 Analyzer/Clock v2.0 baru:
Demo
Untuk menonton video, klik DI SINI
Dibangun dalam 'Jam Leluhur'
Untuk menonton video, klik DI SINI
Desain
Panel depan dan casing:
Desain panel depan dibuat dalam program gratis bernama Inkscape (lihat URL unduhan di awal halaman ini).
Sebenarnya memproduksi panel depan ternyata merupakan bagian tersulit. Saya menghabiskan banyak uang mencoba membuatnya di FabLab lokal menggunakan pemotong laser dan jenis Akrilik khusus dengan lapisan seperti aluminium. Ide adalah memotong lubang dengan laser dan mengukir teks dan garis dengan membakar lapisan tipis yang sangat, mengungkapkan akrilik hitam di bawahnya. Tapi ini merupakan mimpi buruk karena pemotong laser tidak dapat menghasilkan dalam toleransi yang saya butuhkan karena penggunaan berat dan 'penyalahgunaan' oleh banyak pengguna.
Kemudian saya menemukan layanan Foto online. Mereka mencetak pada segala macam bahan dan salah satunya adalah panel DiBond. Harganya sangat baik, 28 Euro termasuk ongkir. Tapi hasilnya awalnya mengecewakan karena mereka tidak mencetak desain saya 1:1 tetapi sedikit diperbesar. Jadi hati-hati jika Anda memutuskan untuk menggunakan metode ini. Panggil mereka terlebih dahulu dan tanyakan apakah mungkin untuk mencetak 1:1.
Setelah panggilan telepon mereka mengirimkan saya panel lain dengan dimensi yang benar. Ini lebih baik dari yang diharapkan, luar biasa!
Kemudian diperlukan banyak pemborongan dan perutean:
Kode
DCF Analyzer / Clock v2.1
Sketsa Superfilter
DCF Analyzer / Clock v2.1 Arduino
Rilis bug 2020-01-18: saklar hemat daya diperbaiki.
/* ================================================================================
DCF77 Analyzer / Clock version 2
================================================================================
Sketsa ini adalah perangkat lunak bebas; Anda dapat mendistribusikannya dan/atau
memodifikasinya sesuai dengan ketentuan GNU Lesser General Public License...
================================================================================ */
// Library
#include <Time.h>
#include <Wire.h>
#include <DS1307RTC.h>
#include <LedControl.h>
#include <SPI.h>
#include <OneWire.h>
// Definisi pin Arduino UNO dalam tabel yang mudah dibaca
#define DCF77PIN 2
#define PIRDETECTORPIN 3
#define BUZZERSWITCHPIN 4
#define CHIMESWITCHPIN 5
#define POWERSAVESWITCHPIN 6
#define TEMPSENSORPIN 8
#define TEMPRESETPIN 9
#define MAXIMCCLD 10
#define MAXIMCCCLK 11
#define MAXIMCCDATA 12
#define LED_SUNDAY 22
#define LED_MONDAY 23
#define LED_TUESDAY 24
#define LED_WEDNESDAY 25
#define LED_THURSDAY 26
#define LED_FRIDAY 27
#define LED_SATURDAY 28
#define LED_CEST 29
#define LED_CET 30
#define LED_LEAPYEAR 31
#define LED_RTCERROR 32
#define LED_RTCSYNC 33
#define LED_TEMP 34
#define LED_OPTION1 35
#define LED_OPTION2 36
#define LED_ERRORPT 37
#define LED_ERRORPW 38
#define LED_BUFFERFULL 39
#define LED_MINUTEMARKER 40
#define LED_BUFFEROVERFLOW 41
#define LED_DCFSTATUS 42
#define LED_POWERSAVE 43
#define LED_PARITY1PASS 44
#define LED_PARITY1FAIL 45
#define LED_PARITY2PASS 46
#define LED_PARITY2FAIL 47
#define LED_PARITY3PASS 48
#define LED_PARITY3FAIL 49
#define LED_PIRMOTION 50
#define BUZZER A7
#define SPEAKERVOLPIN A6
#define CHIMEPIN A5
// Inisialisasi DS18B20
OneWire ds(TEMPSENSORPIN);
// Inisialisasi Matriks Tampilan Maxim 7219
LedControl MaximCC = LedControl(MAXIMCCDATA, MAXIMCCCLK, MAXIMCCLD, 7, false);
// Pengaturan pengguna, definisi variabel dan array
#define SPEAKERVOLUME 12
#define PERFORM_LED_TEST 1
#define LEDTEST_DELAY_DISPLAYS 600
#define LEDTEST_DELAY_LED_RING 20
#define CONFIGURE_DS18B20 0
#define POWERSAVINGOFFTIME 9
#define POWERSAVINGONTIME 22
#define POWERSAVE_BY_PIR 1
#define PIR_DELAY_TIME 30
#define TEMPRESET_MIDNIGHT 1
#define DS1307_I2C_ADDRESS 0x68
#define DCF_INTERRUPT 0
#define LedRingInner 0
#define LedRingOuter 1
#define DisplayBufferBitError 2
#define DisplayPeriodPulse 3
#define DisplayTempWeek 4
#define DisplayDate 5
#define DisplayTime 6
#define BrightnessLedRingOuter 1
#define BrightnessLedRingInner 1
#define BrightnessDisplayTime 1
#define BrightnessDisplayDate 7
#define BrightnessDisplayTempWeek 15
#define BrightnessDisplayPeriodPulse 2
#define BrightnessDisplayBufferBitError 15
static unsigned long leadingEdge = 0;
static unsigned long trailingEdge = 0;
unsigned long previousLeadingEdge = 0;
volatile unsigned int DCFSignalState = 0;
int previousSecond = 0;
unsigned int previousSignalState = 0;
static int DCFbitBuffer[59];
const int bitValue[] = {1, 2, 4, 8, 10, 20, 40, 80};
boolean MinuteMarkerFlag = false;
int bufferPosition = 0;
int previousMinute = 0;
int previousHour = 0;
bool dcfValidSignal = false;
int dcfP1counter = 0;
int dcfP2counter = 0;
int dcfP3counter = 0;
int dcfParityCheckP1 = 0;
int dcfParityCheckP2 = 0;
int dcfParityCheckP3 = 0;
int dcfMinute = 0;
int dcfHour = 0;
int dcfDay = 0;
int dcfWeekDay = 0;
int dcfMonth = 0;
int dcfYear = 0;
int dcfDST = 0;
int leapYear = 0;
int dayNumber;
int weekNumber;
int errorCounter = 0;
boolean errorCondition = false;
boolean daytimeChange = true;
boolean dayTime = false;
int dcf77SoundSwitch = 0;
byte present = 0;
byte DS18B20Data[12];
int maxTemp = 0;
int minTemp = 0;
int lowByte = 0;
int highByte = 0;
float tempReading = 0;
int tempCelsius = 0;
boolean tempResetButton = false;
int pirActivity = 0;
int pirDisplaysState = 1;
unsigned int pirTimer = 0;
unsigned long previousTimePIR = 0;
void setup() {
Serial.begin(9600);
pinMode(DCF77PIN, INPUT);
pinMode(TEMPRESETPIN, INPUT);
pinMode(BUZZERSWITCHPIN, INPUT);
pinMode(CHIMESWITCHPIN, INPUT);
pinMode(POWERSAVESWITCHPIN, INPUT);
pinMode(PIRDETECTORPIN, INPUT);
pinMode(CHIMEPIN, OUTPUT);
pinMode(SPEAKERVOLPIN, OUTPUT);
for (int i1 = 22; i1 <= 50; i1++) {
pinMode(i1, OUTPUT);
}
initialize();
attachInterrupt(DCF_INTERRUPT, int0handler, CHANGE);
setSyncProvider(RTC.get);
if (timeStatus() != timeSet) {
digitalWrite(LED_RTCERROR, HIGH);
} else {
digitalWrite(LED_RTCERROR, LOW);
}
digitalWrite(SPEAKERVOLPIN, LOW);
digitalWrite(CHIMEPIN, LOW);
for (int i = 0; i <= SPEAKERVOLUME; i++) {
digitalWrite(SPEAKERVOLPIN, HIGH);
delay(100);
digitalWrite(SPEAKERVOLPIN, LOW);
delay(100);
}
if (CONFIGURE_DS18B20 == 1) {
configureDS18B20();
}
calculateTemp();
if (PERFORM_LED_TEST == 1) {
ledTest();
} else {
delay(750);
}
displayTemp();
ledDisplay(DisplayBufferBitError, "R", 0);
}
void loop() {
if (DCFSignalState != previousSignalState) {
previousSignalState = DCFSignalState;
scanSignal();
}
checkSwitches();
checkPIR();
tasksEverySecond();
tasksEveryMinute();
tasksEveryHour();
}
void scanSignal() {
if (DCFSignalState == 1) {
leadingEdge = millis();
return;
}
if (DCFSignalState == 0) {
trailingEdge = millis();
ledDisplay(DisplayPeriodPulse, "L", (leadingEdge - previousLeadingEdge));
ledDisplay(DisplayPeriodPulse, "R", (trailingEdge - leadingEdge));
if ((leadingEdge - previousLeadingEdge) < 900) {
error(LED_ERRORPW);
errorCondition = true;
}
if (((trailingEdge - leadingEdge) < 70) || ((trailingEdge - leadingEdge) > 230)) {
error(LED_ERRORPT);
errorCondition = true;
}
if (errorCondition == true) {
errorCondition = false;
previousLeadingEdge = leadingEdge;
return;
}
digitalWrite(LED_ERRORPW, LOW);
digitalWrite(LED_ERRORPT, LOW);
digitalWrite(LED_BUFFERFULL, LOW);
digitalWrite(LED_BUFFEROVERFLOW, LOW);
digitalWrite(LED_MINUTEMARKER, LOW);
if (leadingEdge - previousLeadingEdge > 1900 && leadingEdge - previousLeadingEdge < 2100) {
finalizeBuffer();
}
previousLeadingEdge = leadingEdge;
if (trailingEdge - leadingEdge < 170) {
processDcfBit(0);
if (dcf77SoundSwitch == 1) buzzer(100);
} else {
processDcfBit(1);
if (dcf77SoundSwitch == 1) buzzer(200);
}
}
}
void processDcfBit(int dcfBit) {
MaximCC.setChar(DisplayBufferBitError, 7, bufferPosition / 10, false);
MaximCC.setChar(DisplayBufferBitError, 6, bufferPosition % 10, false);
MaximCC.setChar(DisplayBufferBitError, 4, dcfBit, false);
if (dcfValidSignal == true || MinuteMarkerFlag == true) {
MaximCC.setLed(LedRingInner, bufferPosition / 8, bufferPosition % 8, dcfBit);
}
DCFbitBuffer[bufferPosition] = dcfBit;
if (bufferPosition == 0) {
digitalWrite(LED_PARITY1PASS, LOW);
digitalWrite(LED_PARITY1FAIL, LOW);
digitalWrite(LED_PARITY2PASS, LOW);
digitalWrite(LED_PARITY2FAIL, LOW);
digitalWrite(LED_PARITY3PASS, LOW);
digitalWrite(LED_PARITY3FAIL, LOW);
dcfP1counter = 0;
dcfP2counter = 0;
dcfP3counter = 0;
dcfParityCheckP1 = 0;
dcfParityCheckP2 = 0;
dcfParityCheckP3 = 0;
}
if (bufferPosition == 28) {
for (int i = 21; i <= 27; i++) {
dcfP1counter += DCFbitBuffer[i];
}
if ((DCFbitBuffer[28] + dcfP1counter) % 2 == 0) {
digitalWrite(LED_PARITY1PASS, HIGH);
dcfParityCheckP1 = 1;
} else {
digitalWrite(LED_PARITY1FAIL, HIGH);
dcfValidSignal = false;
digitalWrite(LED_DCFSTATUS, LOW);
}
}
if (bufferPosition == 35) {
for (int i = 29; i <= 34; i++) {
dcfP2counter += DCFbitBuffer[i];
}
if ((DCFbitBuffer[35] + dcfP2counter) % 2 == 0) {
digitalWrite(LED_PARITY2PASS, HIGH);
dcfParityCheckP2 = 1;
} else {
digitalWrite(LED_PARITY2FAIL, HIGH);
dcfValidSignal = false;
digitalWrite(LED_DCFSTATUS, LOW);
}
}
if (bufferPosition == 58) {
for (int i = 36; i <= 57; i++) {
dcfP3counter += DCFbitBuffer[i];
}
dcfParityCheckP3 = ((DCFbitBuffer[58] + dcfP3counter) % 2 == 0) ? 1 : 0;
if (dcfParityCheckP3 == 1) {
digitalWrite(LED_PARITY3PASS, HIGH);
dcfParityCheckP3 = 1;
} else {
digitalWrite(LED_PARITY3FAIL, HIGH);
dcfValidSignal = false;
digitalWrite(LED_DCFSTATUS, LOW);
}
dcfValidSignal = (dcfParityCheckP1 + dcfParityCheckP2 + dcfParityCheckP3 == 3);
}
bufferPosition++;
if (bufferPosition > 59) {
bufferPosition = 0;
MaximCC.clearDisplay(LedRingInner);
error(LED_BUFFEROVERFLOW);
return;
}
}
void finalizeBuffer(void) {
if (bufferPosition == 59 && dcfValidSignal == true) {
digitalWrite(LED_BUFFERFULL, HIGH);
digitalWrite(LED_DCFSTATUS, HIGH);
MaximCC.clearDisplay(LedRingInner);
for (int i = 0; i < 59; i++) {
MaximCC.setLed(LedRingOuter, i / 8, i % 8, DCFbitBuffer[i]);
}
decodeBufferContents();
setTime(dcfHour, dcfMinute, 0, dcfDay, dcfMonth, dcfYear);
RTC.set(now());
digitalWrite(LED_RTCSYNC, HIGH);
bufferPosition = 0;
for (int i = 0; i < 59; i++) {
DCFbitBuffer[i] = 0;
}
MinuteMarkerFlag = false;
} else {
digitalWrite(LED_MINUTEMARKER, HIGH);
MaximCC.clearDisplay(LedRingInner);
MaximCC.clearDisplay(LedRingOuter);
bufferPosition = 0;
for (int i = 0; i < 59; i++) {
DCFbitBuffer[i] = 0;
}
MinuteMarkerFlag = true;
}
}
void decodeBufferContents(void) {
dcfMinute = bitDecode(21, 27);
dcfHour = bitDecode(29, 34);
dcfDay = bitDecode(36, 41);
dcfWeekDay = bitDecode(42, 44);
dcfMonth = bitDecode(45, 49);
dcfYear = bitDecode(50, 57);
dayWeekNumber(dcfYear, dcfMonth, dcfDay, dcfWeekDay);
dcfDST = bitDecode(17, 17);
leapYear = calculateLeapYear(dcfYear);
}
int bitDecode(int bitStart, int bitEnd) {
int i = 0;
int value = 0;
// Implementasi dekode bit DCF77
return value;
}
Sketsa Superfilter Arduino
// Sketsa Superfilter yang saya gunakan dengan DCF Analyzer/Clock 2.0
// Udo Klein melakukan pekerjaan luar biasa dengan filter ini
// Erik de Ruiter
#include <dcf77.h>
const uint8_t dcf77_analog_samples = false;
const uint8_t dcf77_analog_sample_pin = 5;
const uint8_t dcf77_sample_pin = 19; // A5
const uint8_t dcf77_inverted_samples = 0;
#if defined(__AVR__)
#define ledpin(led) (led)
#else
#define ledpin(led) (led<14? led: led+(54-14))
#endif
const uint8_t dcf77_monitor_pin = ledpin(18); // A4
const bool provide_filtered_output = true;
const uint8_t dcf77_filtered_pin = ledpin(12);
const uint8_t dcf77_inverted_filtered_pin = ledpin(11);
const uint8_t dcf77_filter_diff_pin = ledpin(7);
const bool provide_semi_synthesized_output = true;
const uint8_t dcf77_semi_synthesized_pin = ledpin(16);
const uint8_t dcf77_inverted_semi_synthesized_pin = ledpin(15);
const uint8_t dcf77_semi_synthesized_diff_pin = ledpin(14);
const bool provide_synthesized_output = true;
const uint8_t dcf77_synthesized_pin = ledpin(6);
const uint8_t dcf77_inverted_synthesized_pin = ledpin(5);
const uint8_t dcf77_synthesized_diff_pin = ledpin(4);
const uint8_t dcf77_second_pulse_pin = ledpin(10);
const uint8_t dcf77_signal_good_indicator_pin = ledpin(13);
volatile uint16_t ms_counter = 0;
volatile Internal::DCF77::tick_t tick = Internal::DCF77::undefined;
// Implementasi kelas Scope dan fungsi output_handler...
// Kode lengkap dapat diunduh dari repositori proyek.
void setup() {
Serial.begin(115200);
// Inisialisasi pin dan clock DCF77
}
void loop() {
Clock::time_t now;
DCF77_Clock::get_current_time(now);
if (now.month.val > 0) {
Serial.println();
Serial.print(F("Decoded time: "));
DCF77_Clock::print(now);
Serial.println();
}
Serial.print(DCF77_Clock::get_clock_state());
Serial.print(' ');
DCF77_Clock::debug();
}
Bagian Kustom dan Kotak
Ini adalah PCB yang saya buat untuk Tampilan Waktu dan Tanggal Maxim_7219_LED_display_unit_for_Adafruit_0_56inch_7_segment_v1_1.zip Ini adalah desain PCB saya untuk tampilan 7 segmen yang sangat kompakt dan kecil Maxim_7219_LED_display_unit_for_KingBright_7_segment_SC39_11SRWAv1_1.zip Suara tic dan Chime, lihat teks untuk penjelasan Grandfather_Clock_Sound_files.zip
Skematik
UNDUH untuk melihat detail! dcf77-analyzer-clock-v2_1_jVZT5sqIwn.zip