.NET Micro Framework (part 4)

In questo quarto post mostriamo come creare una piccola applicazione di prova e farne il deploy sulla scheda. Per collegamento della scheda FEZmini e dell’aggiornamento del firmware all’ultima versione disponibile vedi il post precedente.

Poiché la scheda include un LED che è gestibile da codice, proviamo a realizzare una piccola applicazione che lo faccia lampeggiare codificando un messaggio a piacere col codice Morse.

Un po’ di teoria sulla comunicazione Morse

La codifica di un testo in codice Morse è assai banale. Vi sono solo un paio di cose che è opportuno rimarcare:

1. Ad ogni carattere corrisponde un codice, secondo la seguente tabella:

image

2. Ogni codice è formato da una sequenza punti e linee;

3. tra un codice e l’altro (quindi tra un carattere e l’altro) vi è una “separazione di carattere”

3. tra una parola e l’altra vi è una “separazione di parola”

4. Il tutto viene codificato in un segnale (ad onda quadra) che alterna valori “on” e “off”, secondo il seguente schema:

image

Tutto ciò premesso, iniziamo con l’aggiunta di un nuovo progetto alla soluzione precedentemente creata (vedi post # 2), che chiameremo “FEZMiniLab.BlinkingLed”:

image

e impostiamo il novo progetto come “StartUp Project”:

image

Modifichiamo ora il file program.cs come segue:

using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;

namespace FEZMiniLab.BlinkingLed
{
    public class Program
    {
        // Essendo un programma di prova, il messaggio è impostato direttamente qui,
        // nel codice
        private static string msg = "SOS";
        
        // Nell'ambito delle trasmissioni in codice Morse si usa
        // l'acronimo wpm (word per minute) per dichiarare la velocità
        // media di trasmissione. Poiché le parole sono di diversa lunghezza,
        // per definizione viene usata la parola PARIS, che corrisponde ad una
        // sequenza di 49 segnali (che per comodità nei calcoli si arrotonda a 50).
        // Da cui deriva che, data una determinata velocità W, i corrispondenti
        // baud (simboli trasmessi per secondo) sono pari a: W * 50 / 60 .
        // Poiché le velocità tipiche di trasmissione variano dai 15 wpm ai 50 wpm
        // abbiamo velocità di trasmissione che vanno dai 12,5 baud ai 41,6 baud.
        // Ma per distinguere visivamente il codice morse quando viene mostrato
        // tramite sorgente luminosa, specialmente con chi non è addestrato, è
        // bene mantenersi su velocità ben più basse, pari a 5 wpm, corrispondente
        // a 4,17 baud.
        private static double baud = 4.17;

        // Una volta definita la velocità di trasmissione, è facile ottenere la durata
        // del segnale (in millisecondi):
        private static int signalDuration = (int)System.Math.Round(1000 / baud);
        
        public static void Main()
        {
            // Inizializza il LED presente nella scheda
            bool ledState = false;
            OutputPort led = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, ledState);

            // Istanzia la Tavola di Codifica Morse
            MorseTable morseTable = new MorseTable();
            
            // Codifica il messaggio
            bool[] morseSignalStream = morseTable.Encode(msg);

            while (true)
            {
                // Mostra il segnale Morse tramite il LED
                foreach (bool signalState in morseSignalStream)
                {
                    led.Write(signalState);

                    // Mantieni il segnale per un tempo pari alla durata del
                    // segnale stesso
                    Thread.Sleep(signalDuration);
                }

                // Sleep for 1 second
                Thread.Sleep(2000);
            }
        }

    }
}

Aggiungiamo al progetto la classe “MorseTable.cs”:

using System.Collections;

namespace FEZMiniLab.BlinkingLed
{
    /// <summary>
    /// Enumerazione dei valori Morse.
    /// </summary>
    enum MorseValue
    {
        dot,
        line
    }
    
    /// <summary>
    /// Gestisce la conversione dei messaggi da testo alfanumerico a codice morse
    /// </summary>
    class MorseTable
    {
        private static MorseValue DOT = MorseValue.dot;
        private static MorseValue LINE = MorseValue.line;

        private Hashtable alfaNumericDictionary;

