Writing custom driver for arduino shield

All,

I am about to try my first port of an Arduino based shield to run on a Cerbuino Bee. The shield I am using is an Olimex EKG / EMG. I have the source code (arduino sketch) for the shield. It runs great with an Arduino Uno with consistent wave forms returned. I need to port this to C# and .NETMF. I am a seasoned .Net developer but relatively new to .NETMF and Gadgeteer. I am not sure where to begin with this. I assume that I will need to isolate the pins that I am trying to read from but have never done this before. Any advice / guidance would be much appreciated. I am pasting the arduino code below as a reference in case someone on the forum is kind enough to offer help. Best regards and thanks in advance.


struct Tx_packet
{
  uint8_t	sync0;		// = 0xa5
  uint8_t	sync1;		// = 0x5a
  uint8_t	version;	// = 2 (packet version)
  uint8_t	count;		// packet counter. Increases by 1 each packet.
  uint16_t	data[6];	// 10-bit sample (= 0 - 1023) in big endian (Motorola) format.
  uint8_t	switches;	// State of PD5 to PD2, in bits 3 to 0.
};
*/
/**********************************************************/
 #include <compat/deprecated.h>
 #include <FlexiTimer2.h>
//http://www.arduino.cc/playground/Main/FlexiTimer2

// All definitions
 #define NUMCHANNELS 6
 #define HEADERLEN 4
 #define PACKETLEN (NUMCHANNELS * 2 + HEADERLEN + 1)
 #define SAMPFREQ 256                      // ADC sampling rate 256
 #define TIMER2VAL (1024/(SAMPFREQ))       // Set 256Hz sampling frequency                    
 #define LED1  13
 #define CAL_SIG 9

// Global constants and variables
volatile unsigned char TXBuf[PACKETLEN];  //The transmission packet
volatile unsigned char TXIndex;           //Next byte to write in the transmission packet.
volatile unsigned char CurrentCh;         //Current channel being sampled.
volatile unsigned char counter = 0;	  //Additional divider used to generate CAL_SIG
volatile unsigned int ADC_Value = 0;	  //ADC current value

//~~~~~~~~~~
// Functions
//~~~~~~~~~~

/****************************************************/
/*  Function name: Toggle_LED1                      */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Switches-over LED1.                   */
/****************************************************/
void Toggle_LED1(void){

 if((digitalRead(LED1))==HIGH){ digitalWrite(LED1,LOW); }
 else{ digitalWrite(LED1,HIGH); }
 
}


/****************************************************/
/*  Function name: toggle_GAL_SIG                   */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Switches-over GAL_SIG.                */
/****************************************************/
void toggle_GAL_SIG(void){
  
 if(digitalRead(CAL_SIG) == HIGH){ digitalWrite(CAL_SIG, LOW); }
 else{ digitalWrite(CAL_SIG, HIGH); }
 
}


