Mbed with the DS3231 RTC

I have a great little RTC from Love Electronics, it uses the DS3231 chip which is similar to the DS1307 (as shown in this article http://www.l8ter.com/?p=417)

image 

Since I am starting out using mbed (https://mbed.org) it is probably a good example to see if the DS3231 works with the LPC1768.  I started by using the DS1307 library as a guide, but then noticed that there are a few differences. (Note there is an mbed DS3231 library you can import)

image

If you have a look at the datasheets there are a few changes between the models, mainly the DS3231 has 2 alarms and is more accurate.

DS3231 http://datasheets.maximintegrated.com/en/ds/DS3231.pdf

DS1307 https://www.sparkfun.com/datasheets/Components/DS1307.pdf

DS1307

image

DS3231

image

Differences between the DS1307 and the DS1307:

1) Address 00h , BIT 7 , should be 0 not CH (Start stop feature does not exist on DS3231)

2) Address 02h for some reason the mask (on the example driver) was set to 196 but in reality you would want to ignore BIT 7 and BIT 6, use 0xC0 not 196. (Bug?)

3) Address 05h BIT 7 is flipped every time there is an overflow on the years register (06h), so you need to ignore this bit when reading the month.

 

Seconds..

When reading address 00g to get the seconds for some reason they are in the range 0 to 599 and loop every minute. This makes sense but it is not what the datasheet states – Range 00-59.

I built my own .NET Gadgeteer module…

Ever wondered how the pro’s physically manage to make a module? Well ok, you can get a fancy pick and place machine or send off for someone to assemble your module, but you can do it by hand. It is not as hard as expected.

As part of a .NET Gadgeteer hands on event at the Modern Jago in Shoreditch we were delighted to have Justin Wilson from Ingenuity Micro attend and show us how things are done (www.ingenuitymicro.com). He has designed and built an nice collection of .NET Gadgeteer modules and mainboards and expects to have them available shortly.

Justin has designed a multicolour LED module for .NET Gadgeteer, it is a 3 colour LED on a PCB with 2 Gadgeteer sockets. The module is nice as it is low cost simple and clean, and you can chain them together without the need for a co-processor on each module.

So the article title may be misleading, I really just got to assemble a module that Justin designed, rather than designing a new one. However after the assembly I felt I really had built something. (It was also amazingly good fun)

1) The first step is to design a PCB and get it printed, there are plenty of places that will produce PCBs and it is common to use EAGLE (http://www.cadsoftusa.com) to draw the schematics. Justin came along with a sheet of “Ingenuity Micro RGB Pixel v1.0” PCBs and all the components that are needed for the assembly. It is worth mentioning that some of the components are very very small, the black dot in the picture below is a NAND gate, it has a total of 5 pins. When you compare it with the Gadgeteer socket you can see that this could be a troublesome task. This PCB looks a bit rough as they were just snapped along the edges to separate them from the sheet.

image

2) The next step is to add some solder paste to each of the pads where we plan to solder components. There are two methods.

  • The first is is to get a small screwdriver, or toothpick and put a tiny blob of paste on each and every pad that you need to solder components. This is very time consuming and can result in an uneven distribution of solder risking dry joints or bridging between pins. The workshop was over by the time I got to build my module so this was the method I had to use. 
  • The second method is to use a solder mask. This is a very thin piece of plastic (like a overhead transparency) with holes cut (usually laser cut) for all the points you want paste. The key is to align the mask up exactly with the PCB and then in one go spread the paste across the top. It is handy to make a holder to align the PCB and mask, as shown below. Wherever there is a hole in the mask, solder paste will be deposited. Be careful to remove the solder mask cleanly in one move so as to not smudge the paste that has been left behind. If you have the correct solder mask, the correct way round you will end up with every pad on the PCB nicely coated with solder paste. (EAGLE can produce solder masks: http://www.cadsoftusa.com , and there are companies that will laser cut your EAGLE mask out of plastic)

 image

3) It is easiest to place all the components on one side of the PCB in one go. Be sure to place all the components the correct way round! The RGB Pixel only has one component on the top, an SPI LED with 6 pads, which is nice an big, so easy to place. Check to see that all the pads/pins are correctly aligned and that there are no bridges of paste between pins. Don’t worry too much as the solder and flux will pull the component into place a bit.

4) Once all the components are in the correct place, it is time to heat the board. Some people use an oven, but as it was a workshop it is easier to use a Hot air rework station (basically a very hot air blower) they are amazingly cheep and I got one from Amazon, not too sure how long it will last, but cant complain as it was a real bargain. The hot air station goes up to 450C which is enough to burn almost anything! I run my one at 250C but check to see what temperature your paste recommends. Also you might want to check the maximum temperature tolerance of your components. The picture below shows the LED being heated, the solder starts gray and paste like, but when it comes up to temperature it will quickly flow around the pins/pads and collect in all the places you wanted it to be, just like magic. It will go silver and shiny when ready.

image 

5) Next step was to do the other side of the PCB.  This was more complex as there are 2 capacitors (C1,C2), 1 resister (R1) , 1 NAND gate and 2 Gadgeteer sockets and they are all really tiny, the paste helps hold them in place. (Completed module below)

image

6) The next step is to test the module, and write some software. If you are writing a driver for a new module, be sure to follow the module builders guide http://gadgeteer.codeplex.com/releases/view/105388

Justin provided a driver for his module, so it was just a case of testing the various methods. There is a code sample below.

  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. using Microsoft.SPOT;
  5. using Microsoft.SPOT.Presentation;
  6. using Microsoft.SPOT.Presentation.Controls;
  7. using Microsoft.SPOT.Presentation.Media;
  8. using Microsoft.SPOT.Touch;
  9.  
  10. using Gadgeteer.Networking;
  11. using GT = Gadgeteer;
  12. using GTM = Gadgeteer.Modules;
  13. using Gadgeteer.Modules.IngenuityMIcro;
  14.  
  15. namespace IngenuityMicroPixelTest
  16. {
  17.     public partial class Program
  18.     {       
  19.         void ProgramStarted()
  20.         {                       
  21.             Debug.Print("Program Started");
  22.             RgbPixel led = new RgbPixel(6);
  23.             led.NumPixel(1);            
  24.             led.Set(0, 255, 0,0, true);//RED
  25.             led.Set(0, 0, 255, 0, true);//GREEN
  26.             led.Set(0, 0, 0, 255,   true);//BLUE
  27.             led.Set(0, 255, 255, 255, true);//WHITE
  28.  
  29.             led.Fade();            
  30.         }
  31.     }
  32. }

Fantastic it worked !!! (Trying not to be too surprised) The module is great, you can chain multiple together and they are very bright! Check out the colours below…, it is a multicolour LED with 0-255 for each colour, resulting in a possible 16million different colours.

image

image

image

Hardware used:

Love Electronics USB DC Power Module:

http://gadgeteering.net/module/love-electronics-usb-dc-power-module

GHI FEZ Cerberus:

http://gadgeteering.net/mainboard/ghi-fez-cerberus

RGB Pixel : Ingenuity Micro

 

Happy gadgeteering.net

PIR module for .NET Gadgeteer (Motion Sensor)