        public MorseTable()
        {
            alfaNumericDictionary = new Hashtable();

            alfaNumericDictionary.Add("A", new MorseValue[] { DOT, LINE });
            alfaNumericDictionary.Add("B", new MorseValue[] { LINE, DOT, DOT, DOT });
            alfaNumericDictionary.Add("C", new MorseValue[] { LINE, DOT, LINE, DOT });
            alfaNumericDictionary.Add("D", new MorseValue[] { LINE, DOT, DOT });
            alfaNumericDictionary.Add("E", new MorseValue[] { DOT });
            alfaNumericDictionary.Add("F", new MorseValue[] { DOT, DOT, LINE, DOT });
            alfaNumericDictionary.Add("G", new MorseValue[] { LINE, LINE, DOT });
            alfaNumericDictionary.Add("H", new MorseValue[] { DOT, DOT, DOT, DOT });
            alfaNumericDictionary.Add("I", new MorseValue[] { DOT, DOT });
            alfaNumericDictionary.Add("J", new MorseValue[] { DOT, LINE, LINE, LINE });
            alfaNumericDictionary.Add("K", new MorseValue[] { LINE, DOT, LINE });
            alfaNumericDictionary.Add("L", new MorseValue[] { DOT, LINE, DOT, DOT });
            alfaNumericDictionary.Add("M", new MorseValue[] { LINE, LINE });
            alfaNumericDictionary.Add("N", new MorseValue[] { LINE, DOT });
            alfaNumericDictionary.Add("O", new MorseValue[] { LINE, LINE, LINE });
            alfaNumericDictionary.Add("Q", new MorseValue[] { DOT, LINE, LINE, DOT });
            alfaNumericDictionary.Add("R", new MorseValue[] { DOT, LINE, DOT });
            alfaNumericDictionary.Add("S", new MorseValue[] { DOT, DOT, DOT });
            alfaNumericDictionary.Add("T", new MorseValue[] { LINE });
            alfaNumericDictionary.Add("U", new MorseValue[] { DOT, DOT, LINE });
            alfaNumericDictionary.Add("V", new MorseValue[] { DOT, DOT, DOT, LINE });
            alfaNumericDictionary.Add("W", new MorseValue[] { DOT, LINE, LINE });
            alfaNumericDictionary.Add("X", new MorseValue[] { LINE, DOT, DOT, LINE });
            alfaNumericDictionary.Add("Y", new MorseValue[] { LINE, DOT, LINE, LINE });
            alfaNumericDictionary.Add("Z", new MorseValue[] { LINE, LINE, DOT, DOT });
            alfaNumericDictionary.Add("0", new MorseValue[] { LINE, LINE, LINE, LINE, LINE });
            alfaNumericDictionary.Add("1", new MorseValue[] { DOT, LINE, LINE, LINE, LINE });
            alfaNumericDictionary.Add("2", new MorseValue[] { DOT, DOT, LINE, LINE, LINE });
            alfaNumericDictionary.Add("3", new MorseValue[] { DOT, DOT, DOT, LINE, LINE });
            alfaNumericDictionary.Add("4", new MorseValue[] { DOT, DOT, DOT, DOT, LINE });
            alfaNumericDictionary.Add("5", new MorseValue[] { DOT, DOT, DOT, DOT, DOT });
            alfaNumericDictionary.Add("6", new MorseValue[] { LINE, DOT, DOT, DOT, DOT });
            alfaNumericDictionary.Add("7", new MorseValue[] { LINE, LINE, DOT, DOT, DOT });
            alfaNumericDictionary.Add("8", new MorseValue[] { LINE, LINE, LINE, DOT, DOT });
            alfaNumericDictionary.Add("9", new MorseValue[] { LINE, LINE, LINE, LINE, DOT });
            alfaNumericDictionary.Add(".", new MorseValue[] { DOT, LINE, DOT, LINE, DOT, LINE });
            alfaNumericDictionary.Add(",", new MorseValue[] { LINE, LINE, DOT, DOT, LINE, LINE });
            alfaNumericDictionary.Add(":", new MorseValue[] { LINE, LINE, LINE, DOT, DOT, DOT });
            alfaNumericDictionary.Add("?", new MorseValue[] { DOT, DOT, LINE, LINE, DOT, DOT });
            alfaNumericDictionary.Add("=", new MorseValue[] { LINE, DOT, DOT, DOT, LINE });
            alfaNumericDictionary.Add("-", new MorseValue[] { LINE, DOT, DOT, DOT, DOT, LINE });
            alfaNumericDictionary.Add("(", new MorseValue[] { LINE, DOT,LINE, LINE, DOT });
            alfaNumericDictionary.Add(")", new MorseValue[] { LINE, DOT, LINE, LINE, DOT, LINE});
            alfaNumericDictionary.Add("\"", new MorseValue[] { DOT, LINE, DOT, DOT, LINE, DOT });
            alfaNumericDictionary.Add("'", new MorseValue[] { DOT, LINE, LINE, LINE, LINE, DOT });
            alfaNumericDictionary.Add("/", new MorseValue[] { LINE, DOT, DOT, LINE, DOT });
            alfaNumericDictionary.Add("_", new MorseValue[] { DOT, DOT, LINE, LINE, DOT, LINE });
            alfaNumericDictionary.Add("@", new MorseValue[] { DOT, LINE, LINE, DOT, LINE, DOT });
        }