/****************************************************/
/*  Function name: setup                            */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Initializes all peripherals           */
/****************************************************/
void setup() {

 noInterrupts();  // Disable all interrupts before initialization
 
 // LED1
 pinMode(LED1, OUTPUT);  //Setup LED1 direction
 digitalWrite(LED1,LOW); //Setup LED1 state
 pinMode(CAL_SIG, OUTPUT);
 
 //Write packet header and footer
 TXBuf[0] = 0xa5;    //Sync 0
 TXBuf[1] = 0x5a;    //Sync 1
 TXBuf[2] = 2;       //Protocol version
 TXBuf[3] = 0;       //Packet counter
 TXBuf[4] = 0x02;    //CH1 High Byte
 TXBuf[5] = 0x00;    //CH1 Low Byte
 TXBuf[6] = 0x02;    //CH2 High Byte
 TXBuf[7] = 0x00;    //CH2 Low Byte
 TXBuf[8] = 0x02;    //CH3 High Byte
 TXBuf[9] = 0x00;    //CH3 Low Byte
 TXBuf[10] = 0x02;   //CH4 High Byte
 TXBuf[11] = 0x00;   //CH4 Low Byte
 TXBuf[12] = 0x02;   //CH5 High Byte
 TXBuf[13] = 0x00;   //CH5 Low Byte
 TXBuf[14] = 0x02;   //CH6 High Byte
 TXBuf[15] = 0x00;   //CH6 Low Byte 
 TXBuf[2 * NUMCHANNELS + HEADERLEN] =  0x01;	// Switches state

 // Timer2
 // Timer2 is used to setup the analag channels sampling frequency and packet update.
 // Whenever interrupt occures, the current read packet is sent to the PC
 // In addition the CAL_SIG is generated as well, so Timer1 is not required in this case!
 FlexiTimer2::set(TIMER2VAL, Timer2_Overflow_ISR);
 FlexiTimer2::start();
 
 // Serial Port
 Serial.begin(57600);
 //Set speed to 57600 bps
 
 // MCU sleep mode = idle.
 //outb(MCUCR,(inp(MCUCR) | (1<<SE)) & (~(1<<SM0) | ~(1<<SM1) | ~(1<<SM2)));
 
 interrupts();  // Enable all interrupts after initialization has been completed
}

/****************************************************/
/*  Function name: Timer2_Overflow_ISR              */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Determines ADC sampling frequency.    */
/****************************************************/
void Timer2_Overflow_ISR()
{
  // Toggle LED1 with ADC sampling frequency /2
  Toggle_LED1();
  
  //Read the 6 ADC inputs and store current values in Packet
  for(CurrentCh=0;CurrentCh<6;CurrentCh++){
    ADC_Value = analogRead(CurrentCh);
    TXBuf[((2*CurrentCh) + HEADERLEN)] = ((unsigned char)((ADC_Value & 0xFF00) >> 8));	// Write High Byte
    TXBuf[((2*CurrentCh) + HEADERLEN + 1)] = ((unsigned char)(ADC_Value & 0x00FF));	// Write Low Byte
  }
	 
  // Send Packet
  for(TXIndex=0;TXIndex<17;TXIndex++){
    Serial.write(TXBuf[TXIndex]);
  }
  
  // Increment the packet counter
  TXBuf[3]++;			
  
  // Generate the CAL_SIGnal
  counter++;		// increment the devider counter
  if(counter == 12){	// 250/12/2 = 10.4Hz ->Toggle frequency
    counter = 0;
    toggle_GAL_SIG();	// Generate CAL signal with frequ ~10Hz
  }
}


/****************************************************/
/*  Function name: loop                             */
/*  Parameters                                      */
/*    Input   :  No	                            */
/*    Output  :  No                                 */
/*    Action: Puts MCU into sleep mode.             */
/****************************************************/
void loop() {
  
 __asm__ __volatile__ ("sleep");
 
} 

Hi,
interesting project.
As I understand the schematic of the Olimex EKG shield and the arduino code Cerbuino Bee has to sample the analog input every 4 ms (sampling frequency 256 Hz). I don’t know if the managed code is fast enough to keep in time. Perhaps one of the other forum member has empirical knowledge which sampling frequencies can be reached. Furthermore the garbage collector has to be considered which will interfere with the sampling oft he ECG signal. I’m curious whether a NETMF device is the right choice for this task.

Edit: After some tests I have to apologize to NETMF and the Cerbuino Bee. Cerbuino Bee will do the job with ease.

Thanks so much for the reply. You make an excellent point. I had been so focused on getting the practical implementation done that I had not considered the performance problems yet. I have gotten a successful implementation of an EEG completed and it is sampling at about half the frequency of the EKG and therefor I did not hit some of the challenges. The GC cycles seem to be fine on it. That said however I had almost reached the same conclusion anyway because the Arduino implementation is working well. In the future it would be awesome to see more bio-metric devices in the NETMF world though. Thanks again for the advice.