(Module: http://gadgeteering.net/module/ghi-pir-sensor)

We met up last night to have a look at a .NET Gadgeteer module, picking a nice easy module to look at to begin with for: http://www.meetup.com/GadgeteerSouthCoast/. Despite being called the GHI PIR Sensor it is found in the toolbox as a “Motion Sensor”

image

Here is the view from the top of the module:

WP_20130524_012

There is just the one event which is triggered whenever the sensor spots an IR source (Heat/person), called Motion_Sensed. There is a property that can be checked to see if the sensor can still see the source once it was triggered.

image

I would have expected to see a data sheen in codeplex, http://gadgeteer.codeplex.com/SourceControl/latest, but there is nothing. Next stop is the module manufacture website/forum. In this case it is GHI and I am not the first to ask questions about the PIR module:

Based on this information I have labelled the POTS and jumper below.

PIR

Motion_Sensed event

First lets see how often this event gets triggered. First add code for the event:

  1. Debug.Print("Program Started");
  2. startTime = DateTime.Now;
  3. motion_Sensor.Motion_Sensed += new GTM.GHIElectronics.Motion_Sensor.Motion_SensorEventHandler(motion_Sensor_Motion_Sensed);

And here is what we will execute every time it is triggered:

  1. void motion_Sensor_Motion_Sensed(GTM.GHIElectronics.Motion_Sensor sender, GTM.GHIElectronics.Motion_Sensor.Motion_SensorState state)
  2. {
  3.     Debug.Print("Time span = " + (DateTime.UtcNow – startTime).ToString());
  4.     startTime = DateTime.UtcNow;
  5.     Debug.Print("TRIGGER");
  6. }

By setting the time POT to min (anti-clockwise) so the events will be triggered as often as possible. (The distance POT is at the mid point). We get the following output.

Using mainboard GHI Electronics FEZHydra version 1.2
Program Started
Sensor = False
Time span = 00:00:09.9934515
TRIGGER
The thread ‘<No Name>’ (0×3) has exited with code 0 (0×0).
Time span = 00:00:07.8206963
TRIGGER
Time span = 00:00:07.7379289
TRIGGER
Time span = 00:00:06.4709773
TRIGGER
Time span = 00:00:08.7667315
TRIGGER
Time span = 00:00:07.6156723
TRIGGER
Time span = 00:00:06.2445184
TRIGGER
Time span = 00:00:06.6116992

By setting the time POT to max (Clockwise) we get the following output:

Time span = 00:03:42.0452275
TRIGGER
Time span = 00:03:58.3361766
TRIGGER
Time span = 00:03:41.4596557
TRIGGER
Time span = 00:03:40.7306982

So the time range is around 7sec (min) to 230sec (max) which is in keeping with the data sheet, which I think says 5 sec – 300 sec :

image

Changing the distance POT certainly did change range or sensitivity but it was not so easy to work out the exact trigger distances. however the datasheet shows a range or around 5 –7m and it certainly gets triggered within those ranges.

image

SensorStillActive property

There is a property that is readable and is of type Boolean, but what can we use it for?

If we run in with the time POT on minimum, distance on 50% and the jumper set to “Repeatedly trigger” we get the following output:

TRIGGER
State changed to: False @ 00:00:00.0011277
State changed to: True @ 00:00:02.2871399
Time span = 00:00:09.5215143
TRIGGER
State changed to: False @ 00:00:00.0650803
State changed to: True @ 00:00:02.4655180

And if we set the jumper to “Single trigger” we get the following:

TRIGGER
State changed to: False @ 00:00:00.0010278
State changed to: True @ 00:00:08.3824435
Time span = 00:00:11.9519795
TRIGGER
State changed to: False @ 00:00:00.0300698
State changed to: True @ 00:00:03.0303744
Time span = 00:00:06.5282752
TRIGGER
State changed to: False @ 00:00:00.0019021
State changed to: True @ 00:00:02.5016538
Time span = 00:00:04.7239091
TRIGGER
State changed to: False @ 00:00:00.0771596
State changed to: True @ 00:00:02.9764812
State changed to: False @ 00:00:06.4768678
Time span = 00:00:06.4945881
TRIGGER
State changed to: True @ 00:00:07.4823129
Time span = 00:00:11.0135769

So I am unsure what the jumper does, but the event is triggered when the SensorStillActive goes to false, it is held false for around 3 seconds then returns to true. The trigger happens every 6sec as expected. I suspect this will get held to false as long as the sensor can still detect an IR source, however my one always stops sensing despite being waved around at a source. Here is what the datasheet has to say about the jumper:

image

 

Have some source code:

  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. using Microsoft.SPOT;
  5. using Microsoft.SPOT.Presentation;
  6. using Microsoft.SPOT.Presentation.Controls;
  7. using Microsoft.SPOT.Presentation.Media;
  8. using Microsoft.SPOT.Touch;
  9.  
  10. using Gadgeteer.Networking;
  11. using GT = Gadgeteer;
  12. using GTM = Gadgeteer.Modules;
  13.  
  14. namespace CompassDriverExample
  15. {
  16.     public partial class Program
  17.     {
  18.         private DateTime startTime;
  19.         private bool sensorState;
  20.            
  21.         void ProgramStarted()
  22.         {
  23.             Debug.Print("Program Started");
  24.             startTime = DateTime.Now;
  25.             sensorState = motion_Sensor.SensorStillActive;
  26.             motion_Sensor.Motion_Sensed += new GTM.GHIElectronics.Motion_Sensor.Motion_SensorEventHandler(motion_Sensor_Motion_Sensed);
  27.             GT.Timer tmr = new GT.Timer(50, GT.Timer.BehaviorType.RunContinuously);
  28.             tmr.Tick += new GT.Timer.TickEventHandler(tmr_Tick);
  29.             tmr.Start();
  30.         }
  31.  
  32.         
  33.         void tmr_Tick(GT.Timer timer)
  34.         {
  35.             if (motion_Sensor.SensorStillActive != sensorState)
  36.             {
  37.                 Debug.Print("State changed to: " + motion_Sensor.SensorStillActive + " @ "+ (DateTime.UtcNow – startTime).ToString());
  38.                 sensorState = motion_Sensor.SensorStillActive;
  39.             }
  40.         }
  41.  
  42.         void motion_Sensor_Motion_Sensed(GTM.GHIElectronics.Motion_Sensor sender, GTM.GHIElectronics.Motion_Sensor.Motion_SensorState state)
  43.         {
  44.             Debug.Print("Time span = " + (DateTime.UtcNow – startTime).ToString());
  45.             startTime = DateTime.UtcNow;
  46.             Debug.Print("TRIGGER");
  47.         }
  48.     }
  49. }

I want to edit the .NET Gadgeteer drivers for a particular module!

When you drag a module from the toolbox onto the designer it references the correct DLL’s and adds import statements to the program.cs.

image

Fig 1: Adding a compass module to the designer.

This way you are ready to just use the module in your code. This is the normal way to to things… However what if you want to make a change to the driver for a particular module, for example to:

  • add an event, method or property,
  • perhaps fix an annoying bug,
  • add extra debug, or work out how the hardware works,
  • load a different driver, such as the GPS/Bluetooth module drivers from Codeplex.com ,
  • copy the driver for a similar piece of hardware,
  • you want to compile a driver for a different version of NetMF (e.g. upgrade 4.1 –> 4.2)
  • you cant be bothered to get WIX setup and working just to make a small change.

The correct way to change a driver is to get the source code VS solution, edit it and then rebuild. This will build an installer (msi) which can be installed and distributed with the changes intact. This process is fine for the final edits and distribution of a driver, but what if you just want to try a few changes? This is a long process as each change results in a build, reinstall , test.

This post is designed to show how to simply edit drivers or include different drivers. It is NOT a substitute for building the install files which should be distributed with each hardware module.

Example

Lets say we want to make an edit to the compass driver, perhaps it does not function as we expected. Here is a summary of what to do:

  1. Get the source code. (Either the existing source, or substitute with a different one)
  2. Remove the existing driver from your solution.
  3. Add the (new) source code to your solution.
  4. Add the module to your project.
  5. Edit the driver and test.

Get the source code.

There are multiple drivers for some modules, and the alternatives can often be better. (Well at least until the manufacturer incorporates the changes into the distributed driver.)

If you are looking for other drivers try: http://www.codeplex.com/site/search?query=.net%20gadgeteer

The source for the distributed drivers lives here: http://gadgeteer.codeplex.com/SourceControl/latest

I use SVN to get the latest source, but if you just want the source then click the download button. It will give you a zip, be sure to expand it to somewhere sensible, we will be using it later.

image

Remove the existing driver from your solution.

This step is important as it can drive you crazy later by swapping your new driver with the old one when you least suspect it.

Here is some background information first:

If we look a the code behind the designer we can see what happens when the compass module is added.

image

Click “Show all files” if you cannot see the “Program.gadgeteer.cs” file. This file is generated EVERY time you save the designer (the place you drag modules onto). Inside this file there is the following code. (For this example project)

  1. namespace CompassDriverExample {
  2.     using Gadgeteer;
  3.     using GTM = Gadgeteer.Modules;
  4.     
  5.     
  6.     public partial class Program : Gadgeteer.Program {
  7.         
  8.         private Gadgeteer.Modules.Seeed.Compass compass;
  9.         
  10.         public static void Main() {
  11.             // Important to initialize the Mainboard first
  12.             Program.Mainboard = new LoveElectronics.Gadgeteer.ArgonR1();
  13.             Program p = new Program();
  14.             p.InitializeModules();
  15.             p.ProgramStarted();
  16.             // Starts Dispatcher
  17.             p.Run();
  18.         }
  19.         
  20.         private void InitializeModules() {
  21.             this.compass = new GTM.Seeed.Compass(8);
  22.         }
  23.     }
  24. }

The first line of importance creates a compass variable of type Gadgeteer.Modules.Seeed.Compass.

  1. private Gadgeteer.Modules.Seeed.Compass compass;

The second line of importance is the one that creates an instance of the compass object and specifies the socket number.

  1. this.compass = new GTM.Seeed.Compass(8);

In order to create the compass object it needs to know about it. This is achieved automatically by adding a reference to the compiled DLL, when you dragged in a new module.

image

Here are the things you need to change to remove the old driver:

1) Delete the module from the designer.

- This will remove it from the generated code and will stop the auto generated code swapping back to the old driver when you save.

2) Check that the references on your project no longer reference the DLL for the old driver. (highlighted above)

or

You could just start with a blank project.

The reason I show all this is that later on you will need to instantiate the object yourself. If you don’t know what to write you can copy the auto generated code … more later.

Add the (new) source code to your solution.

At this point you have a blank project or one that does NOT reference/use the module that you plan to edit the driver for. First we need to add the source code, this is the code that you downloaded earlier or can be a different driver that you wish to try.

image

In Visual studio right click on the solution and add an existing project by navigating to the correct project. (This can be a little confusing as it tends to be a long way down the folder structure, and you need to be sure of the version you are using, v4.2)

image

Inside the Compass –> software folder you are looking for the compass.csproj file. (I would expect to see 2 versions, one for 4.1 and one for 4.2, but this may not be the case if an old driver template was used.)

image 

A little note: In this particular case there is just a driver for the 4.1 version of NetMF, yet when you install the latest SDK the compass is available in 4.2. This means that the manufacturer has updated the driver but not uploaded the changes to Codeplex. Be sure to encourage manufacturers to keep the source updated. This could also be the case if the Codeplex source code has different methods/signatures. Since my project is v4.2 I will just choose to update the target framework in the project properties.(Good example of how to upgrade versions)

