Tunes Module throwing unhandled exception with Cerberus 1.4

Starting to dust off my boards now that I have time to work on some projects. Ran into a snag.

Upgraded a project from 4.2 to 4.3; now getting an Unhandled exception on my Tunes module. Using Cerberus version 1.4 board and upgraded to the 4.3.8.1 firmware. Programming with VS 2013.

Reviewed an older post of a year ago and there was a known issue, but that was with different mainboards and the device actually played at least one note. I’m getting an Unhanlded exception the instant I call “tunes.play()”.

I re-created everything brand-new with a new board, (still a Cerberus, since I don’t have what I need to make this work on a Spider yet) project, and ran into the same issue.

Here’s the simplest part of the code: There is a button event that triggers this.

// play heard from the front door
Tunes.MusicNote note = new Tunes.MusicNote(Tunes.Tone.D4, 200);
tunes.AddNote(note);
tunes.Play();

I noticed there was a post about adding an alternate driver but I just found a .CS file and not an actual solution file. But that could be due to my own ignorance and/or blindness. (both sometimes) So if the alternate drive is the answer I’ll go grab that and give it a try; it just didn’t match the scenario I’m in.

If you need more info I’ll post up everything I’m doing.

Much thanks for any help!

…just looked through my stuff and I don’t have an extra Tunes module to test.

Going to fix that tomorrow and order a few just in case.

Please give us more code, and the output from debugging.

@ njbuch - Sure, now I’m having an issue just with the button event. Can’t even get to the Tunes event. Replaced the board with a brand new one and still having the same issue.

Here’s the output from the debug mode:

Found debugger!

Create TS.

Loading start at 807a53c, end 80a04c8

Assembly: mscorlib (4.3.1.0) Assembly: Microsoft.SPOT.Native (4.3.1.0) Assembly: Microsoft.SPOT.Hardware (4.3.1.0)
Assembly: Microsoft.SPOT.Graphics (4.3.1.0) Assembly: Microsoft.SPOT.TinyCore (4.3.1.0)
Assembly: Microsoft.SPOT.IO (4.3.1.0) Assembly: System.IO (4.3.1.0) Assembly: Microsoft.SPOT.Hardware.Usb (4.3.1.0)
Assembly: Microsoft.SPOT.Hardware.SerialPort (4.3.1.0) Assembly: Microsoft.SPOT.Hardware.PWM (4.3.1.0)
Loading Deployment Assemblies.

Attaching deployed file.

Assembly: GTM.GHIElectronics.LED7C (4.3.8.1) Attaching deployed file.

Assembly: Microsoft.SPOT.Net (4.3.1.0) Attaching deployed file.

Assembly: GHI.Hardware (4.3.8.1) Attaching deployed file.

Assembly: GHIElectronics.Gadgeteer.FEZCerberus (4.3.8.1) Attaching deployed file.

Assembly: Gadgeteer (2.43.1.0) Attaching deployed file.

Assembly: GTM.GHIElectronics.Button (4.3.8.1) Attaching deployed file.

Assembly: HomebaseV3 (1.0.0.0) Attaching deployed file.

Assembly: GTM.IngenuityMicro.RfPipe (4.3.1.0) Attaching deployed file.

Assembly: GTM.GHIElectronics.USBClientDP (4.3.8.1) Attaching deployed file.

Assembly: GTM.GHIElectronics.Tunes (4.3.8.1) Attaching deployed file.

Assembly: GHI.Usb (4.3.8.1) Resolving.

The debugging target runtime is loading the application assemblies and starting execution.
Ready.

‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\mscorlib.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Native.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Graphics.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.TinyCore.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.IO.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\System.IO.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.Usb.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.SerialPort.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.PWM.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Net.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\Microsoft .NET Gadgeteer\Core\Assemblies.NET Micro Framework 4.3\le\Gadgeteer.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics.NET Gadgeteer SDK\Modules\LED7C\NETMF 4.3\le\GTM.GHIElectronics.LED7C.dll’
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics\NETMF v4.3 SDK\Libraries\le\GHI.Hardware.dll’
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics\NETMF v4.3 SDK\Libraries\le\GHI.Usb.dll’
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics.NET Gadgeteer SDK\Mainboards\FEZCerberus\NETMF 4.3\le\GHIElectronics.Gadgeteer.FEZCerberus.dll’
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics.NET Gadgeteer SDK\Modules\Button\NETMF 4.3\le\GTM.GHIElectronics.Button.dll’
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\IngenuityMicro\Microsoft .NET Gadgeteer\Modules\RfPipe\NETMF 4.3\le\GTM.IngenuityMicro.RfPipe.dll’, Symbols loaded.
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics.NET Gadgeteer SDK\Modules\Tunes\NETMF 4.3\le\GTM.GHIElectronics.Tunes.dll’
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘C:\Program Files (x86)\GHI Electronics.NET Gadgeteer SDK\Modules\USBClientDP\NETMF 4.3\le\GTM.GHIElectronics.USBClientDP.dll’
‘Microsoft.SPOT.Debugger.CorDebug.12.dll’ (Managed): Loaded ‘c:\HomebaseV3\HomebaseV3\bin\Debug\le\HomebaseV3.exe’, Symbols loaded.
The thread ‘’ (0x2) has exited with code 0 (0x0).
Using mainboard GHI Electronics FEZ Cerberus version 1.3
The thread ‘’ (0x3) has exited with code 0 (0x0).
A first chance exception of type ‘System.NullReferenceException’ occurred in GTM.GHIElectronics.Tunes.dll
Error invoking method “Gadgeteer.Modules.GHIElectronics.Button” (check arguments to Program.BeginInvoke are correct)
A first chance exception of type ‘System.NullReferenceException’ occurred in GTM.GHIElectronics.Tunes.dll
Error invoking method “Gadgeteer.Modules.GHIElectronics.Button” (check arguments to Program.BeginInvoke are correct)
The program ‘[3] Micro Framework application: Managed’ has exited with code 0 (0x0).

Here’s the code: (I commented out the Network portion, had the same issue with that but that may be due to the Network cable not plugged in at bootup)

using System;
using System.Text;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

using Microsoft.SPOT.Hardware;
using Gadgeteer.Networking;
using GHIElectronics.Gadgeteer;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using Gadgeteer;
using Gadgeteer.Modules.IngenuityMicro;

namespace HomebaseV3
{
    public partial class Program
    {
        private string currentPolledTemp = string.Empty;
        private bool receivedTemp = false;
        
        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            rfPipe.DataReceived +=rfPipe_DataReceived;
            button.ButtonPressed +=button_ButtonPressed;          

            Mainboard.SetDebugLED(false);

            led7C.SetColor(LED7C.Color.Off);
            led7C2.SetColor(LED7C.Color.Off);

            //if (ethernetENC28.IsNetworkConnected)
            //{
            //    ethernetENC28.NetworkUp += ethernetENC28_NetworkUp;
            //}
        }

void button_ButtonPressed(GTM.GHIElectronics.Button sender, GTM.GHIElectronics.Button.ButtonState state)
{
            // when button is pressed - send out a signal to all receivers        
            Mainboard.SetDebugLED(false);  
            
            rfPipe.SendData("fd power poll");
            
            Thread.Sleep(750);

            rfPipe.SendData("sd power poll");

    

            Tunes.MusicNote note = new Tunes.MusicNote(Tunes.Tone.F4, 100);
            tunes.AddNote(note);
            tunes.Play();
            
            led7C.SetColor(LED7C.Color.Off);

            // reset polled flag
            receivedTemp = false;

}

void rfPipe_DataReceived(string val)
{

 #region "fd opened"
 	    if (val == "fd opened")
            {
                led7C.SetColor(LED7C.Color.Red);
                led7C2.SetColor(LED7C.Color.Red);

                Tunes.MusicNote note = new Tunes.MusicNote(Tunes.Tone.C4, 400);
                tunes.AddNote(note);

                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));