Cheers…

@ slrbatman - I am very interested in this shield. Can you please tag your code with code tags so it is readable.

How does this shield work? I can help with RLP if that is needed. Why a high speed read is needed?

Gus,

I added code tags. Like I said earlier, the Arduino version is working and I would prefer it all on NETMF but for my prototype I can live with it. If you think it is doable and won’t take months I would definitely welcome the help to get it onto NETMF though.

Unless you see something that I don’t, the Arduino implementation isn’t using RLP. Do you really think radio link would be necessary? I am not super concerned about dropped packets as long as they are random and infrequent. If an 8 bit UNO can run it with little issue even though its unmanaged c, I would think that it would be somehow doable with the extra resources on a spider. Especially if this was all that is running on the board other than a Bluetooth or XBee module.

All of that said, there are a lot of modules out there for other platforms for medical diagnostics but very limited (at least as far as I can find) for the NETMF / Gadgeteer platform. Any hope of more Gadgeteer compatible medical modules coming from you guys in the future?

I added the Eagle schematic for the EKG board.

Regards,
Steve…

Isn’t the output from the shield analog? So you only need to read analog?

Yes, it has 1 channel and writes through analog. However, the calibration is through digital d4/d9.

Then I do not see why would this not work on NETMF. I will order one and mess with it on my free time.

1 Like

@ Gus
The Olimex shield is -as I see it - merely an analog amplifier for the ECG signal. The signal of each ECG channel is sampled every 4 ms by the MCU (in professional ECG systems the signal is sampled every 1 ms). If only one channel is sampled there is a time of 4 ms to read the analog value (perhaps get a timestamp to know when it was sampled) and write these values to a buffer (the arduino seems to be able to sample 6 channels in 4 ms). In another thread something has to be done with the sampled data, either actualize a graphic display with the new data or send them over some serial connection to e.g. a PC where the curve is graphically displayed. I wonder how this can be done continuously in managed code. With RLP it should of course be possible if you leave a time gap in sampling data to do the C# stuff, but I don’t know whether there is a real advantage to use a NETMF device.

@ slrbatman
What are you doing with the gathered data? Send them to a PC to display the curve? If so, which program are you using on the PC to display the curve?

With the Arduino implementation I am using a USB connection. I have a .NET application that is listening on the dedicated COM port and reading the data. I then display it in a custom graph. Like I said, this is working well so I know it can be done. I would like to do the same thing with a Cerbuino Bee and an XBee module or if that doesn’t work with a Spider and Bluetooth. I am very comfortable with the PC side but not so much with the embedded side.

Even with managed code, I would think that the extra horsepower available via an ARM 32 bit processor vs. the AVR 8 bit board, could mitigate some of the performance challenges. Maybe I am too new at this to understand all of the dynamics at play. Are you guys aware of any other options for doing EKG / EMG in the NETMF world? Maybe I am hammering at this unnecessarily…

@ slrbatman
Is your PC Application “open source”? If so, I would be interested to get a copy to do some tests with my Cerbuino.

Some time ago I did some speed tests with our NETMF mainboards and nobody stated, that the results were not correct.

https://www.ghielectronics.com/community/forum/topic?id=14647&page=2

No, I apologize it is baseline code that is for a medical treatment that we have built a new company around. The code is controlled by a complex neural network that is all custom. It would be time consuming to unravel the listener object and graphing from within all of the other code. I am nearing 1 million lines of code within the core platform.

You guys have all been very generous with your knowledge and demo code, I hate to not reciprocate but this is all part of a prototype that we will begin selling in September. I filed a patent for the product last week. As soon as the patent is published I will post a link so you guys can see the science behind what we are doing. There won’t be anything quite like it on the market. In clinical trials we are seeing a better than 90% success rate with about 35 medical diseases / issues. We are seeing a 100% success rate with eliminating the symptoms of motor control diseases like Parkinson’s. The embedded devices are all running diagnostics that feed back into my neural network which uses the live diagnostics to control and modulates the treatment. I have a solid implementation now of an EEG, a PulseOx, and a orientation monitor with 2 accelerometers and 2 gyros. The EKG / EMG was the last piece and proving to be the most difficult.

