Tuesday, July 4, 2017

OPC, OLED and some LEDs for Good Measure

If you've ever been to an industrial plant that makes anything that flies (Airplanes, Helicopters, Space Shuttles, etc.) they more than likely have some large Ovens, Presses or Autoclaves that are part of their production process.    OLE for Process Control (OPC) is basically a set of standards / specifications for industrial telecommunications.   If the equipment has the support built in to be an OPC Server, and you have support in your software for OPC to connect to the OPC Server, you can communicate in a common way to that equipment. Most often the transport medium is either a Serial connection or an Ethernet Connection.   I work for a company that not only builds the Ovens, Presses and Autoclaves, but also creates the software that controls them (my job).   Think of these as a typical oven and you are baking a cake.  You mix up the recipe, preheat the oven and bake the cake for a set period of time and temperature.   Now imagine if you didn't need to set the temperature or timer. Instead, the oven automatically used the recipe to control the oven to bake the cake until it was ready and then turned off the oven.  It would require some temperature controlling abilities and some sensors to monitor how far along the cake is done and once done, it would quickly cool down the oven so it didn't continue to bake.   This is similar to what any of these industrial devices do.  But in some instances, you need to have not only temperature, but also pressure and vacuum.

Being that I have the good fortune of being able to work from home and these industrial devices are rather large (some of them you can put an entire wing of a plane into them), I needed some way to test without having to have connectivity to one of these devices.   My company does have VPN, and I can connect to the office network and even connect to and control a 'real' device.  However, that also requires someone on-site, monitoring the device, just for safety sake.  I needed a better option for the early testing phase (then once I know the bugs are under control and things have stabilized, I can move on to a 'live' environment).   Arduino to the rescue.

First off, the requirements:

