Interrupts - what's the Gadgeeter equivalent in .NETMF of InterruptPort.EnableInterrupt() and InterruptPort.ClearInterrupt()?

I’ve tinkered and searched for over 2 hours to try and figure this out. I’m porting the following code for a HC-SR04 Rangefinder plugged into @ ransomhall’s MakeBread module. The top code below works on my NetDuino plugged into a breadboard with Echo on D6 and Trigger on D7.

NETMF code:


        private static int ticks;

        private static InterruptPort EchoPin = new InterruptPort(Pins.GPIO_PIN_D6, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
        private static OutputPort TriggerPin = new OutputPort(Pins.GPIO_PIN_D7, false);

        public static void Main()
        {
            EchoPin.OnInterrupt += new NativeEventHandler(port_OnInterrupt);
            EchoPin.DisableInterrupt();
            while (true)
            {
                EchoPin.EnableInterrupt();
                TriggerPin.Write(false);
                Thread.Sleep(2);
                TriggerPin.Write(true);
                Thread.Sleep(10);
                TriggerPin.Write(false);
                Thread.Sleep(2);    

            }
        }

        private static void port_OnInterrupt(uint port, uint state, DateTime time)
        {
            if (state == 0) // falling edge, end of pulse
            {
                int pulseWidth = (int)time.Ticks - ticks;
                // valid for 20°C
                //int pulseWidthMilliSeconds = pulseWidth * 10 / 582;
                //valid for 24°C
                int pulseWidthMilliSeconds = (pulseWidth * 10 / (int)578.29);
                Debug.Print("Distance = " + pulseWidthMilliSeconds.ToString() + " millimètres.");
            }
            else
            {
                ticks = (int)time.Ticks;
            }
            EchoPin.ClearInterrupt();
        }


I’m doing it in a gadgeteer module, which shouldn’t make a difference, but here’s what I’ve got:

Gadgeteer Code:


   private static int _ticks;
        private GTI.InterruptInput input;

        private Socket _socket;
        private InterruptInput _input;
        private DigitalOutput _output;        
        private uint _state;
        private DateTime _time;
        private bool _monitoring = false;
        private string _currentDistance = "-1";

        /// <summary>bool: Whether the rangefinder is monitoring current distance or not.</summary>        
        public bool IsMonitoring()
        {
            return _monitoring;
        }

        /// <summary>string: Current Distance in millimeters</summary>        
        public string CurrentDistance()
        {            
            return _currentDistance;        
        }
        
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>        
        public MakeBreadRangeFinder(int socketNumber)
        {
            _socket = Socket.GetSocket(socketNumber, true, this, null);

            // The HC-SR04 Rangefinder uses the following pins on the makebread module:
            //
            // VCC (5v power): Pin 2 (5V)
            // Trig (Input): Pin 6 (P6)
            // Echo (Output): Pin 7 (P7)
            // GND (Ground): Ping 10 (GND)
            //

            _input = new GTI.InterruptInput(_socket, GT.Socket.Pin.Six, GTI.GlitchFilterMode.Off, GTI.ResistorMode.Disabled, GTI.InterruptMode.RisingAndFallingEdge, this);
            _output = new GTI.DigitalOutput(_socket, GT.Socket.Pin.Seven, false, this);
            // This registers a handler for the interrupt event of the interrupt input (which is bereleased)
            this.input.Interrupt += new GTI.InterruptInput.InterruptEventHandler(this._input_Interrupt);

            if (_monitoring)
            {
                
                _output.Write(false);
                Thread.Sleep(2);
                _output.Write(true);
                Thread.Sleep(10);
                _output.Write(false);
                Thread.Sleep(2);
            }


            Thread.Sleep(-1);
        }

        private  void _input_Interrupt(GTI.InterruptInput input, bool value)
        {
            if (_state == 0) // falling edge, end of pulse
            {
                int pulseWidth = (int)_time.Ticks - _ticks;
                // valid for 20°C
                //int pulseWidthMilliSeconds = pulseWidth * 10 / 582;
                //valid for 24°C
                int pulseWidthMilliSeconds = (pulseWidth * 10 / (int)578.29);
                _currentDistance = pulseWidthMilliSeconds.ToString();                
            }
            else
            {
                _ticks = (int)_time.Ticks;
            }
            
        }


Your constructor is sleeping forever, blocking the system from running. Take this line out and you should be fine:

Thread.Sleep(-1);

I was just reading another thread (of course, which I can’t find now) and Gus said to use that instead of while(true){ } …

After taking that out, I now get a NullReferenceException on:

this.input.Interrupt += new GTI.InterruptInput.InterruptEventHandler(this._input_Interrupt);

Gus was referring to using that inside main function. With Gadgeteer it is different.

You’re absolutely correct, I was looking at an interrupt thread in straight .NETMF… thought it was a Gadgeteer thread at the time.

Still can’t figure out the null exception. Walked away, got some dinner and now going to beat on it some more and see what I can come up with :wink:

Steven not Gus :slight_smile:

Actually it was you Gus - I just had the thread open and can’t find it again… haha!

Found it!

InterruptPort -OnInterrupt time parameter : http://www.tinyclr.com/forum/topic?id=812&page=3#msg9080

In the FEZ forum!

Ok, so I moved the code straight to new gadgeteer 4.1 project. I have my MakeBread module connected to socket 12, Power to socket 2, nothing else connected.

I’m getting the same exception on the InterruptEventHandler:

A first chance exception of type ‘System.NullReferenceException’ occurred in MakeBreadRangeFinderTest.exe
An unhandled exception of type ‘System.NullReferenceException’ occurred in MakeBreadRangeFinderTest.exe


using System;
using System.Threading;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;
using Gadgeteer.Interfaces;
using Gadgeteer;
using Gadgeteer.Modules.GHIElectronics;

namespace MakeBreadRangeFinderTest
{
    public partial class Program
    {

        private static int _ticks;
        private GTI.InterruptInput input;

        private Socket _socket;
        private InterruptInput _input;
        private DigitalOutput _output;
        private uint _state;
        private DateTime _time;
        private bool _monitoring = true;
        private string _currentDistance = "-1";

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            GT.Socket _socket = GT.Socket.GetSocket(12, true, null, "LeftRangeFinder");
            _input = new GT.Interfaces.InterruptInput(_socket, Socket.Pin.Six, GlitchFilterMode.Off, ResistorMode.Disabled, InterruptMode.RisingAndFallingEdge, null);
            _output = new DigitalOutput(_socket, Socket.Pin.Seven, false, null);


            // This line is the one that throws the exception
            this.input.Interrupt += new InterruptInput.InterruptEventHandler(input_Interrupt);  // <----------- This line

            while (_monitoring)
            {

                _output.Write(false);
                Thread.Sleep(2);
                _output.Write(true);
                Thread.Sleep(10);
                _output.Write(false);
                Thread.Sleep(2);
            }
            

        }

        void input_Interrupt(InterruptInput sender, bool value)
        {
            if (_state == 0) // falling edge, end of pulse
            {
                int pulseWidth = (int)_time.Ticks - _ticks;
                // valid for 20°C
                //int pulseWidthMilliSeconds = pulseWidth * 10 / 582;
                //valid for 24°C
                int pulseWidthMilliSeconds = (pulseWidth * 10 / (int)578.29);
                _currentDistance = pulseWidthMilliSeconds.ToString();
            }
            else
            {
                _ticks = (int)_time.Ticks;
            }
        }

        
    }
}



