Wednesday, March 18, 2015

1Sheeld Libraries for Netduino, revisited.

NOTE:  See the original post here for additional information (including version updates).

I have updated the 1Sheeld Libraries and Samples for Netduinos.   Things have changed a bit and those of you creating your own sketches will need to modify your code a bit if you decide to use the latest version (but, frankly, things have gotten a lot simpler and I recommend that you upgrade and take a couple minutes to update your sketches ... for no other reason than the older libraries will no longer be upgraded).

I'm going to give you a look at a simple sketch, both before and after the updates, so you can see the differences ... I'll use the SimpleLED sample for this discussion.

First a look at the before code...  Note: I've removed the using statements to simplify things (nothing has changed regardless of whether you use the old libraries or the new libraries with the using statements).

Here's the Program.cs  ... it just instantiates an Led class and calls Setup(), then Loop() endlessly.

 namespace SimpleLED  
 {  
   public class Program  
   {  
     public static void Main()  
     {  
       // write your code here  
       Led led = new Led();  
       led.Setup();  
       while (true)  
         led.Loop();  
     }  
   }  
 }  

Here's the Led.cs ... equivalent to the Arduino Blink sample.

 namespace SimpleLED  
 {  
   public class Led  
   {  
     OneSheeld sheeld = null;  
     public void Setup()  
     {  
       sheeld = new OneSheeld();  
       sheeld.begin();  
     }  
     public void Loop()  
     {  
       OneSheeld.LED.setHigh();  
       Thread.Sleep(1000);  
       OneSheeld.LED.setLow();  
       Thread.Sleep(1000);  
     }  
   }  
 }  

And here's a look at the after code (using the new way) ...

Here is Program.cs ... it still instantiates the Led(), but it is now using the Run function from the OneSheeldUser class included in the new OneSheeld library,   Note ... if I included a using OneSheeldClasses;  in Program.cs it could be called like this ... OneSheeldUser.Run(new Led());  ... just saying.   Also, if you really want to ... you can use the old way (i.e. the old Program.cs would work just fine).

 namespace SimpleLED  
 {  
   public class Program  
   {  
     public static void Main()  
     {  
       OneSheeldClasses.OneSheeldUser.Run(new Led());  
     }  
   }  
 }  

and here's the new Led.cs ...

 namespace SimpleLED  
 {  
   public class Led : OneSheeldUser, IOneSheeldSketch  
   {  
     public void Setup()  
     {  
       OneSheeld.begin();  
     }  
     public void Loop()  
     {  
       LED.setHigh();  
       Thread.Sleep(1000);  
       LED.setLow();  
       Thread.Sleep(1000);  
     }  
   }  
 }  

As you should notice, there is no instantiation of the OneSheeld class like before (and if you have one in your code ... REMOVE IT!!!!) ... and you don't have to reference the OneSheeld class to get to the Shield classes.    This makes Netduino sketches look very similar to the Arduino counterparts.

Now let's look at how things are working now.

If you will notice, the Led class is now derived from the REQUIRED OneSheeldUser class ... what this does is simplify things for us ...

For example:   When the OneSheeld.begin() function is called ... this is now where the OneSheeld class gets instantiated.

If you look at the OneSheeldUser class you will see the following code (towards the end of the class) ...

     public OneSheeldClass _OneSheeld = null;  
     public OneSheeldClass OneSheeld  
     {  
       get  
       {  
         if (_OneSheeld == null)  
         {  
           _OneSheeld = new OneSheeldClass();  
           OneSheeldMain.Init(_OneSheeld);  
         }  
         return _OneSheeld;  
       }  
       set { }  
     }  

What happens is this .... when OneSheeld (of the OneSheeld.begin()) is called, it is actually using the getter above ... and if _OneSheeld (with the underline to make it unique) is null, it will instantiate the OneSheeldClass for you. and return it.   Then when the .begin() part is called, the OneSheeld is actually pointing to an object that was just instantiated.   From this point on, every call reference to OneSheeld, will use that same Object (since _OneSheeld is not null, it will not be re-instantiated).   That really simplifies things for our sketches, we no longer have to instantiate the OneSheeld class ourselves.

The other thing that the OneSheeldUser class does for us is simplifies using Shields. For example the call to LED.setHigh() will actually start here ... again, you don't have to reference OneSheeld before referencing the Shield (in fact, it will throw an error now when compiled if you do), LED isn't actually part of the OneSheeld class anymore).

     public static LedShield LED  
     {  
       get { return OneSheeldMain._LED; }  
       set { }  
     }  