                tunes.Play();

                Mainboard.SetDebugLED(true);
                Thread.Sleep(30000);

                return;

            } // end if front door opened

 #endregion

 #region "sd opened"

            if (val == "sd opened")
            {
                led7C.SetColor(LED7C.Color.Blue);
                led7C2.SetColor(LED7C.Color.Blue);

                Tunes.MusicNote note = new Tunes.MusicNote(Tunes.Tone.C4, 400);
                tunes.AddNote(note);

                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.A2, 500));
                tunes.AddNote(new Tunes.MusicNote(Tunes.Tone.C5, 500));

                tunes.Play();

                Mainboard.SetDebugLED(true);
                Thread.Sleep(30000);

                return;

            } // end if study door opened

 #endregion

 #region "yes fd power"

            if (val == "yes fd power")
            {
                // play heard from the front door
                Tunes.MusicNote note = new Tunes.MusicNote(Tunes.Tone.D4, 500);
                tunes.AddNote(note);
                tunes.Play();
                

            } // end if we receive a front door poll response

 #endregion

 #region "yes sd power"

            if (val == "yes sd power")
            {
                // play heard from the front door
                Tunes.MusicNote note = new Tunes.MusicNote(Tunes.Tone.C5, 200);
                tunes.AddNote(note);
                tunes.Play();

            } // end if we receive a front door poll response
 #endregion

 #region "temperature signal received"
            // receives: Temp: 84
            if (val.ToLower().Substring(0,4) == "temp")
            {
                
                // check if the temp was triple digits
                if (val.Length > 8)
                {
                    // temp received, assign value to global
                    currentPolledTemp = val.Substring(7,2);

                    // raise polled flag
                    receivedTemp = true;
                }
                else      // temp was double digits
                {
                    // temp received, assign value to global
                    currentPolledTemp = val.Substring(6,3);
                     
                    // raise polled flag
                    receivedTemp = true;
                }
                
            } // end if we receive a front door poll response
 #endregion


}


        //void ethernetENC28_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
        //{
        //    // network is up
        //    if (ethernetENC28.IsNetworkUp)
        //    {
        //        // process code
        //        ethernetENC28.UseStaticIP("192.168.17.17", "255.255.255.0", "192.168.17.1");

        //        WebEvent postTemp = WebServer.SetupWebEvent("bedroomTemp");
        //        postTemp.WebEventReceived += postTemp_WebEventReceived;
        //    }

        //}

         private string FormatDuration(TimeSpan x)
        {
            return x.Days.ToString("D") + ":" + x.Hours.ToString("D2") + ":" + x.Minutes.ToString("D2") + ":" +
                   x.Seconds.ToString("D2");
        }


        //void postTemp_WebEventReceived(string path, WebServer.HttpMethod method, Responder responder)
        //{
        //    // web event 
        //     var uptime = GT.Timer.GetMachineTime();
 
        //    if (receivedTemp)
        //    {
        //        var HTML =
        //            Encoding.UTF8.GetBytes(
        //                @ "<head><style type=""text/css"">.style1{width: 100%;}.auto-style1 {text-align: center;}.auto-style2 {font-size: large;}.auto-style3 {text-align: right;}" +
        //                @ "</style></head><html><body bgcolor=""33cc33""><p><div class=""auto-style1""><span class=""auto-style2"">Uptime = " +
        //                FormatDuration(uptime) +
        //                @ "</span><br class=""auto-style2""/></div><table class=""style1""><tr><td class=""auto-style3"">Temp</td><td>&nbsp;</td><td>" +
        //                currentPolledTemp + " " + 
        //                @ "</td></tr></table></body></html>");
 
        //        responder.Respond(HTML, "text/html");
        //    }
        //    else
        //    {
        //        // have not received temperature since bootup
        //        var HTML =
        //            Encoding.UTF8.GetBytes(
        //                @ "<head><style type=""text/css"">.style1{width: 100%;}.auto-style1 {text-align: center;}.auto-style2 {font-size: large;}.auto-style3 {text-align: right;}" +
        //                @ "</style></head><html><body bgcolor=""33cc33""><p><div class=""auto-style1""><span class=""auto-style2"">Uptime = " +
        //                FormatDuration(uptime) +
        //                @ "</span><br class=""auto-style2""/></div><table class=""style1""><tr><td class=""auto-style3"">Temp</td><td>&nbsp;</td><td>" +
        //                "No Temperature Reading Since Startup" + 
        //                @ "</td></tr></table></body></html>");
 
        //        responder.Respond(HTML, "text/html");
        //    }
            

        //}
    
    }
}