Ha, so as it turns out, I put in an input that wasn’t initialized!!

 
            // This line is the one that throws the exception
            this.input.Interrupt += new InterruptInput.InterruptEventHandler(input_Interrupt);  

should be this:

             
            _.input.Interrupt += new InterruptInput.InterruptEventHandler(input_Interrupt);  

That gets rid of the null exception, but my eventhandler still never fires.

Any clue how to EnableInterrupt on _input ?

Setting this:

 _input.SynchronousUnsafeEventInvocation = true; 

now makes the event handler fire.

Next, to figure out how to get the state of the Interrupt…

you are still blocking the event handler in the Program start by using the while loop… move that code out of the ProgramStarted:


while (_monitoring)
            {
 
                _output.Write(false);
                Thread.Sleep(2);
                _output.Write(true);
                Thread.Sleep(10);
                _output.Write(false);
                Thread.Sleep(2);
            }

read this please:
http://blogs.msdn.com/b/net_gadgeteer/archive/2011/12/19/why-not-while-true.aspx

Cheers,

I wasn’t getting a blocking error, but did as you and the post suggested and added a timer and am running the writes in the timer.

I’m still getting 0 as the value, so not sure why it’s not actually calculating the number of Ticks between the interrupt firing.

More hacking :wink:

I also moved the Interrupt port to Pin 3 instead of 7 based on this thread I found:

your app should start a new thread in ProgramStarted() that does your toggling of pins (but remember you’re never going to get that portion to be a perfectly timed waveform), rather than in a timer.

When you say “it’s getting 0 as the value” can you elaborate? So you ARE getting the interrupt handler to fire? breakpoints are your friend too :slight_smile:

Yeah, I’m getting the handler to fire now, after setting the SynchronousUnsafeEventInvocation to true.