image 

Add the module to your project

At this stage you should have multiple projects in your solution. Your project as well as the project for the module you are wanting to edit. So now we have all the source code in place. Lets use it.

First we need to let our project know about the new project by adding a reference, but instead of referencing the DLL we reference the project with the source code.

image

It is under projects not the .NET tab:

image

You will then see a reference to the Compass project, note that this is different to the reference show above when you let the designer import it.

image

Now we are ready to use the module. Remember all the automatically generated code that the designer does for you, well now you have to do it yourself. Instead of adding to code to the generated file (you will loose it , when it auto generates) we add it to the main program.cs file. (only 2 lines)

First create a private variable for the compass then in the program started create an instance, on socket 8. Be sure not to use the socket in the designer – you will get a warning if you do so fear not.

  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. using Microsoft.SPOT;
  5. using Microsoft.SPOT.Presentation;
  6. using Microsoft.SPOT.Presentation.Controls;
  7. using Microsoft.SPOT.Presentation.Media;
  8. using Microsoft.SPOT.Touch;
  9.  
  10. using Gadgeteer.Networking;
  11. using GT = Gadgeteer;
  12. using GTM = Gadgeteer.Modules;
  13.  
  14. namespace CompassDriverExample
  15. {
  16.     public partial class Program
  17.     {
  18.         private Gadgeteer.Modules.Seeed.Compass compass; //Declare the compass
  19.              
  20.         void ProgramStarted()
  21.         {
  22.             this.compass = new GTM.Seeed.Compass(8);// create an instance on socket 8
  23.             Debug.Print("Program Started");
  24.         }
  25.     }
  26. }

Edit the driver and test.

If you have made it this far then you have a solution that builds a module from source and instantiates the module, ready for use. Have a go to see if it works as expected and all the methods are there.

image

Now down to the best bit, you can now just change the module source and deploy as you normally would. The compass project will be rebuild every time. For example you may want to know exactly what the ‘Gain’ value is and how it is set. The module has a setgain method which takes a Gain property. The property ranges from Gain1 to Gian8, if you hold your mouse over the property it will display a tooltip, but lets go look at the source.

image

If you right click on a method (and have the source available) you can select “Go to definition” and it will take you to the code – got to love Visual Studio.

image

The method source simply writes a value to a register, where the value is of type ‘Gain’ (Right click on ‘Gain’ and go to definition)

  1. public void SetGain(Gain gain)
  2.         {
  3.             Write(Register.CRB, (byte)gain);
  4.         }

Here are the values :

  1. public enum Gain : byte
  2.         {
  3.             /// <summary>
  4.             /// +/- 0.88 Ga
  5.             /// </summary>
  6.             Gain1 = 0×00,
  7.  
  8.             /// <summary>
  9.             /// +/- 1.2 Ga (default)
  10.             /// </summary>
  11.             Gain2 = 0×20,
  12.  
  13.             /// <summary>
  14.             /// +/- 1.9 Ga
  15.             /// </summary>
  16.             Gain3 = 0×40,
  17.  
  18.             /// <summary>
  19.             /// +/- 2.5 Ga
  20.             /// </summary>
  21.             Gain4 = 0×60,
  22.  
  23.             /// <summary>
  24.             /// +/- 4.0 Ga
  25.             /// </summary>
  26.             Gain5 = 0×80,
  27.  
  28.             /// <summary>
  29.             /// +/- 4.7 Ga
  30.             /// </summary>
  31.             Gain6 = 0xA0,
  32.  
  33.             /// <summary>
  34.             /// +/- 5.6 Ga
  35.             /// </summary>
  36.             Gain7 = 0xC0,
  37.  
  38.             /// <summary>
  39.             /// +/- 8.1 Ga
  40.             /// </summary>
  41.             Gain8 = 0xE0,
  42.  
  43.         }

If you rename one of these values, it will have an immediate effect on your code. For example:

  1. public enum Gain : byte
  2.         {
  3.             /// <summary>
  4.             /// +/- 0.88 Ga
  5.             /// </summary>
  6.             Gain1RenamedToSomethingBetter = 0×00,
  7.  
  8.             /// <summary>

Will immediately change in your program.cs

image

Job done, you are now able to edit the driver and have the changes take effect immediately. Compile is same as always and the solution will deploy your latest code to the hardware when you deploy. This way you can edit and change code until you are satisfied. Once that is done you can then go back to the Compass SOLUTION and build an installer…..

Advanced – where did these numbers come from?

But you have to wonder what these values are and what do they mean? Here is where we need the datasheet. A few folders up from the source code there is a ‘Hardware’ folder that has a datasheet. I you have a custom modules or there is not datasheet you can do very well by searching for the chip number. If stuck pester the manufacturer to add a link to the datasheet for the module. 

This compass module is a HMC5883 and there is a data sheet in the Hardware folder (in the downloaded zip from codeplex) :

image

These can be hard to read, but we can use the bits we are interested in. For example searching the datasheet for ‘Gain’ shows that there is a gain control and that there are 3 bits dedicated to changing it.

 image

Further on in the datasheet there is a section about ‘configuration register B’ this is for setting the gain. Remember that the gain method just writes a byte (8 bits) to a register (Register B), table 10 lets us know that the register is 1 byte (8 bits) – same as our code. So now we just need to check the values that are to be written. Table 10 on page 13 shows that bits 7,6,5 are for setting the gain and 4-0 must be set to zero. Table 12 shows the different gain values and shows how this changes the output.

image

For example the default gain is 1024 counts/Gauss and this is done by setting the 8 bits of the register to [0,0,1,0,0,0,0,0] but if we look at the code the values are written in hex not binary.

If you are not comfortable converting, believe it or not calculator is your friend! Change calculator into programmer mode:

image

Select binary and then input the binary you want to convert, in this case [0,0,1,0,0,0,0,0]  (note you do not need to leading zeros)

image

If you then select hex, it will convert the value. (Answer 20)

image

Since the compiler would not know if we meant 20 (twenty) or 20 (thirty two in decimal,or binary [0,0,1,0,0,0,0,0]) there is a prefix added to a number to let it know that it is hex, the prefix is ‘0x’. And sure enough if we look at the code the default gain is ‘0x20’ which is the correct value according to the data sheet.

  1. public enum Gain : byte
  2.         {
  3.             /// <summary>
  4.             /// +/- 0.88 Ga
  5.             /// </summary>
  6.             Gain1RenamedToSomethingBetter = 0×00,
  7.  
  8.             /// <summary>
  9.             /// +/- 1.2 Ga (default)
  10.             /// </summary>
  11.             Gain2 = 0×20,

Have a look around the datasheet, perhaps there are hardware features that are not implemented in code, so you can improve the performance, enhance the driver, solve bugs or trouble shoot issues.

For example register A can be used to set the sensor rate (default 15Hz) as well as a bias for different axis, but if you right click on the register name and select ‘find all references’, it shows that the driver never uses it….

image

Happy Gadgeteering…. check www.gadgeteering.net for a complete list of hardware.

Tips

If you are using the express version of Visual Studio it only supports one language. So if you have a VB project and import a C# project, you wont get any debug and things will look a bit strange.

Remember that since you are compiling from source you can now add a break point inside the driver! (F9) This is a fantastic way to halt execution and inspect what is going on, with out having to write tonnes of output lines……

.NET Gadgeteer is causing me to get a BSOD

There is a case, where you can get a blue screen of death (BSOD) where your computer just dies/reboots, usually during a NetMF or .NET Gadgeteer deploy and especially if you hit the reset button (on the device) or attempt to pull out the USB cable.

This problem is to do with the USB driver and it has been addressed, so why are you still getting it? Well you are using the wrong driver :-). The old drivers run in kernel mode and hence can cause a BSOD, but this has since been switched to WinUSB which runs in user mode, so cannot BSOD. There are some issues with WinUSB and virtualised installs and not all hardware supports it.

The solution: swap the driver. Note this does not work for all hardware. If you cannot swap the driver for various known reasons. Learn to recognise the symptoms to avoid a BSOD. Usually Visual studio just sits on the deployment stage, if you do almost anything it will BSOD. However if you click ‘Build –> Cancel’ and wait 2 sec it will cancel and you can reset/redeploy. (Note on VS Express you don’t have this menu item.)

GHI have a great article on this http://www.ghielectronics.com/docs/109/usb-drivers-choices-including-winusb

The steps below let you check to see what driver you are using and how to swap it. (Use the GHI description for more info or if you get stuck)

What driver am I currently using?

During a deploy I hit the reset button on my spider today and it rebooted my laptop, strange as I thought I was using the WinUSB driver. Open Device Manager and look for the Gadgeteer device. In the case of a Spider mainboard it will appear as a .NET debugable device. Here is the device driver information for the old USB drivers, so turns out I am running the wrong one, hence the BSOD.

DriverThatBSOD

Swapping the driver.

If you click “Update Driver” you can navigate to the GHI drivers directory and select the WinUSB driver.

image

Once that has completed you will see that new drivers have been loaded. Horary! The spider mainboard also now appears under a different section in device manager.

image

image

(WinUSB drivers)

 

Notes:

GHI firmware from 14Feb (listed as updated 18 Feb) 2013

VS 2010

Spider mainboard.

Colour sensor + LED strip video

[UPDATE] RGB Slider – LED Strip with RGB slider 

As requested here is an example of my colour sensor in continuous mode setting the colours of the LED strip. The code below uses the tweaks shown in these two posts:

1) http://stevenjohnston.co.uk/2013/04/15/colour-sensor-on-net-gadgeteer-tcs3414/