@ njbuch - A quick summary of the code, is that I have an RFPipe and other GHI mainboards setup at different locations of the house. When you press the button (the only button), it sends out an RF signal just to find out if they are powered up. No matter what, the Cerberus plays a sound to let me know the button was pushed. That’s all that’s for.

Quite simple. Things ran fine with 4.2 but having a ton of small unhandled exceptions, not sure why.

Hmm, I dont see any obvious mistakes in the code, based on a brief look. Its clean and simple.

Maybe you should try downloading the source for the tunes driver (its pretty simple), delete the reference in your project and put this file instead: [url]https://bitbucket.org/ghi_elect/gadgeteer/src/86e246d051bf5e78ec1923b173410c5e47591bf8/Modules/GHIElectronics/Tunes/Tunes_43/Tunes_43.cs?at=master&fileviewer=file-view-default[/url]

Then you can try debugging, and see what goes wrong.
:think:

@ verbosewater - I had issues with older button module versions. Is your button module the latest Version 1.4?
Perhaps you can try with another event instead of the button.

@ njbuch - Thanks, I’m doing bare bones stuff; trying to keep it as simple as possible on the ‘home’ module (the board with all of the logic for the systems). I have several more to add in but can’t since I’m stuck with such a simple bug. I’ll add the driver and go from there…and if that fails then I’m going back to 4.2 and won’t ever upgrade any GHI boards.

@ RoSchmi - The button is older; purchased over 2 years ago. If that means I have to buy all new modules every time I upgrade that would stink. Every time I have upgraded a board, my project stops working because the new code isn’t backward compatible. Very annoying…but I haven’t found anything else out there as simple as what GHI makes.

@ verbosewater -
no, it’s not the button, please try this code:


void button_ButtonPressed(GTM.GHIElectronics.Button sender, GTM.GHIElectronics.Button.ButtonState state)
        {
            Tunes.MusicNote note = new Tunes.MusicNote(Tunes.Tone.F4, 100);
            Tunes.Melody tunesMelody = new Tunes.Melody();
            tunesMelody.Add(note);
            tunes.Play(tunesMelody);
        }

1 Like

@ verbosewater - There’s a bug with the AddNote function in the current SDK. You can work around it by using the Play method with the Melody or MusicNote parameter.


var note = new Tunes.MusicNote(Tunes.Tone.D4, 200);
tunes.Play(note);


var melody = new Tunes.Melody();
melody.Add(new Tunes.MusicNote(Tunes.Tone.C5, 500));
melody.Add(new Tunes.MusicNote(Tunes.Tone.A2, 500));
tunes.Play(melody);

@ John -
@ Roschmi -

You guys called it, changed how to call tunes and it’s back to being perfect. I can add the extra modules now (including network integration with a SQL server).

…and within a couple days GHI and the community has this solved. I don’t take any of you, or what you do, for granted. Thank you.

@ cyberh0me - Simple as in I don’t have to learn soldering or how to read any kind of circuit diagrams. That’s just the first two things that popped in my head.

1 Like