I am a solid programmer in enterprise server and big iron environments but brand new to the micro embedded world. It definitely requires you to think about things from a different perspective. I am also terrible with a soldering iron. All of the help with the micro code has been invaluable.

All of that said, given my tight time frames do you guys think I should continue pursuing the Olimex board or try something else? I like the cerbuino bee a lot. IT is a nifty little board and I much prefer the NETMF world and Visual Studio over Arduino given my core platform the devices are talking to is all C# / .NET.

1 Like

No need to apologize, usually it would be silly to reveal important parts of commercial projects.
I cannot say anything good or bad about the Olimex board.
However I’m interested in this projects and will do some tests reading analog signals with the Cerbuino Bee.

I’m happy that after some tests I have to apologize to NETMF, GHI and Cerbuino Bee.
Cerbuino Bee is able to read the Analog Signal and send the value together with a short timestamp and a sync sequence in less than 180 mikroseconds.
Here is the test-program:


using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Microsoft.SPOT.Hardware;
using GTI = Gadgeteer.SocketInterfaces;
using Gadgeteer.Modules.GHIElectronics;

namespace Analog_Potentiometer
{
    public partial class Program
    {
        private Thread readerThread;

        private AnalogInput Sensor_0;
                                        //private AnalogInput Sensor_1;  //Not used
                                        //private AnalogInput Sensor_2;  //Not used
        private AnalogInput Sensor_3;
        private AnalogInput Sensor_4;
        private AnalogInput Sensor_5;
                                         //private AnalogInput Sensor_6;  //May not be used
        private AnalogInput Sensor_7;
       
        private int Sensor_Raw_Reading = 0;
        private byte[] SendArray_Value;
        private byte[] SendArray_Time;
        private byte[] SendArray = new byte[12];

        TimeSpan ts;
        TimeSpan ts1;
        long Empty = 0;
        long Load = 0;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            Debug.Print("Program Started");
            usbSerial.Configure(38400, GTI.SerialParity.None, GTI.SerialStopBits.One, 8, GTI.HardwareFlowControl.NotRequired);
            if (!usbSerial.Port.IsOpen)
            {
                usbSerial.Port.Open();
            }

            // This is 4 times FF to syncronize on the receiver side
            SendArray[0] = 0xFF;
            SendArray[1] = 0xFF;
            SendArray[2] = 0xFF;
            SendArray[3] = 0xFF;

            // On Cerbuino Bee: Socket 2, Pin 3
            Sensor_0 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

            //Sensor_1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_1);  //Not used
            //Sensor_2 = new AnalogInput(Cpu.AnalogChannel.ANALOG_2);  //Not used

            // On Cerbuino Bee: Socket 3, Pin 3
            Sensor_3 = new AnalogInput(Cpu.AnalogChannel.ANALOG_3);
            
            // On Cerbuino Bee: Arduino Socket A4
            Sensor_4 = new AnalogInput(Cpu.AnalogChannel.ANALOG_4);

            // On Cerbuino Bee: Arduino Socket A5
            Sensor_5 = new AnalogInput(Cpu.AnalogChannel.ANALOG_5);

            //Sensor_6 = new AnalogInput(Cpu.AnalogChannel.ANALOG_6);   //May not be used

            // On Cerbuino Bee: Arduino Socket A3 
            Sensor_7 = new AnalogInput(Cpu.AnalogChannel.ANALOG_7);

            readerThread = new Thread(new ThreadStart(runReaderThread));
            readerThread.Start();

        }