2) http://stevenjohnston.co.uk/2013/04/11/individually-addressable-multicolour-leds-on-net-gadgeteer/

Video

The colour changes are much better that this video captures but it is hard to record them (Rather than looking at the ‘white’ over saturated LED, look at the surrounding colour!) In the first few seconds of the video I reset the entire 5m of LEDs and you can see the delay in sending the messages all the way to the last LED. Running the SPI bus at 14MHz I get a refresh of about 5hz (5 full refreshes per second)

[youtube=http://www.youtube.com/watch?v=Fo0yq2GQGaU]

Source code

  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. using Microsoft.SPOT;
  5. using Microsoft.SPOT.Presentation;
  6. using Microsoft.SPOT.Presentation.Controls;
  7. using Microsoft.SPOT.Presentation.Media;
  8. using Microsoft.SPOT.Touch;
  9.  
  10. using Gadgeteer.Networking;
  11. using GT = Gadgeteer;
  12. using Gadgeteer.Interfaces;
  13. using GTM = Gadgeteer.Modules;
  14. using Gadgeteer.Modules.GHIElectronics;
  15.  
  16. namespace MagicLights
  17. {
  18.     public partial class Program
  19.     {
  20.         FEZtive feztive;
  21.         void ProgramStarted()
  22.         {
  23.             Debug.Print("Program Started");
  24.             //Calibrate the colour sensor
  25.             colorSense.SetIntegrationTime(ColorSense.TimingRegister.FallingEdge_FreeRunning_400ms);//Set the time span for integration
  26.             colorSense.SetGain(ColorSense.GainRegister.Gain16x_PreScalerDivide4);//Set Gain and PreScalar.
  27.  
  28.             //Init the LED strip (test other SPI bus speeds)
  29.             feztive = new GTM.GHIElectronics.FEZtive(9);
  30.             //feztive.Initialize(160, 500);//OK
  31.             //feztive.Initialize(160, 1000);//OK
  32.             //feztive.Initialize(160, 2000);//OK
  33.             //feztive.Initialize(160, 4000);//OK
  34.             //feztive.Initialize(160, 8000);//OK
  35.             //feztive.Initialize(160, 16000);//Some LEDS are the wrong colour
  36.             //feztive.Initialize(160, 12000);//OK
  37.             //feztive.Initialize(160, 14000);//OK
  38.             //feztive.Initialize(160, 15000);//Some LEDS are the wrong colour
  39.             //feztive.Initialize(160, 14500);//Some LEDS are the wrong colour
  40.             feztive.Initialize(160, 14000);//OK
  41.  
  42.             //Show some colours
  43.             feztive.SetAll(feztive.Black);
  44.             // Set every LED to Red
  45.             feztive.SetAll(feztive.Red);
  46.             // Set every LED to Red
  47.             feztive.SetAll(feztive.Red);
  48.             // Set every LED to Green
  49.             feztive.SetAll(feztive.Green);
  50.  
  51.             //Toggle the colour sensor white LEDs
  52.             button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
  53.  
  54.             //Threads: http://blogs.msdn.com/b/netmfteam/archive/2011/01/17/threads-and-thread-priorities-in-netmf.aspx
  55.             //Dont block the main thread/dispatcher          
  56.             Thread t = new Thread(ContiniousReader);
  57.             t.Start();
  58.         }
  59.  
  60.         //Some variables for debug
  61.         uint r = 0, g = 0, b = 0, c = 0;
  62.         private void ContiniousReader()
  63.         {
  64.             //Loop forever – seperate threads!
  65.             while (true)
  66.             {
  67.                 ColorSense.ColorChannels cChan = colorSense.ReadColorChannels();
  68.                 if (cChan.Red > r) { r = cChan.Red; }
  69.                 Debug.Print("RED: " + cChan.Red + " MAX" + r);//DEBUG – check for saturation
  70.                 if (cChan.Green > g) { g = cChan.Green; }
  71.                 Debug.Print("GREEN: " + cChan.Green + " MAX" + g);
  72.                 if (cChan.Blue > b) { b = cChan.Blue; }
  73.                 Debug.Print("BLUE: " + cChan.Blue + " MAX" + b);
  74.                 if (cChan.Clear > c) { c = cChan.Clear; }
  75.                 Debug.Print("CLEAR: " + cChan.Clear + " MAX" + c);
  76.                 GTM.GHIElectronics.Color newColour = new GTM.GHIElectronics.Color(
  77.                   normaliseColourChannel(cChan.Red),
  78.                   normaliseColourChannel(cChan.Green),
  79.                   normaliseColourChannel(cChan.Blue));
  80.                 feztive.SetAll(newColour);
  81.  
  82.             }
  83.         }
  84.  
  85.  
  86.         // Take values in the range 0-65k and scale to 0-127       
  87.         byte normaliseColourChannel(uint value)
  88.         {
  89.             var res = ((value / 65536.0) * 127.0);
  90.             return (byte)res;
  91.         }
  92.  
  93.         bool ledStatus = false;//Last known state
  94.         void button_ButtonPressed(Button sender, Button.ButtonState state)
  95.         {
  96.             ledStatus = !ledStatus;
  97.             colorSense.ToggleOnboardLED(ledStatus);
  98.         }
  99.     }
  100. }

Colour sensor on .NET Gadgeteer (TCS3414)

Last post I was looking at the individually addressable LEDs and used the ‘ColorSense’ module to detect colours. (But did not get very good results)

This post addresses the issues with the colour sensor.

I am using the ColorSense REV 1.2 which has a TCS3414 digital colour sensor and 4 white LEDs to illuminate any sources. Each sensor has 16 photodiodes, 4 per channel (R,G,B,Clear)  each channel has a 16 bit ADC and we are communicating to the module using I2C. The original driver form GHI can be found in Codeplex: http://gadgeteer.codeplex.com/SourceControl/changeset/view/24955#333941

There are a few settings you may wish to change in order to calibrate she sensor for the task you have in mind. (you may even want to change these values during operation)

1) Integration time.

You can change the length of time that the sensor takes per integration cycle. There are 3 settings and 12ms is the default. (10ms / 400ms being the other options.) The code to enable changes to this is shown below. (If you are getting saturated, lower the integration time)

  1. public struct TimingRegister
  2. {
  3.     //http://www.ghielectronics.com/downloads/man/ColorSense_TCS3404_TCS3414-A.pdf
  4.     public static byte RegisterAddress = 0×81;
  5.     public static byte FallingEdge_FreeRunning_12ms = 0×00;
  6.     public static byte FallingEdge_FreeRunning_100ms = 0×01;
  7.     public static byte FallingEdge_FreeRunning_400ms = 0×02;
  8. }

  1. public void SetIntegrationTime(byte value)
  2. {
  3.     softwareI2C.Write(colorAddress, new byte[] { TimingRegister.RegisterAddress, value});
  4. }

2) Setting the Gain and the prescalar.

The gain and prescalar are set in a single operation. The gain provides “gain control for all four parallel ADC channels” and can be used to ensure the ADC range is modified to avoid saturation. I.e. if the output is always low, then increasing the gain will improve the sensitivity and the channel outputs will increase. The gain can be changed to 4 discrete multipliers, 1x, 4x, 16x and 64x – 64x would make the sensor very sensitive. (Saturation can be avoided by lowering the integration time)

The prescalar divides the ADC output, thus reducing the sensitivity of the ADC. There are 7 different prescalars -  divide by 1,2,4,8,16,32,64.

