tag:blogger.com,1999:blog-67377131024445012592024-03-12T21:01:52.685-07:00KO7M - Ham Radio BlogThe ramblings of one home-brew electronics, firmware engineer and pilot.Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.comBlogger257125tag:blogger.com,1999:blog-6737713102444501259.post-26294289969714174552023-05-07T22:39:00.000-07:002023-05-07T22:39:03.490-07:00Update after a long lapse<p> Greetings all. It has been much too long since I was actively blogging and I am going to do my best to restart that activity as it is too easy to let it slip away if I am not careful.</p><p>Well a lot has happened including a global pandemic, work furlough, rehire, restructuring our engineering focus from pro-audio to more consumer/creator focus as well as navigating extreme electronics parts shortages to the point that we are being quoted 52 week lead times in some cases. That is insanity and more like going out of business lead times. So we have had to go out to spot markets and buy up inventory wherever we can find it, but even that was insufficient in some cases. There, we had to redesign existing products to use different MCU parts that were at least available.</p><p>Fortunately that is a bit on the mend and now our biggest challenge from my perspective is to meet the desired new product milestones with completely insufficient staff. Never a dull moment, or so it seems.</p><p>On the home front I have experienced the loss of two family members, my aunt and my older brother. There is nothing easy about any of that. It is truly a process and you must give yourself permission and the necessary time to process these kinds of losses.</p><p>On the ham radio side, I have been spending time both at work and at home in building my expertise on software defined radios and digital signal processing techniques. Fortunately I have the most amazing colleagues that are patient with me and allow me to ask stupid questions about things that I don't understand through both work projects and learning experiments mostly in audio signal processing. As I grow in knowledge here I will begin to write up my experiences in the hopes that someone else may find it useful.</p><p>Well, let's keep this short rather than writing an epic and I look forward to sharing my experiences with everyone. <br /></p>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-83042458351749302412022-12-31T19:59:00.006-08:002022-12-31T19:59:36.659-08:00Happy New Year Everybody!<p>Happy New Year!</p><p>While it has been quite a ride over the last few years, here's to hoping those to come will be without quite so many challenges. Make 2023 the best year yet!</p><p> Jeff <br /></p><p>Be at war with your vices, at peace with your neighbors, and let every New Year find you a better person.</p><p>Benjamin Franklin
</p>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-87499869542061960282020-06-02T10:20:00.004-07:002022-04-02T08:06:42.132-07:00Iambic Keyer<div>
<span face=""trebuchet ms" , sans-serif"><span style="font-size: medium;"><b>Prelude</b></span></span><br />
<span face=""trebuchet ms" , sans-serif"><span style="font-size: medium;"><b><br /></b></span></span>
<span face=""trebuchet ms" , sans-serif"><span style="font-size: medium;"><span style="font-size: small;">This article is a work in progress. I have not yet posted the complete code below as I am trying to decide if I should make it a single file, leave it as multiple files or provide a link to a public github location. I am actively updating this post and will remove these comments when the posting is complete. I appreciate your patience meanwhile.</span></span></span><br />
<span face=""trebuchet ms" , sans-serif"><span style="font-size: medium;"><span style="font-size: small;"> </span><br /><b></b></span></span><br />
<span face=""trebuchet ms" , sans-serif"><span style="font-size: medium;"><b>Overview</b></span></span></div>
<div>
<span style="font-family: "trebuchet";"><br /></span></div>
<div>
<span face=""trebuchet ms" , sans-serif"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">Back in 2012 I published a <a href="http://ko7m.blogspot.com/2012/02/propeller-iambic-keyer-or-transmitter.html">project</a> that implemented an iambic keyer/transmitter using the <a href="https://www.parallax.com/microcontrollers/propeller">Parallax Propeller</a>.</span><span style="font-size: small;"> This was a simple project and worked well though the chipset used to implement it is not mainstream. The code is still available in the github link posted in that article should you be interested. This article will describe the architecture and implementation details of a new and expanded version of the concepts originally implemented using the propeller but using a more commonly used micro-controller.</span></span></span></div>
<div>
<span face=""trebuchet ms" , sans-serif"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;"><br /></span></span></span></div>
<div>
<span face=""trebuchet ms" , sans-serif"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">Here, I intend to use a garden variety Arduino based on the lowly 8-bit AVR processors as found in the Uno, Nano and other variants (ATMega328). My plan is to publish an annotated source code discussion and at the end the full source that can be simply copied and pasted if you are using an Arduino Uno or equivalent. Porting to a different board should be as simple as remapping I/O pins used for paddles, transmitter keying output, sidetone PWM and CW speed control. If you need help with this, drop me a note at ko7m at arrl dot net and I will help you out.</span></span></span></div>
<div>
<span face=""trebuchet ms" , sans-serif"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;"><br /></span></span></span></div>
<div>
<span face=""trebuchet ms" , sans-serif"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">The code describe here is implemented as a C++ class named (oddly enough) "keyer". Since this class will be reading and controlling several GPIO pins, in general it makes little sense to have more than a single instance of the class although you certainly could implement a multi-operator keyer (single arduino controlling multiple transmitters, keyed by multiple operators), it seems unlikely in general that this would be necessary. I have therefore implemented a singleton static instance of the class within the keyer::getInstance() method that will return a pointer to the singleton class object. This should be sufficient for 99.9% of the use cases in a typical ham radio station.</span></span></span></div>
<div>
<br /></div>
<div>
<span style="font-size: medium;"><b><span style="font-family: "trebuchet";"><span face=""trebuchet ms" , sans-serif">File Structure</span></span></b></span></div>
<div>
<br /></div>
<div>
<span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">There is a single ko7mKeyer.ino file containing the normal Arduino sketch setup and loop functions. Beyond this file, there is an analog conversion bit of code that handles setup of the ADC (analogue to digital conversion) clocks (analog.h and analog.cpp). There is a timer module that also provides base handlers for pin change interrupts (timer.h and timer.cpp). The rest of the code is in ko7mKeyer.h and ko7mKeyer.cpp. Each of these files will show up as separate text tabs in the Arduino IDE. When you build the project all files will be built as necessary.</span></span></div>
<div>
<br /></div>
<div style="margin-left: 40px; text-align: left;">
<img alt="" src="data:image/png;base64,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" /></div>
<div>
<br /></div>
<div>
<div>
<span style="font-size: medium;"><b><span style="font-family: "trebuchet";"><span face=""trebuchet ms" , sans-serif">Code Walk-through</span></span></b></span></div>
<div>
<span face=""trebuchet ms" , sans-serif"><br /></span></div>
<div>
<span face=""trebuchet ms" , sans-serif">Here we will visit the code and describe its function section by section. We pull in keyer definitions from ko7mKeyer.h and declare a pointer to the keyer class.</span></div>
<div>
<span style="font-family: "courier";"><br /></span></div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";">// ko7mKeyer.ino - Iambic ko7m keyer<br />//<br />// Copyright 2014-2020 - Jeff Whitlatch - ko7m<br />//<br /><br />#include "ko7mKeyer.h"<br /><br />keyer *pKeyer = NULL;</span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><br /></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">During the normal Arduino setup, we initialize the serial port for debug output. We also speed up the ADC by modifying the clock prescaler. We want analogRead operations to be a quick as possible. This modification of the clock will significantly reduce the time required to determine the position of the CW speed pot at the cost of a minor amount of noise. We then set up pin change interrupts on the keyer paddle pins and the debounce timeout timer. At that point we are ready to grab the keyer class object pointer and initialize the keyer using default settings.</span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><br /><span style="font-family: "courier new" , "courier" , monospace;">// Application start initialization<br />//<br />void setup()<br />{<br /> Serial.begin(115200);<br /> Serial.println();<br /> Serial.println("ko7m keyer");<br /> <br /> // Change the ADC clock prescaler to speed up ADC conversion. This will help<br /> // the performance of the CW speed potentiometer.<br /> //<br /> adcInit(rate38k46Hz);<br /><br /> // Common timer and pin change interrupt initialization<br /> //<br /> timer::getInstance()->Init();<br /><br /> // Retrieve the singleton instance of the keyer object and hang if it cannot be found<br /> //<br /> pKeyer = keyer::getInstance();<br /> if (pKeyer != NULL)<br /> {<br /> pKeyer->Init();<br /> pKeyer->setSpeed(defaultWPM);<br /> }<br /> else<br /> while(1); // Hang the processor as there is no keyer object<br />}</span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><br /></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">Now we have the main program loop which keeps track of the period between calls to the keyer check method to move the keyer state machines along. If the CW speed pot has moved, the new words per minute (WPM) value is returned and displayed on the debug output monitor.</span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><br /><span style="font-family: "courier new" , "courier" , monospace;">// Main program loop<br />//<br />void loop()<br />{<br /> static unsigned long lastms = 0;<br /> unsigned long msCounter = millis();<br /> unsigned long ms = msCounter - lastms;<br /> static int wpmLast = 0;<br /><br /> // Pass the number of milliseconds since the last call to control the period between<br /> // reads of the CW speed pot to a reasonable rate<br /> //<br /> int wpm = pKeyer->check(ms);<br /> if (wpm != wpmLast)<br /> {<br /> Serial.print(wpm, DEC);<br /> Serial.println(" WPM");<br /> wpmLast = wpm;<br /> }<br /><br /> lastms = msCounter;<br />}</span></span></span></span><br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">I will not go into the function of the analog.cpp and analog.h files. They basically provide the ability to change the analog to digital (ADC) conversion clock prescaler which controls how long analogRead takes to return. We want it to be fast without introducing too much noise so that checking the CW speed pot every 1/2 second or so does not interfere with the timing of the keyer. I leave the analysis of these two files to the interested reader. If you have questions, drop me a note at ko7m at arrl dot net and I will do my best to help.</span></span></span></span></span><br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">The timer files provide two services, a 3 millisecond debounce timer and pin change interrupt handlers for the keyer paddles. It must be remembered that these two files are very specific to the particular Arduino that is in use, in this case the Arduino Uno.</span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif"><br /></span></span></span></span></span>
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">// timer.h - Timer handler<br />//<br /><br />#pragma once<br /><br />#include <Arduino.h><br /><br />class timer<br />{<br />public:<br /> static timer *getInstance();<br /> void Init();<br />};</span></span></span></span></span></span></span></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span face=""trebuchet ms" , sans-serif"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif"><span style="font-family: "times";">Timer.h declares the timer class and Timer.cpp provide the implementation of the class. The basic idea behind this code is that certain digital I/O pins are set to use pin change interrupts so that anytime the state of these pins change, an interrupt is called to handle the change. This eliminates polling of the paddles to detect when they are pressed. Since the paddle contacts can bounce, the interrupt handler will reset a 3 ms timeout anytime any of the pins change. If the timer itself times out, this means that none of the pins (paddles) has changed for 3 ms. The handler for the timer timeout event will then update the new debounced pin state.</span></span></span></span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">// timer.cpp - Timer1 handler<br />//<br /><br />#include "timer.h"<br /><br />// Externals<br />//<br />// Callback functions for pin change and timer interrupt service routines<br />//<br />bool keyerPinChange();<br />void keyerTimer();<br /><br />// Create a static singleton instance of this class and return a pointer</span></span></span></span></span></span></span></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">// to the instance<br />//<br />timer *timer::getInstance()<br />{<br /> static timer _timerInstance;<br /> return &_timerInstance;<br />}<br /><br />// Pin change interrupts for Arduino Uno ATMega328<br />//<br />// Digital pin PC Pin Vector Ctl Reg Port PCICR Bit Pin Mask Reg<br />// D0-D7 PCINT16-23 PCINT2_vect PCIR2 PD PCIE2 PCMSK2<br />// D8-D13 PCINT0-5 PCINT0_vect PCIR0 PB PCIE0 PCMSK0<br />// D14-D19 (A0-A5) PCINT8-13 PCINT1_vect PCIR1 PC PCIE1 PCMSK1<br />//<br />// Initialize pin change interrupts for rotary encoder, keyer paddles<br />// and straight key. Obviously this code is hardware configuration specific.<br />// PCMSK0 needs to be set to enable each pin that will use pin change interrupts.<br />//<br />void timer::Init()<br />{<br /> cli(); // Disable interrupts<br /><br /> // Port B configuration<br /> // Set pin mask for paddle pins 11 and 12 on Port B<br /> //<br /> PCMSK0 |= B00011000;<br /><br /> // Set pin change interrupt enable for port B (paddles)<br /> //<br /> PCICR |= (1<<PCIE0);<br /><br /> // Set timer 1 for 3 ms debounce timeout<br /> //<br /> TCCR1A = 0; // set entire TCCR1A register to 0<br /> TCCR1B = 0; // same for TCCR1B<br /> TCNT1 = 0; // initialize counter value to 0<br /><br /> // set compare match register 3 ms<br /> //<br /> OCR1A = 48000;<br /><br /> // turn on CTC mode<br /> //<br /> TCCR1B |= (1 << WGM12);<br /><br /> // Set bits for no prescaler<br /> //<br /> TCCR1B |= (1 << CS10); <br /><br /> // enable timer compare interrupt<br /> //<br /> TIMSK1 |= (1 << OCIE1A);<br /><br /> sei(); // Re-enable interrupts<br />}<br /><br />// Pin change interrupt handlers for any GPIO pin that enables pin</span></span></span></span></span></span></span></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">// change interrupts. Calls each handler for each pin or pin group<br />//<br />// Pin change ISR for port B<br />//<br />ISR(PCINT0_vect)<br />{<br /> // Call handler and reset timer as long as any handler returns true.<br /> //<br /> if (keyerPinChange())<br /> {<br /> TCNT1 = 0; // Zero the timer<br /> TIMSK1 |= (1 << OCIE1A); // Re-enable it to start the debounce time over<br /> }<br />}<br /><br />// Timer ISR - We get here if the debounce timer times out. Call back the paddle<br />// and key handlers to process debounced digital inputs. Disables Timer 1 until</span></span></span></span></span></span></span></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">// the next pin change event.<br />//<br />ISR(TIMER1_COMPA_vect)<br />{<br /> // Call the handlers<br /> //<br /> keyerTimer();<br /><br /> // Disable Timer<br /> //<br /> TIMSK1 &= ~(1 << OCIE1A);<br />}</span></span></span></span></span></span></span></span><br />
<br />
<span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">Now that we have debug output and the ability to detect and debounce changes in the keyer paddles, we are ready to take on the main keyer code. The default class constructor just initializes some of the state variables to sane values. If you provide a separate I/O pin for a straight key input, it will need be initialized as an input with a pull-up resistor. If a pull-up is provided in hardware, you can comment out the line. A static instance of the class is instantiated in the getInstance method and an accessor function is provided to return a pointer to that class instance.</span></span><br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// keyer.cpp - Iambic keyer<br />//<br />// Copyright 2014 - Jeff Whitlatch - ko7m<br />//<br />// http://morse-rss-news.sourceforge.net/keyerdoc/modeab.pdf<br />// http://morse-rss-news.sourceforge.net/keyerdoc/K7QO_Iambic_Paddle.pdf<br />// https://ag6qr.net/index.php/2017/01/06/iambic-a-or-b-or-does-it-matter/<br />// https://www.qsl.net/pa2ohh/iambic.htm<br />//</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br />#include "ko7mKeyer.h"<br /><br />// Default constructor<br />//<br />keyer::keyer()<br />{<br /> dahState = ditState = 1;<br /> dahBuffer = ditBuffer = dahMemory = ditMemory = dahIambicBuf = ditIambicBuf=0;<br /> pinMode(defaultStraightKeyPin, INPUT_PULLUP);<br />}<br /><br />// Create a static singleton instance of this class and return a pointer to the</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// instance<br />//<br />keyer *keyer::getInstance()<br />{<br /> static keyer _keyerInstance;<br /> return &_keyerInstance;<br />}</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">Read an analogue input connected to speed control pot and return the current WPM value. Ideally the speed control would be a linear pot. If using a log taper pot, you should wire it so the slowest changing end of the taper is at the end of the speed range that you typically spend your time. If you spend most of your time at 30 WPM or more, you will want the slowest tuning of the speed at that end of the range. If on the other hand, you spend most of your time at 15 WPM, you will want the slowest tuning of the speed at the low end of the range. Use a linear taper pot if at all possible for best results at all speeds. We also provide set/get accessor methods for things like current WPM speed, sidetone frequency, etc. </span></span></span></span></span></span></span></span></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br /></span></span></span></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// Read the CW speed pot and return the value in words-per-minute<br />//<br />int keyer::readSpeedControl()<br />{<br /> int wpm = defaultWPM;<br /> <br /> int speed = analogRead(speedPin);<br /><br /> // Handle noisy ADC value at extremes. cap anything above or below the</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> // min/max value<br /> //<br /> if (speed < analogMin) speed = analogMin; <br /> if (speed > analogMax) speed = analogMax;<br /><br /> // Remap analogue input range to min/max WPM<br /> // Speed control min/max should be customized in ko7mKeyer.h to the</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> // desired range.<br /><br /> // Direct mapping of the pot range to the CW speed range<br /> //<br /> wpm = map(speed, analogMin, analogMax, minWPM, maxWPM);<br /><br /> // Linear taper mapping of a log taper pot</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> //<br /> //wpm = multiMap(speed, inVal, outVal, 11);<br /><br /> return wpm;<br />}<br /><br />// Return current WPM speed value<br />//<br />int keyer::getSpeed()<br />{<br /> return WPM;<br />}<br /><br />// Set new WPM speed value, constrained to the min/max values<br />//<br />void keyer::setSpeed(int newWPM)<br />{<br /> WPM = min( max( newWPM, minWPM ), maxWPM );<br /> ditTime = 1200 / WPM;<br />}<br /><br />// Returns the current sidetone frequency<br />//<br />int keyer::getSidetoneFreq()<br />{<br /> return sidetoneFreq;<br />}</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span face=""trebuchet ms" , sans-serif"><br /></span></span></span></span></span></span></span></span></span></span></span></span></span>
<span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">Sidetone is a raw PWM output and should be minimally filtered with an R/C low pass filter. Since the Arduino PWM samples at 32 Khz, Nyquist says that the highest frequency we can produce is 16 Khz. In reality Ardunino tone range limits are 31 - 65535 Hz. So putting a low pass filter cutoff around 15 KHz will be more than adequate.<br /><br />cutoff frequency = 1 / (2 * pi * R * C)<br /><br />Using a 1000 ohm resistor for R:<br /><br />15000 = 1/(2000 * pi * C)<br />C = 1/(30000000 * pi)<br />C = 1.06103295e-8 farad or 0.0106103295 microfarads.<br /><br />I suggest using .01 uF with a 1K0 resistor</span></span><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"></span></span></span></span></span></span><br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// Set the generated sidetone frequency. </span></span></span></span></span></span></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"></span></span></span></span>//</span></span></span></span></span></span><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br />void keyer::setSidetoneFreq(int freq)<br />{<br /> sidetoneFreq = freq;<br />}<br /><br />// Swap the keyer paddles from default of dit on the left and dah on the right<br />//<br />void keyer::swapPaddles()<br />{<br /> ditPin ^= dahPin;<br /> dahPin = ditPin ^ dahPin;<br /> ditPin ^= dahPin;<br />}</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br /><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">Initialize the Arduino pins used by the keyer. Full control is provide for all pins. speedControl pin must be analogue input. The rest can be either digital or analogue. If using pin change interrupts on paddles, you will need to tend to the pin enable mask and for convenience limit the paddles to the same port on the Arduino. Sidetone pin must support PWM output. If you modify the dit and dah paddle pins, you will need to change the code in keyerPinChange and keyerTimer callback functions at the bottom of this file. You will also need to tend to the timer::Init method to set the pins change interrupt mask to enable interrupts on the new paddle pins and port in the timer.h and timer.cpp files. Contact me for assistance if you have issues and need to change which pins are used.</span></span></span></span></span></span></span></span><br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// Initialize the I/O pins used by the keyer<br />//<br />void keyer::initPins(int dit, int dah, int key, int sidetone, int speedControl)<br />{<br /> ditPin = dit;<br /> dahPin = dah;<br /> keyPin = key;<br /> sidetonePin = sidetone;<br /> speedPin = speedControl;<br /><br /> // If using external pull-up resistors, change INPUT_PULLUP to INPUT<br /> //<br /> pinMode(ditPin, INPUT_PULLUP);<br /> pinMode(dahPin, INPUT_PULLUP);<br /> pinMode(speedPin, INPUT);<br /> pinMode(keyPin, OUTPUT);<br /> pinMode(sidetonePin, OUTPUT);<br /><br /> WPM = defaultWPM;<br /> CPM = 0;<br /> ditBuffer = dahBuffer = 0;<br /> keyerMode = keyerModeSave = defaultMode;<br /> ditTime = 1200 / WPM;<br /> keyFlags = activeHigh;<br /> sidetoneFreq = defaultSidetoneFreq;</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br /><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">If during initPins we find that the dah key is stuck low, this will be interpreted to mean that a TS (tip-sleeve) straight key plug has been inserted in the paddle TRS (tip-ring-sleeve) jack. Since there is no ring contact on a TS plug, the effect will be to short the DAH key to ground. In this event, we will set the keyerMode to keyerModeStraight where the DAH key will be ignored. We are only checking at init time (power-up) so the straight key would need be plugged in before powering on the keyer device in order for this detection to properly function.</span></span></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br /> if (!digitalRead(dahPin))<br /> {<br /> delay(stuckKeyDelay);<br /> if (!digitalRead(dahPin))<br /> keyerMode = keyerModeSave = keyerModeStraight;<br /> }<br />}</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br /></span></span></span></span></span></span>
<span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">The keyer design has provision for two mechanisms for shifting from receive to transmit and sending a carrier. A 3.5mm TRS jack is provided for standard iambic paddles along with a simple TR jack for a PTT, transmit key, foot pedal or whatever. Pulling any of these inputs low will start transmitting. The keyer supports Iambic A, Iambic B, straight key, bug and single paddle modes.<br /><br />Iambic A and B behave as in the description in the links at the top of this posting. Straight key mode allows either the dit paddle or a straight key plugged into it or a separate straight key jack to key the transmitter. Bug mode allows the dit paddle to repeat while the dah paddle must be pressed manually for every dah sent. Single paddle mode is (currently) the same as Iambic A, though in theory no Iambic output can be generated as only the dit or dah paddle switch physically can close at any given time.<br /><br />TODO: Single paddle and bug mode need to prevent any iambic action while allowing a current symbol to complete. For example if you are using a squeeze paddle while in either of these modes, the closure of both paddle switches should not be physically possible on a single paddle or bug. So, we need to simulate that if possible by detecting the mode and the fact that both paddles are closed and allowing the currently playing symbol to complete and then sending the other symbol. If for example while in bug mode, if you hold the dah paddle and then tap the dit paddle, what should happen? The same thing as on a mechanical bug of course. Tapping the dit paddle would force the release of the dah paddle and play dits as long as the dit paddle was closed, complete the last dit playing and then go key up. This should be doable. A single paddle should do likewise, except pressing the opposite paddle should force the original string of symbols to stop and the opposite symbol to play until the original paddle opens and then closes again.<br /><br />If we are in straight key mode, ignore the dah paddle. This should allow the straight key to be connected either with a TRS or a TS 3.5mm plug into the paddle TRS jack. The dit paddle should be wired to the tip and the dah paddle to the ring. So, if the dah paddle is continually shorted to ground, we will ignore it and not generate any iambic output as long as it is physically plugged in when the keyer is powered up.<br /><br />When the straight key (PTT) input is used, the keyer automatically switches to straight key mode. We restore the keyer mode whenever either paddle is pressed to whatever mode we were in before pressing the separate straight key input.<br /><br />The following code provides for the ability to check debounced paddle state and generate dit and dah symbols as determined by the current mode and state of the keyer.</span></span></div>
<div>
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"></span></span></span></span></span></span><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// Initialize the Arduino pins used by the keyer. The default set of pins is used.<br />//<br />void keyer::Init()<br />{<br /> initPins(defaultDitPin, defaultDahPin, defaultKeyPin, defaultSidetonePin, defaultSpeedPin);<br />}<br /><br />// Initialize the Arduino pins used by the keyer. The default set of pins is used.<br />//<br />void keyer::initPins()<br />{<br /> initPins(defaultDitPin, defaultDahPin, defaultKeyPin, defaultSidetonePin, defaultSpeedPin);<br />}</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// If the debounced dit paddle is depressed, or if we have buffered a dit, then<br />// we should generate a dit.<br />//<br />void keyer::checkDitPaddle()<br />{<br /> bool fSendit = false;<br /><br /> // Save the (inverted) state of the paddle so it will not change while</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> // we process it<br /> //<br /> if (!ditState)<br /> ditBuffer = 1; // This indicates we need to send a dit as that</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> // paddle is pressed<br /><br /> fSendit = (ditBuffer || ditMemory);<br /><br /> switch (keyerModeSave)<br /> {<br /> case keyerModeStraight:<br /> if (ditBuffer)<br /> keyDown();<br /> else<br /> keyUp();<br /> break;<br /><br /> default:<br /> if (fSendit)<br /> sendDit();<br /> break;<br /> }<br /> ditBuffer = 0;<br />}<br /><br />void keyer::checkDahPaddle()<br />{<br /> bool fSendit = false;<br /><br /> // There is no dah paddle if a straight key is plugged into the paddle jack<br /> //<br /> if (keyerModeSave == keyerModeStraight)<br /> return;<br /><br /> // Save the (inverted) state of the paddle so it will not change while</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> // we process it<br /> //<br /> if (!dahState)<br /> dahBuffer = 1;<br /> <br /> fSendit = (dahBuffer || dahMemory);<br /><br /> switch (keyerModeSave)<br /> {<br /> // TODO: This needs a fix<br /> //<br /> case keyerModeBug:<br /> if (dahBuffer)<br /> keyDown();<br /> else<br /> keyUp();<br /> break;<br /> <br /> default: <br /> if (fSendit)<br /> sendDah();<br /> break;<br /> }<br /> dahBuffer = 0;<br />}<br /><br />// This assumes there is a separate straight key or transmit key input<br />// on the defaultStraightKeyPin pin that will send a carrier as long as</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// the input is low<br />//<br />void keyer::checkStraightKey()<br />{<br /> if (!digitalRead(defaultStraightKeyPin))<br /> {<br /> ditBuffer = 1;<br /> keyerMode = keyerModeStraight;<br /> }<br />}<br /><br />// Check both paddles and the separate straight key input<br />//<br />void keyer::checkPaddles()<br />{<br /> checkDitPaddle();<br /> checkDahPaddle();<br /> checkStraightKey();<br />}<br /><br />// Create dit or dah duration while checking for paddles to change.<br />// In Iambic modes, we always look for the opposite paddle to the<br />// element currently being sent. If no current element is being sent<br />// we look for both paddles.<br />//<br />void keyer::delayAndWatchKey(int duration)<br />{<br /> if (duration > 0)<br /> {<br /> endTime = millis() + duration;<br /> while (millis() < endTime)<br /> {<br /> // Whichever symbol is being sent, watch the other key<br /> //<br /> switch(beingSent)<br /> {<br /> case sendingDit:<br /> if (!dahState) dahMemory = 1;<br /> break;<br /> case sendingDah:<br /> if (!ditState) ditMemory = 1;<br /> // Intentional fall thru<br /> default:<br /> break;<br /> }<br /> }<br /> }<br />}<br /><br />// Send a dit while checking for paddles to change<br />//<br />void keyer::sendDit()<br />{<br /> beingSent = sendingDit;<br /> keyDown();<br /> delayAndWatchKey(ditTime); // Wait 1 dit time while checking paddles<br /> keyUp();<br /> beingSent = sendingNothing;<br /> delayAndWatchKey(ditTime);<br /> ditMemory = 0;<br />}<br /><br />// Send a dah while checking for paddles to change<br />//<br />void keyer::sendDah()<br />{<br /> beingSent = sendingDah;<br /> keyDown();<br /> delayAndWatchKey(3 * ditTime); // wait 3 dit timees while checking paddles<br /> keyUp();<br /> beingSent = sendingNothing;<br /> delayAndWatchKey(ditTime);<br /> dahMemory = 0;<br />}</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><br /></span></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span face=""trebuchet ms" , sans-serif">The following methods will provide for keying the I/O pin responsible for keying the transmitter. Both active high and active low output is possible as desired and indicated by keyFlags.</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br />// Key the transmitter by setting the keyPin value high or low as indicated.<br />// Additionally, sidetone is generated. This can be disable by commenting out<br />// the appropriate digitalWrite or tone function call below.<br />//<br />void keyer::keyDown()<br />{<br /> switch (keyFlags)<br /> {<br /> case activeLow:<br /> digitalWrite(keyPin, LOW);<br /> break;<br /> case activeHigh:<br /> digitalWrite(keyPin, HIGH);<br /> break;<br /> }<br /> tone(sidetonePin, sidetoneFreq);<br />}<br /><br />// Unkey the transmitter and sidetone<br />//<br />void keyer::keyUp()<br />{<br /> switch (keyFlags)<br /> {<br /> case activeLow:<br /> digitalWrite(keyPin, HIGH);<br /> break;<br /> case activeHigh:<br /> digitalWrite(keyPin, LOW);<br /> break;<br /> }<br /> noTone(sidetonePin);<br />}</span></span></span></span></span></span><br />
<span style="font-size: small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: xx-small;"><span face=""trebuchet ms" , sans-serif"><br /></span></span></span></span></span></span>
<span style="font-size: small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: xx-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: xx-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;">The keyer check method is to be called from the main loop passing the number of milliseconds since the last call. This argument is intended to allow timing periodic events by accumulating the amount of time that has passed until periodic intervals are reached. Currently used to limit how often we check the status of the CW speed pot for changes. We check the paddles and send any buffered dits and dahs. We read the CW speed pot and update the keyer speed anytime it changes and return the current wpm value to the caller.</span></span></span></span></span></span></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br /></span></span></span></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// Conventional keyer processing from the main loop. </span></span></span></span></span></span></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">//<br />int keyer::check(uint32_t msCounter)<br />{<br /> int wpm = getSpeed(); <br /> static uint32_t last_time = 0;<br /><br /> // Check for any change in the paddles<br /> checkPaddles();<br /><br /> if (msCounter - last_time >= SPEEDCHECKMS)<br /> {<br /> last_time = 0;<br /> setSpeed(wpm = readSpeedControl());<br /> }<br /> last_time += msCounter;<br /><br /> return wpm; // Pass back any changes in CW speed<br />}</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><br /></span></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">The last set of methods in the keyer are call-back functions from interrupt handlers whenever the keyer paddes are opened or closed or when the debounce timer expires. This code is very hardware specific. As written, both dit and dah paddle pins must be on PORTB pins 10 and 11 on the UNO. The trick here is to minimize interrupt latency, the code is written to assume a paticular port and set of two pins. If the default pins are changed, the logic below must change to match the new pins and port. Drop me a note if you have questions</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><br />// Callback function for pin change interrupt handler. Save the last state of the<br />// paddle I/O pins. Update the last value from the pin port. Return true if<br />// </span></span></span></span></span></span><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">either </span></span></span></span></span></span>of the pins has changed.<br />//<br />bool keyerPinChange()<br />{<br /> static uint8_t last;<br /> uint8_t prior;<br /><br /> prior = last;<br /> last = (PINB >> 3) & 0x03;<br /><br /> return (prior != last);<br />}<br /><br />// Timer 1 callback handler for keyer paddles. If this timer interrupt fires, then<br />// there have been no changes in either keyer paddle for 3 ms. Update the in-memory<br />// state variables to represent the position of both paddles.<br />//<br />void keyerTimer()<br />{<br /> keyer *pKeyer = keyer::getInstance();<br /> uint8_t newCode = (PINB >> 3) & 0x3;<br /> uint8_t oldCode = ((pKeyer->dahState << 1) | (pKeyer->ditState));<br /><br /> if (newCode != oldCode)<br /> {</span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> </span></span></span></span></span></span><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif">// ditState and dahState will represent debounced paddle state in real time</span></span></span></span></span></span></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> // </span></span></span></span></span></span></span></span></span></span></span></span><br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "times";"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"><span face=""trebuchet ms" , sans-serif"> </span></span></span></span></span></span> pKeyer->ditState = (newCode >> 0) & 0x01; <br /> pKeyer->dahState = (newCode >> 1) & 0x01;<br /> }<br />}</span></span></span> </span></span></span></div>
</div>
<div>
<br />
<span face=""trebuchet ms" , sans-serif"><span style="font-size: small;">The keyer header file defines the various I/O pins used and the keyer class.</span></span><br />
<br />
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">// ko7mKeyer.h - Morse code iambic keyer library<br />//<br />// Copyright 2014 - Jeff Whitlatch - ko7m<br />//<br /><br />#pragma once<br /><br />#include <Arduino.h><br />#include "analog.h"<br />#include "timer.h"<br /><br />// Enumerations<br />//<br />enum { keyerModeA, keyerModeB, keyerModeSinglePaddle, keyerModeBug, keyerModeStraight, keyerModeFence };<br />enum { activeHigh, activeLow };<br />enum { sendingNothing, sendingDit, sendingDah };<br /><br />// Constants<br />//<br />const int defaultDitPin = 11;<br />const int defaultDahPin = 12;<br />const int defaultSidetonePin = 10;<br />const int defaultKeyPin = A1;<br />const int defaultSpeedPin = A2;<br />const int defaultStraightKeyPin = PD4;<br /><br />const int defaultSidetoneFreq = 700;<br /><br />const int defaultWPM = 35;<br />const uint8_t maxWPM = 60;<br />const uint8_t minWPM = 10;<br /><br />const int cwOffset = 700; // 700 Hz default CW offset<br /><br />const int defaultMode = keyerModeA;<br /><br />const int analogMin = 10;<br />const int analogMax = 1010;<br /><br />const int stuckKeyDelay = 500; // dahKey stuck for 1/2 second at boot, switch to straight key<br /><br />const uint32_t SPEEDCHECKMS = 500; // Check speed changes every 1/2 second<br /><br />class keyer<br />{<br /> public:<br /> keyer();<br /> keyer(int dit, int dah, int key, int sidetone, int speedControl);<br /> static keyer *getInstance();<br /><br /> void Init();<br /> void initPins();<br /> void initPins(int dit, int dah, int key, int sidetone, int speedControl);<br /><br /> int check(uint32_t msCounter);<br /> int readSpeedControl();<br /> int getSpeed();<br /> void setSpeed(int newWPM);<br /> int getSidetoneFreq();<br /> void setSidetoneFreq(int newFreq);<br /> void swapPaddles();<br /> void checkPaddles();<br /> void sendBuffer();<br /><br /> int keyerModeSave;<br /><br /> uint8_t ditState; // The actual status of the paddle I/O pins in real time, debounced<br /> uint8_t dahState;<br /> <br /> private: <br /> uint8_t ditBuffer; // Buffer of paddle state during processing<br /> uint8_t dahBuffer;<br /> uint8_t ditMemory; // Single symbol buffers<br /> uint8_t dahMemory;<br /> uint8_t ditIambicBuf; // Iambic B symbol buffers<br /> uint8_t dahIambicBuf;<br /><br /> int WPM;<br /> int CPM;<br /><br /> int keyerMode;<br /> int ditTime;<br /><br /> int keyFlags;<br /> int beingSent;<br /><br /> int ditPin;<br /> int dahPin;<br /> int keyPin;<br /> int sidetonePin;<br /> int speedPin;<br /><br /> int sidetoneFreq;<br /><br /> uint32_t endTime;<br /><br /> void checkDitPaddle();<br /> void checkDahPaddle();<br /> void checkStraightKey();<br /> void delayAndWatchKey(int duration);<br /> void sendDit();<br /> void sendDah();<br /> void keyDown();<br /> void keyUp();<br /><br /> // CW speed potentiometer interpolation table<br /> // The speed pot is a log taper device. Here we create an interpolation table to remap<br /> // the output to be more linear. The following table is used to build the interpolation<br /> // table used below.<br /><br /> //Position Resistance ADC WPM<br /> // 0.00% 0.00% 0 10<br /> // 10.00% 2.00% 20 13<br /> // 20.00% 4.00% 41 16<br /> // 30.00% 6.00% 61 19<br /> // 40.00% 8.00% 82 22<br /> // 50.00% 10.00% 102 25<br /> // 60.00% 20.00% 205 28<br /> // 70.00% 35.00% 358 31<br /> // 80.00% 55.00% 563 34<br /> // 90.00% 82.00% 839 37<br /> // 100.00% 100.00% 1023 40<br /><br /><br /> // Input values (ADC values)<br /> int inVal[11] = { 0, 10, 20, 41, 82, 102, 205, 358, 563, 829, 1023 };<br /><br /> // Output values (WPM values)<br /> int outVal[11] = { 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40 };<br />};</span></span></div>
Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-53637761105661204532020-05-31T10:46:00.001-07:002020-05-31T10:46:23.341-07:00Alzheimers Poem<div style="text-align: center;">Do not ask me to remember<br />Don't try to make me understand<br />Let me rest and know you're with me<br />Kiss my cheek and hold my hand</div><div style="text-align: center;"><br /></div><div style="text-align: center;">I'm confused beyond your concept<br />I'm sad and sick and lost<br />All I know is that I need you<br />To be with me at all cost</div><div style="text-align: center;"><br /></div><div style="text-align: center;">Do not lose your patience with me<br />Do not scold or curse or cry<br />I can't help the way I'm acting<br />Can't be different though I try</div><div style="text-align: center;"><br /></div><div style="text-align: center;">Just remember that I need you<br />That the best of me is gone<br />Please don't fail to stand beside me<br />Love me 'til my life is done</div><div style="text-align: center;"><br /></div><div style="text-align: center;">by Owen Darnell</div><div style="text-align: center;"><br /></div>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-54545249052559700392020-01-25T08:30:00.000-08:002020-01-25T10:16:39.302-08:00Something to PonderThe paradox of our time in history is that we have taller buildings but shorter tempers, wider freeways, but narrower viewpoints. We spend more, but have less, we buy more, but enjoy less. We have bigger houses and smaller families, more conveniences, but less time. We have more degrees but less sense, more knowledge, but less judgment, more experts, yet more problems, more medicine, but less wellness.<br />
<br />
We drink too much, smoke too much, spend too recklessly, laugh too little, drive too fast, get too angry, stay up too late, get up too tired, read too little, watch TV too much, and pray too seldom.<br />
<br />
We have multiplied our possessions, but reduced our values. We talk too much, love too seldom, and hate too often.<br />
<br />
We've learned how to make a living, but not a life. We've added years to life not life to years. We've been all the way to the moon and back, but have trouble crossing the street to meet a new neighbor. We conquered outer space but not inner space. We've done larger things, but not better things.<br />
<br />
We've cleaned up the air, but polluted the soul. We've conquered the atom, but not our prejudice. We write more, but learn less. We plan more, but accomplish less. We've learned to rush, but not to wait. We build more computers to hold more information, to produce more copies than ever, but we communicate less and less.<br />
<br />
These are the times of fast foods and slow digestion, big men and small character, steep profits and shallow relationships. These are the days of two incomes but more divorce, fancier houses, but broken homes. These are days of quick trips, disposable diapers, throwaway morality, one night stands, overweight bodies, and pills that do everything from cheer, to quiet, to kill. It is a time when there is much in the showroom window and nothing in the stockroom. A time when technology can bring this letter to you, and a time when you can choose either to share this insight, or to just hit delete.<br />
<br />
Remember to spend some time with your loved ones, because they are not going to be around forever.<br />
<br />
Remember, say a kind word to someone who looks up to you in awe, because that little person soon will grow up and leave your side.<br />
<br />
Remember, to give a warm hug to the one next to you, because that is the only treasure you can give with your heart and it doesn't cost a cent.<br />
<br />
Remember, to say, 'I love you' to your partner and your loved ones, but most of all mean it. A kiss and an embrace will mend hurt when it comes from deep inside of you.<br />
<br />
Remember to hold hands and cherish the moment for someday that person will not be there again.<br />
<br />
Give time to love, give time to speak! And give time to share the precious thoughts in your mind.<br />
<br />
And always remember, life is not measured by the number of breaths we take, but by those moments that take our breath away.<br />
<br />
<a href="https://en.wikipedia.org/wiki/George_Carlin">George Carlin</a> - 1937-2008Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-44166236486635446942020-01-01T21:40:00.001-08:002020-01-01T21:40:34.534-08:00Happy New YearHappy New Year everyone. Make it a good one. Don't forget to stop and smell the roses and spend time on who and what is important to you. Resist the urge to take anyone or anything for granted. Give a little and leave those you touch better than you found them. Above all, forgive those who have wronged you and don't let hate win. Happy New Year.Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-49436407254328614982018-12-25T07:11:00.002-08:002018-12-25T07:11:16.860-08:00Merry Christmas!
<div class="post-header">
</div>
<span style="font-family: "trebuchet ms" , sans-serif;">Politically correct or not, let me take this opportunity to wish everyone a Merry Christmas and a Happy New Year.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Afrikaans:</b> Geseënde Kersfees</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Afrikander: </b>Een Plesierige Kerfees</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>African/ Eritrean/ Tigrinja:</b> Rehus-Beal-Ledeats</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Albanian: </b>Gezur Krislinjden</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Arabic:</b> Milad Majid</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Argentine:</b> Feliz Navidad</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Armenian:</b> Shenoraavor Nor Dari yev Pari Gaghand</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Azeri:</b> Tezze Iliniz Yahsi Olsun</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Bahasa Malaysia:</b> Selamat Hari Natal</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Basque:</b> Zorionak eta Urte Berri On!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Bengali:</b> Shuvo Naba Barsha</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Bohemian:</b> Vesele Vanoce</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Bosnian:</b> (BOSANSKI) Cestit Bozic i Sretna Nova godina</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Brazilian:</b> Feliz Natal</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Breton:</b> Nedeleg laouen na bloavezh mat</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Bulgarian:</b> Tchestita Koleda; Tchestito Rojdestvo Hristovo</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Catalan:</b> Bon Nadal i un Bon Any Nou!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Chile:</b> Feliz Navidad</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Chinese:</b> (Cantonese) Gun Tso Sun Tan'Gung Haw Sun</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Chinese:</b> (Mandarin) Sheng Dan Kuai Le</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Choctaw:</b> Yukpa, Nitak Hollo Chito</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Columbia:</b> Feliz Navidad y Próspero Año Nuevo</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Cornish:</b> Nadelik looan na looan blethen noweth</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Corsian:</b> Pace e salute</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Crazanian:</b> Rot Yikji Dol La Roo</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Cree: </b>Mitho Makosi Kesikansi</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Croatian:</b> Sretan Bozic</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Czech:</b> Prejeme Vam Vesele Vanoce a stastny Novy Rok</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Danish:</b> Glædelig Jul</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Duri:</b> Christmas-e- Shoma Mobarak</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Dutch:</b> Vrolijk Kerstfeest en een Gelukkig Nieuwjaar! or Zalig Kerstfeast</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>English:</b> Merry Christmas</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Eskimo:</b> (inupik) Jutdlime pivdluarit ukiortame pivdluaritlo!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Esperanto:</b> Gajan Kristnaskon</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Estonian:</b> Rõõmsaid Jõulupühi</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Ethiopian:</b> (Amharic) Melkin Yelidet Beaal</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Faeroese:</b> Gledhilig jol og eydnurikt nyggjar!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Farsi: </b>Cristmas-e-shoma mobarak bashad</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Finnish: </b>Hyvaa joulua</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Flemish:</b> Zalig Kerstfeest en Gelukkig nieuw jaar</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>French: Joyeux Noel</b></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Frisian:</b> Noflike Krystdagen en in protte Lok en Seine yn it Nije Jier!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Galician: </b>Bo Nada</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Gaelic:</b> Nollaig chridheil agus Bliadhna mhath ùr!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>German:</b> Fröhliche Weihnachten</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Greek: </b>Kala Christouyenna!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Haiti:</b> (Creole) Jwaye Nowel or to Jesus Edo Bri'cho o Rish D'Shato Brichto</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Hausa: </b>Barka da Kirsimatikuma Barka da Sabuwar Shekara!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Hawaiian:</b> Mele Kalikimaka</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Hebrew: </b>Mo'adim Lesimkha. Chena tova</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Hindi:</b> Shub Naya Baras (good New Year not Merry Christmas)</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Hungarian:</b> Boldog Karácsonyt</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Icelandic:</b> Gledileg Jol</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Indonesian:</b> Selamat Hari Natal</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Iraqi:</b> Idah Saidan Wa Sanah Jadidah</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Irish:</b> Nollaig Shona Dhuit, or Nodlaig mhaith chugnat</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Iroquois:</b> Ojenyunyat Sungwiyadeson honungradon nagwutut. Ojenyunyat osrasay.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Italian:</b> Buone Feste Natalizie</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Japanese:</b> Shinnen omedeto. Kurisumasu Omedeto</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Jiberish:</b> Mithag Crithagsigathmithags</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Korean: </b>Sung Tan Chuk Ha</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Kurdish:</b> Serî sallî nwê pîroz</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Lao: </b>souksan van Christmas</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Latin:</b> Natale hilare et Annum Faustum!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Latvian:</b> Prieci'gus Ziemsve'tkus un Laimi'gu Jauno Gadu!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Lausitzian:</b> Wjesole hody a strowe nowe leto</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Lettish:</b> Priecigus Ziemassvetkus</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Lithuanian: </b>Linksmu Kaledu</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Low Saxon:</b> Heughliche Winachten un 'n moi Nijaar</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Luxembourgish:</b> Schèine Chreschtdaag an e gudde Rutsch</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Macedonian:</b> Sreken Bozhik</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Maltese:</b> IL-Milied It-tajjeb</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Manx:</b> Nollick ghennal as blein vie noa</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Maori:</b> Meri Kirihimete</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Marathi:</b> Shub Naya Varsh (good New Year not Merry Christmas)</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Navajo:</b> Merry Keshmish</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Norwegian:</b> God Jul, or Gledelig Jul</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Occitan: </b>Pulit nadal e bona annado</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Papiamento:</b> Bon Pasco </span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Papua New Guinea: </b>Bikpela hamamas blong dispela Krismas na Nupela yia i go long yu</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Pennsylvania German: </b>En frehlicher Grischtdaag un en hallich Nei Yaahr!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Peru:</b> Feliz Navidad y un Venturoso Año Nuevo</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Philippines:</b> Maligayang Pasko!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Polish:</b> Wesolych Swiat Bozego Narodzenia or Boze Narodzenie</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Portuguese:</b> Feliz Natal</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Pushto:</b> Christmas Aao Ne-way Kaal Mo Mobarak Sha</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Rapa-Nui (Easter Island):</b> Mata-Ki-Te-Rangi. Te-Pito-O-Te-Henua</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Rhetian:</b> Bellas festas da nadal e bun onn</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Romanche: </b>(sursilvan dialect): Legreivlas fiastas da Nadal e bien niev onn!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Rumanian:</b> Sarbatori vesele or Craciun fericit</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Russian: </b>Pozdrevlyayu s prazdnikom Rozhdestva is Novim Godom</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Sami: </b>Buorrit Juovllat</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Samoan: </b>La Maunia Le Kilisimasi Ma Le Tausaga Fou</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Sardinian: </b>Bonu nadale e prosperu annu nou</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Scots Gaelic: </b>Nollaig Chridheil dhuibh</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Serbian: </b>Hristos se rodi.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Singhalese:</b> Subha nath thalak Vewa. Subha Aluth Awrudhak Vewa</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Slovak: </b>Vesele Vianoce. A stastlivy Novy Rok</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Slovene:</b> Vesele Bozicne Praznike Srecno Novo Leto or Vesel Bozic in srecno Novo leto</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Spanish:</b> Feliz Navidad</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Swedish: </b>God Jul and (Och) Ett Gott Nytt Ã…r</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Switzerland (Swiss-German): </b>Schöni Wienachte</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Tagalog:</b> Maligayamg Pasko. Masaganang Bagong Taon</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Tamil:</b> (Tamizh) Nathar Puthu Varuda Valthukkal (good New Year not Merry Christmas)</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Trukeese:</b> (Micronesian) Neekiriisimas annim oo iyer seefe feyiyeech!</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Thai: </b>Sawadee Pee Mai or souksan wan Christmas</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Turkish:</b> Noeliniz Ve Yeni Yiliniz Kutlu Olsun</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Ukrainian: </b>Z Rizdvom Khrystovym or S rozhdestvom Kristovym</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Urdu:</b> Naya Saal Mubarak Ho (good New Year not Merry Christmas)</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Vietnamese:</b> Chuc Mung Giang Sinh</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Welsh:</b> Nadolig Llawen</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><b>Yoruba:</b> E ku odun, e ku iye'dun!</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-29775634965223594902018-04-13T20:56:00.001-07:002018-04-13T20:58:09.434-07:00New Due Case<span style="font-family: "trebuchet ms" , sans-serif; font-size: small;">I recently picked up a nice little laser-cut case for my Arduino Due. This one is by GeauxRobot. They did a nice job on it including making little T-shaped button plastics to make the reset and erase buttons available.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif; font-size: small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif; font-size: small;">This one does a nice job of supporting the ends of the board. All the I/O connectors are nice and flush with the top of the case so shields can be handled as well as the SPI and JTAG connectors being accessible.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif; font-size: small;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-5L5Nccgfhg8/WtF71twxPdI/AAAAAAAAHqo/3VevOxpfnoEZPHXT7AfOD5gNVUuDzAcfACLcBGAs/s1600/20180413_205059.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="225" src="https://1.bp.blogspot.com/-5L5Nccgfhg8/WtF71twxPdI/AAAAAAAAHqo/3VevOxpfnoEZPHXT7AfOD5gNVUuDzAcfACLcBGAs/s400/20180413_205059.jpg" width="400" /></a></div>
<span style="font-family: "trebuchet ms" , sans-serif; font-size: small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif; font-size: small;">Available on Amazon. Be sure to use smile.amazon.com and support your favorite charity!</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-56292787937453118642018-01-27T14:01:00.003-08:002022-09-14T19:49:24.297-07:00Optical Isolator<span face=""trebuchet ms" , sans-serif">For some time now I have been chasing problems where I have multiple pieces of mains-powered equipment connected together via USB that for seemingly random reasons refuse to communicate.</span><br />
<br />
<span face=""trebuchet ms" , sans-serif">Speculating that at least some of this behaviour is related the common ground through the USB cable and having separate power sources, I have built an optical isolator.</span><br />
<br />
<span face=""trebuchet ms" , sans-serif">This incarnation takes 3V3 - 5V inputs and separates the power sources from each side. There is only an optical signal connection between the sides. The solution utilizes a 6N137 optical isolator. I think in some circuits the amount of drive required may be problematic so version 2 will have a input buffer. The output is inverted so a simple transistor (2N3906) inverter is used to set that right again.</span><br />
<br />
<span face=""trebuchet ms" , sans-serif">So far so good, no "failure to communicate" issues have been seen with the isolation in place. This particular board was already available from OSHPark thanks to Jason Pepas (http://github.com/pepaslabs) so all I had to do was build it. My version has input output buffering, level conversion and an FTDI USB to UART chip.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-xBpnZY6JQQ4/Wmz0WY_7_II/AAAAAAAAHYQ/6YrB7tPJXnIV2exg3053fTrnP7lh_a9dQCKgBGAs/s1600/20180127_134111.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1526" data-original-width="1600" height="381" src="https://1.bp.blogspot.com/-xBpnZY6JQQ4/Wmz0WY_7_II/AAAAAAAAHYQ/6YrB7tPJXnIV2exg3053fTrnP7lh_a9dQCKgBGAs/s400/20180127_134111.jpg" width="400" /> </a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span face=""trebuchet ms" , sans-serif">Jason was kind enough to publish the gerbers, Kicad model and a PDF of the schematic. He also did a nice write-up on his testing experiences.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-CrcWUTBryPI/Wmz2o5Ev3AI/AAAAAAAAHYc/IdXfr4AH8R8NhDcNdWhNEx5VjebT4RFHgCLcBGAs/s1600/OptoIsolator.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="677" data-original-width="1277" height="211" src="https://1.bp.blogspot.com/-CrcWUTBryPI/Wmz2o5Ev3AI/AAAAAAAAHYc/IdXfr4AH8R8NhDcNdWhNEx5VjebT4RFHgCLcBGAs/s400/OptoIsolator.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<span face=""trebuchet ms" , sans-serif"> </span> </div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com2tag:blogger.com,1999:blog-6737713102444501259.post-24349732346037403062017-12-31T17:24:00.003-08:002017-12-31T17:24:48.214-08:00Happy New Year!<span style="font-family: "Trebuchet MS", sans-serif;">I wish you all the best in 2018. Happy New Year everyone!</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-90713875202742523562017-12-21T22:56:00.000-08:002017-12-23T21:52:20.314-08:00Christmas Greetings and Puzzle - UPDATED<h2>
<span style="font-family: "trebuchet ms" , sans-serif;">Just for fun... </span></h2>
<span style="font-family: "trebuchet ms" , sans-serif;">Wow, what a year, eh? I wish all the best for you all in the holidays and the new year. Here is a little puzzle for you, just for fun. It is a coloring puzzle, so just shade in the number of squares shown in each row and column with at least one space between. For example, if you see 4 1 3 1 in a row then you would shade in 4 squares, skip at least 1, shade 1 square, skip at least 1, shade 3, skip at least 1 and then shade 1 square. But, remember it has to work both vertically and horizontally. If you get stuck, I will try to help you out.</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">My best to you and your families in this holiday season and regardless of political or social correctness, Merry Christmas and Happy New Year.</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">73's de Jeff - ko7m</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">PS: There was an error in the previously posted image. Fifth row from the bottom, numbers should be 131231. I have updated the image below. My apologies. </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-trR9_GqlzLs/Wj81M0bBzCI/AAAAAAAAHRw/3WmFXp-pmm40tx99ldpTkE-n1nHHM0nSgCLcBGAs/s1600/Screenshot%2Bfrom%2B2017-12-23%2B21-02-29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="511" data-original-width="445" height="400" src="https://3.bp.blogspot.com/-trR9_GqlzLs/Wj81M0bBzCI/AAAAAAAAHRw/3WmFXp-pmm40tx99ldpTkE-n1nHHM0nSgCLcBGAs/s400/Screenshot%2Bfrom%2B2017-12-23%2B21-02-29.png" width="347" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-43261890794745982022017-11-26T17:23:00.000-08:002017-11-26T22:14:50.667-08:00Web Server Hack<span style="font-family: "trebuchet ms" , sans-serif;">I needed a quick hack of a web server that would simply serve up files on an embedded Linux machine that has only a minimal kernel and shell. This assumes a working knowledge of Linux and shell scripting. The script text should be put in a text file and marked as executable. In my case I chose to use /usr/bin/wwwd.</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">Tell Linux this is a shell script.</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">#! /bin/sh</span></span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">#<span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;"> </span></span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">Define the end of line sequence and the location of our web pages.</span></span><br /><br />eol=$(printf "\r\n")<br />base=/var/www</span></span></span><br />
<br />
<span style="font-size: small;">W<span style="font-family: "trebuchet ms" , sans-serif;">e let inetd deal with the network part of being a web server. We just read the HTTP request and then parse (and ignore) the header of the request.</span></span> <br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><br />read request</span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">Find the end of the header (it is separated from the body by a blank line.)</span></span></span></span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><br />while /bin/true; do<br /> read header<br /> [ "$header" == "$eol" ] && break;<br />done</span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">Parse the request and construct a path to the desired file.</span></span></span></span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;"></span></span><br />url="${request#GET }"<br />url="${url% HTTP/*}"<br />filename="$base$url"<span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;"> </span></span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">If the file exists, then send it to the client with a minimal HTTP header. If the file does not exist, then issue a 404 error.</span></span></span></span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;"> </span></span><br />if [ -f "$filename" ]; then<br /> echo -e "HTTP/1.1 200 OK\r"<br /> echo -e "Content-Type: text/html\r"<br /> echo -e "\r"<br /> cat "$filename"<br /> echo -e "\r"<br />else<br /> echo -e "HTTP/1.1 404 Not Found\r"<br /> echo -e "Content-Type: text/html\r"<br /> echo -e "\r"<br /> echo -e "404 Not Found\r"<br /><span style="font-family: "Courier New", Courier, monospace;"> echo -e "\r"<br />fi</span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-family: "Courier New", Courier, monospace;"></span><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;">exit </span></span></span></span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">That is really all there is to this hack and this is about as minimalistic as you can get. If you want to add CGI capabilities, we would need to construct an environment variable to pass to the program to be executed to give it the query that was issued and then pass the program output back as the result of the HTTP GET request. Since I don't need this ability yet, I will leave that as an exercise for the interested reader. </span></span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">In order to integrate with inetd we need to add a line to /etc/inetd.conf to tell inetd what application to launch when web connections are received. In my case this script is named /usr/bin/wwwd. Don't forget to make it executable:</span></span></span></span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;"><br /></span></span></span></span></span>
<span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">chmod +x /usr/bin/wwwd</span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;"><br /></span></span></span></span></span>
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">Add the following line, adjusting the path as necessary to point to your script. You can find full details on inetd <a href="http://etutorials.org/Linux+systems/how+linux+works/Chapter+6+Network+Services/6.3+The+inetd+Daemon/">here</a>.</span></span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">www stream tcp nowait nobody /usr/bin/wwwd wwwd </span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">You then need to restart inetd which can be accomplished by killing that process. On my embedded Linux machine, the following command line will suffice:</span></span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">kill -SIGHUP $(cat /var/run/inetd.pid)</span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">You should now be able to point your web browser to your device and fetch any of the files (HTML or otherwise) that are contained in /var/www.</span></span></span></span></span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: small;"><span style="font-family: "trebuchet ms" , sans-serif;">As always, if you have any questions or issues, I am happy to help. </span></span> </span></span><br /> </span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-71907484561217062182017-11-23T09:14:00.002-08:002017-11-23T09:14:58.674-08:00Happy Thanksgiving Everyone<span style="font-family: "Trebuchet MS", sans-serif;">We always say "Rest in Peace" when someone has moved on. What I hope for all is we can "Live in Peace". Happy Thanksgiving.</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-45753281084804767122017-10-29T11:43:00.001-07:002017-10-29T17:36:29.553-07:00A fun toy...<span style="font-family: "trebuchet ms" , sans-serif;">Had a bit of fun today running a 6502 processor simulator on an Arduino that was running a copy of EhBasic. Not much practical you can do with it, but kinda fun to play around with a tiny micro-controller (Arduino) simulating the same processor that was used in the original Apple computers and running a basic emulator on the simulated processor.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-btImLIiBgWc/WfYdvxVdGFI/AAAAAAAAG-8/n1kU0U-KszEZ76e0j-vEMfVWV3kZHRAQQCLcBGAs/s1600/Screenshot%2Bfrom%2B2017-10-29%2B11-25-18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="500" data-original-width="619" height="322" src="https://3.bp.blogspot.com/-btImLIiBgWc/WfYdvxVdGFI/AAAAAAAAG-8/n1kU0U-KszEZ76e0j-vEMfVWV3kZHRAQQCLcBGAs/s400/Screenshot%2Bfrom%2B2017-10-29%2B11-25-18.png" width="400" /></a></div>
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">This was fun for me because my first experience with micro-controllers was when the MOS Technology 6502 was introduced at the Wescon trade show in San Francisco in 1975. I was at the time working for Hewlett Packard Advanced Products Division in Santa Clara, CA interestingly enough in the same group of teams that employed Steve Wozniak who later went on to found Apple Computer.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">A group of us went to the trade show in the fall of 1975 and were able to purchase the 6502 for $25. The hardware and programming documentation manuals were another $10. At the time, Motorola was selling the 6800 microprocessor for a single unit price of $175 and ended up dropping the price to $69 in single unit quantities with further price reductions to follow. Bringing our loot home spawned a lot of effort to build and program working systems. Cross-compilers and assemblers were built and utilized to program our new hardware.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">I still have my 6502 system built back in the mid-1970's. I will provide some pictures as soon as I figure out what box contains that little treasure.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">It is fun to think that the meager capabilities represented by an Arduino is able to simulate the silicon that was available back when I first started working with micro-controllers. We have indeed come a long way.</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-85352621524259889272017-10-15T19:41:00.002-07:002017-10-15T21:42:22.058-07:00Sorry for neglect of blog for so long<span style="font-family: "trebuchet ms" , sans-serif;">Hi Folks,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">It has been a while since I tended to my blog for a lot of reasons which I won't bore you with. I have a backlog of comments that are pending approval for publishing which I will be working through in chronological order. My apologies again for being tardy in tending to these comments as they are important to me. Thank you for your patience as I get caught up on blog maintenance.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span><span style="font-family: "trebuchet ms" , sans-serif;">UPDATE: I have published all outstanding comments to posts. I have attempted to reply to them but seem to be having some difficulty in doing so currently. Google is kindly tossing my replies to comments in the bin after I submit them silently. Thank you for your patience as I sort this out.</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">UPDATE: I have sorted out my problems replying to comments to posts. It seems that google Chrome is for whatever reason failing to post the updates. I have for the moment switched for Firefox and the problem does not repeat, so likely something in my Chrome settings. This will work in the meantime. Thanks again for your patience. </span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-70544487455048720002017-05-27T13:16:00.001-07:002017-05-27T13:16:24.994-07:00In case of fire...<span style="font-family: Trebuchet MS, sans-serif;">In case of fire alarm:</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"> % git commit</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"> % git push</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">Grab the KX3 and leave the building...</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com2tag:blogger.com,1999:blog-6737713102444501259.post-40133942172926103312017-04-29T15:54:00.000-07:002017-04-30T12:43:13.651-07:00Github updated<span style="font-family: "trebuchet ms" , sans-serif;">I have started to collect some useful tool source code for Arduino projects into a github repository. Some of these projects have been published on this blog, but not all of them. I will try to to maintain anything that I post here also on github for the convenience of those that find any of my examples useful.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">You can find my Arduino github repository <a href="https://github.com/ko7m/Arduino/">here</a>.</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-24804446328490776762017-04-20T11:30:00.003-07:002017-11-06T20:10:00.394-08:00UPDATED: A Silly Little Project<span style="font-family: "trebuchet ms" , sans-serif;">I have a <a href="https://www.freetronics.com.au/products/etherten">Freetronics EtherTen</a> Arduino Uno-compatible board that includes SD card and Ethernet support on-board. It is a cool little board that I use a lot for prototyping a lot of projects. I also have a <a href="https://www.freetronics.com.au/collections/display/products/lcd-keypad-shield">Freetronics LCD and Keypad Shield</a> that is usually attached to it. This combo usually knocks around in my go bag, but in-between I wanted it to provide some useful function at my workspace when not otherwise occupied.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">What I decided to do is build a two time zone clock that keeps synchronized to the NIST time servers.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/--tKUuhRfsco/WPjyYuwGDZI/AAAAAAAAF1g/5O4c7xup37wddMXZdv7Iyp99kenibAM-QCLcB/s1600/20170420_103007.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://4.bp.blogspot.com/--tKUuhRfsco/WPjyYuwGDZI/AAAAAAAAF1g/5O4c7xup37wddMXZdv7Iyp99kenibAM-QCLcB/s400/20170420_103007.jpg" width="400" /></a></div>
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">So, the gist here was to have UTC and my local time zone displayed along with the local time zone date. I didn't ever want to have to mess with setting it and wanted it to automatically handle daylight savings time. The clock uses the millisecond timer to keep time, resetting to time.nist.gov every hour. The update from the time server takes a couple seconds to accomplish, so we don't want to hammer on the network. Once per hour appears to be completely adequate.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">The code is a collection of code snips from the internet and some clever logic to avoid a bunch of if-then-else logic when calculating daylight savings time. I can't really claim much ownership of anything other than this unique instance of these snippits. My thanks goes out to the original authors for the inspiration and willingness to share code.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">So, let's walk through the code from the top. Here we have all necessary include files and initialization of the LCD and Ethernet bits. We use UDP to communicate with the NIST server.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// UDP NTP Client - Implements an NTP set clock</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">//</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Displays UTC and Pacific time and date on 16x2 LCD display synchronized to internet time server time.nist.gov.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Calculates daylights savings time, handles leap years</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">//</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Jeff Whitlatch ko7m - 23 Mar 2017</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">#include <Ethernet.h></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">#include <EthernetUdp.h></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">#include <LiquidCrystal.h></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Freetronics 16x2 version</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">LiquidCrystal lcd(8,9,4,5,6,7);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// MAC address to use for Ethernet adapter</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">unsigned int localPort = 8888; // local port to listen for UDP packets</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// A UDP instance to let us send and receive packets over UDP</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">EthernetUDP Udp;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Here we set up a textual month array and process the setup() function which sets up the serial debug port, ethernet port and starts the UDP listener on the local port 8888.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">const char *szMonth[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">void setup()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Open serial communications and wait for port to open:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.begin(115200);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println("Setting up ethernet");</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Init the LCD display</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.begin(16, 2);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // start Ethernet and UDP</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (Ethernet.begin(mac) == 0)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println("Failed to configure Ethernet using DHCP");</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // no point in carrying on, so do nothing forevermore:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> for (;;);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.begin(localPort);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">One-time initialization of some time-related globals is next.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">unsigned long secsSince1900 = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int hour = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int minute = 59;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int second = 58;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int day = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int month = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int year = 1900;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int dow = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">boolean fDST = false;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">char buf[256];</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">Now, we get into the main loop which keeps the display updated predominantly. Some optimization should be done here to only update the LCD when it changes, but in this simple code, I am updating it every time through the main loop.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Once per hour and on first boot, we send a NTP time request packet to the NIST server and parse the result that is returned. We just extract the information of interest. NTP is described in </span><span style="font-family: "trebuchet ms" , sans-serif;"><a href="https://tools.ietf.org/html/rfc1305">RFC 1305</a> and returns time as the number of seconds since 1 Jan 1900.</span><br />
<span style="font-size: 16.96px;"><span style="font-family: "trebuchet ms" , sans-serif;"><br /></span></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">void loop()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Once every hour, update from the net, this takes a couple seconds so we don't do every time thru loop</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (minute == 59 && second == 58)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> sendNTPpacket(timeServer); // send an NTP packet to a time server</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> delay(1000);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (Udp.parsePacket())</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // We've received a packet, read the data from it</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // the timestamp starts at byte 40 of the received packet and is four bytes,</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // or two words, long. First, extract the two words:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // combine the four bytes (two words) into a long integer</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // this is NTP time (seconds since Jan 1 1900):</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> secsSince1900 = highWord << 16 | lowWord;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.print("Seconds since Jan 1 1900 = ");</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println(secsSince1900);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Now <a href="https://en.wikipedia.org/wiki/Unix_time">Unix time</a> is the number of seconds since 1 Jan 1970, so we need to convert.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> const unsigned long seventyYears = 2208988800UL;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // subtract seventy years:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long epoch = secsSince1900 - seventyYears;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Now, we can calculate the time of day.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> hour = (epoch / 60 / 60) % 24;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> minute = (epoch / 60) % 60;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> second = epoch % 60;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="text-align: justify;">The algorithm implements a </span><a href="http://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar" style="text-align: justify;">proleptic Gregorian calendar</a><span style="text-align: justify;">. That is, the rules which adopted the Julian calendar in 1582 in Rome are applied both backwards and forwards in time. This includes a year 0, and then negative years before that, all following the rules for the Gregorian calendar. </span><span style="text-align: justify;">The accuracy of the algorithms under these rules is exact, until overflow occurs. Using 32 bit arithmetic, overflow occurs approximately at +/- 5.8 million years. Using 64 bit arithmetic overflow occurs far beyond +/- the age of the universe. The intent is to make range checking superfluous.</span></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="text-align: justify;"><br /></span></span>
<br />
<div style="text-align: justify;">
<span style="font-family: "trebuchet ms" , sans-serif;">These algorithm internally assumes that March 1 is the first day of the year. This is convenient because it puts the leap day, Feb. 29 as the last day of the year, or actually the preceding year. That is, Feb. 15, 2000, is considered by this algorithm to be the 15th day of the last month of the year 1999. This detail is only important for understanding how the algorithm works.</span></div>
<div style="text-align: justify;">
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span></div>
<div style="text-align: justify;">
<span style="font-family: "trebuchet ms" , sans-serif;">Additionally the algorithm makes use of the concept of an <i>era</i>. This concept is very handy in creating an algorithm that is valid over extremely large ranges. An era is a 400 year period. As it turns out, the calendar exactly repeats itself every 400 years. And so we first compute the era of a <code>year/month/day</code> triple, or the era of a serial date, and then factor the era out of the computation. The rest of the computation centers on concepts such as:</span></div>
<br />
<ul>
<li><span style="font-family: "trebuchet ms" , sans-serif;">What is the year of the era (<code style="text-align: justify;">yoe</code><span style="text-align: justify;">)? This is always in the range</span><span style="text-align: justify;"> </span><code style="text-align: justify;">[0, 399]</code><span style="text-align: justify;">.</span></span></li>
<li><span style="font-family: "trebuchet ms" , sans-serif;">What is the day of the era (<code style="text-align: justify;">doe</code><span style="text-align: justify;">)? This is always in the range</span><span style="text-align: justify;"> </span><code style="text-align: justify;">[0, 146096]</code><span style="text-align: justify;">.</span></span></li>
</ul>
<br />
<div>
<div style="text-align: justify;">
<span style="font-family: "trebuchet ms" , sans-serif;">Further details on the calculations can be obtained from the wonderful write-up <a href="http://howardhinnant.github.io/date_algorithms.html#civil_from_days">here</a>.</span></div>
<div style="text-align: justify;">
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span></div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Algorithm: http://howardhinnant.github.io/date_algorithms.html#civil_from_days</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // an era is a 400 year period starting 1 Mar 0000. </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> long z = epoch / 86400 + 719468;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> long era = (z >= 0 ? z : z - 146096) / 146097;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long doe = static_cast<unsigned long>(z - era * 146097);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long doy = doe - (365*yoe + yoe/4 - yoe/100);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long mp = (5*doy + 2)/153;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> month = mp + (mp < 10 ? 3 : -9);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> day = doy - (153*mp+2)/5 + 1;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> year = static_cast<int>(yoe) + era * 400;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> year += (month <= 2);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> dow = dowFromDate(day, month, year);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> fDST = isDST(day, month, dow);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Now, we have all the bits necessary for printing, so we build a text buffer and send it to the LCD display.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Build the print buffer</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> sprintf(buf, "%2d:%02d UTC %2d %s", hour, minute, day, szMonth[month-1]);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.setCursor(0, 0);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.print(buf);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Print the second time zone</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (hour < 8) hour += 24;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> sprintf(buf, "%2d:%02d %s %d", hour - 7, minute, fDST ? "PDT" : "PST", year);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.setCursor(0, 1);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.print(buf);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Keep the network alive every 60 seconds</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (second % 60 == 0)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Ethernet.maintain();</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Accuracy depends on system clock accuracy, corrected once an hour.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> delay(1000);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> secsSince1900++;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">I am not going to detail the NTP protocol here. The following is sufficient for the meager needs of this simple application. We build the UDP request packet and send if off to time.nist.gov.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// send an NTP request to the time server at the given address</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">void sendNTPpacket(char* address)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // set all bytes in the buffer to 0</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> memset(packetBuffer, 0, NTP_PACKET_SIZE);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Initialize values needed to form NTP request</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // (see URL above for details on the packets)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[0] = 0b11100011; // LI, Version, Mode</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[1] = 0; // Stratum, or type of clock</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[2] = 6; // Polling Interval</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[3] = 0xEC; // Peer Clock Precision</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // 8 bytes of zero for Root Delay & Root Dispersion</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[12] = 49;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[13] = 0x4E;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[14] = 49;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[15] = 52;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // all NTP fields have been given values, now</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // you can send a packet requesting a timestamp:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.beginPacket(address, 123); //NTP requests are to port 123</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.write(packetBuffer, NTP_PACKET_SIZE);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.endPacket();</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">The determination of daylight savings time is a bit of a moving target over time, but this function encapsulates the yes/no decision to allow for appropriate correction of the local time display along with the date.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// In most of the US, DST starts on the second Sunday of March and </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// ends on the first </span><span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Sunday of November at 2 AM both times.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">bool isDST(int day, int month, int dow)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (month < 3 || month > 11) return false;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (month > 3 && month < 11) return true;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> int prevSunday = day - dow;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // In March, we are DST if our previous Sunday was on or after the 8th.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (month == 3) return prevSunday >= 8;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // In November, we must be before the first Sunday to be DST which means the</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // previous Sunday must be before the 1st.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return prevSunday <=0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> </span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"><span style="font-family: "trebuchet ms" , sans-serif;">This one is kind-of fun. See if you can describe this one liner in words... :) </span></span></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: xx-small;"><span style="font-family: "trebuchet ms" , sans-serif;"><span style="font-family: "trebuchet ms" , sans-serif;"> </span></span></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Day of week calculation</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int dowFromDate(int d, int m, int y)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return (d += m < 3 ? y-- : y - 2, 23*m/9 + d + 4 + y/4- y/100 + y/400)%7;</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<br />
<span style="font-family: "trebuchet ms" , sans-serif;">And, that is really all there is to it. I am sure the reader can adapt the code for different time zone pairs, but if you run into difficulty I will be happy to assist if you drop me a note at ko7m at ARRL dot net. For your convenience, here is the entire code file:</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// UDP NTP Client - Implements an NTP set clock</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">//</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Displays UTC and Pacific time and date on 16x2 LCD display synchronized to internet time server time.nist.gov.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Calculates daylights savings time, handles leap years</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">//</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Jeff Whitlatch - 23 Mar 2017</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">#include <Ethernet.h></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">#include <EthernetUdp.h></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">#include <LiquidCrystal.h></span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Freetronics 16x2 version</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">LiquidCrystal lcd(8,9,4,5,6,7);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// MAC address to use for Ethernet adapter</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">unsigned int localPort = 8888; // local port to listen for UDP packets</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// A UDP instance to let us send and receive packets over UDP</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">EthernetUDP Udp;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">const char *szMonth[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">void setup()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Open serial communications and wait for port to open:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.begin(115200);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println("Setting up ethernet");</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Init the LCD display</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.begin(16, 2);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // start Ethernet and UDP</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (Ethernet.begin(mac) == 0)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println("Failed to configure Ethernet using DHCP");</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // no point in carrying on, so do nothing forevermore:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> for (;;);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.begin(localPort);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">unsigned long secsSince1900 = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int hour = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int minute = 59;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int second = 58;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int day = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int month = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int year = 1900;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int dow = 0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">boolean fDST = false;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">char buf[256];</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">void loop()</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Once every hour, update from the net, this takes a couple seconds so we don't do every time thru loop</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (minute == 59 && second == 58)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> sendNTPpacket(timeServer); // send an NTP packet to a time server</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> delay(1000);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (Udp.parsePacket())</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> {</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // We've received a packet, read the data from it</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // the timestamp starts at byte 40 of the received packet and is four bytes,</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // or two words, long. First, extract the two words:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // combine the four bytes (two words) into a long integer</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // this is NTP time (seconds since Jan 1 1900):</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> secsSince1900 = highWord << 16 | lowWord;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.print("Seconds since Jan 1 1900 = ");</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Serial.println(secsSince1900);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> const unsigned long seventyYears = 2208988800UL;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // subtract seventy years:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long epoch = secsSince1900 - seventyYears;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> hour = (epoch / 60 / 60) % 24;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> minute = (epoch / 60) % 60;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> second = epoch % 60;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Algorithm from http://howardhinnant.github.io/date_algorithms.html#civil_from_days</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // an era is a 400 year period starting 1 Mar 0000. </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> long z = epoch / 86400 + 719468;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> long era = (z >= 0 ? z : z - 146096) / 146097;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long doe = static_cast<unsigned long>(z - era * 146097);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long doy = doe - (365*yoe + yoe/4 - yoe/100);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> unsigned long mp = (5*doy + 2)/153;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> month = mp + (mp < 10 ? 3 : -9);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> day = doy - (153*mp+2)/5 + 1;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> year = static_cast<int>(yoe) + era * 400;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> year += (month <= 2);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> dow = dowFromDate(day, month, year);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> fDST = isDST(day, month, dow);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Build the print buffer</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> sprintf(buf, "%2d:%02d UTC %2d %s", hour, minute, day, szMonth[month-1]);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.setCursor(0, 0);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.print(buf);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Print the second time zone</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (hour < 8) hour += 24;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> sprintf(buf, "%2d:%02d %s %d", hour - 7, minute, fDST ? "PDT" : "PST", year);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.setCursor(0, 1);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> lcd.print(buf);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Keep the network alive every 60 seconds</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (second % 60 == 0)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Ethernet.maintain();</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Accuracy depends on system clock accuracy, corrected once an hour.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> delay(1000);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> secsSince1900++;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// send an NTP request to the time server at the given address</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">void sendNTPpacket(char* address)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // set all bytes in the buffer to 0</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> memset(packetBuffer, 0, NTP_PACKET_SIZE);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // Initialize values needed to form NTP request</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // (see URL above for details on the packets)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[0] = 0b11100011; // LI, Version, Mode</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[1] = 0; // Stratum, or type of clock</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[2] = 6; // Polling Interval</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[3] = 0xEC; // Peer Clock Precision</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // 8 bytes of zero for Root Delay & Root Dispersion</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[12] = 49;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[13] = 0x4E;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[14] = 49;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> packetBuffer[15] = 52;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // all NTP fields have been given values, now</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // you can send a packet requesting a timestamp:</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.beginPacket(address, 123); //NTP requests are to port 123</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.write(packetBuffer, NTP_PACKET_SIZE);</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> Udp.endPacket();</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// In most of the US, DST starts on the second Sunday of March and ends on the first </span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Sunday of November at 2 am both times.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">bool isDST(int day, int month, int dow)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (month < 3 || month > 11) return false;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (month > 3 && month < 11) return true;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> int prevSunday = day - dow;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // In March, we are DST if our previous sunday was on or after the 8th.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (month == 3) return prevSunday >= 8;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // In November, we must be before the first Sunday to be DST which means the</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> // previous sunday must be before the 1st.</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return prevSunday <=0;</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">// Day of week calculation</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">int dowFromDate(int d, int m, int y)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return (d += m < 3 ? y-- : y - 2, 23*m/9 + d + 4 + y/4- y/100 + y/400)%7;</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span></div>
Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-35625477299614385922017-04-18T11:00:00.002-07:002017-04-18T16:30:36.270-07:00Arduino IDE issues<span style="font-family: "trebuchet ms" , sans-serif;">Well, it has been a long time since I blogged anything so I thought I would jot down some notes about my experiences of late using recent builds of the Arduino IDE.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">I have over the last couple of years been working almost exclusively on Linux workstations, specifically Ubuntu 16.04 LTS and later and my comments are based on my experience on that platform. I have not yet attempted to reproduce any of this on Windows though I have no reason to doubt that experience would be the same.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">In my case, I have loaded up Arduino 1.8.1 on Linux and I have several comments about the later builds and this one in particular.</span><br />
<br />
<ol>
<li><span style="font-family: "trebuchet ms" , sans-serif;">There has been a lot of work done on board management and library management and this from my perspective has been a very worthwhile addition to the system. Having the ability to be notified when updates have been produced to the set of development boards supported as well as the libraries is a huge improvement over the older experience. Trying to make sure you get a library zip file loaded in exactly the correct folder without destroying the base install is a welcome addition.</span></li>
<li><span style="font-family: "trebuchet ms" , sans-serif;">In the same vein, there has been a bit of expansion on the scope of the 8 and 32 bit processors supported. This support is bundled up in packages that can be individually installed to add or remove support.</span></li>
<li><span style="font-family: "trebuchet ms" , sans-serif;">Someone finally addressed the issue of the serial monitor and serial plotter output windows having to be closed in order to recompile and upload the code. Thank you!</span></li>
</ol>
<span style="font-family: "trebuchet ms" , sans-serif;">There has also been a lot of work in turning on (by default) the various code optimization flags for the compilers and this has resulted in a lot of code space savings for my projects and I know for others. It has not come without a price however and this is one of the things I want to highlight here.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">One thing I have noticed is that the "link-time optimization" flags have been added to the default options during compile and link. If you enable verbose output in file->preferences dialogue shown below, you can see these additional command line options during compilation.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-pBaggmYaOZk/WPZO-HV0koI/AAAAAAAAF1E/MySnbRVgXQEblCKY6Z_PZlTvY9Yb8TvnwCLcB/s1600/Screenshot%2Bfrom%2B2017-04-18%2B10-35-27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="310" src="https://1.bp.blogspot.com/-pBaggmYaOZk/WPZO-HV0koI/AAAAAAAAF1E/MySnbRVgXQEblCKY6Z_PZlTvY9Yb8TvnwCLcB/s400/Screenshot%2Bfrom%2B2017-04-18%2B10-35-27.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<span style="font-family: "trebuchet ms" , sans-serif;">While I am a fan of compact code, enabling these options on one of my projects has resulted in the code linker seg faulting during compilation. I have not yet created a simple repro of the issue. The following code illustrates the issue, but does not fail to compile, so my understanding of the issue is as yet incomplete. I wrote this to try and reproduce the issue, but it fails to fail...</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">class data</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">public:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> int a;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> int b;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">};</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">class classB</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">public:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> void doSomething(data *pData);</span><span style="font-family: "courier new" , "courier" , monospace;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">};</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">class classA</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">public:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> void doit();</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">private:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> classB b;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> data d; </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">};</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">void classB::doSomething(data *pData)</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> pData->a = 1;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> pData->b = 2;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">void classA::doit()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> b.doSomething(&d);</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">classA *pA;</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">void setup()</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> pA = new classA();</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">}</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">void loop() </span><br />
<span style="font-family: "courier new" , "courier" , monospace;">{</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> pA->doit();</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;">}</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">The basic idea is that I have a class (classA) that has two other classes embedded in it (classB and data). ClassB has a method (doSomething) that gets passed a pointer to class data which it attempts to use to access public variables in class data. In my failure case, if I comment out the call to b.doSomething(&d) everything compiles fine. If I uncomment it, the GNU linker Segment Faults and terminates. The code above does not reproduce the symptoms. In my project that does reproduce the problem, if I remove the -flto command line option to disable the link-time optimizer, there is no crash and with the option set it does crash.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">There is a lot of discussion on the GNU tools blogs about this problem though my repo seems to be somewhat unique, so I have not been able to narrow the problem down further. <a href="https://github.com/arduino/Arduino/issues/660">https://github.com/arduino/Arduino/issues/660</a></span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">My temporary solution is to disable the link time optimizer and finish up the project. Once it is complete, I will re-enable the option and see if the problem still reproduces. I am not quite ready to publish this project, so I am going to wait until it is finished before reporting a bug back to GNU as I will have to publish the code to them to get it investigated. Otherwise, I will build the tool myself from the source and debug it myself. I just don't want to take the time at this moment with a side trip into debugging my toolchain.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Mostly, I wanted to publish this so that readers will be aware of it and if anyone has encountered the same problem, I would love to compare notes.</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-55472408637986423982017-01-09T19:47:00.001-08:002017-01-09T19:50:03.024-08:00HC SVNT DRACONES<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-OlJ84316Raw/WHRYwWHTrVI/AAAAAAAAFcQ/RJT0AfXnDpcPBGPEDEkrR1O4uVWaFaJ0QCLcB/s1600/QRCode.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-OlJ84316Raw/WHRYwWHTrVI/AAAAAAAAFcQ/RJT0AfXnDpcPBGPEDEkrR1O4uVWaFaJ0QCLcB/s1600/QRCode.png" /></a></div>
<br />
<br />
<div style="text-align: center;">
<span style="font-family: Trebuchet MS, sans-serif;">Friends don't let friends pet watchdogs from timers or interrupts...</span></div>
Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com1tag:blogger.com,1999:blog-6737713102444501259.post-80122570490302000172016-12-31T22:24:00.002-08:002016-12-31T22:24:42.808-08:00Happy New Year<span style="font-family: Trebuchet MS, sans-serif;">I wish you all the best in 2017. Happy New Year!</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-19835026241693073452016-12-24T18:07:00.001-08:002016-12-24T18:08:09.820-08:00Christmas Thoughts<span style="font-family: "trebuchet ms" , sans-serif;">Well, here we are again, approaching Christmas time.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">And I give thanks for the year, even the worst of it was fine.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">As I look around me and see Christmas cheer,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">I wonder why this mood can't last throughout the year.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Perhaps we'd really like to keep Christmas everyday,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">But we lose the spirit, because self gets in the way.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">So I write this poem, and put before the eyes of men,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">That cannot see others beyond the veil of self.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">If we could just see past their faults and love the soul beyond,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">We could give the gift of love to make our brothers strong.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">Yes, we could give the same gift, in just a different way,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">As the gift given the world on that first Christmas day.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">I see the end approaching and love is in short supply,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">Souls lost and lonely, should cause our hearts to cry.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;">So may a sense of selflessness guide me throughout the year,</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;">To love all those around me, and spread to them Christmas cheer.</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-84282237234202413142016-12-15T08:33:00.001-08:002016-12-15T08:44:48.894-08:00St. Lucia - 10 Metre Contest<span style="font-family: "trebuchet ms" , sans-serif;">While on St. Lucia, we decided to put a 10 metre station into the contest last weekend. We ran a single station with multiple operators on CW and SSB. Band conditions were pretty abismal, but I think we did pretty well all things considered.</span><br />
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> ARRL10M Summary Sheet</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> Start Date : 2016-12-10</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> CallSign Used : J68HF</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Operator(s) : K0BBC KI8R W6HFP</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;">Operator Category : MULTI-OP</span><br />
<span style="font-family: "courier new" , "courier" , monospace;">Assisted Category : ASSISTED</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Band : 10M</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Power : LOW</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Mode : MIXED</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Gridsquare : FK94MC</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> Name : Buddies In The Carribean</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Address : Manowar Road</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Cap Estate Gros Islet</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Country : St Lucia</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> ARRL Section : DX</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Club/Team : Minnesota Wireless Assn</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Software : N1MM Logger+ 1.0.5995.0</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> Band Mode QSOs Pts S-P Cty Pt/Q</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 28 CW 321 1284 49 16 4.0</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> 28 USB 242 484 40 8 2.0</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Total Both 563 1768 89 24 3.1</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"> Score : 199,784</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Rig : FT-991</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<span style="font-family: "courier new" , "courier" , monospace;"></span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> Antennas : Buddipole 3 element yagi</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-QmCFiptQMZo/WFLHwbTpjFI/AAAAAAAAFTQ/jSZKPFWXWfEctNTn4RD3xf7SWm7McdYogCLcB/s1600/IMG_20161210_130550.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="298" src="https://3.bp.blogspot.com/-QmCFiptQMZo/WFLHwbTpjFI/AAAAAAAAFTQ/jSZKPFWXWfEctNTn4RD3xf7SWm7McdYogCLcB/s400/IMG_20161210_130550.jpg" width="400" /></a></div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-6565242452145533822016-12-07T14:11:00.002-08:002016-12-15T19:01:46.739-08:00St. Lucia - Day Four<span style="font-family: "trebuchet ms" , sans-serif;">Today has been non-stop contacts across the entire group on most bands. 17 metres has been hopping most of the day. I was able to work many European contries this morning and this afternoon was spent working US stations.</span><br />
<div>
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "trebuchet ms" , sans-serif;">So far my modest efforts have yielded four continents, 27 states in the US and 29 unique countries. Some of the guys here will work 24 hours a day so it seems, but I tend to go in spurts and then get involved in a project.</span></div>
<div>
<span style="font-family: "trebuchet ms" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "trebuchet ms" , sans-serif;">Middle of the afternoon one of our group came down to the gazebo to join me for a while and set up his gear. When he powered up the 12V supply for his radio BOOM! He apparently had been working so far from a 110V outlet and the gazebo is 220V only. Oh sigh... We opened up the supply but there had been a rather significant explosion in there and so he tossed it in the bin. Apparently there is one or two of these that bite the dust in this way on every trip of the group down here. I switched everything to 220V before I left home to avoid such nasties. Oh well.</span></div>
Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0tag:blogger.com,1999:blog-6737713102444501259.post-20122725913925661902016-12-07T14:01:00.001-08:002016-12-07T14:01:56.126-08:00St. Lucia - Day Three<span style="font-family: Trebuchet MS, sans-serif;">Last evening I was sitting out on the gazebo listening to the surf and watching a cruise ship wander by the north end of the island. It is a lousy picture from a mobile phone, but you can get the idea.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-1nBETU9Ju4A/WEiCFd3UaAI/AAAAAAAAFJs/nFovs9QECp8xwxNK3UifT9ADVIpd1dswQCLcB/s1600/Capture.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="https://3.bp.blogspot.com/-1nBETU9Ju4A/WEiCFd3UaAI/AAAAAAAAFJs/nFovs9QECp8xwxNK3UifT9ADVIpd1dswQCLcB/s400/Capture.PNG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<span style="font-family: Trebuchet MS, sans-serif;">I was able to extricate my gear from the authorities and before heading back to the villa, we had lunch on the beach. The island has lots of dogs running around loose and there was an assortment of them hanging out under the tables looking for a handout.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-ihndyeW4zqE/WEiEnU82U_I/AAAAAAAAFJ0/RAyGm8HwbrwGjXZbjPamKGtjZB6Gd5vAACLcB/s1600/Capture.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="265" src="https://2.bp.blogspot.com/-ihndyeW4zqE/WEiEnU82U_I/AAAAAAAAFJ0/RAyGm8HwbrwGjXZbjPamKGtjZB6Gd5vAACLcB/s400/Capture.PNG" width="400" /></a></div>
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">After getting back to the villa I was able to quickly get on the air and make about 25 contacts in quick succession and later in the evening our wonderful villa hosts brought us a very fine Indian cuisine meal to cap off the evening.</span>Jeff ko7mhttp://www.blogger.com/profile/17258789919264407403noreply@blogger.com0