It will reference the previously created OneSheeldMain class (which is the base class of the OneSheeldClass) and passes back the _LED object (which is the live Shield object) ... but how did the _LED object get created???? ... well ... if you look back at the OneSheeld getter ... there's a call in there to OneSheeldMain.Init(_OneSheeld) ... the init function not only stores access to the _OneSheeld object for other OneSheeld classes to use, but also instantiates ALL of the Shields with a call to the IncludedShieldsDefines.cs InitShields() function ...

     public static void Init(OneSheeldClass onesheeld)  
     {  
       OneSheeld = onesheeld;  

       OneShieldMain.InitShields();
     }  

...which then creates the shield objects so we can use them (All Shields can be used similarly to the LED calls without having to reference OneSheeld first).   Our OneSheeldUser class is taking care of that behind the scenes for us.   Note:  There are #defines at the top of the IncludedShieldsDefines.cs module that allow you to specify one or more shields are not to be created (saving memory, if needed).  The #if/#end if around the shield creation (in the below sample, the _LED = new LedShield(); is an example of how that works).   If the #define LED_SHIELD was commented out, the _LED object wouldn't get created (and would remain null) ... in addition, future use of the _LED (through the LED getter, for example) would cause an error in your sketch.   Just make sure any shields that you use are being created (by making sure the #define isn't commented out).   To comment out the line, so a shield doesn't get created simply put two / in front of the #define for the shield #define ... for example // #define LED_SHIELD  would guarantee that the _LED object does not get created (and should not be used by your sketch).   To make sure it does get created, make sure the // is not in front of the #define for whatever shields you are using.   The OneSheeld library WILL need to be rebuilt for the change to take effect (so, if you do add or remove the // to or from one or more #defines, make absolutely sure you rebuild the OneSheeld library).

     static void InitShields()  
     {  
        .  
        .  
        .  
       #if LED_SHIELD  
         _LED = new LedShield();  
       #endif  
       .  
       .  
       .  
     }  

If you don't understand some of this ... that's OK ... you really don't have to ... all you need to remember to do is derive your classes (specifically the ones that call OneSheeld.begin()) from the OneSheeldUser class!!!!) ... and you may need to know how to comment out (or uncomment a #define) and rebuild the OneSheeld library, but that's really it.

You have probably already noticed that the Led class (and all of the Library Sample classes that use OneSheeld.begin() are also derived from the IOneSheeldSketch interface.   This is why we don't have to call the Setup() and Loop() ourselves ...

In the Program.cs module, where we called the OneSheeldUser.Run() function ... that Run() function is calling Setup() and Loop() endlessly for us.   See....

     public static void Run(IOneSheeldSketch sketch)  
     {  
       sketch.Setup();  
       while (true)  
         sketch.Loop();  
     }  

The Run function expects an IOneSheeldSketch object as a parameter ... the IOneSheeldSketch interface just indicates that any class that derives from it must include a Setup() and Loop() function.

   public interface IOneSheeldSketch  
   {  
     void Setup();  
     void Loop();  
   }  

Hint:  If you do use the IOneSheeldSketch interface, after you derive your class from it ... right click on it and select the implement interface option and it will create the Setup() and Loop() functions for you ... which, by default will look like this ...

     public void Setup()  
     {  
       throw new NotImplementedException();  
     }  
     public void Loop()  
     {  
       throw new NotImplementedException();  
     }  

 ... you just delete the throw new NotImplementedException(); from the functions and start coding ... of course you can just create your own public void Setup() {} and public void Loop() {} without using the implement interface option.  Just remember to capitalize both of the function names.

From this point on, all future shield additions will include support for the OneSheeldUser class and IOneSheeldSketch interface.

Updates

03-18-2015 - Another small update was put on Github ... after I wrote the above post ... this update allows for JsonKeyChains to be specified in the same fashion that the Arduino allows (i.e. response["weather"][0]["main"].query(); ... taken from the Advanced Internet Sample).

03-20-2015 - Cleaned up the post (should make more sense now) ... sorry was in a hurry when I wrote it.

03-20-2015 - Added support similar to Arduinos IncludedShieldsDefines.h file.   The file that allows #defines to be commented out for Netduino is OneSheeldMain.cs.   Updates have been checked in to Github.

03-20-2015 - Decided it would be nice to have an IncludedShieldsDefines.cs file for as much similarity to the Arduino Libraries as-possible.  So, I have made the OneSheeldMain.cs partial and made an IncludedShieldsDefines.cs (which is just another part of the OneSheeldMain class but allows the #defines (and where they are used) to be broken out from other parts of the OneSheeldMain.cs module).   Updates have been checked into Github.   You should now be able to easily cut down on the memory footprint of your sketches that use the 1Sheeld library on Netduino by commenting out unused shields in the IncludedShieldsDefines.cs module (and rebuilding the Library and sketch).  Note: This doesn't change the size of the Library, it just changes what Shields get created for Sketches to use.

No comments:

Post a Comment