I have put the gain and prescalar options into a struct with a supporting method to change them:

  1. public struct GainRegister
  2. {
  3.     public static byte RegisterAddress = 0×87;
  4.     //Reserved bit (7 = high)
  5.     static byte Reserved = 0×80;
  6.     //Gain – This field switches the common analog gain of the four ADC channels. Four gain modes are provide
  7.     //bit 5:4
  8.     static byte Gain1x = 0×00;
  9.     static byte Gain4x = 0×10;
  10.     static byte Gain16x = 0×20;
  11.     static byte Gain64x = 0×30;
  12.     //Prescaler. This field controls a 6-bit digital prescaler and divider. The prescaler reduces the
  13.     //sensitivity of each ADC integrator as shown below:
  14.     //Bit 2:0
  15.     static byte PreScalerDivide1 = 0×00;
  16.     static byte PreScalerDivide2 = 0×01;
  17.     static byte PreScalerDivide4 = 0×02;
  18.     static byte PreScalerDivide8 = 0×03;
  19.     static byte PreScalerDivide16 = 0×04;
  20.     static byte PreScalerDivide32 = 0×05;
  21.     static byte PreScalerDivide64 = 0×06;
  22.     public static byte Gain1x_PreScalerDivide1 = (byte)(Reserved | Gain1x | PreScalerDivide1);
  23.     public static byte Gain4x_PreScalerDivide1 = (byte)(Reserved | Gain4x | PreScalerDivide1);
  24.     public static byte Gain16x_PreScalerDivide1 = (byte)(Reserved | Gain16x | PreScalerDivide1);
  25.     public static byte Gain64x_PreScalerDivide1 = (byte)(Reserved | Gain64x | PreScalerDivide1);
  26.     public static byte Gain1x_PreScalerDivide2 = (byte)(Reserved | Gain1x | PreScalerDivide2);
  27.     public static byte Gain1x_PreScalerDivide4 = (byte)(Reserved | Gain1x | PreScalerDivide4);
  28.     public static byte Gain1x_PreScalerDivide8 = (byte)(Reserved | Gain1x | PreScalerDivide8);
  29.     public static byte Gain1x_PreScalerDivide16 = (byte)(Reserved | Gain1x | PreScalerDivide16);
  30.     public static byte Gain1x_PreScalerDivide32 = (byte)(Reserved | Gain1x | PreScalerDivide32);
  31.     public static byte Gain1x_PreScalerDivide64 = (byte)(Reserved | Gain1x | PreScalerDivide64);
  32.     public static byte Gain4x_PreScalerDivide2 = (byte)(Reserved | Gain4x | PreScalerDivide2);
  33.     public static byte Gain4x_PreScalerDivide4 = (byte)(Reserved | Gain4x | PreScalerDivide4);
  34.     public static byte Gain4x_PreScalerDivide8 = (byte)(Reserved | Gain4x | PreScalerDivide8);
  35.     public static byte Gain4x_PreScalerDivide16 = (byte)(Reserved | Gain4x | PreScalerDivide16);
  36.     public static byte Gain4x_PreScalerDivide32 = (byte)(Reserved | Gain4x | PreScalerDivide32);
  37.     public static byte Gain4x_PreScalerDivide64 = (byte)(Reserved | Gain4x | PreScalerDivide64);
  38.     public static byte Gain16x_PreScalerDivide2 = (byte)(Reserved | Gain16x | PreScalerDivide2);
  39.     public static byte Gain16x_PreScalerDivide4 = (byte)(Reserved | Gain16x | PreScalerDivide4);
  40.     public static byte Gain16x_PreScalerDivide8 = (byte)(Reserved | Gain16x | PreScalerDivide8);
  41.     public static byte Gain16x_PreScalerDivide16 = (byte)(Reserved | Gain16x | PreScalerDivide16);
  42.     public static byte Gain16x_PreScalerDivide32 = (byte)(Reserved | Gain16x | PreScalerDivide32);
  43.     public static byte Gain16x_PreScalerDivide64 = (byte)(Reserved | Gain16x | PreScalerDivide64);
  44.     public static byte Gain64x_PreScalerDivide2 = (byte)(Reserved | Gain64x | PreScalerDivide2);
  45.     public static byte Gain64x_PreScalerDivide4 = (byte)(Reserved | Gain64x | PreScalerDivide4);
  46.     public static byte Gain64x_PreScalerDivide8 = (byte)(Reserved | Gain64x | PreScalerDivide8);
  47.     public static byte Gain64x_PreScalerDivide16 = (byte)(Reserved | Gain64x | PreScalerDivide16);
  48.     public static byte Gain64x_PreScalerDivide32 = (byte)(Reserved | Gain64x | PreScalerDivide64);
  49.     public static byte Gain64x_PreScalerDivide64 = (byte)(Reserved | Gain64x | PreScalerDivide64);
  50.  
  51. }

  1. public void SetGain(byte value)
  2. {
  3.     softwareI2C.Write(colorAddress, new byte[] { GainRegister.RegisterAddress,value });
  4. }

3) Enable disable

It may also be useful to enable/disable the module.

  1. public struct ControlRegister
  2. {
  3.     public static byte RegisterAddress = 0×80;
  4.  
  5.     public static byte EnableADC_PowerOn = 0×03;
  6.     public static byte DisableADC_PowerOff = 0×00;
  7. }

  1. public void EnableSensor(bool enable)
  2. {
  3.     if (enable)
  4.     {
  5.         softwareI2C.Write(colorAddress, new byte[] { ControlRegister.RegisterAddress, ControlRegister.EnableADC_PowerOn });
  6.     }
  7.     else
  8.     {
  9.         softwareI2C.Write(colorAddress, new byte[] { ControlRegister.RegisterAddress, ControlRegister.DisableADC_PowerOff });
  10.     }
  11. }

Example

For the task I have in mind setting the following provides me with RGB values between 0-65k and rarely gets saturated (About as sensitive as I dare push it) . Note there are public methods on this module to change these parameters.

  1. this.EnableSensor(true);//Power sensor on
  2. this.SetIntegrationTime(TimingRegister.FallingEdge_FreeRunning_100ms);//Set the time span for integration
  3. this.SetGain(GainRegister.Gain64x_PreScalerDivide2);//Set Gain and PreScalar.