1 - There are a bunch of system status bits (Equipment Ready, Run Active, Run Paused, Connectivity Monitor and Alarms (when something doesn't seem right) that I want to be able to monitor even when the screen of my computer is off ... some different colored LEDs would do the trick.
2 - I wanted also to be able to monitor some of the actual control and sensor values (Temperature In/Out, Pressure In/Out, Vacuum In/Out, etc).  The Out data is the controlling part, it sets the value that the devices need to work on getting to (Heat/Cooling, Pressure +/- and/or Vacuum +/-).  The In data is how monitor how we're doing in reaching the goals of the Recipe.   Again, I wouldn't mind seeing this when the screen to the computer was off... a small OLED Display module should do the trick.
3 - I wanted to communicate using OPC.  Yes, I could just use a Serial or Ethernet connection, but then I'd have to write an Arduino driver for our software... but since we already support OPC, it would be nice not to have to write another driver ... if I could find an Arduino OPC library/server solution... and I did ... the OPC Server for Arduino by Software Tools for Makers.

And, basic component requirements:

- An Arduino Mega (an UNO barely has enough memory for the OLED Display module, let alone adding the OPC support) ... I have an official Mega and I have a clone from Keyestudio ... I'm using the Keyestudio for this project ... $11.99 at the time of this writing on Amazon.
- An OLE Display Module (from AdaFruit) ... $9.99 on Amazon.
- 5 different colored LEDs (and 330 Ohm resistors) ...  Elfeland 375 LED (5-color, 2 size) set for $11.99 on Amazon ... I'll likely never need to buy LEDs again ... the resistors I used were also from a set of resistors I bought on Amazon (Elenco 365 resistor set) ... 5 resistors for each OHM ... so perfect # for 330 OHM resistors) ... at the time of this writing, they are $12.93 on Amazon.
- Some patch cables and a breadboard to hook everything up ... I had these already, but they aren't terribly expensive.

I won't bore you with connecting everything, there are plenty of tutorials for connecting LEDs to an Arduino and the OLE Display Module connection is as simple as it gets.

Now the software:

- We need the Adafruit SSD1306 library, which also requires the Adafruith GFX library from Adafruit for the OLED Display Module.
- We need the OPC library from Software Tools for Makers for OPC Support (get this from the Arduino Library Manager)
- I also use a Timer library by Simon Monk ... this allows me to read/write to the OPC Server and update the OLED at a set timed interval event (rather than having it occur every iteration of the loop() function ... or try to control it within the loop() function) ... just search from Timer for Arduino Simon Monk in Google ... The Arduino Playground link (for me it was the second in the list) ... but I think the first link(to Github) is where you'll end up getting it from ... but the Playground link shows you examples of it's use.
- Everything else is standard Arduino.

OPC 'Clients' use preset Tags defined in the OPC Server.   So, for example, if I wanted to get (or set) the temperature of an oven ... I might have a Tag on the OPC Server called Temp and I would read (or write) based on the Tag to get (or set) the temperature values from an OPC Client.

The OPC Library allows me to setup those tags (including different data types for the values).

The actual OPC Server in this instance is a combination of the setup part (the tags) within the Arduino code and a windows application that you can register / de-register as needed that actually communicates to the Arduino from the PC through the standard OPC Enumeration process (for more info check out the OPC Foundation pages) and provides the OPC connectivity (the beauty of OPC is that a lot of manufactures of PLCs, DACUs, etc. do have support for OPC, so you don't need to worry about the details of communicating with those devices, all you need to do, is set up the Tags and run the OPC Server for the device and then communicate using the OPC standards from any Clients.

So, I setup all the tags I needed.   I setup 2 Temperature Controls (read/write), a Pressure Control (read/write), a Vacuum Control (read/write).  8 Temperature Sensors for each Temperature Control (read only), 4 Vacuum Sensors (read only) and the 5 aforementioned system bits (read/write).

Some example Code...

// Initialize The OPC Serial Object
OPCSerial aOPCSerial;

.
.
.

// Then in the setup() function ...

 aOPCSerial.setup(); // Setup the OPC Serial device.

  // Add some tags that we want to use from our OPC compatible software.
  // Temperature Control 'write'
  aOPCSerial.addItem("TSP",opc_readwrite, opc_int, item_int_tempsp);  
  // Temperature Control 'read'
  aOPCSerial.addItem("TPV",opc_read, opc_int, item_int_temppv);        
  // The 8 Temperature Sensors
  aOPCSerial.addItem("TC1",opc_read, opc_int, item_int_tc);                
  aOPCSerial.addItem("TC2",opc_read, opc_int, item_int_tc);
  aOPCSerial.addItem("TC3",opc_read, opc_int, item_int_tc);
  aOPCSerial.addItem("TC4",opc_read, opc_int, item_int_tc);
  aOPCSerial.addItem("TC5",opc_read, opc_int, item_int_tc);
  aOPCSerial.addItem("TC6",opc_read, opc_int, item_int_tc);
  aOPCSerial.addItem("TC7",opc_read, opc_int, item_int_tc);
  aOPCSerial.addItem("TC8",opc_read, opc_int, item_int_tc);
  .
  .
  .
  etc.  // other controls / sensors / system bits / etc.

  // The Temperature Control callbacks for the OPC Server  ...

// Save the value that was sent to us from the OPC 'Client'
int item_int_tempsp(const char *itemID, const opcOperation opcOP, const int value)
{
  if (opcOP == opc_write)
    TempSP = value;
 
  return TempSP;
}

// Return the value that was requested from the OPC 'Client'
int item_int_temppv(const char *itemID, const opcOperation opcOP, const int value)
{
  return TempPV;
}

// The sensor callback ... I store the items in an integer array, so I figure out the array index
// based on the tag number and return the value.
int item_int_tc(const char *itemID, const opcOperation opcOP, const int value)
{
  int tcnum = atoi(&itemID[2]) - 1;
  return TC[tcnum];
}

Let's take a second and talk about Controls vs Sensors.  Controls in my world are both readable and writeable ... for example: when I write a value for a temperature control, I'm asking the OPC connected device to Heat Up or Cool Down to a specific temperature.   The read portion of the control not only monitors the device, but the OPC Client software we have also controls how far apart the write and read values can get without recognizing it as a possible problem (and throw an alarm if it is).   Sensors are read-only, any number of sensors can be active in a device to monitor whatever is 'baking'.   So, for example, back the cake scenario, let's a assume it's square.  We might place a sensor an inch or so from each corner and then one right in the middle ... and we'd monitor those sensors not only for temperature, but we'd also compare for consistency (this may also determine how fast the control tells the device to heat up).


Moving on:

I wrote the code to display most everything on the OLED Display module.   I used the top line (which is yellow to show the system bits, the I displayed (blue) the Temperature(s) In/Out, the Pressure In/Out and the Vacuum In/Out ... I also added a line for Avg Temp1 and Avg Vacuum sensor values (a little short on room for the Temp2 values, but I can see those if needed on the computer screen).  I might add a toggle button someday so I can have a second view of data on the OLED, where when I push the button it would toggle between a couple different screens, but that's a future project. Update: I actually did exactly as described.  I added a button and when pressed it changes back and forth between 2 different pages of information ... dependent on the page #, the OLED displays only that page's info.  I left the System Bits at the top on both pages.

A sample of what's being sent to the OLED.  The yellow are the status bits, the blue are the details (note: SP = Out, PV = In, TCs are temperature sensors, VTs are vacuum sensors).  The Equipment is ready (E:1), We're not running anything (R:0), We are not Pause (H:0 - H = Hold), the connection monitor (that goes on and off every 5 seconds) is current on (W:1 - W = WatchDog), there are no alarms (A:0).   The Temperatures (T1 and T2) are idle and 80 degrees (not real temperatures, just default value set by our software) and the sensors are keeping up at 80 degrees.  The Pressure and Vacuum are at 0.  Avg Temperature Sensors values are at 80 degrees, the Avg Vacuum Sensor values are at -1.  Update: The Avg Sensor values are now on Page 2 of the OLED Display and accessed by a pushbutton connected to the Arduino.  Also the Avg Vacuum Sensor value now shows 0 when idle... there was a bug ... discussed later.


// Initialize the OLED display object
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
.
.
.
Then in the setup() function ...

// Begin the OLED display device (using the correct i2c address of 0x3C)
// Note: There is an i2cScanner program that will tell you this address, it may be 
//           useful to download and run this to make sure you know the correct address.
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 

// Clear the buffer.
  display.clearDisplay();

  // Show the System Bits
  // Arguably the most important part of this is that you use print if you 
  // don't go to the next line and you use println if you do.
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.print("E:");
  display.print(EquipmentStatus); 
  display.print(" R:");
  display.print(RunStatus); 
  display.print(" H:");
  display.print(HoldStatus); 
  display.print(" W:");
  display.print(WatchDog); 
  display.print(" A:");
  display.println(ProcessAlarm); 
  display.println(""); // Blank Line

  // Show the rest of the code (Controls)
 display.print("T1-SP:");
 display.print((InTransition) ? TransTempSP : TempSP);
 display.print(" PV:");
 display.println((InTransition) ? TransTempSP : TempPV); 
 display.print("T2-SP:");
 display.print((InTransition) ? TransTemp2SP : Temp2SP);
 display.print(" PV:");
 display.println((InTransition) ? TransTemp2PV : Temp2PV);
 display.print("Pr-SP:");
 display.print((InTransition) ? TransPressSP : PressSP);
 display.print(" PV:");
 display.println((InTransition) ? TransPressSP : PressPV); 
 display.print("Vc-SP:");
 display.print((InTransition) ? TransVacSP : VacSP);
 display.print(" PV:");
 display.println((InTransition) ? TransVacPV : VacPV); 
 .
 .
 .
 etc.

// and finally ... don't forget this or it won't display on the OLED!!!
  display.display();


I then added the code to turn on/off the LEDs dependent on the system bits (yes, they are duplicated by the OLED display, but it's nice to be able to look into my home office and see what's currently happening during a test (which is pretty much impossible at a distance with the OLED).   This is typical Arduino LED handling.

The final step was to add code so that the input of the controls and sensors basically followed the controls (to simulate a running device).  There may be a slight bug or initialization error in the Avg Vacuum values (VT) ... it shows -1 on the OLED Display monitor ... it probably should be 0 ... it doesn't really matter, but it's something to look at in the future. Update: yes it was a bug, it now shows 0 as it should when idle ... it was actually a divide-by-zero error... interesting that it returned a -1.

// The simulation just follows the value written to the Temperature Control (so it increases or
// decreases both the control read and all of the Temperature Sensors ... by a predefined incremental
// amount every time the timer updates ... so the speed at which the device heats up or cools down is
// controlled by both how often the timer updates AND the increment value).

void updateTemp()
{
  // Temperature Handling
  if (TempSP != TempPV)
  {
    if (TempInc > 0 && TempPV > TempSP)
    {
      TempInc = -1 * TempIncVal;
    }
    else if (TempInc < 0 && TempPV < TempSP)
    {
      TempInc = TempIncVal;
    }
 
    TempPV += TempInc;
 
    for (int i = 0; i < 8; i++)
    {
        TC[i] += TempInc;
    }
  }
}

A photo of the whole unit ... equipment status is on ... everything else is currently off (although the connectivity bit will come back on in < 5 seconds).  There are 5 LEDs although it's a bit hard to tell.  Blue - Alarm Notification, White - Connectivity On/Off every 5 seconds (controlled with our software), Green - Running a Recipe, Red - Run is Paused and Yellow - Equipment Ready).  Update: There is now a Button on the right side of the breadboard with a 10K OHM resistor and the appropriate patch cables ... a press of the button now toggles the OLED display back and forth between two pages of information.



This ended up being a pretty fun (and a useful real-world) project.

A couple notes:

You have to close and de-register the OPC Server for Arduino if you are using the Serial support to upload a new/updated program to the Arduino ... then re-register when you are ready to run ... just keep that in mind.   If you use the Ethernet support (also available with the OPC Server for Arduino), that shouldn't be an issue.

The OLED Display module should be connected to 3.3V (although connecting to 5V seems to work too).

Typically you can query the OPC Server for a list of available Tags.  I have been unable to get this to work with the OPC Server for Arduino although that might be due to the fact that (at least the Serial mode) doesn't allow multiple connections (from different users).   Our software uses two connections, one for typical execution (our service user), one for configuration (the currently logged in user)).   This works on every other OPC Server I've seen (including those using Serial communication for transport).

Sunday, October 11, 2015

Parallax Smart Card Reader - Revisited

I believe I have mentioned that at some time, I was going to implement an Arduino Library for the Parallax Smart Card Reader.   Well, that time is now.

The library has support for the 3 smart cards that are also available from Parallax.   The samples are equivalent to the stand-alone samples that have been available for quite some time now.   The big difference is that using the 3 smart cards is now very similar.   You initialize the cards with an Initialize() function, you read and write data to the cards with Read() and Write() functions.   There is an Authenticate() function available for the IS23SC4442 card.   The parameters are similar for each card (but they aren't always identical ... the IS24SC16A card requires a Page parameter, where the IS24SC02A doesn't, for example).   Update:  This library now has 3 additional functions for each card:  WriteString(), ReadString() and ClearString() ... basically you can write a string at a location on a page of the memory (note, it does NOT attempt to write past page boundaries ... very important for the IS24SC16A card!) ... so for a single page card, you could write a string starting at a specific location...WriteString(0x50, "Test");  Then if you needed to, at some time, you could read data into a string buffer ... ReadString(0x50, 4, buffer);  // buffer needs to be 1 byte larger than required in order to handle NULL terminator ... so a definition of char buffer[5]; would be fine in the previous example.  Finally if you need to clear the string you would simply call ClearString(0x50, 4); to set the 4 non-null memory locations to NULL (or 0x00).  The IS24SC16A version of the functions do have a Page # as the first parameter.  The location parameter is zero-based ... so if you wanted to clear all memory in a single-page card or all memory of a page in the IS24SC16A card, you could call ClearString(0x00, 256); ... or ClearString(5, 0x00, 256); to clear page 5 of the IS24SC16A card.  Each of the card samples have a new function showing samples of using the new String functions. Remember a page is 256 bytes in size, regardless of the card type.  So the maximum string size is going to be 256 bytes ... if you have a string that takes up an entire page, the buffer size would still need to be 257 to allow for the null terminator returned from the ReadString() functions.

Note:  Initially there wasn't an addressable Read() function for the IS23SC4442 card (i.e. the ability to pass a location to read from).  Even though the Card doesn't seem to directly support it, I worked out a way to make it possible to support it from the Library.

Also Note:  The Constructor for each Card actually accepts 5 parameters ... those parameters are int IOPin, int CLKPin, int CDPin, int RSTPin, int Delay ... but they all default to int IOPin = A4, int CLKPin = A5, int CDPin = 3, int RSTPin = 2, int Delay = 50  ... so you don't have to specify the parameters unless you want to change the values (and the samples don't use the parameters) ... but if you wanted to, you could do something like:   IS24SC02A CardReader(A4, A5, 3, 2, 50);  which would just be the defaults, but can be changed as-needed.   Also, if you are familiar with how default parameter values work, you know that you really don't have to always specify all parameters, but I'm not going to take the time to explain that here.

Final Note: The IS23SC4442 card can be locked if authentication fails, however the IS23SC4442.cpp Authenticate() function is coded to help make sure that doesn't happen (but also means that it doesn't really have the full protection that it could have).   I have put comments in the code showing what was there (when I locked a card!!!!), and you are welcome to change it back to provide full security support.   BUT, if you do, be very careful.  When you lock a card, it can't be unlocked and is no longer usable!!!

Please see the original post about the Parallax Smart Card Reader, because a lot of it is still very relevant.

The Parallax Smart Card Reader Library is now downloadable directly from the Arduino Library Manager.   Simply search for Parallax.

--- or ---

You can find the Parallax Smart Card Reader Library on Github Here.

NOTE: If you are using an older version of the Arduino IDE that does not include the more recent Arduino Library Manager, you may need to move all of the files in the libraries\SmartCardReader\src directory to the root libraries\SmartCardReader directory.   Also, the .properties file may need to be deleted.   Neither of these actions will change the ability to use the library (or samples) and should work fine after doing so.   At the time that I updated the libraries for Library Manager support, I was using version 1.6.9 of the Arduino IDE.


Updates:

10-11-2015 - First release of Library available on GitHub.

10-11-2015 - Added Addressable Read Support to IS23SC4442 Card.    Checked into GitHub.

10-12-2015- A couple minor changes (just some cleanup).  Checked into GitHub.

10-12-2015 - Improved consistency of examples (again, just some cleanup).  Checked into Github.

10-15-2015 - Fix a potentional issue with the IS23SC4442 card when swapping multiple cards in and out of the Card Reader.  Checked into Github.

10-15-2015 - Added String Functions to Library.  Checked into Github.

05-23-2016 - Now available from the Arduino Library Manager (changes required are also checked into Github).

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.

Monday, March 2, 2015

1Sheeld on a Raspberry Pi using Alamode (or not)

The previous post was about using the Arduino IDE to build and run sketches for a 1Sheeld shield with the 1Sheeld plugged directly into a pcDuino.

This post describes the changes necessary to do similar ... but in this case the 1Sheeld is sitting on an AlaMode (Arduino compatible board) which, in-turn is sitting on a Raspberry Pi.   I have a Raspberry Pi 2 B ... but this should work on any version of the Raspberry Pi that the AlaMode is compatible with.   NOTE: Further investigation shows that these changes are necessary whether you use an Alamode or you simply have an Arduino plugged in to a USB Port.

First off, changes necessary are similar to pcDuino ... so, I'll go through each file that needs to be changed.   NOTE: I'm going to start with the base OneSheeld Arduino libraries, the changes made for pcDuino are NOT included.

Make sure you have the Arduino IDE and Alamode working on the Raspberry Pi before attempting this.  NOTE: Again, this is required even for Arduinos plugged into a Pi USB Port.  Also, make sure to have a copy of the OneSheeld Arduino Libraries and Examples on the Raspberry Pi ... I just copied them to the /usr/share/arduino/libraries directory (you will need to have root privileges to copy them there though) ... you can also copy them to your sketchbook location as specified in the Arduino IDE preferences (you may have to create the directory ... and you will have to create and put them in a libraries directory under the sketchbook directory ... otherwise, the IDE won't find the libraries during compilation of any sketches).

 <<< TERMIO ISSUES - ALAMODE SPECIFIC >>>  
 You may want to add this somewhere to your start-up configuration ...  
 or create an easily accessible script somewhere.  
   
 stty -onlcr -icrnl -F /dev/ttyS0  (or /dev/ttyS1, etc ... dependent on which is relevant)
   
 If you don't, using some of the 1Sheeld Shields won't work as-expected ...   
 and if you ever need to you can always do this to reset to default:   
   
 stty onlcr icrnl -F /dev/ttyS0  (or /dev/ttyS1, etc ... dependent on which is relevant) 
   
 There are a couple of shields that are affected if these changes   
 aren't in place (Terminal and Internet Shields are known to have problems).  



The only files that need to be changed are OneSheeld.h and OneSheeld.cpp.  All changes are highlighted in Red.

1. OneSheeld.h

For v1.3 libraries, after the #include "Arduino.h"
For v1.4 libraries, after the #include "ShieldParent.h"
For v1.5+ libraries, after the #include "ShieldsInstantiation.h"

add the following #defines ...

 #define ARDUINO_LINUX 1  
 #define RASPBERRYPI 1  

This will do 2 things for us ... the ARDUINO_LINUX define will allow the OneSheeld classes to compile properly (so it knows what version of string is available on the Rasberrry Pi) and the RASPBERRYPI #define will allow us to fix a couple other problems.

2. OneSheeld.cpp

Code in the begin function and where the class is instantiated both need to be changed...

 void OneSheeldClass::begin(long baudRate)  
 {  
  #if defined(__AVR_ATmega32U4__) || (defined(ARDUINO_LINUX) && !defined(RASPBERRYPI))  
  Serial1.begin(baudRate);  
  #else  
  Serial.begin(baudRate);  
  #endif  
 }  
 
 #if defined(__AVR_ATmega32U4__) || (defined(ARDUINO_LINUX) && !defined(RASPBERRYPI))  
 OneSheeldClass OneSheeld(Serial1);  
 void serialEvent1()  
 #else  
 OneSheeldClass OneSheeld(Serial);  
 void serialEvent()  
 #endif  
 {  
  OneSheeld.processInput();    
 }  

In both of cases, we want to use the Serial object NOT the Serial1 object when compiling for the Raspberry Pi.  So, even though ARDUINO_LINUX is #defined, we now can check for the RASPBERRYPI #define, and create and use the Serial object, rather than the Serial1 object.

---------

That is literally ALL you need to do.

I originally had a statement here about removing the two #defines if you were going to plug an Arduino into one of the Pi's USB ports (indicating you would have to remove the defines manually before compiling).  Further investigation has proven that these changes ARE REQUIRED for Arduino device compilation too.

A couple additional notes:   On the Raspberry Pi/Alamode combo ... you DO need to switch the UART switch when uploading to the Alamode (just as if you were communicating to an UNO)...and back when running a sketch.   Also, I just leave the power switch set to 3.3V ... it works fine.

Updates

03-04-2015 - changed text to indicate changes are required for Arduino devices connected through a USB Port as-well (not just the Alamode).

Friday, February 20, 2015

Using the Arduino IDE to run 1Sheeld sketches on a pcDuino

In a previous post, I provided a modified library and samples for using the 1Sheeld on a pcDuino (2 or 3) using the pcDuino Arduino-ish libraries (c_environment).   If, however, you want to compile sketches for the 1Sheeld on the pcDunio using the Arduino IDE, there are some additional things you need to do.   I've also taken pains to make sure that the changes I talk about here are integrated into the Arduino Libraries in such a way that you can still use them if you are connecting an Arduino to the pcDuino and using the 1Sheeld on the Arduino ... so, the exact same libraries can be used for Ardunio/1Sheeld sketches or pcDuino/1Sheeld sketches.

 <<< TERMIO ISSUES >>>  
 You may want to add this somewhere to your start-up configuration ... 
 or create an easily accessible script somewhere.  
   
 stty -onlcr -icrnl -F /dev/ttyS1   
   
 If you don't, using some of the Shields won't work as-expected ... 
 and if you ever need to you can always do this to reset to default: 

 stty onlcr icrnl -F /dev/ttyS1  
   
 If you really want to make a code change ... you will have to modify
 the Serial.cpp module to add this somewhere in the begin function 
 (I tested with it right before the tcsetattr() call ...   
   
  Opt.c_iflag &= ~(ICRNL);   
  Opt.c_oflag &= ~(ONLCR);   
   
 There are a couple of shields that are affected if these changes aren't
 in place (Terminal and Internet Shields are known to have problems).  

First, let me say that the changes to the OneSheeld library are pretty much identical to the changes made to compile with the c_environment (arduino-ish libraries).   Second, from what I can tell at this point, you won't have to change the sample sketches at all (no additional forward declarations required) and third, originally, you had to make changes to the Serial.cpp file to get this all to work ... but now, it's all OneSheeld library changes.  Every thing that needs to be changed or added is highlighted in Red below.

Note:  New v1.5 requirements ... After copying the Arduino Library and Samples to you pcDuino, delete the library.properties file from the library directory (OneSheeld) or the Arduino IDE included with pcDuinos will throw an error (and not load).

So, let's get started with the code changes.

--------------------------

OneSheeld Library changes ... 

1. OneSheeld.h - should be found in /home/ubnutu/Arduino/libraries/OneSheeld 

Add the following to just below the #define OneSheeld_h line ...

 #ifdef PCDUINO_IDE  
 #define PCDUINO 1  
 #define ARDUINO_LINUX 1  
 #endif  

The reason for these additional lines is to change things a bit for pcDuino when the OneSheeld libraries compile.   The ARDUINO_LINUX #define is already used by the OneSheeld libraries and defining it fixes a couple things so the libraries will compile for us.   The PCDUINO #define is new and we're going to make a couple changes to the OneSheeld libraries so things will compile properly.  The #ifdef PCDUINO_IDE check indicates that we only want to do the other #defines if we are compiling for the PCDUINO.  If we have an Arduino connected, we won't #define PCDUINO or ARDUINO_LINUX at all.

Also add the following  above the #include "Stream.h" line.

 #ifdef PCDUINO  
   typedef unsigned char byte;  
 #endif   

2. OneSheeld.cpp - again, should be found in /home/ubnutu/Arduino/libraries/OneSheeld 
put the following code around the #include "HardwareSerial.h" line ...


 #ifdef PCDUINO  
 #include "Serial.h"  
 #else  
 #include "HardwareSerial.h"  
 #endif  

also, add the extra checks for the PCDUINO definition in the begin method and where the OneSheeld object gets instantiated ... 


 void OneSheeldClass::begin(long baudRate)  
 {  
  #if defined(__AVR_ATmega32U4__) || (defined(ARDUINO_LINUX) && !defined(PCDUINO))  
  Serial1.begin(baudRate);  
  #else  
  Serial.begin(baudRate);  
  #endif  
 }  

 #if defined(__AVR_ATmega32U4__) || (defined(ARDUINO_LINUX) && !defined(PCDUINO))  
 OneSheeldClass OneSheeld(Serial1);  
 void serialEvent1()  
 #else  
 OneSheeldClass OneSheeld(Serial);  
 void serialEvent()  
 #endif  
 {  
 #ifdef PCDUINO  
  Serial.process_recv();  
 #endif  
  OneSheeld.processInput();     
 }

Finally, we need to add an additional parameter to the pulseIn calls in the analogRead method ...

 static unsigned long analogReadTimeout = 1000000L;  
 
 unsigned char OneSheeldClass::analogRead(int pin)  
 {  
   double period=(double)pulseIn(pin,HIGH,analogReadTimeout)+(double)pulseIn(pin,LOW,analogReadTimeout);  
   double duty=(double)pulseIn(pin,HIGH,analogReadTimeout);  
   double fraction=duty/period;  
   unsigned char pwm_out=(unsigned char)(ceil)(fraction*255);  
   return pwm_out;  
 }  

That is all that you should have to do to use the Arduino IDE when compiling OneSheeld sketches with the 1Sheeld sitting on the pcDuino.   

Also, if you already had the pcDuino OneSheeld library changes in place, make sure you revert back to the original Serial.cpp module ... remove the comment tags that commented out the serialEvent() and serialEventRun() functions and delete the newer serialEventRun() function.

  /*   
  #define serialEvent_implemented   
  void serialEvent()   
  {   
   Serial.process_recv();   
  }   
  void serialEventRun(void)   
  {   
  #ifdef serialEvent_implemented   
   serialEvent();   
  #endif   
  }   
  */   
   
  void serialEventRun(void)   
  {   
  Serial.process_recv();   
  serialEvent();   
  }   

Please remember to select the pcDuino as the Board in the Arduino IDE.   Make sure you pick the right Serial Port (if you don't have an Arduino connected, it should be the only port available).  

Also, set the power switch to 3.3V (since that's what pcDuino wants) ... and you can just leave the UART/SWITCH set to UART ... since you will be just compiling to the files system and running an executable.  That switch can just stay where it is.

These are the same changes that I made for the c_environment version. The big difference is the Arduino IDE version doesn't need the forward declarations added to the samples (which would probably have worked if needed).

Updates

02-24-2015 - removed requirement to modify the pcDuino version of Arduino.h ... now the only original pcDuino library change is to Serial.cpp (and it's the exact same change that is needed for the Arduino-ish [c_environment] version). 

03-05-2015 - no changes are required to the pcDuino library now (none, zero, zilch, zip) ... all necessary changes are part of the OneSheeld library ... and the changes made to the OneSheeld Library are done in a way (wrapped in #ifdefs)  that you can still use the library for either pcDuino compiles or Arduino compiles without any additional changes.

03-09-2015 - Updated for compatibility with Arduino v1.4 library and samples.

05-06-2015 - New requirement for v1.5 library noted above.

Monday, February 16, 2015

All post's source code downloads now point to Github

As of 02-16-2015 all source code (libraries, sample, sketches, etc) download links in the Arduino CodeDog blog posts that I've created have been changed and now point to public Github repositories.

All future source code downloads will point to Github repositories as well.

Friday, February 6, 2015

1Sheeld's GamePad and a Gameduino and you are Good-To-Go

After converting the Arduino Libraries to C# for Netduino I needed a little play time.  So, I decided to dig out my Gameduino and see what I could come up with.  It didn't take long to think "hmmm, 1Sheeld's GamePad may just work for the Frogger demo that the Gameduino folks have provided" ... and a small change to the Frogger Sketch (adding 1Sheeld Gamepad support) and voila, Frogger on an Arduino with 1Sheeld GamePad support...very, very cool!


Here's the entire Controller class using the OneSheeld library GamePad support, #include the OneSheeld.h at the top of the Frogger sketch and you will be crossing the street with a frog in tow in no time.

 class Controller {  
  public:  
   void begin() {  
    OneSheeld.begin();  
    prev = 0;  
   }  
   byte read() {  
    byte r = 0;  
    if (GamePad.isDownPressed())  
     r |= CONTROL_DOWN;  
    if (GamePad.isUpPressed())  
     r |= CONTROL_UP;  
    if (GamePad.isLeftPressed())  
     r |= CONTROL_LEFT;  
    if (GamePad.isRightPressed())  
     r |= CONTROL_RIGHT;  
    byte edge = r & ~prev;  
    prev = r;  
    return edge;  
   }  
  private:  
   byte prev;  
 };  

Put the 1Sheeld on an Arduino UNO r3 (or similar), put the Gameduino on the 1Sheeld.

For Manic Miner (updated):

Well, what I thought would work doesn't (the code I placed here earlier just doesn't seem to want to work).   What I have found is that it seems like it's best to just define pins on the 1Sheeld GamePad (rather than using the GamePad class) and then tweak the Manic Miner code like below:

 /* Setup 1Sheeld Gamepad Buttons to point to some pins... I used analog pins, but it probably doesn't matter. */  
 #define PIN_L A2  
 #define PIN_R A3  
 #define PIN_J A5  
   
 /* This was already in the code, but I just copied and pasted everything that needs some changes ... nothing here though */  
 #define CONTROL_LEFT 1  
 #define CONTROL_RIGHT 2  
 #define CONTROL_JUMP 4  
   
 /* Don't do any of the digitalWrite's in the setup_control() function ... I just commented them out... all we have to do is setup the pin modes for input. */  
 static byte setup_control()  
 {  
  pinMode(PIN_L, INPUT);  
  digitalWrite(PIN_L, HIGH);   
  pinMode(PIN_R, INPUT);  
  digitalWrite(PIN_R, HIGH);  
  pinMode(PIN_J, INPUT);  
  digitalWrite(PIN_J, HIGH);  
 }  
   
 /* Finally get rid of the ! in front of the digital read calls, we want the character to jump or move when we press the button(s) ... note, this was fine for the hardware joysticks, but for the 1Sheeld Game Pad, we need to do things a bit different! */  
 static byte control()  
 {  
  byte r = 0;  
  if (!digitalRead(PIN_J))  
   r |= CONTROL_JUMP;  
  if (!digitalRead(PIN_L))  
   r |= CONTROL_LEFT;  
  if (!digitalRead(PIN_R))  
   r |= CONTROL_RIGHT;  
  return r;  
 }  

One of the benefits of doing it this way is you don't have to load the OneSheeld libraries at all, so the code stays small enough to run on an UNO too!!!

Frogger could have been done similarly.

Updates

02-15-2015 - Updated changes for Manic Miner to work.