        private void runReaderThread()
        {
            ts = Microsoft.SPOT.Hardware.Utility.GetMachineTime();
            ts1 = Microsoft.SPOT.Hardware.Utility.GetMachineTime();
            Empty = ts1.Subtract(ts).Ticks;
            Debug.Print("Empty= " + Empty.ToString() + " ticks");
            Debug.Print("Ticks Per MSec=" + TimeSpan.TicksPerMillisecond.ToString());
            Debug.Print("\r\n");

            while (true)
            {
                ts = Microsoft.SPOT.Hardware.Utility.GetMachineTime();

                //Sensor_Reading = Sensor_0.Read();       //Socket 2, Pin 3
                //Sensor_Reading = Sensor_3.Read();       //Socket 3, Pin 3
                //Sensor_Reading = Sensor_4.Read();       //Arduino Socket A4
                Sensor_Raw_Reading = Sensor_5.ReadRaw();  //Arduino Socket A5
                //Sensor_Reading = Sensor_7.Read();       //Arduino Socket A3

                ts1 = Microsoft.SPOT.Hardware.Utility.GetMachineTime();

                SendArray_Value = BitConverter.GetBytes(Sensor_Raw_Reading);
                SendArray_Time = BitConverter.GetBytes(ts1.Milliseconds);
                Array.Copy(SendArray_Value, 0, SendArray, 4, 4);
                Array.Copy(SendArray_Time, 0, SendArray, 8, 4);

                usbSerial.Port.Write(SendArray);

                ts1 = Microsoft.SPOT.Hardware.Utility.GetMachineTime();

                Load = ts1.Subtract(ts).Ticks;

                Debug.Print("Duration of one Potentiometer-Reading: " + (Load - Empty).ToString() 
                    + " ticks or " + (Load - Empty) / (TimeSpan.TicksPerMillisecond / 1000) + " microseconds");
                Debug.Print("Time before operation: Seconds: " + ts.Seconds.ToString() + "   Milliseconds: " 
                    + ts.Milliseconds.ToString() + "   Ticks: " + ts.Ticks.ToString());
                Debug.Print("Time after operation: Seconds : " + ts1.Seconds.ToString() + "   Milliseconds: " 
                    + ts1.Milliseconds.ToString() + "   Ticks: " + ts1.Ticks.ToString() + "\r\n");

                Debug.Print("Potentiometer-Reading: " + Sensor_Raw_Reading);
                Thread.Sleep(1000);
            }

        }
    }
}

1 Like

Hi Ro,

I got the solution fully deployed on an Arduino Uno R3 but today got some time to turn back to looking at it running on a GHI board. I took your solution as is below and deployed it to a Cerbuino Bee running 4.3. Every time it tries to initialize the Analog channel it throws an exception. I pasted the exception below.

The line of code it is barfing on is:

// On Cerbuino Bee: Socket 2, Pin 3
Sensor_0 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);


Found debugger!

Create TS.

 Loading start at 8078af4, end 809ea80

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

Attaching deployed file.

   Assembly: GHIElectronics.Gadgeteer.FEZCerbuinoBee (4.3.6.0)  Attaching deployed file.

   Assembly: Gadgeteer (2.43.1.0)  Attaching deployed file.

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

   Assembly: GTM.GHIElectronics.USBSerial (4.3.6.0)  Attaching deployed file.

   Assembly: Gadgeteer.Serial (2.43.1.0)  Attaching deployed file.

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

   Assembly: GHI.Usb (4.3.6.0)  Attaching deployed file.

   Assembly: GHI.Pins (4.3.6.0)  Attaching deployed file.

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

   Assembly: nsECG (1.0.0.0)  Attaching deployed file.

   Assembly: GTM.GHIElectronics.LED7C (4.3.6.0)  Resolving.

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

'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\mscorlib.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Native.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Graphics.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.TinyCore.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.IO.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\System.IO.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.Usb.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.SerialPort.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Hardware.PWM.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.3\Assemblies\le\Microsoft.SPOT.Net.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Gadgeteer\Core\Assemblies\.NET Micro Framework 4.3\le\Gadgeteer.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI NETMF v4.3 SDK\Libraries\le\GHI.Hardware.dll'
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI NETMF v4.3 SDK\Libraries\le\GHI.Usb.dll'
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI NETMF v4.3 SDK\Libraries\le\GHI.Pins.dll'
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI .NET Gadgeteer SDK\Mainboards\FEZCerbuinoBee\NETMF 4.3\le\GHIElectronics.Gadgeteer.FEZCerbuinoBee.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\Microsoft .NET Gadgeteer\Core\Assemblies\.NET Micro Framework 4.3\le\Gadgeteer.Serial.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI .NET Gadgeteer SDK\Modules\USBSerial\NETMF 4.3\le\GTM.GHIElectronics.USBSerial.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI .NET Gadgeteer SDK\Modules\Button\NETMF 4.3\le\GTM.GHIElectronics.Button.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\Program Files (x86)\GHI Electronics\GHI .NET Gadgeteer SDK\Modules\LED7C\NETMF 4.3\le\GTM.GHIElectronics.LED7C.dll', Symbols loaded.
'Microsoft.SPOT.Debugger.CorDebug.12.dll' (Managed): Loaded 'C:\nsDevelopment\nsECG\nsECG\bin\Debug\le\nsECG.exe', Symbols loaded.
The thread '<No Name>' (0x2) has exited with code 0 (0x0).
Using mainboard GHI Electronics FEZ Cerbuino Bee version 1.2
Program Started
A first chance exception of type 'System.InvalidOperationException' occurred in Microsoft.SPOT.Hardware.dll
A first chance exception of type 'System.InvalidOperationException' occurred in Microsoft.SPOT.Hardware.dll
An unhandled exception of type 'System.InvalidOperationException' occurred in Microsoft.SPOT.Hardware.dll

The program '[8] Micro Framework application: Managed' has exited with code 0 (0x0).

Any ideas what might be happing here? I would really like to get this going in .NetMF.

Best Regards,
Steve…

Hi,
as a first shot I would think, that perhaps the analog Input which you want to use is already reserved for a module on one of the Gadgeteer sockets.
Do you get the same exception with the code of my CodeShare contribution?
https://www.ghielectronics.com/community/codeshare/entry/1036

Hi Ro,

First let me thank you so much for the very fast reply. You guys are awesome. I don’t think I have ever seen a forum that is so well watched.

You are correct, there was a conflict with the button module. I removed it and the code works fine now.

On a totally separate question, I have been playing around with the pulse oximeter code and would like to see every heart beat not just the deltas. Apparently the module’s heartbeat event is written to only capture deltas or is this a limitation on the board itself?

Regards,
Steve…

Hi Ro,

I hit send too soon. I meant to also ask what you thought of this Olimex board. Do you think it is adequate or should I continue looking for something better for my prototype. If continue looking, any ideas of something comparably better?

Regards,
Steve…

I don’t have a pulsoximeter module and don’t know the code. I would guess that the slope of the signal (delta) is better suitable to discriminate between pulswaves and artifacts than the amplitude of the signal. As for the ECG module it should be possible as well to sample the pulse curve and display it one a graphic display connected with the NETMF device or send the data to e.g. a PC to be displayed there.
Considering the price of the Olimex board IMO it gives a quite good signal quality. I’m not an expert, but I know that amplifying and filtering of an ECG signal is not an easy task. Whether the quality is sufficient, depends on what you want to do. As a cheap device for surveying the heart action it should be sufficient, for a professional standard ECG in my opinion it is clearly not. I don’t know other ecg modules to be used with microcontrollers, theoretically every ecg-amplifierer can be used with prices from some hundred to more than 1000 $.