Complete code below

  1. using System;
  2. using Microsoft.SPOT;
  3.  
  4. using GT = Gadgeteer;
  5. using GTM = Gadgeteer.Modules;
  6. using GTI = Gadgeteer.Interfaces;
  7.  
  8. namespace Gadgeteer.Modules.GHIElectronics
  9. {
  10.     //http://www.ghielectronics.com/downloads/man/ColorSense_TCS3404_TCS3414-A.pdf    
  11.     //ADDRESS REGISTER NAME REGISTER FUNCTION – Set MSB high ( OR register address with 0×80)
  12.     // COMMAND Specifies register address
  13.     //00h CONTROL Control of basic functions
  14.     //01h TIMING Integration time/gain control
  15.     //02h INTERRUPT Interrupt control
  16.     //03h INT SOURCE Interrupt source
  17.     //04h ID Part number/ Rev ID
  18.     //07h GAIN ADC gain control
  19.     //08h LOW_THRESH_LOW_BYTE Low byte of low interrupt threshold
  20.     //09h LOW_THRESH_HIGH_BYTE High byte of low interrupt threshold
  21.     //0Ah HIGH_THRESH_LOW_BYTE Low byte of high interrupt threshold
  22.     //0Bh HIGH_THRESH_HIGH_BYTE High byte of high interrupt threshold
  23.     //0Fh  SMBus block read (10h through 17h)
  24.     //10h DATA1LOW Low byte of ADC green channel
  25.     //11h DATA1HIGH High byte of ADC green channel
  26.     //12h DATA2LOW Low byte of ADC red channel
  27.     //13h DATA2HIGH High byte of ADC red channel
  28.     //14h DATA3LOW Low byte of ADC blue channel
  29.     //15h DATA3HIGH High byte of ADC blue channel
  30.     //16h DATA4LOW Low byte of ADC clear channel
  31.     //17h DATA4HIGH High byte of ADC clear channel
  32.  
  33.  
  34.     /// <summary>
  35.     /// A ColorSense module for Microsoft .NET Gadgeteer
  36.     /// </summary>
  37.     public class ColorSense : GTM.Module
  38.     {
  39.         private static GTI.DigitalOutput LEDControl;
  40.         private static GTI.SoftwareI2C softwareI2C;
  41.         private static byte[] readRegisterData = new byte[2];
  42.         private const byte colorAddress = 0×39;//Address for the Colour sense module on I2C
  43.  
  44.         // Note: A constructor summary is auto-generated by the doc builder.
  45.         /// <summary></summary>
  46.         /// <param name="socketNumber">The socket that this module is plugged in to.</param>
  47.         public ColorSense(int socketNumber)
  48.         {
  49.             Socket socket = Socket.GetSocket(socketNumber, true, this, null);
  50.  
  51.             socket.EnsureTypeIsSupported(new char[] { 'X', 'Y' }, this);
  52.  
  53.             LEDControl = new GTI.DigitalOutput(socket, Socket.Pin.Three, false, this);
  54.  
  55.             softwareI2C = new GTI.SoftwareI2C(socket, Socket.Pin.Five, Socket.Pin.Four, this);
  56.  
  57.             // Send COMMAND to access control register for chip power-up
  58.             // Send to power-up chip
  59.             this.EnableSensor(true);//Power sensor on
  60.             this.SetIntegrationTime(TimingRegister.FallingEdge_FreeRunning_100ms);//Set the time span for integration
  61.             this.SetGain(GainRegister.Gain64x_PreScalerDivide2);//Set Gain and PreScalar.
  62.         }
  63.  
  64.         /// <summary>
  65.         /// Turn the sensor on/off
  66.         /// </summary>
  67.         /// <param name="enable"></param>
  68.         public void EnableSensor(bool enable)
  69.         {
  70.             if (enable)
  71.             {
  72.                 softwareI2C.Write(colorAddress, new byte[] { ControlRegister.RegisterAddress, ControlRegister.EnableADC_PowerOn });
  73.             }
  74.             else
  75.             {
  76.                 softwareI2C.Write(colorAddress, new byte[] { ControlRegister.RegisterAddress, ControlRegister.DisableADC_PowerOff });
  77.             }
  78.         }
  79.  
  80.         /// <summary>
  81.         /// The TIMING register controls the synchronization and integration time of the ADC channels. The Timing
  82.         /// Register settings apply to all four ADC channels.
  83.         /// </summary>
  84.         /// <param name="value"></param>
  85.         public void SetIntegrationTime(byte value)
  86.         {
  87.             softwareI2C.Write(colorAddress, new byte[] { TimingRegister.RegisterAddress, value});
  88.         }
  89.  
  90.         /// <summary>
  91.         /// http://www.ghielectronics.com/downloads/man/ColorSense_TCS3404_TCS3414-A.pdf
  92.         /// The Gain register provides a common gain control adjustment for all four parallel ADC output channels. Two
  93.         //gain bits [5:4] in the Gain Register allow the relative gain to be adjusted from 1 to 64 in 4 increments. The
  94.         //advantage of the gain adjust is to extend the dynamic range of the light input up to a factor of 64 before analog
  95.         //or digital saturation occurs. If analog saturation has occurred, lowering the gain sensitivity will likely prevent
  96.         //analog saturation especially when the integration time is relatively short. For longer integration times, the 16-bit
  97.         //output could be in digital saturation (64K). If lowering the gain to 1 does not prevent digital saturation from
  98.         //occurring, the use of PRESCALER can be useful.
  99.         //The PRESCALER is 3 bits [2:0] in the gain register that divides down the output count (i.e. shifts the LSB of the
  100.         //count value to the right). The PRESCALER adjustment range is divide by 1 to 64 in multiples of 2.
  101.         //The most sensitive gain setting of the device would be when GAIN is set to 11b (64), and PRESCALER is set
  102.         //to 000b (divide by 1). The least sensitive part setting would be GAIN 00 (1) and PRESCALER 110 (divide by
  103.         //64). If the part continues to be in digital saturation at the least sensitive setting, the integration time can be
  104.         //lowered (see Timing Register section).
  105.         /// </summary>
  106.         /// <param name="value">GainRegister value </param>
  107.         public void SetGain(byte value)
  108.         {
  109.             softwareI2C.Write(colorAddress, new byte[] { GainRegister.RegisterAddress,value });
  110.         }
  111.  
  112.         /// <summary>
  113.         /// Toggles the on-board LEDs to the passed in state.
  114.         /// </summary>
  115.         /// <param name="LEDState">State to set the LEDs to.</param>
  116.         public void ToggleOnboardLED(bool LEDState)
  117.         {
  118.             LEDControl.Write(LEDState);
  119.         }
  120.  
  121.         /// <summary>
  122.         /// Reads the current color from the sensor and returns the results
  123.         /// </summary>
  124.         /// <returns>Returns an instance of the ColorChannels structure, holding the current measurement of color values.</returns>
  125.         public ColorChannels ReadColorChannels()
  126.         {
  127.             ColorChannels returnData;
  128.  
  129.             byte[] TransmitBuffer = new byte[1];
  130.  
  131.             TransmitBuffer[0] = 0×90; // Send COMMAND to access Green Color Channel register for chip
  132.             returnData.Green = readWord(0×90, TransmitBuffer)[0];
  133.             TransmitBuffer[0] = 0×91;
  134.             returnData.Green |= (uint)readWord(0×91, TransmitBuffer)[0] << 8;
  135.             //returnData.Green = 256 * (uint)readWord(TransmitBuffer)[0] + returnData.Green;
  136.  
  137.             TransmitBuffer[0] = 0×92; // Send COMMAND to access Red Color Channel register for chip
  138.             returnData.Red = readWord(0×92, TransmitBuffer)[0];
  139.             TransmitBuffer[0] = 0×93;
  140.             returnData.Red |= (uint)readWord(0×93, TransmitBuffer)[0] << 8;
  141.  
  142.             TransmitBuffer[0] = 0×94; // Send COMMAND to access Blue Color Channel register for chip
  143.             returnData.Blue = readWord(0×94, TransmitBuffer)[0];
  144.             TransmitBuffer[0] = 0×95;
  145.             returnData.Blue |= (uint)readWord(0×95, TransmitBuffer)[0] << 8;
  146.  
  147.             TransmitBuffer[0] = 0×96; // Send COMMAND to access Clear Channel register for chip
  148.             returnData.Clear = readWord(0×96, TransmitBuffer)[0];
  149.             TransmitBuffer[0] = 0×97;
  150.             returnData.Clear |= (uint)readWord(0×97, TransmitBuffer)[0] << 8;
  151.  
  152.             return returnData;
  153.         }
  154.  
  155.         private byte[] readWord(byte address, byte[] CommandBytes)
  156.         {
  157.             softwareI2C.WriteRead(colorAddress, CommandBytes, readRegisterData);
  158.             return readRegisterData;
  159.         }
  160.  
  161.         /// <summary>
  162.         /// Structure to hold color data
  163.         /// </summary>
  164.         public struct ColorChannels
  165.         {
  166.             /// <summary>
  167.             /// Intensity of green-filtered channel
  168.             /// </summary>
  169.             public uint Green;
  170.             /// <summary>
  171.             /// Intensity of red-filtered channel
  172.             /// </summary>
  173.             public uint Red;
  174.             /// <summary>
  175.             /// Intensity of blue-filtered channel
  176.             /// </summary>
  177.             public uint Blue;
  178.             /// <summary>
  179.             /// Intensity of non-filtered channel
  180.             /// </summary>
  181.             public uint Clear;
  182.         }
  183.  
  184.         /// <summary>
  185.         /// Enable / disable
  186.         /// </summary>
  187.         public struct ControlRegister
  188.         {
  189.             public static byte RegisterAddress = 0×80;
  190.             public static byte EnableADC_PowerOn = 0×03;
  191.             public static byte DisableADC_PowerOff = 0×00;
  192.         }
  193.  
  194.         /// <summary>
  195.         /// Integration time
  196.         /// </summary>
  197.         public struct TimingRegister
  198.         {
  199.             public static byte RegisterAddress = 0×81;
  200.             public static byte FallingEdge_FreeRunning_12ms = 0×00;
  201.             public static byte FallingEdge_FreeRunning_100ms = 0×01;
  202.             public static byte FallingEdge_FreeRunning_400ms = 0×02;
  203.         }
  204.  
  205.         /// <summary>
  206.         /// ADC Gain control- Gain = ADC adjustment, PreScaler = divides down the digital output
  207.         /// </summary>
  208.         public struct GainRegister
  209.         {
  210.             public static byte RegisterAddress = 0×87;
  211.             //Reserved bit (7 = high)
  212.             static byte Reserved = 0×80;
  213.             //Gain – This field switches the common analog gain of the four ADC channels. Four gain modes are provide
  214.             //bit 5:4
  215.             static byte Gain1x = 0×00;
  216.             static byte Gain4x = 0×10;
  217.             static byte Gain16x = 0×20;
  218.             static byte Gain64x = 0×30;
  219.             //Prescaler. This field controls a 6-bit digital prescaler and divider. The prescaler reduces the
  220.             //sensitivity of each ADC integrator as shown below:
  221.             //Bit 2:0
  222.             static byte PreScalerDivide1 = 0×00;
  223.             static byte PreScalerDivide2 = 0×01;
  224.             static byte PreScalerDivide4 = 0×02;
  225.             static byte PreScalerDivide8 = 0×03;
  226.             static byte PreScalerDivide16 = 0×04;
  227.             static byte PreScalerDivide32 = 0×05;
  228.             static byte PreScalerDivide64 = 0×06;
  229.             public static byte Gain1x_PreScalerDivide1 = (byte)(Reserved | Gain1x | PreScalerDivide1);
  230.             public static byte Gain4x_PreScalerDivide1 = (byte)(Reserved | Gain4x | PreScalerDivide1);
  231.             public static byte Gain16x_PreScalerDivide1 = (byte)(Reserved | Gain16x | PreScalerDivide1);
  232.             public static byte Gain64x_PreScalerDivide1 = (byte)(Reserved | Gain64x | PreScalerDivide1);
  233.             public static byte Gain1x_PreScalerDivide2 = (byte)(Reserved | Gain1x | PreScalerDivide2);
  234.             public static byte Gain1x_PreScalerDivide4 = (byte)(Reserved | Gain1x | PreScalerDivide4);
  235.             public static byte Gain1x_PreScalerDivide8 = (byte)(Reserved | Gain1x | PreScalerDivide8);
  236.             public static byte Gain1x_PreScalerDivide16 = (byte)(Reserved | Gain1x | PreScalerDivide16);
  237.             public static byte Gain1x_PreScalerDivide32 = (byte)(Reserved | Gain1x | PreScalerDivide32);
  238.             public static byte Gain1x_PreScalerDivide64 = (byte)(Reserved | Gain1x | PreScalerDivide64);
  239.             public static byte Gain4x_PreScalerDivide2 = (byte)(Reserved | Gain4x | PreScalerDivide2);
  240.             public static byte Gain4x_PreScalerDivide4 = (byte)(Reserved | Gain4x | PreScalerDivide4);
  241.             public static byte Gain4x_PreScalerDivide8 = (byte)(Reserved | Gain4x | PreScalerDivide8);
  242.             public static byte Gain4x_PreScalerDivide16 = (byte)(Reserved | Gain4x | PreScalerDivide16);
  243.             public static byte Gain4x_PreScalerDivide32 = (byte)(Reserved | Gain4x | PreScalerDivide32);
  244.             public static byte Gain4x_PreScalerDivide64 = (byte)(Reserved | Gain4x | PreScalerDivide64);
  245.             public static byte Gain16x_PreScalerDivide2 = (byte)(Reserved | Gain16x | PreScalerDivide2);
  246.             public static byte Gain16x_PreScalerDivide4 = (byte)(Reserved | Gain16x | PreScalerDivide4);
  247.             public static byte Gain16x_PreScalerDivide8 = (byte)(Reserved | Gain16x | PreScalerDivide8);
  248.             public static byte Gain16x_PreScalerDivide16 = (byte)(Reserved | Gain16x | PreScalerDivide16);
  249.             public static byte Gain16x_PreScalerDivide32 = (byte)(Reserved | Gain16x | PreScalerDivide32);
  250.             public static byte Gain16x_PreScalerDivide64 = (byte)(Reserved | Gain16x | PreScalerDivide64);
  251.             public static byte Gain64x_PreScalerDivide2 = (byte)(Reserved | Gain64x | PreScalerDivide2);
  252.             public static byte Gain64x_PreScalerDivide4 = (byte)(Reserved | Gain64x | PreScalerDivide4);
  253.             public static byte Gain64x_PreScalerDivide8 = (byte)(Reserved | Gain64x | PreScalerDivide8);
  254.             public static byte Gain64x_PreScalerDivide16 = (byte)(Reserved | Gain64x | PreScalerDivide16);
  255.             public static byte Gain64x_PreScalerDivide32 = (byte)(Reserved | Gain64x | PreScalerDivide64);
  256.             public static byte Gain64x_PreScalerDivide64 = (byte)(Reserved | Gain64x | PreScalerDivide64);
  257.  
  258.         }
  259.     }
  260. }

