(Keywords: MSF, Arduino, Cumbria, Rugby, Library, Example, Hitachi, LCD, 60kHz)
If you ever want to really annoy me, a good way of doing it is to tell me that you deliberately set your clocks ten minutes fast because “otherwise, I’d be late for all my appointments”. I have killed people for less than that.
Knowing the right time is one of my little obsessions. It doesn’t have to be accurate down to the microsecond (I’m not that bad!) but certainly to within a few seconds.
All my home computers adjust their time by NTP. My wristwatch, and any clock in the house which is “mine” (as opposed to my wife’s) has a Rugby MSF receiver. And some of my wife’s look like they’d make suitable projects for modification …
If I’m going to build any time-related projects, I’m going to try my hardest to include MSF support. The mini-project I describe here just shows how I receive and decode an MSF signal, to use as a building-block in other projects.
The receiver
I love eBay for getting hold of components. MSF decoders can be purchased for around a tenner, and take all the hard work out of the hardware side. The one I bought looks like this:
As you can see, it even comes with an aerial wound to the correct frequency (60kHz for the UK, 77.5kHz for mainland Europe).
There are four connections on the board – the supply voltage (anything between 3v and 5v is fine), ground, the enable line, and the signal out.
The enable line is present so that you can optionally put the receiver into “sleep” mode when your microcontroller doesn’t need it. As the receiver only pulls a maximum of 10mA (but typically around half that) when running, I tend to keep it permanently-on for simplicity – just tie the enable line to 0v.
The receiver board features a surface mount LED so you can easily see that your aerial is connected and aligned correctly – you should see it flash around once a second, with a long flash (about half a second) marking the beginning of each minute.
The signal-out connection should be connected directly to a digital-in pin of your microcontroller – for this project, I’m using digital pin 3 of an Arduino Nano.
For a display, I’m using a common-as-muck Hitachi LCD display I had knocking around. But if you don’t have one of those, I’ve left some serial prints in the example code (commented-out) you can see with the serial monitor.
The software
Credit for the decoding-logic should go to Jarkman (website here) as all the hard stuff is his work! My involvement has been to strip-out the dependency on the Arduino “Time” library and made the code more ‘passive’.
The library works by registering an interrupt whenever the digital pin changes (goes from high to low, or low to high). By timing these changes, the code determines whether these pulses mark the beginning of the minute, a binary 0 (a short pulse) or a binary 1 (a longer pulse).
When a minute of binary data has been stored, the MSFDecoder library attempts to decode it. If it passes the checks (parity, plus some guaranteed patterns as specified in the MSF standard) then the decoded time is stored in the MSF instance, and flagged as valid.
It is then up to the host code to read this new date and time, act on it, and then clear the valid flag.
I have modified the code to work in this slightly-indirect manner because I expect most of my time-based projects to use an RTC module … so I want my projects to rely on that for time information, and then just adjust it on a daily basis if a valid MSF time is received (say, at 5am).
Example code
Here is a very minimal example sketch that uses the MSFDecoder library, and drives the Hitachi display:
// MSFDecoderExample // Simple Arduino sketch that demonstrates use of MSFDecoder library. // Kris Adcock 9th March 2013 #include <LiquidCrystal.h> #include <MSFDecoder.h> MSFDecoder MSF; // our instance of the decoder library, // Only one possible per project! (But then, why would you have more than one decoder connected?) LiquidCrystal lcd(12 /*RS*/, 11 /*Enable*/, 7 /*D4*/, 8 /*D5*/, 9 /*D6*/, 10 /*D7*/); // Our lcd module bool g_bPrevCarrierState; byte g_iPrevBitCount; // These globals are just to prevent needless updating of the screen. // Whether YOU need them or not depends on your project. // Anything relating to serial debugging has been commented-out. Feel free to re-add it if you don't have an // LCD module. void setup() { MSF.init(); lcd.begin(16, 4); //Serial.begin(115200); g_bPrevCarrierState = false; g_iPrevBitCount = 255; } void loop() { // print current progress of decoding. // (Only really done for reasons of feedback - you could happily ignore this if you don't need it.) bool bCarrierState = MSF.getHasCarrier(); byte iBitCount = MSF.getBitCount(); if ((bCarrierState != g_bPrevCarrierState) || (bCarrierState == true && iBitCount != g_iPrevBitCount)) { lcd.setCursor(14, 0); if (bCarrierState) { if (iBitCount < 10) lcd.print('0'); lcd.print(iBitCount); //Serial.print(iBitCount); } else { lcd.print("--"); //Serial.print("--"); } } g_bPrevCarrierState = bCarrierState; g_iPrevBitCount = iBitCount; // check if there's a freshly-decoded time in the decoder ... if (MSF.m_bHasValidTime) { // yep! char buffer[20]; sprintf(buffer, "%02d:%02d", MSF.m_iHour, MSF.m_iMinute); lcd.setCursor(0, 0); lcd.print(buffer); //Serial.println(buffer); sprintf(buffer, "%02d/%02d/20%02d", MSF.m_iDay, MSF.m_iMonth, MSF.m_iYear); lcd.setCursor(0, 1); lcd.print(buffer); //Serial.println(buffer); // lastly, clear the flag so we don't bother getting it on the next iteration ... MSF.m_bHasValidTime = false; } }
Note that of all the above code, the only bits you need to care about are the declaration of the MSF object, and that final if() right at the bottom.
To compile, download the MSFDecoder library and install it in your Arduino library folder as usual. I’ve included a copy of the above example too.
Closing notes
- It is very easy for the receiver to get a bad signal – particularly if you’re working near a lot of computer equipment, wifi hardware, metal objects … I have had good success by installing aerial and receiver in a small, plastic project box, and then connecting it to my Arduino via a standard headphone extension cable (you only need three connections – +5v, signal out, and 0v). Then you can put the aerial out of the way, and still keep working. But it’s something to be aware of when building a circuit that uses MSF: your project needs to be resilient enough to deal with many failed decode attempts!
- Note that the MSF pin is currently hardcoded in the MSFDecoder library – if you need to use a different pin, then you’ll have to modify that (and the interrupt number).
Downloads
- MSFDecoder library and example sketch