        public bool[] Encode(string msg)
        {
            ArrayList signalStream = new ArrayList();
            MorseValue[] morseValues;
            string lastLiteral = "";
            string literal;

            foreach(char c in msg)
            {
                literal = c.ToString().ToUpper();

                if (literal == " ")
                {
                    // Codifica lo spazio come un wordSpace solo se il precedente carattere
                    // non era uno spazio (in pratica elimina gli spazi ripetuti)
                    if (lastLiteral != " ")
                    {
                        // Aggiungi un wordSpaceSignal (una sequenza di 7 "off" signal)
                        signalStream.Add(false);
                        signalStream.Add(false);
                        signalStream.Add(false);
                        signalStream.Add(false);
                        signalStream.Add(false);
                        signalStream.Add(false);
                        signalStream.Add(false);
                    }
                }
                else
                {
                    if ((lastLiteral != " ") && (lastLiteral.Length != 0))
                    {
                        // Aggiungi un charSpaceSignal (una sequenza di 3 "off" signal)
                        signalStream.Add(false);
                        signalStream.Add(false);
                        signalStream.Add(false);
                    }
                    
                    // Da notare che nel Micro Framework (almeno fino alla versione 4.1) non ci sono i Generics
                    // quindi bisogna usare il cast esplicito al tipo MorseValue[]
                    // che è il tipo del valore della coppia <chiave, valore> della hash table.
                    morseValues = (MorseValue[])alfaNumericDictionary[literal];

                    bool firstValue = true;
                    foreach(MorseValue morseValue in morseValues)
                    {
                        if (!firstValue)
                        {
                            // Aggiungi un valueSpaceSignal (1 "off" signal)
                            signalStream.Add(false);
                        }
                        
                        switch (morseValue)
                        {
                            case MorseValue.dot:
                                // Aggiungi un dotSignal (1 "on" signal)
                                signalStream.Add(true);
                                break;
                            case MorseValue.line:
                                // Aggiungi un dotSignal (una sequenza di 3 "on" signal)
                                signalStream.Add(true);
                                signalStream.Add(true);
                                signalStream.Add(true);
                                break;
                            default:
                                break;
                        }

                        firstValue = false;
                    }
                }

                lastLiteral = literal;
            }

            // Spenge il segnale
            signalStream.Add(false);

            // Ritorna un array di bool
            return (bool[]) signalStream.ToArray(typeof(bool));
        }

    }
}