Individually addressable multicolour LED’s on .NET Gadgeteer

[UPDATE]: Sarah and Jan emailed me to mention that the 10A@5v power supply is a bit of an overkill. I agree but this is the recommendation from GHI. When I tested my strip on full power it was under 3A@5v . Jan claims his is around 2.6A@5v . This does not account for peak current draw, so you may hit issues, but perhaps the full 10A is a bit much.

Who does not like LED’s? They are fun and amazing, especially if you by a 5m strip of individually addressable multicolour LED’s. I had a whole pile of important work to get done today but for some reason the FEZtive kit I ordered ages ago was just begging to be used. More importantly I found a power supply that will give 10A@5v so I had to have a play.

The first hurdle was finding out how to power the module 160×3 LEDs (RGB) is a lot to power and GHI recommend a good power supply which is attached to the FEZtive module board part A, this way you are not drawing power through the .NET Gadgeteer mainboard. In the picture below you can see my power connector. (Not I have not split the PCB as I only need one connector for now and this way I don’t loose the other half.)

WP_20130411_005

Next I need to connect the connect the LED strip to the PCB, my strip had a connector on each end and as I planed to run this as a single strip, I cut the connector off the end and attached it to the PCB. This way I can disconnect the LED strip. Now I have power, data (Gadgeteer cable) and a connector for the lights.

 WP_20130411_006

Connect it all up and we are ready for some software….

WP_20130411_007

There is a great example of how the use the module here : http://wiki.tinyclr.com/index.php?title=FEZtive_Module 

At the time of writing the FEZtive module is not part of the main GHI installer (I guess it will be in time) so you need to download the project from the link above. Be sure to add this project to your solution (or new project),

image 

Next add a reference from your project to the FEZtive project you just downloaded. (I am using NetMF 4.2 so add a reference to the _42 project.)

image

