I have been testing my Netduino Go with a couple Hitec HS311 servos, a Parallex IR sensor and a Parallax Ultrasonic PING))) distance sensor. I am creating this application to read the pins of the ShieldBase on their own thread then update some values back on the initial thread. This works fine when I create the application and a thread that reads the IR input pin state then fires an event based on the state change of the pin. However, when I create a second thread for the PING)) sensor and run the application I get an exception thrown from the ShieldBase. With this I created a locking object that locks each read or write to the ShieldBase pins. This could have an adversed effect based on how long it takes to release the lock on the object. My question is, how does Netduino Go and the ShieldBase handles multi-threading when it comes to the read/write operation? It seems to me that only one pin can be read or written to at any given operation.
Again, I must admit this is my second programming code and so I ask for your patience and understanding. What I am trying to accomplished is to elimate the need to create a while loop that have all my reads and write operation and use event driven style to does different actions that can happen simultaneously.
Here is my code with thread locking:
namespace Multithread_Robot_Application { //Event signature public delegate void ObjectStateChangedHandler(object sender, object state); public class Program { object _Lock = new object(); public event ObjectStateChangedHandler IRValueChanged; //Private member variables static InputPort _IRSensor; static TristatePort _PingSensor; static PWM _Servo1; static PWM _Servo2; static ShieldBase _ShieldBase; /// <summary> /// Sets the degree to which servo 1 moves to. value 0.1 - 180 /// </summary> public double Servo1Degree { set { _Servo1.Duration = CalculatePulseFromDegrees(value); } } /// <summary> /// Sets the degree to which servo 2 moves to. value 0.1 - 180 /// </summary> public double Servo2Degree { set { _Servo2.Duration = CalculatePulseFromDegrees(value); } } public static void Main() { // write your code here Program _Application = new Program(); //Create the application instance _ShieldBase = new ShieldBase(GoSockets.Socket4); //Initialize shieldbase on Go Socket //Register for the Infared value changed _Application.IRValueChanged += (obj, state) => { Debug.Print("The interrupt value is: " + (bool)state); //Simulate a double swing door opening and closing if((bool)state) { //Doors open _Application.Servo1Degree = 180.0; _Application.Servo2Degree = 0.9; } else { //Doors closed _Application.Servo1Degree = 90.0; _Application.Servo2Degree = 90.0; } }; //Initial components connected to the shieldbase _Application.InitializeComponents(); //Starts a thread to listen to the ping sensor Thread t1 = new Thread(_Application.TriggerPingSensor); t1.Start(); //Starts a thread to listen to the IR sensor Thread t2 = new Thread(_Application.TriggerIRSensor); t2.Start(); Thread.Sleep(Timeout.Infinite); } /// <summary> /// Initializes all the components connected to the /// </summary> private void InitializeComponents() { _Servo1 = new PWM(_ShieldBase.PWMChannels.PWM_0, 20000, 750, PWM.ScaleFactor.Microseconds, false); _Servo2 = new PWM(_ShieldBase.PWMChannels.PWM_1, 20000, 1500, PWM.ScaleFactor.Microseconds, false); _Servo1.Start(); _Servo2.Start(); //Not sure why I could not initialize these on a digital pin _PingSensor = new TristatePort(_ShieldBase.Pins.GPIO_PIN_A1, false, true, Port.ResistorMode.Disabled); _IRSensor = new InputPort(_ShieldBase.Pins.GPIO_PIN_A0, false, Port.ResistorMode.Disabled); } /// <summary> /// Calculates the duration for the degree /// </summary> /// <param name="degree">double: 0.1 - 180.</param> /// <returns>uint representation</returns> private uint CalculatePulseFromDegrees(double degree) { uint neutralPulse = 1500; //This represent the neutral pulse time (in microsecond) of the servo typically when the servo is at 90 degrees. ; float factor = neutralPulse / 90; //This value represent pulse time when the servo is at 1 degree; uint outPulse = (uint)(factor * degree); return outPulse; } #region Public Methods /// <summary> /// This reads the state of the port on a Parallax IR Sensor. /// </summary> public void TriggerIRSensor() { bool oldValue = false; while(true) { bool ret; lock(_Lock) { ret = _IRSensor.Read(); } if(ret != oldValue) { oldValue = ret; Debug.Print(ret.ToString()); //Raise event if the Infared value changes if(IRValueChanged != null) IRValueChanged(this, ret); } Thread.Sleep(50); } } /// <summary> /// Uses a tristate port to send a pulse to the /// </summary> public void TriggerPingSensor() { while(true) { var val = false; lock(_Lock) { _PingSensor.Active = true; _PingSensor.Write(true); _PingSensor.Active = false; } int timeElapsed = 0; while(val != true && timeElapsed != 18) { timeElapsed++; lock(_Lock) { val = _PingSensor.Read(); } if(val) Debug.Print(val.ToString() + " @ " + timeElapsed); Thread.Sleep(1); } Thread.Sleep(2); Debug.Print(val.ToString()); } } #endregion } }
Thanks