Chi lo desidera può crearsi un progetto di test per la suddetta classe, io il progetto l’ho chiamato “FEZMiniLab.BlinkingLed.Test” e ho scritto i seguenti test nel file “MorseTableTest.cs”:

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FEZMiniLab.BlinkingLed.Test
{
    /// <summary>
    ///This is a test class for MorseTableTest and is intended
    ///to contain all MorseTableTest Unit Tests
    ///</summary>
    [TestClass()]
    public class MorseTableTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        [TestMethod()]
        public void EncodeTest_U()
        {
            MorseTable target = new MorseTable();
            string msg = "U";
            bool[] expected = new bool[] {
                                           true, false, true, false, true, true, true, // ..-
                                           false                                       // fine trasmissione
                                         } ;
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod()]
        public void EncodeTest_N()
        {
            MorseTable target = new MorseTable();
            string msg = "N";
            bool[] expected = new bool[] {
                                           true, true, true, false, true,              // -.
                                           false                                       // fine trasmissione
                                         };
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod()]
        public void EncodeTest_UN()
        {
            MorseTable target = new MorseTable();
            string msg = "UN";
            bool[] expected = new bool[] {
                                            true, false, true, false, true, true, true,   // U
                                            false, false, false,                          // spazio tra i caratteri
                                            true, true, true, false, true,                // N
                                            false                                         // fine trasmissione
                                         };
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod()]
        public void EncodeTest_A()
        {
            MorseTable target = new MorseTable();
            string msg = "A";
            bool[] expected = new bool[] {
                                           true, false, true, true, true,                // .-
                                           false                                         // fine trasmissione
                                         };
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod()]
        public void EncodeTest_G()
        {
            MorseTable target = new MorseTable();
            string msg = "G";
            bool[] expected = new bool[] {
                                           true, true, true, false, true, true, true, false, true,  //--.
                                           false                                                    // fine trasmissione
                                         };
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }

       [TestMethod()]
        public void EncodeTest_O()
        {
            MorseTable target = new MorseTable();
            string msg = "O";
            bool[] expected = new bool[] {
                                           true, true, true, false, true, true, true, false, true, true, true,   // ---
                                           false                                                                 // fine trasmissione
                                         };
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod()]
        public void EncodeTest_AGO()
        {
            MorseTable target = new MorseTable();
            string msg = "AGO";
            bool[] expected = new bool[] {
                                            true, false, true, true, true,                                              // A
                                            false, false, false,                                                        // spazio tra i caratteri
                                            true, true, true, false, true, true, true, false, true,                     // G
                                            false, false, false,                                                        // spazio tra i caratteri
                                            true, true, true, false, true, true, true, false, true, true, true,         // O
                                            false                                                                       // fine trasmissione
                                         };
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }

        [TestMethod()]
        public void EncodeTest_UN_AGO()
        {
            MorseTable target = new MorseTable();
            string msg = "UN AGO";
            bool[] expected = new bool[] {
                                            true, false, true, false, true, true, true,                             // U
                                            false, false, false,                                                    // spazio tra i caratteri
                                            true, true, true, false, true,                                          // N
                                            false, false, false, false, false, false, false,                        // spazio tra le parole
                                            true, false, true, true, true,                                          // A
                                            false, false, false,                                                    // spazio tra i caratteri
                                            true, true, true, false, true, true, true, false, true,                 // G
                                            false, false, false,                                                    // spazio tra i caratteri
                                            true, true, true, false, true, true, true, false, true, true, true,     // O
                                            false                                                                   // fine trasmissione
                                         };
            bool[] actual = target.Encode(msg);
            CollectionAssert.AreEqual(expected, actual);
        }
    }
}

Dopo aver verificato che i test passino:

image

Andiamo finalmente a fare il download dell’applicazione nella scheda, per vedere se all’atto pratico il tutto funziona come desiderato, seguendo i seguenti passi:

1. Apriamo le proprietà del progetto “FEZMiniLab.BlinkingLed” e impostiamo il trasporto esattamente come in figura:

image

2. Ogni altro eventuale progetto presente nella soluzione (nel mio caso il progetto “FEZMiniLab.HelloWorld”) deve avere il trasporto settato su “Emulatore” perché ho notato che se più progetti sono settati col traporto verso la scheda, Visual Studio ignora l’impostazione di “StartUp Project” e fa il deploy dell’ultimo progetto utilizzato (cosa che mi ha fatto veramente saltare i nervi, fino a che non ho capito e trovato come aggirare il problema).

3. Facciamo F5 e verifichiamo che l’applicazione sia partita impostando un breakpoint subito dopo l’inizio del metodo Main:

image

4. A questo punto non rimane che ripremere F5 e verificare che il LED lampeggi secondo le regole della codifica Morse. Nel nostro caso, avendo impostato come messaggio il famoso “SOS”, avremo tre lampeggi brevi, seguiti da tre lampeggi lunghi, infine tre lampeggi brevi. Poi, dopo la pausa (impostata a 2 secondi) il ciclo ricomincia.

Infine, togliendo l'alimentazione e poi ripristinandola, è possibile notare che una volta scaricata nella memoria, l'applicazione viene automaticamente avviata ad ogni riaccensione della scheda.

Guardare per credere:

Questo conclude il primo ciclo di post sul Micro Framework. Nel prossimo ciclo parleremo di un progetto più complesso, nel quale il Micro Framework e la scheda FEZMini giocheranno un ruolo importante.

«marzo»
domlunmarmergiovensab
272812345
6789101112
13141516171819
20212223242526
272829303112
3456789