Since you cannot drag and drop the module from the designer (not yet) you need to create the object on the socket that you are using, I have a Spider with SPI on socket 9. Here is the example code from: http://wiki.tinyclr.com/index.php?title=FEZtive_Module 

  1. public partial class Program
  2. {
  3.     // This method is run when the mainboard is powered up or reset.
  4.  
  5.     FEZtive feztive;
  6.     void ProgramStarted()
  7.     {
  8.         Debug.Print("Program Started");
  9.         feztive = new GTM.GHIElectronics.FEZtive(9);
  10.         // Initialize the module to use 80 LEDs, with an SPI clock rate of 4Mhz
  11.         feztive.Initialize(160, 4000);
  12.         // Set every LED to Black (off)
  13.         feztive.SetAll(feztive.Black);
  14.         // Set every LED to Red
  15.         feztive.SetAll(feztive.Red);
  16.         // Set every LED to Red
  17.         feztive.SetAll(feztive.Red);
  18.         // Set every LED to Green
  19.         feztive.SetAll(feztive.Green);

Fantastic this works! (It is hard to take a picture, they look better in real life)

WP_20130411_012

 

The FEZtive part B

The LED’s are connected by 4 lines (Gnd, CI, DI, +5v) and you don’t need anything on the ‘end’ of the LED strip, as it is just one big long SPI bus. So in theory you can cut the strip (there are marked points to cut after each LED- see the white line) into different sizes. The PCB that my kit came with has 2 parts and A and a B, each with 5 areas to connect an LED strip. Think of these PCBs as U-turns in the LED strip. You can cut it into 5 x 1m strips and then zigzag them between part A and part B, note that this will make the order of the LEDs into a long snake, so if you are writing text or showing a picture you will need to calculate which LED is where. (Yes you can link multiple together – I only have one)

WP_20130411_017

How does it work?

If you look at the GHI driver for this module you can see that there is a large array (one for each LED) which contains a custom ‘colour’ object. There are a series of public methods that let you manipulate this array (change the colour) and then there is a draw method that will ‘write’ the entire array of ‘colour’ objects to the SPI bus.

There are some interesting effects of this. Imaging you have a LONG strip, say 5m and decide to set the colour to blue, you can see the new colour advancing along the strip. It is easier to see if you slow the clock speed down.

  1. feztive = new GTM.GHIElectronics.FEZtive(9);
  2. feztive.Initialize(160, 500);

This got me thinking, how is it working? Imagine you built a new (very large) HD TV out of this would it work? Well not exactly, you would get rather ugly ‘scan’ lines and the refresh would be poor.

Change the colour

I have a .NET Gadgeteer colour sensor and setup a button that would read the sensor and set all LEDs to that colour. Handy to test and a little more fun as you can point it at an object, press a button and all the LEDs change colour.

Here is a bit of code:

  1. void button_ButtonPressed(Button sender, Button.ButtonState state)
  2. {
  3.    // colorSense.ToggleOnboardLED(true);
  4.    ColorSense.ColorChannels cChan = colorSense.ReadColorChannels();            
  5.    GTM.GHIElectronics.Color newColour = new GTM.GHIElectronics.Color((byte)(cChan.Red / 2), (byte)(cChan.Green / 2), (byte)(cChan.Blue / 2));
  6.    //  colorSense.ToggleOnboardLED(false);
  7.    feztive.SetAll(newColour);
  8. }

Something is a bit strange here, the colour sensor returns a uint for each of RGB as well as an intensity. The FEZtive LED strip takes a value between 0-127 for each of the RGB. For now I halved the channels, but this is not correct and for extreme lighting conditions it will produce strange results. I plan to normalise these values, but so I can test a bit more this will do for now. I could not find a datasheet for the Colour sensor module and the source code does not give away much. humph! – GHI? The driver reads 2 bytes fore each channel 2^16 colour options per channel, but I noticed that with a bright light the values rarely top 10,000. (e.g RED: 9715, GREEN: 12368, BLUE: 11918) [UPDATE : http://stevenjohnston.co.uk/2013/04/15/colour-sensor-on-net-gadgeteer-tcs3414/]

  1. public ColorChannels ReadColorChannels()
  2. {
  3.     ColorChannels returnData;
  4.  
  5.     byte[] TransmitBuffer = new byte[1];
  6.  
  7.     TransmitBuffer[0] = 0×90; // Send COMMAND to access Green Color Channel register for chip
  8.     returnData.Green = readWord(0×90, TransmitBuffer)[0];
  9.     TransmitBuffer[0] = 0×91;
  10.     returnData.Green |= (uint)readWord(0×91, TransmitBuffer)[0] << 8;

The LDP8806

WP_20130411_017

Each multicolour led on the strip has a processor next to it, yes every one! This is the LDP8806 – anyone got a data sheet?

Something is interesting here, you just tell the Gadgeteer initialiser how many LEDs there are, and then ram data down the SPI bus. If you say there are only 10 LEDs then only the first 10 in your strip will work. This means that the data is fed to the first led in the strip, the colour is set and then all subsequent data is passed down the SPI bus, to the next LED and so on. 

There is a series of zeros that are passed into SPI before and after each call, and this depends on the length of the strip. (presumably to set the LED into a state where it can obtain a new colour). I think this explains why each value is limited to a range of 0-127, the high bit is being used to command each LED.

  1. _zeros = new byte[3 * ((numLEDS + 63) / 64)];

Ok, enough speculation, here is a much better explanation: https://github.com/adafruit/LPD8806/blob/master/LPD8806.cpp

Can it go faster?

For a bit of fun I decided to see what SPI clock rates would work with my 5m strip.

  1. FEZtive feztive;
  2. void ProgramStarted()
  3. {
  4.     Debug.Print("Program Started");
  5.     feztive = new GTM.GHIElectronics.FEZtive(9);
  6.     feztive.Initialize(160, 500);//OK
  7.     feztive.Initialize(160, 1000);//OK
  8.     feztive.Initialize(160, 2000);//OK
  9.     feztive.Initialize(160, 4000);//OK
  10.     feztive.Initialize(160, 8000);//OK
  11.     feztive.Initialize(160, 16000);//Some LEDS are the wrong colour
  12.     feztive.Initialize(160, 12000);//OK
  13.     feztive.Initialize(160, 14000);//OK
  14.     feztive.Initialize(160, 15000);//Some LEDS are the wrong colour
  15.     feztive.Initialize(160, 14500);//Some LEDS are the wrong colour
  16.     feztive.Initialize(160, 14000);//OK

So it would look like 14MHz is as fast as this will run without errors. It is not exactly scientific, so the plan is to run at at this speed for a bit and see how it copes.

.NET Gadgeteer digital camera with a SerCam

[UPDATE:  C# and VB solutions are at the end, the first part is ONLY an explanation. You do not need to follow the steps, just look at the code at the very end if you are after a fix. ]

I have had a few people mention that they hit issues when swapping from the GHI Camera module to a GHI SerCam module. So here is an explanation and example:

First remember that things are constantly being updated so make sure you have the updates! (Updates/Debug help)

I am using NetMF QFE2, version 4.2 with the GHI software last updated Feb 18, 2013, (I also have no idea if the source code on codeplex has been updated to reflect this release.)

(Expect this post to get out of date, it is here to help those who need a workaround NOW – solution at the bottom)

Issue 1

1) The NullReferenceException:

Create a new project and call the take picture method on the camera.

 

  1. void button_ButtonPressed(Button sender, Button.ButtonState state)
  2. {
  3.     serCam.TakePicture();
  4. }

When you run the code it throws an exception. The exception is in the SerCam.dll and you do not have the source code in your project so you cant see what is going on.

image

I downloaded the latest GHI source from codeplex (http://gadgeteer.codeplex.com/)

Link the downloaded code into your project:

1) Delete the reference to the GHI SerCam.dll

2) Add the 4.2 SerCam project to your solution (codeplex source folder : MainModulesGHIElectronicsSerCamSoftwareSerCamSerCam_42).

3) Add a reference from your project to the codeplex SerCam.

Right now you are good to go, when it deploys it will now halt execution at the code that is causing the problem :

 image

The problem here is that the code assumes that there is at least one delegate to call when the StartDataCaptured event is triggered. A simple work around (if you don’t want to change the source) is to ensure that all events have something to call, even if it is empty. Then when you run the code you should not get the NullReferenceException.

  1. void ProgramStarted()
  2. {            
  3.     Debug.Print("Program Started");
  4.     serCam.StartDataCaptured += new SerCam.StartDataCapturedEventHandler(serCam_StartDataCaptured);
  5.     serCam.FinishDataCaptured += new SerCam.FinishDataCapturedEventHandler(serCam_FinishDataCaptured);
  6.     serCam.OnDataCaptured += new SerCam.DataCapturedEventHandler(serCam_OnDataCaptured);
  7.     serCam.TakePicture();
  8. }
  9.  
  10. }
  11.  
  12. void serCam_OnDataCaptured(SerCam sender, byte[] data)
  13. {            
  14. }
  15.  
  16. void serCam_FinishDataCaptured(SerCam sender)
  17. {
  18. }
  19.  
  20. void serCam_StartDataCaptured(SerCam sender, int sizeImage)
  21. {
  22. }

 

Issue 2

The methods are not the same as the camera I am used to, where is the Picture object, how do i get it and why is it not returned to me?

There are 3 events on the SerCam, one that is triggered at the start of a picture capture (StartDataCaptured), one that is triggered once the picture has been captured (FinishDataCaptured) and one that is triggered when some data is available (OnDataCaptured). The key here is that you only get the picture back in chunks, handy if memory is an issue. You have to take these chunks and piece them together. If we take a picture and set a breakpoint in the ReadFrameBuffer method (the one that triggers the OnDataCaptured event)  we can see that the picture is split into blocks, each block is returned as it is read. There are 95 blocks in this particular picture, so we have to put them all back together to reconstruct the picture for the display.

image

<snip>

image

 

Solution/workaround

Make sure there is code to execute for every event and rebuild the picture.

1) First create a Byte array to store the reconstructed picture in your program, I will call it rawPicData:

  1. namespace SerCamExample
  2. {
  3.     public partial class Program
  4.     {
  5.  
  6.         byte[] rawPicData;
  7.         void ProgramStarted()
  8.         {            
  9.             Debug.Print("Program Started");

3) When we start to take a picture lets reset the rawPicData array. That way we know it is initialised and is reset for every picture.

  1. void serCam_StartDataCaptured(SerCam sender, int sizeImage)
  2. {
  3.     rawPicData = new byte[] { };
  4. }

4) Every time there is a block (chunk) of picture data ready, we read it and append it to the end of the picture array (Not the optimal solution, it will be a bit slow, but it is one line). I added a debug line so you can see that something is happening.

  1. void serCam_OnDataCaptured(SerCam sender, byte[] data)
  2. {
  3.     rawPicData = Microsoft.SPOT.Hardware.Utility.CombineArrays(rawPicData, data);
  4.     Debug.Print("Working…");
  5. }

5) Finally when the camera has finished taking a picture and you have read all the blocks, the FinishedDataCaptured event will be triggered. Now you can take the byte array and convert it into a picture object. You can only do this once you have ALL the blocks and you should check for an exception, in case of an error.  In this case it is Jpeg

  1. var picB = new Bitmap(rawPicData, Bitmap.BitmapImageType.Jpeg);

You can then display the picture:

  1. display_T35.SimpleGraphics.DisplayImage(picB, 0, 0);

Source Code

If you read nothing above and just want a working example here it is:

  1. using System;
  2. using System.Collections;
  3. using System.Threading;
  4. using Microsoft.SPOT;
  5. using Microsoft.SPOT.Presentation;
  6. using Microsoft.SPOT.Presentation.Controls;
  7. using Microsoft.SPOT.Presentation.Media;
  8. using Microsoft.SPOT.Touch;
  9. using Gadgeteer.Networking;
  10. using GT = Gadgeteer;
  11. using GTM = Gadgeteer.Modules;
  12. using Gadgeteer.Modules.GHIElectronics;
  13.  
  14. namespace SerCamExample
  15. {
  16.     public partial class Program
  17.     {
  18.         byte[] rawPicData;
  19.         void ProgramStarted()
  20.         {            
  21.             Debug.Print("Program Started");
  22.             serCam.StartDataCaptured += new SerCam.StartDataCapturedEventHandler(serCam_StartDataCaptured);
  23.             serCam.FinishDataCaptured += new SerCam.FinishDataCapturedEventHandler(serCam_FinishDataCaptured);
  24.             serCam.OnDataCaptured += new SerCam.DataCapturedEventHandler(serCam_OnDataCaptured);           
  25.             button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);        
  26.         }
  27.         
  28.         void serCam_OnDataCaptured(SerCam sender, byte[] data)
  29.         {
  30.             rawPicData = Microsoft.SPOT.Hardware.Utility.CombineArrays(rawPicData, data);
  31.             Debug.Print("Working…");
  32.         }
  33.  
  34.         void serCam_FinishDataCaptured(SerCam sender)
  35.         {
  36.             var picB = new Bitmap(rawPicData, Bitmap.BitmapImageType.Jpeg);
  37.             display_T35.SimpleGraphics.DisplayImage(picB, 0, 0);
  38.         }
  39.  
  40.         void serCam_StartDataCaptured(SerCam sender, int sizeImage)
  41.         {
  42.             rawPicData = new byte[] { };
  43.         }
  44.  
  45.         void button_ButtonPressed(Button sender, Button.ButtonState state)
  46.         {
  47.             serCam.TakePicture();
  48.         }        
  49.     }
  50. }

And in VB (Thanks to Sue)

  1. Imports GT = Gadgeteer
  2. Imports GTM = Gadgeteer.Modules
  3. Imports Gadgeteer.Modules.GHIElectronics
  4.  
  5.  
  6. Partial Public Class Program
  7.     ' Dim datajpg As Byte() = {8}
  8.     Dim datajpg() As Byte = New Byte() {}
  9.     Dim index As Integer
  10.     ' This is run when the mainboard is powered up or reset.
  11.     Public Sub ProgramStarted()       
  12.         ' Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
  13.         Debug.Print("Program Started")
  14.         serCam.SetImageSize(serCam.Camera_Resolution.SIZE_QVGA)
  15.  
  16.     End Sub
  17.  
  18.     Private Sub button_ButtonPressed(sender As Gadgeteer.Modules.GHIElectronics.Button, state As Gadgeteer.Modules.GHIElectronics.Button.ButtonState) Handles button.ButtonPressed
  19.         serCam.TakePicture()
  20.         Debug.Print("Taken picture")
  21.  
  22.     End Sub
  23.  
  24.     Private Sub serCam_FinishDataCaptured(sender As Gadgeteer.Modules.GHIElectronics.SerCam) Handles serCam.FinishDataCaptured
  25.         Dim picture As Bitmap = New Bitmap(datajpg, Bitmap.BitmapImageType.Jpeg)
  26.         display_T35.SimpleGraphics.DisplayImage(picture, 0, 0)
  27.  
  28.     End Sub
  29.  
  30.     Private Sub serCam_OnDataCaptured(sender As Gadgeteer.Modules.GHIElectronics.SerCam, data() As Byte) Handles serCam.OnDataCaptured
  31.  
  32.         datajpg = Microsoft.SPOT.Hardware.Utility.CombineArrays(datajpg, data)
  33.     End Sub
  34.  
  35.     Private Sub serCam_StartDataCaptured(sender As Gadgeteer.Modules.GHIElectronics.SerCam, sizeImage As Integer) Handles serCam.StartDataCaptured
  36.         ' At present need an empty sub here to stop a Null Reference error occurring.
  37.         ' Hopefully this will be fixed soon (11/4/2013)
  38.     End Sub
  39. End Class