Last active
August 18, 2024 20:56
-
-
Save scruss/e8aa693db68bfd8f869b60b439073fa6 to your computer and use it in GitHub Desktop.
minimally modified EverSet ES100 WWVB board Arduino example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
// es100_host_arduino.c | |
// | |
// Xtendwave ES100 Example Host MCU Code for Arduino - version 0002 | |
// | |
// Copyright 2013 Xtendwave | |
// | |
// THIS SOFTWARE IS PROVIDED TO YOU "AS IS," AND WE MAKE NO EXPRESS OR IMPLIED | |
// WARRANTIES WHATSOEVER WITH RESPECT TO ITS FUNCTIONALITY, OPERABILITY, OR USE, | |
// INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE, OR INFRINGEMENT. WE EXPRESSLY DISCLAIM ANY | |
// LIABILITY WHATSOEVER FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR | |
// SPECIAL DAMAGES, REGARDLESS OF THE FORM OF ACTION OR LEGAL THEORY UNDER | |
// WHICH THE LIABILITY MAY BE ASSERTED, EVEN IF ADVISED OF THE POSSIBILITY OR | |
// LIKELIHOOD OF SUCH DAMAGES. | |
// | |
// minor mods 2024-08, scruss: | |
// * changed IRQ and EN pins to match Keith Greiner's example | |
// https://sites.google.com/site/wwvbreceiverwitharduino/ | |
// * reformatted final time output to include leading zeroes | |
// | |
// NB: it takes some time to get any output from this program, and sometimes | |
// it can take almost two hours to return a result. You should see a | |
// "IRQ status = 0x4" within about five minutes of startup if everything's | |
// working. Program also ends after returning UTC time. | |
// | |
// Sample output from | |
// grabserial --systime --device=/dev/ttyACM0 --baudrate=9600 | |
// | |
// [16:19:24.499967 0.000002] enabling es100 | |
// [16:19:24.598070 0.098174] count = 0, waiting for interrupt ... received | |
// [16:21:37.449777 132.851708] IRQ status = 0x4 | |
// [16:21:37.469786 0.020011] count = 1, waiting for interrupt ... received | |
// [16:23:50.993526 133.523738] IRQ status = 0x1 | |
// [16:23:51.013547 0.020023] received UTC time = 2024-08-18 20:23:51 | |
// [16:23:51.054692 0.041143] second boundary occurred at timer count = 266497 | |
// [16:23:51.107795 0.053103] status register = 0x63 | |
// [16:23:51.132770 0.024975] next DST transition = month 11, day 3, hour 2 | |
// | |
// This software contains example C code that shows how an Arduino host | |
// microcontroller can control an ES100 device. The software uses the | |
// Serial.print() function to display the time. | |
// | |
// 4 pins are used. 1 digital output pin controls EN and 1 digital input pin | |
// monitors IRQ. Two pins, SCL and SDA, connect to the ES100 serial interface. | |
// | |
// This software was testing using Arduino Software version 1.5.2 and an Arduino Due. | |
// | |
// User-Supplied Functions | |
// | |
// mcu_init | |
// mcu_gpio_set_high | |
// mcu_gpio_set_low | |
// mcu_gpio_read | |
// mcu_timer_read | |
// mcu_timer_wait_us | |
// | |
// General I2C Functions | |
// | |
// i2c_write | |
// i2c_read | |
// | |
// ES100 Functions | |
// | |
// es100_write_register | |
// es100_read_register | |
// es100_enable | |
// es100_disable | |
// es100_start_rx | |
// es100_wait_for_irq | |
// es100_get_irq_status | |
// es100_read_time | |
// | |
// Other | |
// | |
// setup | |
// loop | |
// | |
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
#include <Wire.h> | |
//------------------------------------------------------------------------------ | |
// MCU constants - USER TO MODIFY | |
//------------------------------------------------------------------------------ | |
// GPIO pins | |
// #define GPIO_EN 4 | |
// #define GPIO_IRQ 2 | |
#define GPIO_EN 22 | |
#define GPIO_IRQ 24 | |
//------------------------------------------------------------------------------ | |
// constants | |
//------------------------------------------------------------------------------ | |
// I2C slave address | |
#define ES100_SLAVE_ADDR 0x32 | |
// enable delay time | |
//#define ENABLE_DELAY_US 1500 | |
#define ENABLE_DELAY_US 100000 // 100ms: module version 1 only | |
// I2C clock high/low time | |
#define SCL_TIME_US 2 | |
// ES100 API register addresses | |
#define ES100_CONTROL0_REG 0x00 | |
#define ES100_CONTROL1_REG 0x01 | |
#define ES100_IRQ_STATUS_REG 0x02 | |
#define ES100_STATUS0_REG 0x03 | |
#define ES100_YEAR_REG 0x04 | |
#define ES100_MONTH_REG 0x05 | |
#define ES100_DAY_REG 0x06 | |
#define ES100_HOUR_REG 0x07 | |
#define ES100_MINUTE_REG 0x08 | |
#define ES100_SECOND_REG 0x09 | |
#define ES100_NEXT_DST_MONTH_REG 0x0A | |
#define ES100_NEXT_DST_DAY_REG 0x0B | |
#define ES100_NEXT_DST_HOUR_REG 0x0C | |
//------------------------------------------------------------------------------ | |
// array to store date and time | |
//------------------------------------------------------------------------------ | |
#define DT_STATUS 0 | |
#define DT_YEAR 1 | |
#define DT_MONTH 2 | |
#define DT_DAY 3 | |
#define DT_HOUR 4 | |
#define DT_MINUTE 5 | |
#define DT_SECOND 6 | |
#define DT_NEXT_DST_MONTH 7 | |
#define DT_NEXT_DST_DAY 8 | |
#define DT_NEXT_DST_HOUR 9 | |
#define DT_LENGTH 10 | |
//------------------------------------------------------------------------------ | |
// mcu functions - USER TO MODIFY | |
//------------------------------------------------------------------------------ | |
void mcu_init(void) { | |
// optional code to initialize GPIO, timer and other MCU functions | |
pinMode(GPIO_EN, OUTPUT); | |
pinMode(GPIO_IRQ, INPUT); | |
Wire.begin(); | |
Serial.begin(9600); | |
} | |
void mcu_gpio_set_high(int pin) { | |
// set pin to a logic one | |
digitalWrite(pin, HIGH); | |
} | |
void mcu_gpio_set_low(int pin) { | |
// set pin to a logic zero | |
digitalWrite(pin, LOW); | |
} | |
int mcu_gpio_read(int pin) { | |
// read current state of input pin | |
return (digitalRead(pin)); | |
} | |
int mcu_timer_read(void) { | |
// read timer value and return as integer | |
return (millis()); | |
} | |
void mcu_timer_wait_us(int count) { | |
// wait 1 microsecond or greater for each count | |
delayMicroseconds(count); | |
} | |
//------------------------------------------------------------------------------ | |
// write I2C data - USER TO MODIFY | |
//------------------------------------------------------------------------------ | |
void i2c_write(uint8_t slave_addr, uint8_t num_bytes, uint8_t *ptr) { | |
int i; | |
Wire.beginTransmission(slave_addr); | |
for (i = 0; i < num_bytes; i++) { | |
Wire.write(ptr[i]); | |
} | |
Wire.endTransmission(); | |
} | |
//------------------------------------------------------------------------------ | |
// read I2C data - USER TO MODIFY | |
//------------------------------------------------------------------------------ | |
void i2c_read(uint8_t slave_addr, uint8_t num_bytes, uint8_t *ptr) { | |
int i; | |
const uint8_t stop_flag = 1; | |
Wire.requestFrom(slave_addr, num_bytes, stop_flag); | |
for (i = 0; (i < num_bytes && Wire.available()); i++) { | |
ptr[i] = Wire.read(); | |
} | |
} | |
//------------------------------------------------------------------------------ | |
// write data to an ES100 API register | |
//------------------------------------------------------------------------------ | |
void es100_write_register(uint8_t addr, uint8_t data) { | |
uint8_t es100_write_array[2]; | |
es100_write_array[0] = addr; | |
es100_write_array[1] = data; | |
i2c_write(ES100_SLAVE_ADDR, 0x2, es100_write_array); | |
} | |
//------------------------------------------------------------------------------ | |
// read data from an ES100 API register | |
//------------------------------------------------------------------------------ | |
uint8_t es100_read_register(uint8_t addr) { | |
uint8_t data; | |
i2c_write(ES100_SLAVE_ADDR, 0x1, &addr); | |
i2c_read(ES100_SLAVE_ADDR, 0x1, &data); | |
return (data); | |
} | |
//------------------------------------------------------------------------------ | |
// enable ES100 | |
//------------------------------------------------------------------------------ | |
void es100_enable(void) { | |
mcu_gpio_set_high(GPIO_EN); | |
} | |
//------------------------------------------------------------------------------ | |
// disable ES100 | |
//------------------------------------------------------------------------------ | |
void es100_disable(void) { | |
mcu_gpio_set_low(GPIO_EN); | |
} | |
//------------------------------------------------------------------------------ | |
// start reception | |
//------------------------------------------------------------------------------ | |
void es100_start_rx() { | |
es100_write_register(ES100_CONTROL0_REG, 0x01); | |
// perform read of control register for i2c debug only | |
es100_read_register(ES100_CONTROL0_REG); | |
} | |
//------------------------------------------------------------------------------ | |
// wait for falling edge of IRQ | |
//------------------------------------------------------------------------------ | |
void es100_wait_for_irq() { | |
while (mcu_gpio_read(GPIO_IRQ)) | |
; // wait until IRQ is low | |
} | |
//------------------------------------------------------------------------------ | |
// read IRQ status register | |
//------------------------------------------------------------------------------ | |
uint8_t es100_get_irq_status() { | |
//Read IRQ status register | |
return (es100_read_register(ES100_IRQ_STATUS_REG)); | |
} | |
//------------------------------------------------------------------------------ | |
// read date and time from API registers | |
//------------------------------------------------------------------------------ | |
void es100_read_time(int dt_array[]) { | |
dt_array[DT_STATUS] = es100_read_register(ES100_STATUS0_REG); | |
dt_array[DT_YEAR] = es100_read_register(ES100_YEAR_REG); | |
dt_array[DT_MONTH] = es100_read_register(ES100_MONTH_REG); | |
dt_array[DT_DAY] = es100_read_register(ES100_DAY_REG); | |
dt_array[DT_HOUR] = es100_read_register(ES100_HOUR_REG); | |
dt_array[DT_MINUTE] = es100_read_register(ES100_MINUTE_REG); | |
dt_array[DT_SECOND] = es100_read_register(ES100_SECOND_REG); | |
dt_array[DT_NEXT_DST_MONTH] = es100_read_register(ES100_NEXT_DST_MONTH_REG); | |
dt_array[DT_NEXT_DST_DAY] = es100_read_register(ES100_NEXT_DST_DAY_REG); | |
dt_array[DT_NEXT_DST_HOUR] = es100_read_register(ES100_NEXT_DST_HOUR_REG); | |
} | |
//------------------------------------------------------------------------------ | |
// top level function to receive time from WWVB | |
//------------------------------------------------------------------------------ | |
int es100_receive(int dt_array[]) { | |
// local variables | |
int irq_status = 0; | |
int current_timer_value; | |
int count = 0; | |
// enable and delay | |
Serial.println("enabling es100"); | |
es100_enable(); | |
mcu_timer_wait_us(ENABLE_DELAY_US); | |
// start reception | |
es100_start_rx(); | |
// loop until time received | |
while (irq_status != 0x01) { | |
// wait for interrupt | |
Serial.print("count = "); | |
Serial.print(count++); | |
Serial.print(", waiting for interrupt ... "); | |
es100_wait_for_irq(); | |
Serial.println("received"); | |
// interrupt defines second boundary, so save current timer value | |
current_timer_value = mcu_timer_read(); | |
// read interrupt status | |
irq_status = es100_get_irq_status(); | |
Serial.print("IRQ status = 0x"); | |
Serial.println(irq_status, HEX); | |
} | |
// read date and time | |
es100_read_time(dt_array); | |
// disable ES100 | |
es100_disable(); | |
// return timer value when interrupt occurred | |
return (current_timer_value); | |
} | |
//------------------------------------------------------------------------------ | |
// setup function - runs once | |
//------------------------------------------------------------------------------ | |
void setup() { | |
// local variables | |
int current_timer_value; | |
int dt_array[DT_LENGTH]; | |
// initialize MCU | |
mcu_init(); | |
// initialize gpios | |
mcu_gpio_set_low(GPIO_EN); | |
// receive time from WWVB | |
current_timer_value = es100_receive(dt_array); | |
// display date and time | |
// these are BCD values, so printing them as hex is correct | |
// but we have to pad them with leading zeroes as if they were decimals | |
Serial.print("received UTC time = 20"); | |
if (dt_array[DT_YEAR] < 10) Serial.print("0"); | |
Serial.print(dt_array[DT_YEAR], HEX); | |
Serial.print("-"); | |
if (dt_array[DT_MONTH] < 10) Serial.print("0"); | |
Serial.print(dt_array[DT_MONTH], HEX); | |
Serial.print("-"); | |
if (dt_array[DT_DAY] < 10) Serial.print("0"); | |
Serial.print(dt_array[DT_DAY], HEX); | |
Serial.print(" "); | |
if (dt_array[DT_HOUR] < 10) Serial.print("0"); | |
Serial.print(dt_array[DT_HOUR], HEX); | |
Serial.print(":"); | |
if (dt_array[DT_MINUTE] < 10) Serial.print("0"); | |
Serial.print(dt_array[DT_MINUTE], HEX); | |
Serial.print(":"); | |
if (dt_array[DT_SECOND] < 10) Serial.print("0"); | |
Serial.println(dt_array[DT_SECOND], HEX); | |
// display timer value when second occurred | |
Serial.print("second boundary occurred at timer count = "); | |
Serial.println(current_timer_value); | |
// display status register | |
Serial.print("status register = 0x"); | |
Serial.println(dt_array[DT_STATUS], HEX); | |
// display next DST transition date | |
Serial.print("next DST transition = month "); | |
Serial.print(dt_array[DT_NEXT_DST_MONTH], HEX); | |
Serial.print(", day "); | |
Serial.print(dt_array[DT_NEXT_DST_DAY], HEX); | |
Serial.print(", hour "); | |
Serial.println(dt_array[DT_NEXT_DST_HOUR], HEX); | |
} | |
//------------------------------------------------------------------------------ | |
// loop function - not used | |
//------------------------------------------------------------------------------ | |
void loop() { | |
// put your main code here, to run repeatedly: | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment