Notes About Working with Various Arduino & Netduino Microcontroller Boards

Saturday, April 6, 2013

Telling the Time with Text to Speech Using a Netduino Plus 2 & EMIC 2 Module

The EMIC 2 text to speech module connects to a microcontroller via a serial connection. It does not require any special libraries, and I have found it is easy to get it working with a Netduino Plus 2. The following example uses the EMIC 2 to announce the time when the button on the Netduino board is pressed. This code connects to a network time server and sets the Netduino's clock when the program starts, so it is assumed that the Netduino is connected to the Internet. Getting the time from an NTP server takes a moment at startup. After the time is set, you could disconnect the Netduino from the network. You could also use a real-time click chip to set the Netduino's internal clock.

For my testing, I have just used an inexpensive, thin 8 Ohm speaker from Sparkfun without any amplification.

Connections


EMIC 2  Netduino Plus 2
GND     GND
5V      5V
SOUT    Digital 0
SIN     Digital 1

SP- & SP+ on the EMIC 2 connect to the speaker.


C# Code

namespace EMIC2
{
    public class Program
    {
        const int DELAY = 1000;
        const String NTP = "time.nist.gov"; // NTP server
        const int TZ = -5;                  // Offset from GMT

        static SerialPort serial;
        public static void Main()
        {
            // Sync clock to current time
            UpdateTimeFromNtpServer(NTP, TZ);

            // Use interrupt to handle button presses
            InterruptPort button = new InterruptPort(Pins.ONBOARD_SW1, true, 
                Port.ResistorMode.Disabled, 
                Port.InterruptMode.InterruptEdgeBoth);
            button.OnInterrupt += new NativeEventHandler(button_OnInterrupt);

            // Connect to EMIC2 via SerialPort
            serial = new SerialPort(SerialPorts.COM1,
                  9600, Parity.None, 8, StopBits.One);
            serial.Open();
            Thread.Sleep(100);
            serial.WriteByte(10);
            String volCmd = "V15\n";
            sendCommand(volCmd);
            Thread.Sleep(-1);
        }

        static void sendCommand(String cmd)
        {
            byte[] cmdArray = new byte[cmd.Length];
            int i = 0;
            foreach (char a in cmd.ToCharArray())
            {
                cmdArray[i++] = (byte)a;
            }
            serial.Write(cmdArray, 0, cmd.Length);
        }

        static void button_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            if (serial.BytesToRead > 0)
            {
                byte[] buf = new byte[1];
                serial.Read(buf, 0, 1);
                if (buf[0] == ':')
                {
                    DateTime now = DateTime.Now;
                    String countCmd = "STime is " + now.Hour.ToString() + " " + 
                        now.Minute.ToString() + "\n";
                    sendCommand(countCmd);
                }
            }
        }
        //============================================================================
        //
        // Code for synchronizing time to NTP server
        //
        //============================================================================ 
        // Network time update code from 
        // http://www.jaypm.com/2011/09/setting-the-netduinos-datetime-automatically/
        public static bool UpdateTimeFromNtpServer(string server, 
            int timeZoneOffset)
        {
            try
            {
                var currentTime = GetNtpTime(server, timeZoneOffset);
                Microsoft.SPOT.Hardware.Utility.SetLocalTime(currentTime);
                return true;
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                return false;
            }
        }

        private static DateTime GetNtpTime(String timeServer, int timeZoneOffset)
        {
            // Find endpoint for TimeServer
            var ep = new IPEndPoint(Dns.GetHostEntry(timeServer).AddressList[0], 123);

            // Make send/receive buffer
            var ntpData = new byte[48];

            // Connect to TimeServer
            using (var s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 
                ProtocolType.Udp))
            {
                // Set 20s send/receive timeout and connect
                s.SendTimeout = s.ReceiveTimeout = 20000;
                s.Connect(ep);
                ntpData[0] = 0x1B;   // Set protocol version
                s.Send(ntpData);     // Send Request
                s.Receive(ntpData);  // Receive Time
                s.Close();
            }

            const byte offsetTransmitTime = 40;
            ulong intpart = 0;
            ulong fractpart = 0;

            for (var i = 0; i <= 3; i++)
                intpart = (intpart << 8) | ntpData[offsetTransmitTime + i];

            for (var i = 4; i <= 7; i++)
                fractpart = (fractpart << 8) | ntpData[offsetTransmitTime + i];

            ulong milliseconds = (intpart * 1000 + (fractpart * 1000) / 0x100000000L);

            var timeSpan = TimeSpan.FromTicks((long)milliseconds * 
                TimeSpan.TicksPerMillisecond);
            var dateTime = new DateTime(1900, 1, 1);
            dateTime += timeSpan;

            var offsetAmount = new TimeSpan(timeZoneOffset, 0, 0);
            var networkDateTime = (dateTime + offsetAmount);

            return networkDateTime;
        }
    }
}

I need to work more on the way the time is read out to handle time on the hour and minutes less than ten after the hour.

The EMIC 2 is very versatile. Have a look at the product page and  manual to get an idea of what you can do with it and how its command language works. 

No comments:

Post a Comment