I was getting 0’s in the straight NETMF code, the time is passed in on the event handler as a parameter… I wasn’t setting that anywhere in my code, so time was always the same value - therefore the difference in Ticks was 0, so the calculation for pulseWidthMilliSeconds ended up being (0 * 10 / (int)578.20) which equals 0…

Yeah, breakpoints are somewhat my friend, but they don’t seem to be helping too much today!

Thanks for the suggestion about moving the Writes out of the timer, will try that and see where it gets me.

Alright, I worked out the issue with getting the time passed into the event handler properly.

I noticed an odd issue while debugging. I am using a distance of 3.5 inches (~90mm) to make sure the sensor is returning the same readings whether in straight NETMF or Gadgeteer.

My Gadgeteer readings were roughly 3 times (~280mm) to what the NETMF setup was returning (~85mm).

I double checked my code, which is basically the same between the two frameworks. I then double checked the wiring and noticed I had the NETMF board feeding 5v to the sensor, Gadgeteer was only feeding 3.3v. I setup both to use the 3.3v pings and they both returned the ~280mm readings. I set both to 5v and although the gadgeteer was reading almost correct, it was around 1cm off (returning 70-75mm instead of 85-90mm).

I read in another thread that even though there is a 5v pin on the extender / MakeBread modules, the pin outs on the board would regulate the 5v down to 3.3v. Seems as if that’s not true, and it’s actually pushing a little more than 5v. I don’t have a multimeter handy to get an exact reading. I tried this on both a P and a Y port on my Hydra, so am pretty sure it’s not a port issue.

Pins are as follows:
VCC (Power) = Pin 2 (5v) or Pin 1 (3.3v)
Trig (Trigger) = Pin 3
Echo (Echo) = Pin 7
GND (Ground) = Pin 10

Any ideas?

Also, I’ll add my library to CodeShare once I have it in Module format, but here is the test rig I’m using for now:


using System;
using System.Threading;
using Microsoft.SPOT;
using GT = Gadgeteer;
using Gadgeteer.Interfaces;
using Gadgeteer;

namespace MakeBreadRangeFinderTest
{
    public partial class Program
    {
        private string _currentDistance = "-1";

        private static InterruptInput _input;
        private static DigitalOutput _output;

        private static int _ticks;
        private DateTime _time;

        private static int _tickTime = 500;

        void ProgramStarted()
        {
            GT.Socket _socket = GT.Socket.GetSocket(12, true, extender, "LeftRangeFinder");
            _input = new InterruptInput(_socket, Socket.Pin.Three, GlitchFilterMode.Off, ResistorMode.Disabled, InterruptMode.RisingAndFallingEdge, null);
            _output = new DigitalOutput(_socket, Socket.Pin.Seven, false, null);

            _input.SynchronousUnsafeEventInvocation = true;
            _input.Interrupt += new InterruptInput.InterruptEventHandler(_input_Interrupt);

            Thread writeThread = new Thread(writeActivity);
            writeThread.Start();
        }

        private static void writeActivity()
        {
            while (true)
            {
                _output.Write(false);
                Thread.Sleep(2);
                _output.Write(true);
                Thread.Sleep(10);
                _output.Write(false);
                Thread.Sleep(2);
                Thread.Sleep(_tickTime);
            }
        }

        void _input_Interrupt(InterruptInput sender, bool value)
        {
            _time = DateTime.Now;
            if (!value) // falling edge, end of pulse
            {
                int pulseWidth = (int)_time.Ticks - _ticks;
                // valid for 20°C
                //int pulseWidthMilliSeconds = pulseWidth * 10 / 582;
                //valid for 24°C
                int pulseWidthMilliSeconds = (pulseWidth * 10 / (int)578.29);
                _currentDistance = pulseWidthMilliSeconds.ToString();
                Debug.Print("time: " + _time + "   Distance = " + pulseWidthMilliSeconds.ToString() + " mm");
            }
            else
            {
                _ticks = (int)_time.Ticks;
            }
        }
    }
}

What is the sensor you are using? Maybe we can test here if we have similar one.

Might it be a Ping))) ? If it is, http://www.tinyclr.com/codeshare/entry/123 is a “low-tech” version that uses polling that might also help your calibration testing.

It’s the HC-SR04 - many vendors have them, I got them from here: http://www.amazon.com/Virtuabotix-Ultrasonic-Distance-Rangefinder-Detection/dp/B0066X9V5K/

@ Brett - my code is basically doing the same as Architect’s, which I got from someone on the netduino forum and altered. It’s waiting for the start and stop of the loop (interrupt is false (0), and measuring the time between.

My main question now is really whether the 5V pin should not be used on the Gadgeteer. I’ll try to find the thread in which the 3.3V vs. 5V was discussed and the comment about it being regulated down to 3.3V.